OpenLB 1.8.1
Loading...
Searching...
No Matches
fieldReduction.hh
Go to the documentation of this file.
1/* This file is part of the OpenLB library
2 *
3 * Copyright (C) 2025 Yuji (Sam) Shimojima
4 * E-mail contact: info@openlb.net
5 * The most recent release of OpenLB can be downloaded at
6 * <http://www.openlb.net/>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public
19 * License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22*/
23
24#ifndef GPU_CUDA_RUSUCTION_OPERATORS_HH
25#define GPU_CUDA_RUSUCTION_OPERATORS_HH
26
27#include "fieldReduction.h"
28
29#include <cooperative_groups.h>
30#include <cooperative_groups/reduce.h>
31
32namespace olb {
33
34namespace gpu {
35
36namespace cuda {
37
38// ref : https://github.com/NVIDIA/cuda-samples/blob/master/Samples/2_Concepts_and_Techniques/reduction/reduction_kernel.cu
39// Utility class used to avoid linker errors with extern
40// unsized shared memory arrays with templated type
41template <class T>
43 __device__ inline operator T*()
44 {
45 extern __shared__ int __smem[];
46 return (T*)__smem;
47 }
48
49 __device__ inline operator const T*() const
50 {
51 extern __shared__ int __smem[];
52 return (T*)__smem;
53 }
54};
55
56// specialize for double to avoid unaligned memory
57// access compile errors
58template <>
59struct SharedMemory<double> {
60 __device__ inline operator double*()
61 {
62 extern __shared__ double __smem_d[];
63 return (double*)__smem_d;
64 }
65
66 __device__ inline operator const double*() const
67 {
68 extern __shared__ double __smem_d[];
69 return (double*)__smem_d;
70 }
71};
72} // namespace cuda
73} // namespace gpu
75template <typename T, typename DESCRIPTOR, typename REDUCTION_OP, typename CONDITION>
76void reduceKernelInBlock(thrust::device_ptr<const T> field, T* g_odata,
78{
79 namespace cg = cooperative_groups; // ref :: https://developer.nvidia.com/blog/cooperative-groups/
80 CONDITION mask {};
81 // Handle to thread block group
82 cg::thread_block cta = cg::this_thread_block();
83 T* sdata = gpu::cuda::SharedMemory<T>();
84
85 // perform first level of reduction,
86 // reading from global memory, writing to shared memory
87 unsigned int tid = threadIdx.x;
88 CellID iCell = blockIdx.x * (blockDim.x * 2) + threadIdx.x; //for 2element per 1thread
89 gpu::cuda::Cell<T, DESCRIPTOR> cell(lattice, iCell);
90
91 T mySum = (iCell < size) && mask(cell) && (cell.template getField<field::reduction::TAG_CORE>() == (int)1)
92 ? field[iCell]
93 : (T)0.0;
94
95 //for 2elements per 1thread
96 if (iCell + blockDim.x < size) {
97 gpu::cuda::Cell<T, DESCRIPTOR> cell2(lattice, iCell + blockDim.x);
98
99 if (mask(cell2) && (cell2.template getField<field::reduction::TAG_CORE>() == (int)1)) {
100 mySum = REDUCTION_OP {}(mySum, thrust::raw_pointer_cast(field)[iCell + blockDim.x]);
101 }
102 }
103 sdata[tid] = mySum;
104 cg::sync(cta);
105
106 // do reduction in shared mem
107 for (unsigned int stride = blockDim.x / 2; stride > 0; stride >>= 1) {
108 if (tid < stride) {
109 sdata[tid] = mySum = REDUCTION_OP {}(mySum, sdata[tid + stride]);
110 }
111
112 cg::sync(cta);
113 }
114
115 // write result for this block to global mem
116 if (tid == 0)
117 g_odata[blockIdx.x] = mySum;
118 return;
119}
120
121template <typename T, typename REDUCTION_OP>
122void reduceKernelInGrid(const T* partialSums, int partialCount, T* d_result) __global__
123{
124 namespace cg = cooperative_groups; // ref :: https://developer.nvidia.com/blog/cooperative-groups/
125 cg::thread_block cta = cg::this_thread_block();
126 T* sdata = gpu::cuda::SharedMemory<T>();
127
128 unsigned int tid = threadIdx.x;
129 unsigned int i = threadIdx.x + blockDim.x * blockIdx.x;
130
131 T mySum = 0.0;
132
133 for (int idx = i; idx < partialCount; idx += blockDim.x) {
134 mySum = REDUCTION_OP {}(mySum, partialSums[idx]);
135 }
136 sdata[tid] = mySum;
137 cg::sync(cta);
138
139 for (unsigned int stride = blockDim.x / 2; stride > 0; stride >>= 1) {
140 if (tid < stride) {
141 sdata[tid] = mySum = REDUCTION_OP{}(mySum, sdata[tid + stride]);
142 }
143 cg::sync(cta);
144 }
145
146 if (tid == 0) {
147 d_result[0] = sdata[0];
148 }
149 return;
150}
151
152template <typename T, typename DESCRIPTOR, typename REDUCTION_OP, typename CONDITION>
153void reductionFunctionDevice(thrust::device_ptr<const T> devPtr,
154 ConcreteBlockLattice<T, DESCRIPTOR, Platform::GPU_CUDA>& blockLattice, T* paramInDevice)
155{
156
158 const auto nCells = lattice.getNcells();
159 const auto block_size = 32;
160 const auto block_count = (nCells + (block_size * 2) - 1) / (block_size * 2); //for 2elements per 1thread
161
162 thrust::device_vector<T> d_partial(block_count, (T)0.0);
163
164 reduceKernelInBlock<T, DESCRIPTOR, REDUCTION_OP, CONDITION><<<block_count, block_size, block_size * sizeof(T)>>>(
165 devPtr, thrust::raw_pointer_cast(d_partial.data()), lattice, nCells);
167
168 reduceKernelInGrid<T, REDUCTION_OP><<<1, block_size, block_size * sizeof(T)>>>(
169 thrust::raw_pointer_cast(d_partial.data()), block_count, paramInDevice);
171
172 return;
173}
174
175template <typename FIELD, typename REDUCTION_OP, typename CONDITION>
176template <typename T, typename DESCRIPTOR>
177void BlockLatticeFieldReductionO<FIELD, REDUCTION_OP, CONDITION>::type<ConcreteBlockLattice<
179{
180 blockLattice.template getData<OperatorParameters<BlockLatticeFieldReductionO>>();
181}
182
183template <typename FIELD, typename REDUCTION_OP, typename CONDITION>
184template <typename T, typename DESCRIPTOR>
187{
188 auto& parameters = blockLattice.template getData<OperatorParameters<BlockLatticeFieldReductionO>>().parameters;
189 const auto& blockField = blockLattice.template getField<FIELD>();
190
192 parameters.template get<fields::array_of<FIELD>>(); //not auto. because of considering 1 dimensional field
193
194 /*note
195 -blockField[iD].deviceData(); is return row pointer T* in device.
196 -thrust::device_pointer_cast(elementField[iD])[0] is that thrust::device_pointer_cast is casting to thrust::device_ptr<T> from T* in device.
197 -thrust::device_pointer_cast(
198 blockField[iD].deviceData()); is that thrust::device_pointer_cast is casting to thrust::device_ptr<T> from T* in device.
199 -thrust::raw_pointer_cast(d_partial.data());, with thrust::device_vector<T> d_partial(block_count, (T)0.0);, is that thrust::raw_pointer_cast is casting to T* from thrust::raw_pointer_cast in device.
200
201 */
202
203 for (unsigned iD = 0; iD < blockField.d; iD++) {
204 cudaPointerAttributes attributes;
205 cudaError_t error = cudaPointerGetAttributes(&attributes, elementField[iD]);
206 if (error == cudaSuccess) {
207 if (attributes.devicePointer != nullptr) {
208 }
209 else if (attributes.hostPointer != nullptr) {
210 std::cout << "elementField is on the host" << std::endl;
211 }
212 }
213 else {
214 std::cerr << "Error in cudaPointerGetAttributes: " << cudaGetErrorString(error) << std::endl;
215 }
216 auto devPtr = thrust::device_pointer_cast(
217 blockField[iD].deviceData()); // thrust::device_pointer_cast is casting to thrust::device_ptr<T> from T*.
218 if (devPtr == nullptr) {
219 std::cerr << "Invalid device pointer at index " << iD << std::endl;
220 return;
221 }
222 reductionFunctionDevice<T, DESCRIPTOR, REDUCTION_OP, CONDITION>(devPtr, blockLattice, elementField[iD]);
223 }
224}
225
226} // namespace olb
227
228#endif //GPU_CUDA_RUSUCTION_OPERATORS_HH
Implementation of BlockLattice on a concrete PLATFORM.
Plain old scalar vector.
Device-side implementation of the Cell concept for post processors.
Definition context.hh:313
Device-side view of a block lattice.
Definition context.hh:244
std::size_t getNcells() const any_platform
Definition context.hh:104
void check()
Check errors.
Definition device.hh:48
Top level namespace for all of OpenLB.
void reduceKernelInBlock(thrust::device_ptr< const T > field, T *g_odata, gpu::cuda::DeviceBlockLattice< T, DESCRIPTOR > lattice, CellID size) __global__
ref: https://github.com/NVIDIA/cuda-samples/blob/master/Samples/2_Concepts_and_Techniques/reduction/r...
std::uint32_t CellID
Type for sequential block-local cell indices.
void reductionFunctionDevice(thrust::device_ptr< const T > devPtr, ConcreteBlockLattice< T, DESCRIPTOR, Platform::GPU_CUDA > &blockLattice, T *paramInDevice)
void reduceKernelInGrid(const T *partialSums, int partialCount, T *d_result) __global__
@ GPU_CUDA
Vector CPU (AVX2 / AVX-512 collision)
Plain wrapper for list of types.
Definition meta.h:276