diff --git a/mrmd/action/ThermodynamicForce.cpp b/mrmd/action/ThermodynamicForce.cpp index 1b1ece8..0e23a56 100644 --- a/mrmd/action/ThermodynamicForce.cpp +++ b/mrmd/action/ThermodynamicForce.cpp @@ -101,7 +101,7 @@ void ThermodynamicForce::update(const real_t& smoothingSigma, const real_t& smoo auto smoothedDensityGradient = data::gradient(smoothedDensityProfile, usePeriodicity_); smoothedDensityGradient.scale(forceFactor_); - force_ += smoothedDensityGradient; + force_ -= smoothedDensityGradient; // reset sampling data Kokkos::deep_copy(densityProfile_.data, 0_r); @@ -125,7 +125,7 @@ void ThermodynamicForce::apply(const data::Atoms& atoms) const { MRMD_DEVICE_ASSERT_LESS(atomsType(idx), forceHistogram.numHistograms); MRMD_DEVICE_ASSERT(!std::isnan(forceHistogram.data(bin, atomsType(idx)))); - atomsForce(idx, 0) -= forceHistogram.data(bin, atomsType(idx)); + atomsForce(idx, 0) += forceHistogram.data(bin, atomsType(idx)); } }; Kokkos::parallel_for("ThermodynamicForce::apply", policy, kernel); @@ -150,7 +150,7 @@ void ThermodynamicForce::apply(const data::Atoms& atoms, const weighting_functio { MRMD_DEVICE_ASSERT_LESS(atomsType(idx), forceHistogram.numHistograms); MRMD_DEVICE_ASSERT(!std::isnan(forceHistogram.data(bin, atomsType(idx)))); - atomsForce(idx, 0) -= forceHistogram.data(bin, atomsType(idx)); + atomsForce(idx, 0) += forceHistogram.data(bin, atomsType(idx)); } }; Kokkos::parallel_for("ThermodynamicForce::apply", policy, kernel); @@ -178,7 +178,7 @@ void ThermodynamicForce::apply(const data::Atoms& atoms, { MRMD_DEVICE_ASSERT_LESS(atomsType(idx), forceHistogram.numHistograms); MRMD_DEVICE_ASSERT(!std::isnan(forceHistogram.data(bin, atomsType(idx)))); - atomsForce(idx, 0) -= forceHistogram.data(bin, atomsType(idx)); + atomsForce(idx, 0) += forceHistogram.data(bin, atomsType(idx)); } }; Kokkos::parallel_for("ThermodynamicForce::apply", policy, kernel); diff --git a/mrmd/data/MultiHistogram.cpp b/mrmd/data/MultiHistogram.cpp index 8f8fd85..9b18474 100644 --- a/mrmd/data/MultiHistogram.cpp +++ b/mrmd/data/MultiHistogram.cpp @@ -35,35 +35,22 @@ ScalarView::HostMirror MultiHistogram::createGrid() const MultiHistogram& MultiHistogram::operator+=(const MultiHistogram& rhs) { - if (numBins != rhs.numBins) exit(EXIT_FAILURE); - assert(min == rhs.min); - assert(max == rhs.max); - - auto lhs = data; - auto policy = Kokkos::MDRangePolicy>({0, 0}, {numBins, numHistograms}); - auto kernel = KOKKOS_LAMBDA(const idx_t idx, const idx_t jdx) - { - lhs(idx, jdx) += rhs.data(idx, jdx); - }; - Kokkos::parallel_for("MultiHistogram::operator+=", policy, kernel); - Kokkos::fence(); + transform(*this, rhs, *this, bin_op::Add()); + return *this; +} +MultiHistogram& MultiHistogram::operator-=(const MultiHistogram& rhs) +{ + transform(*this, rhs, *this, bin_op::Sub()); + return *this; +} +MultiHistogram& MultiHistogram::operator*=(const MultiHistogram& rhs) +{ + transform(*this, rhs, *this, bin_op::Mul()); return *this; } - MultiHistogram& MultiHistogram::operator/=(const MultiHistogram& rhs) { - if (numBins != rhs.numBins) exit(EXIT_FAILURE); - assert(min == rhs.min); - assert(max == rhs.max); - - auto lhs = data; - auto policy = Kokkos::MDRangePolicy>({0, 0}, {numBins, numHistograms}); - auto kernel = KOKKOS_LAMBDA(const idx_t idx, const idx_t jdx) - { - lhs(idx, jdx) /= rhs.data(idx, jdx); - }; - Kokkos::parallel_for("MultiHistogram::operator/=", policy, kernel); - Kokkos::fence(); + transform(*this, rhs, *this, bin_op::Div()); return *this; } diff --git a/mrmd/data/MultiHistogram.hpp b/mrmd/data/MultiHistogram.hpp index 99c8910..469ec33 100644 --- a/mrmd/data/MultiHistogram.hpp +++ b/mrmd/data/MultiHistogram.hpp @@ -18,11 +18,13 @@ #include "assert/assert.hpp" #include "datatypes.hpp" +#include "functional.hpp" namespace mrmd { namespace data { + struct MultiHistogram { MultiHistogram(const std::string& label, @@ -81,7 +83,10 @@ struct MultiHistogram MultiView data; MultiHistogram& operator+=(const MultiHistogram& rhs); + MultiHistogram& operator-=(const MultiHistogram& rhs); + MultiHistogram& operator*=(const MultiHistogram& rhs); MultiHistogram& operator/=(const MultiHistogram& rhs); + void scale(const real_t& scalingFactor); void scale(const ScalarView& scalingFactor); void makeSymmetric(); @@ -123,5 +128,43 @@ data::MultiHistogram smoothen(data::MultiHistogram& input, const real_t& range, const bool periodic = false); +/** + * Applies a binary operation to corresponding elements of two input MultiHistograms and stores + * the result in an output MultiHistogram. + * + * @tparam BinaryOp The type of the binary operation to be applied. + * @param input1 The first input MultiHistogram. + * @param input2 The second input MultiHistogram. + * @param output The MultiHistogram where the result of the binary operation will be stored. + * @param binary_op The binary operation to apply to the elements of input1 and input2. + * + * @pre The dimensions (numBins and numHistograms) of input1, input2, and output must match. + */ + +template +void transform(const MultiHistogram& input1, + const MultiHistogram& input2, + MultiHistogram& output, + const BinaryOp& binary_op) +{ + MRMD_HOST_CHECK_EQUAL(input1.numHistograms, input2.numHistograms); + MRMD_HOST_CHECK_EQUAL(input1.numHistograms, output.numHistograms); + MRMD_HOST_CHECK_EQUAL(input1.numBins, input2.numBins); + MRMD_HOST_CHECK_EQUAL(input1.numBins, output.numBins); + + auto input1Data = input1.data; + auto input2Data = input2.data; + auto outputData = output.data; + + auto policy = + Kokkos::MDRangePolicy>({0, 0}, {input1.numBins, input1.numHistograms}); + auto kernel = KOKKOS_LAMBDA(const idx_t idx, const idx_t jdx) + { + outputData(idx, jdx) = binary_op(input1Data(idx, jdx), input2Data(idx, jdx)); + }; + Kokkos::parallel_for("MultiHistogram::transform", policy, kernel); + Kokkos::fence(); +} + } // namespace data } // namespace mrmd \ No newline at end of file diff --git a/mrmd/data/MultiHistogram.test.cpp b/mrmd/data/MultiHistogram.test.cpp index 0f11cff..240c7b3 100644 --- a/mrmd/data/MultiHistogram.test.cpp +++ b/mrmd/data/MultiHistogram.test.cpp @@ -98,6 +98,56 @@ TEST(MultiHistogram, op_plusequal) } } +TEST(MultiHistogram, op_minusequal) +{ + MultiHistogram histogram("histogram", 0_r, 10_r, 11, 2); + auto h_data = Kokkos::create_mirror_view(histogram.data); + h_data(5, 0) = 10_r; + h_data(5, 1) = 5_r; + Kokkos::deep_copy(histogram.data, h_data); + + MultiHistogram histogram2("histogram", 0_r, 10_r, 11, 2); + auto h_data2 = Kokkos::create_mirror_view(histogram2.data); + h_data2(5, 0) = 8_r; + h_data2(5, 1) = 1_r; + Kokkos::deep_copy(histogram2.data, h_data2); + + histogram -= histogram2; + + Kokkos::deep_copy(h_data, histogram.data); + + for (auto idx = 0; idx < 10; ++idx) + { + EXPECT_FLOAT_EQ(h_data(idx, 0), idx == 5 ? 2_r : 0_r); + EXPECT_FLOAT_EQ(h_data(idx, 1), idx == 5 ? 4_r : 0_r); + } +} + +TEST(MultiHistogram, op_mulequal) +{ + MultiHistogram histogram("histogram", 0_r, 10_r, 11, 2); + auto h_data = Kokkos::create_mirror_view(histogram.data); + h_data(5, 0) = 10_r; + h_data(5, 1) = 5_r; + Kokkos::deep_copy(histogram.data, h_data); + + MultiHistogram histogram2("histogram", 0_r, 10_r, 11, 2); + auto h_data2 = Kokkos::create_mirror_view(histogram2.data); + h_data2(5, 0) = 8_r; + h_data2(5, 1) = 2_r; + Kokkos::deep_copy(histogram2.data, h_data2); + + histogram *= histogram2; + + Kokkos::deep_copy(h_data, histogram.data); + + for (auto idx = 0; idx < 10; ++idx) + { + EXPECT_FLOAT_EQ(h_data(idx, 0), idx == 5 ? 80_r : 0_r); + EXPECT_FLOAT_EQ(h_data(idx, 1), idx == 5 ? 10_r : 0_r); + } +} + TEST(MultiHistogram, op_divequal) { MultiHistogram histogram("histogram", 0_r, 10_r, 11, 2); diff --git a/mrmd/functional.hpp b/mrmd/functional.hpp new file mode 100644 index 0000000..45f5c14 --- /dev/null +++ b/mrmd/functional.hpp @@ -0,0 +1,40 @@ +// Copyright 2024 Sebastian Eibl +// +// 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 +// +// https://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 + +#include "datatypes.hpp" + +namespace mrmd +{ +namespace bin_op +{ +struct Add +{ + KOKKOS_INLINE_FUNCTION real_t operator()(real_t x, real_t y) const { return x + y; } +}; +struct Sub +{ + KOKKOS_INLINE_FUNCTION real_t operator()(real_t x, real_t y) const { return x - y; } +}; +struct Mul +{ + KOKKOS_INLINE_FUNCTION real_t operator()(real_t x, real_t y) const { return x * y; } +}; +struct Div +{ + KOKKOS_INLINE_FUNCTION real_t operator()(real_t x, real_t y) const { return x / y; } +}; +} // namespace bin_op +} // namespace mrmd \ No newline at end of file