diff --git a/CMakeLists.txt b/CMakeLists.txt index 231dc9bab..38201d244 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -741,6 +741,9 @@ if(CUDA_FOUND) gpu_resources gpu_smart_pointers is_gpu_pointer + throw_macros_on_gpu + conditional_storage + blackoilfluidstategpu PROPERTIES LABELS ${gpu_label}) endif() diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index f4aa0dec2..89f80f4b4 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -483,6 +483,10 @@ if (HAVE_CUDA) ADD_CUDA_OR_HIP_FILE(TEST_SOURCE_FILES tests test_gpu_smart_pointers.cu) ADD_CUDA_OR_HIP_FILE(TEST_SOURCE_FILES tests test_gpu_resources.cu) ADD_CUDA_OR_HIP_FILE(TEST_SOURCE_FILES tests test_is_gpu_pointer.cpp) + ADD_CUDA_OR_HIP_FILE(TEST_SOURCE_FILES tests test_throw_macros_on_gpu.cu) + ADD_CUDA_OR_HIP_FILE(TEST_SOURCE_FILES tests test_blackoilfluidstategpu.cu) + ADD_CUDA_OR_HIP_FILE(TEST_SOURCE_FILES tests test_conditional_storage.cu) + # for loop providing the flag --expt-relaxed-constexpr to fix some cuda issues with constexpr if(NOT CONVERT_CUDA_TO_HIP) @@ -490,6 +494,7 @@ if (HAVE_CUDA) tests/gpuistl/test_gpu_ad.cu tests/gpuistl/test_gpu_linear_two_phase_material.cu tests/gpuistl/test_gpuPvt.cu + tests/gpuistl/test_blackoilfluidstategpu ) foreach(file ${CU_FILES_NEEDING_RELAXED_CONSTEXPR}) diff --git a/tests/gpuistl/test_blackoilfluidstategpu.cu b/tests/gpuistl/test_blackoilfluidstategpu.cu new file mode 100644 index 000000000..99b59ee62 --- /dev/null +++ b/tests/gpuistl/test_blackoilfluidstategpu.cu @@ -0,0 +1,186 @@ +/* + Copyright 2025 Equinor ASA + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM is distributed in the hope that 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 OPM. If not, see . +*/ +#include + +#define BOOST_TEST_MODULE TestBlackOilFluidStateGPU + +#include +#include +#include +#include +#include +#include +#include +namespace +{ + +template +struct DummyFluidSystem { + static constexpr auto numPhases = 3u; + static constexpr auto numComponents = 1; + static constexpr auto waterPhaseIdx = 0; + static constexpr auto oilPhaseIdx = 1; + static constexpr auto gasPhaseIdx = 2; + static constexpr auto waterCompIdx = 0; + static constexpr auto oilCompIdx = 0; + static constexpr auto gasCompIdx = 0; + + + static auto reservoirTemperature(int) { return ScalarT{ 0.0 }; } + static auto enthalpyEqualEnergy() { return true; } + static auto molarMass(int, int) { return ScalarT{ 0.0 }; } + template + static auto viscosity(const T&, int, int) { return ScalarT{ 0.0 }; } + + static auto convertRsToXoG(ScalarT, int) { return ScalarT{ 0.0 }; } + static auto convertRvToXg0(ScalarT, int) { return ScalarT{ 0.0 }; } + static auto convertXoGToRs(ScalarT, int) { return ScalarT{ 0.0 }; } + + template + static auto fugacityCoefficient(const T&, int, int, int) { return ScalarT{ 0.0 }; } + + static auto activeToCanonicalPhaseIdx(int) { return 0u; } + static auto canonicalToActivePhaseIdx(int) { return 0u; } + +}; + +template +struct DummyFluidSystemDynamic { + static constexpr auto numPhases = 3u; + static constexpr auto numComponents = 1; + static constexpr auto waterPhaseIdx = 0; + static constexpr auto oilPhaseIdx = 1; + static constexpr auto gasPhaseIdx = 2; + static constexpr auto waterCompIdx = 0; + static constexpr auto oilCompIdx = 0; + static constexpr auto gasCompIdx = 0; + + + OPM_HOST_DEVICE auto reservoirTemperature(int) const { return ScalarT{ 0.0 }; } + OPM_HOST_DEVICE auto enthalpyEqualEnergy() const { return true; } + OPM_HOST_DEVICE auto molarMass(int, int) const { return ScalarT{ 0.0 }; } + template + OPM_HOST_DEVICE auto viscosity(const T&, int, int) const { return ScalarT{ someVariable }; } + + OPM_HOST_DEVICE auto convertRsToXoG(ScalarT, int) const { return ScalarT{ 0.0 }; } + OPM_HOST_DEVICE auto convertRvToXg0(ScalarT, int) const { return ScalarT{ 0.0 }; } + OPM_HOST_DEVICE auto convertXoGToRs(ScalarT, int) const { return ScalarT{ 0.0 }; } + + template + OPM_HOST_DEVICE auto fugacityCoefficient(const T&, int, int, int) const { return ScalarT{ 0.0 }; } + + OPM_HOST_DEVICE auto activeToCanonicalPhaseIdx(int) const { return 0u; } + OPM_HOST_DEVICE auto canonicalToActivePhaseIdx(int) const { return 0u; } + + + double someVariable = 43.2; + +}; + + +template +__global__ void kernelCreatingBlackoilFluidState() { + FluidState state; +} + +template +__global__ void kernelCreatingBlackoilFluidStateDynamic() { + FluidSystem system; + FluidState state(system); +} + +template +__global__ void kernelSetAndGetTotalSaturation(double saturation, double* readSaturation) { + FluidState state; + state.setTotalSaturation(saturation); + *readSaturation = state.totalSaturation(); +} + +template +__global__ void getPressure(Opm::gpuistl::PointerView input, std::array* output) { + for (int i = 0; i < 3; ++i) { + (*output)[i] = input->pressure(i); + } +} + +template +__global__ void getViscosity(FluidSystem input, double* output) { + FluidState state(input); + *output = state.viscosity(0); +} + +} // namespace + +using ScalarT = double; +using FluidState = Opm::BlackOilFluidState>; +using FluidStateDynamic = Opm::BlackOilFluidState>; + + +BOOST_AUTO_TEST_CASE(TestCreation) +{ + + kernelCreatingBlackoilFluidState<<<1, 1>>>(); + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); +} + +BOOST_AUTO_TEST_CASE(TestSaturation) +{ + const double saturation = 0.5; + auto saturationRead = Opm::gpuistl::make_gpu_unique_ptr(0.0); + kernelSetAndGetTotalSaturation<<<1, 1>>>(saturation, saturationRead.get()); + auto saturationFromGPU = Opm::gpuistl::copyFromGPU(saturationRead); + BOOST_CHECK_EQUAL(saturationFromGPU, saturation); + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); +} + +BOOST_AUTO_TEST_CASE(TestPressure) +{ + FluidState state; + state.setPressure(0, 1.0); + state.setPressure(1, 2.0); + state.setPressure(2, 3.0); + + auto stateGPU = Opm::gpuistl::make_gpu_unique_ptr(state); + auto output = Opm::gpuistl::make_gpu_unique_ptr>(); + + getPressure<<<1, 1>>>(Opm::gpuistl::make_view(stateGPU), output.get()); + auto outputCPU = Opm::gpuistl::copyFromGPU(output); + BOOST_CHECK_EQUAL(1.0, outputCPU[0]); + BOOST_CHECK_EQUAL(2.0, outputCPU[1]); + BOOST_CHECK_EQUAL(3.0, outputCPU[2]); +} + +BOOST_AUTO_TEST_CASE(TestDynamicCreation) +{ + kernelCreatingBlackoilFluidStateDynamic><<<1, 1>>>(); + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); +} + +BOOST_AUTO_TEST_CASE(TestPassByValueToGPUDynamic) +{ + DummyFluidSystemDynamic system; + + system.someVariable = 1234; + auto output = Opm::gpuistl::make_gpu_unique_ptr(); + getViscosity<<<1, 1>>>(system, output.get()); + + auto outputCPU = Opm::gpuistl::copyFromGPU(output); + BOOST_CHECK_EQUAL(1234, outputCPU); + +} \ No newline at end of file diff --git a/tests/gpuistl/test_conditional_storage.cu b/tests/gpuistl/test_conditional_storage.cu new file mode 100644 index 000000000..af0dead42 --- /dev/null +++ b/tests/gpuistl/test_conditional_storage.cu @@ -0,0 +1,123 @@ +/* + Copyright 2025 Equinor ASA + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM is distributed in the hope that 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 OPM. If not, see . +*/ +#include + +#define BOOST_TEST_MODULE TestConditionalStorageGPU + +#include +#include +#include +#include +#include +#include + +namespace { + +template +__global__ void createCondtionalStorage() { + // just make sure we can run the constructor + Opm::ConditionalStorage someStorage; + + + Opm::ConditionalStorage other = someStorage; + + other = someStorage; +} +template +__global__ void testEnabledStorage(Opm::ConditionalStorage storage, T* output) { + output[0] = *storage; +} + +template +__global__ void testEnabledStorageArrow(Opm::ConditionalStorage storage, S* output) { + output[0] = storage->someFunc(); +} + +template +__global__ void testEnabledStorage(Opm::ConditionalStorage* storage, T* output) { + output[0] = **storage; +} + +template +__global__ void testEnabledStorageArrow(Opm::ConditionalStorage* storage, S* output) { + output[0] = (*storage)->someFunc(); +} + +struct SomeStruct { + OPM_HOST_DEVICE int someFunc() { + return 123; + } + +}; + +} // namespace + +BOOST_AUTO_TEST_CASE(TestRunConstructor) +{ + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + createCondtionalStorage<<<1, 1>>>(); + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + createCondtionalStorage<<<1, 1>>>(); + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + +} + +BOOST_AUTO_TEST_CASE(TestEnabledStoragePointer) +{ + using namespace Opm; + using CS = ConditionalStorage; + auto storage = Opm::gpuistl::make_gpu_unique_ptr(CS(32.2)); + auto numberFromGPU = Opm::gpuistl::make_gpu_unique_ptr(0.0); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + testEnabledStorage<<<1, 1>>>(storage.get(), numberFromGPU.get()); + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + auto number = Opm::gpuistl::copyFromGPU(numberFromGPU); + BOOST_CHECK_EQUAL(32.2, number); + + auto numberFromGPUFromCall = Opm::gpuistl::make_gpu_unique_ptr(0); + + auto storageSomeStruct = Opm::gpuistl::make_gpu_unique_ptr>(ConditionalStorage()); + testEnabledStorageArrow<<<1, 1>>>(storageSomeStruct.get(), numberFromGPUFromCall.get()); + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + auto numberFromCall = Opm::gpuistl::copyFromGPU(numberFromGPUFromCall); + BOOST_CHECK_EQUAL(123, numberFromCall); +} +BOOST_AUTO_TEST_CASE(TestEnabledStorageCopy) +{ + using namespace Opm; + using CS = ConditionalStorage; + auto storage = CS(32.2); + auto numberFromGPU = Opm::gpuistl::make_gpu_unique_ptr(0.0); + testEnabledStorage<<<1, 1>>>(storage, numberFromGPU.get()); + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + auto number = Opm::gpuistl::copyFromGPU(numberFromGPU); + BOOST_CHECK_EQUAL(32.2, number); + + auto numberFromGPUFromCall = Opm::gpuistl::make_gpu_unique_ptr(0); + + auto storageSomeStruct = ConditionalStorage(); + testEnabledStorageArrow<<<1, 1>>>(storageSomeStruct, numberFromGPUFromCall.get()); + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + auto numberFromCall = Opm::gpuistl::copyFromGPU(numberFromGPUFromCall); + BOOST_CHECK_EQUAL(123, numberFromCall); +} \ No newline at end of file diff --git a/tests/gpuistl/test_throw_macros_on_gpu.cu b/tests/gpuistl/test_throw_macros_on_gpu.cu new file mode 100644 index 000000000..800ed6149 --- /dev/null +++ b/tests/gpuistl/test_throw_macros_on_gpu.cu @@ -0,0 +1,109 @@ +/* + Copyright 2025 Equinor ASA + + This file is part of the Open Porous Media project (OPM). + OPM 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. + OPM is distributed in the hope that 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 OPM. If not, see . +*/ +#include +#include +#include + +#define BOOST_TEST_MODULE TestThrowMacrosOnGPU + +#include +#include +#include +#include +#include + +namespace { +__global__ void codeThatContainsMacros(bool call) { + if (call) { + OPM_THROW(std::logic_error, "Something went wrong"); + OPM_THROW_NOLOG(std::logic_error, "Something went wrong"); + OPM_THROW_PROBLEM(std::logic_error, "Something went wrong"); + } + OPM_ERROR_IF(!call, "Something went horribly wrong"); +} + +// TODO: Check if this is better on HIP +#if 0 // I am leaving this here to show that this is not possible due to limitations in CUDA + // the assert will indeed cause an error, but the CUDA context will be broken for + // the rest of the lifetime of the process, see + // https://forums.developer.nvidia.com/t/how-to-clear-cuda-errors/296393/5 +__global__ void checkThrow() { + OPM_THROW(std::logic_error, "Something went wrong"); +} + +__global__ void checkThrowNoLog() { + OPM_THROW_NOLOG(std::logic_error, "Something went wrong"); +} + +__global__ void checkThrowProblem() { + OPM_THROW_PROBLEM(std::logic_error, "Something went wrong"); +} + +__global__ void checkErrorIf() { + OPM_ERROR_IF(true, "Something went horribly wrong"); +} +#endif +} + +BOOST_AUTO_TEST_CASE(TestKernel) +{ + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + codeThatContainsMacros<<<1, 1>>>(false); + OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + + #if 0 // I am leaving this here to show that this is not possible due to limitations in CUDA + // the assert will indeed cause an error, but the CUDA context will be broken for + // the rest of the lifetime of the process, see + // https://forums.developer.nvidia.com/t/how-to-clear-cuda-errors/296393/5 + codeThatContainsMacros<<<1, 1>>>(true); + // Make sure this actually throws + BOOST_CHECK_THROW(OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()), std::runtime_error); + OPM_GPU_SAFE_CALL(cudaDeviceReset()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + + checkThrow<<<1, 1>>>(); + BOOST_CHECK_THROW(OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()), std::runtime_error); + OPM_GPU_SAFE_CALL(cudaDeviceReset()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + + checkThrowNoLog<<<1, 1>>>(); + BOOST_CHECK_THROW(OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()), std::runtime_error); + OPM_GPU_SAFE_CALL(cudaDeviceReset()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + + checkThrowProblem<<<1, 1>>>(); + BOOST_CHECK_THROW(OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()), std::runtime_error); + OPM_GPU_SAFE_CALL(cudaDeviceReset()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + + checkErrorIf<<<1, 1>>>(); + BOOST_CHECK_THROW(OPM_GPU_SAFE_CALL(cudaDeviceSynchronize()), std::runtime_error); + OPM_GPU_SAFE_CALL(cudaDeviceReset()); + OPM_GPU_SAFE_CALL(cudaGetLastError()); + #endif +} + +BOOST_AUTO_TEST_CASE(TestOutsideKernel) +{ + // This is to make sure that the macros work outside of kernels but inside a .cu file + // ie. inside a file compiled by nvcc/hipcc. + BOOST_CHECK_THROW(OPM_THROW(std::runtime_error, "THROW"), std::runtime_error); + BOOST_CHECK_THROW(OPM_THROW_NOLOG(std::runtime_error, "THROW_NOLOG"), std::runtime_error); + BOOST_CHECK_THROW(OPM_THROW_PROBLEM(std::runtime_error, "THROW_PROBLEM"), std::runtime_error); + BOOST_CHECK_THROW(OPM_ERROR_IF(true, "ERROR_IF"), std::logic_error); +}