improve gpu computation in HHD computation
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
@@ -19,11 +19,11 @@ def main():
|
|||||||
|
|
||||||
# 步骤1:创建传感器对象 (必要步骤),config_name为配置文件名(内置配置文件名)或路径(自定义配置文件路径)
|
# 步骤1:创建传感器对象 (必要步骤),config_name为配置文件名(内置配置文件名)或路径(自定义配置文件路径)
|
||||||
# 方式1: 从视频文件读取
|
# 方式1: 从视频文件读取
|
||||||
# sensor = orisys.Sensor("s1.mp4", config_name="prototype", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True)
|
sensor = orisys.Sensor("s1.mp4", config_name="prototype", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True)
|
||||||
|
|
||||||
|
|
||||||
# 方式2: 从摄像头读取(取消注释使用)
|
# 方式2: 从摄像头读取(取消注释使用)
|
||||||
sensor = orisys.Sensor(0, config_name="./config/lens_test.json", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True)
|
# sensor = orisys.Sensor(0, config_name="./config/lens_test.json", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True)
|
||||||
|
|
||||||
print("\n按 'q' 退出程序,按 'r' 重置光流追踪器")
|
print("\n按 'q' 退出程序,按 'r' 重置光流追踪器")
|
||||||
|
|
||||||
@@ -33,8 +33,8 @@ def main():
|
|||||||
# 步骤 2.1: 获取并拼接图像 (必要步骤)
|
# 步骤 2.1: 获取并拼接图像 (必要步骤)
|
||||||
sensor.get_img()
|
sensor.get_img()
|
||||||
print("img_size_raw:", sensor.img_size_raw)
|
print("img_size_raw:", sensor.img_size_raw)
|
||||||
if sensor.detect_motion(threshold=2):
|
# if sensor.detect_motion(threshold=2):
|
||||||
print("motion detected")
|
# print("motion detected")
|
||||||
# 步骤 2.2: 计算形变场 (必要步骤)
|
# 步骤 2.2: 计算形变场 (必要步骤)
|
||||||
sensor.compute_deformation()
|
sensor.compute_deformation()
|
||||||
# 步骤 2.3: 计算触碰点
|
# 步骤 2.3: 计算触碰点
|
||||||
|
|||||||
+200
-141
@@ -104,6 +104,10 @@ class nHHD(object):
|
|||||||
|
|
||||||
self.psolver.prepare(False)
|
self.psolver.prepare(False)
|
||||||
|
|
||||||
|
# Example pattern for stream usage:
|
||||||
|
self.stream1 = cp.cuda.Stream()
|
||||||
|
self.stream2 = cp.cuda.Stream()
|
||||||
|
|
||||||
# create the 3-component decomposition
|
# create the 3-component decomposition
|
||||||
def decompose(self, vfield, verbose=0):
|
def decompose(self, vfield, verbose=0):
|
||||||
|
|
||||||
@@ -228,20 +232,21 @@ class nHHD(object):
|
|||||||
dudy, dudx = cp.gradient(vfield_gpu[:,:,0], dx0, dx1)
|
dudy, dudx = cp.gradient(vfield_gpu[:,:,0], dx0, dx1)
|
||||||
dvdy, dvdx = cp.gradient(vfield_gpu[:,:,1], dx0, dx1)
|
dvdy, dvdx = cp.gradient(vfield_gpu[:,:,1], dx0, dx1)
|
||||||
|
|
||||||
div_gpu = dudx + dvdy
|
div_gpu = cp.add(dudx, dvdy)
|
||||||
curlw_gpu = dvdx - dudy
|
curlw_gpu = cp.subtract(dvdx, dudy)
|
||||||
|
|
||||||
if verbose > 0:
|
if verbose > 0:
|
||||||
print('div =', div_gpu.shape, float(div_gpu.min()), float(div_gpu.max()))
|
print('div =', div_gpu.shape, float(div_gpu.min()), float(div_gpu.max()))
|
||||||
print('curlw =', curlw_gpu.shape, float(curlw_gpu.min()), float(curlw_gpu.max()))
|
print('curlw =', curlw_gpu.shape, float(curlw_gpu.min()), float(curlw_gpu.max()))
|
||||||
|
|
||||||
# Compute potentials on GPU (solve_gpu returns numpy, so we convert back)
|
|
||||||
nD_cpu = self.psolver.solve_gpu(cp.asnumpy(div_gpu), verbose > 0)
|
|
||||||
nRu_cpu = self.psolver.solve_gpu(cp.asnumpy(curlw_gpu), verbose > 0)
|
|
||||||
|
|
||||||
# Keep potentials on GPU
|
# Compute potentials on GPU (solve_gpu returns numpy, so we convert back)
|
||||||
nD_gpu = cp.asarray(nD_cpu, dtype=cp.float32)
|
with self.stream1:
|
||||||
nRu_gpu = cp.asarray(nRu_cpu, dtype=cp.float32)
|
nD_gpu = self.psolver._solve_gpu_internal(div_gpu)
|
||||||
|
with self.stream2:
|
||||||
|
nRu_gpu = self.psolver._solve_gpu_internal(curlw_gpu)
|
||||||
|
self.stream1.synchronize()
|
||||||
|
self.stream2.synchronize()
|
||||||
|
|
||||||
if verbose > 0:
|
if verbose > 0:
|
||||||
print('nD =', nD_gpu.shape, float(nD_gpu.min()), float(nD_gpu.max()))
|
print('nD =', nD_gpu.shape, float(nD_gpu.min()), float(nD_gpu.max()))
|
||||||
@@ -256,15 +261,15 @@ class nHHD(object):
|
|||||||
|
|
||||||
# Compute rotated gradient on GPU
|
# Compute rotated gradient on GPU
|
||||||
rdy, rdx = cp.gradient(nRu_gpu, dx0, dx1)
|
rdy, rdx = cp.gradient(nRu_gpu, dx0, dx1)
|
||||||
rdy *= -1.0
|
rdy = cp.multiply(rdy, -1.0)
|
||||||
r_gpu = cp.stack((rdy, rdx), axis=-1)
|
r_gpu = cp.stack((rdy, rdx), axis=-1)
|
||||||
|
|
||||||
if verbose > 0:
|
if verbose > 0:
|
||||||
print('r =', r_gpu.shape, float(r_gpu.min()), float(r_gpu.max()))
|
print('r =', r_gpu.shape, float(r_gpu.min()), float(r_gpu.max()))
|
||||||
|
|
||||||
# Compute h on GPU
|
# Compute h on GPU
|
||||||
h_gpu = d_gpu + r_gpu
|
h_gpu = cp.add(d_gpu, r_gpu)
|
||||||
h_gpu = vfield_gpu - h_gpu
|
h_gpu = cp.subtract(vfield_gpu, h_gpu)
|
||||||
|
|
||||||
if verbose > 0:
|
if verbose > 0:
|
||||||
print('h =', h_gpu.shape, float(h_gpu.min()), float(h_gpu.max()))
|
print('h =', h_gpu.shape, float(h_gpu.min()), float(h_gpu.max()))
|
||||||
@@ -276,11 +281,11 @@ class nHHD(object):
|
|||||||
self._nRu_gpu = nRu_gpu
|
self._nRu_gpu = nRu_gpu
|
||||||
|
|
||||||
# Also store CPU copies for compatibility
|
# Also store CPU copies for compatibility
|
||||||
self.d = cp.asnumpy(d_gpu)
|
# self.d = cp.asnumpy(d_gpu)
|
||||||
self.h = cp.asnumpy(h_gpu)
|
# self.h = cp.asnumpy(h_gpu)
|
||||||
self.r = cp.asnumpy(r_gpu)
|
# self.r = cp.asnumpy(r_gpu)
|
||||||
self.nRu = cp.asnumpy(nRu_gpu)
|
# self.nRu = cp.asnumpy(nRu_gpu)
|
||||||
self.nD = nD_cpu
|
# self.nD = cp.asnumpy(nD_gpu)
|
||||||
|
|
||||||
return (d_gpu, h_gpu, r_gpu, nRu_gpu)
|
return (d_gpu, h_gpu, r_gpu, nRu_gpu)
|
||||||
|
|
||||||
@@ -375,13 +380,18 @@ class nHHD(object):
|
|||||||
return self.get_force(vfield, verbose)
|
return self.get_force(vfield, verbose)
|
||||||
|
|
||||||
# Decompose the vector field
|
# Decompose the vector field
|
||||||
self.decompose(vfield, verbose)
|
self.decompose_gpu(vfield, verbose)
|
||||||
|
|
||||||
# Keep data on GPU - avoid transfers by computing directly
|
# Keep data on GPU - avoid transfers by computing directly
|
||||||
d_gpu = cp.asarray(self.d, dtype=cp.float32)
|
# d_gpu = cp.asarray(self.d, dtype=cp.float32)
|
||||||
h_gpu = cp.asarray(self.h, dtype=cp.float32)
|
# h_gpu = cp.asarray(self.h, dtype=cp.float32)
|
||||||
r_gpu = cp.asarray(self.r, dtype=cp.float32)
|
# r_gpu = cp.asarray(self.r, dtype=cp.float32)
|
||||||
nRu_gpu = cp.asarray(self.nRu, dtype=cp.float32)
|
# nRu_gpu = cp.asarray(self.nRu, dtype=cp.float32)
|
||||||
|
|
||||||
|
d_gpu = self._d_gpu
|
||||||
|
h_gpu = self._h_gpu
|
||||||
|
r_gpu = self._r_gpu
|
||||||
|
nRu_gpu = self._nRu_gpu
|
||||||
|
|
||||||
# Use reduction kernels for better performance
|
# Use reduction kernels for better performance
|
||||||
# Compute Sn using L1 norm (sum of absolute values)
|
# Compute Sn using L1 norm (sum of absolute values)
|
||||||
@@ -410,10 +420,17 @@ class nHHD(object):
|
|||||||
r_y = r_gpu[:, :, 1]
|
r_y = r_gpu[:, :, 1]
|
||||||
|
|
||||||
# Compute torque with fused sum
|
# Compute torque with fused sum
|
||||||
Stor = float(cp.sum(
|
# Stor = float(cp.sum(
|
||||||
(mgrid_0 - max_0) * r_y - (mgrid_1 - max_1) * r_x +
|
# (mgrid_0 - max_0) * r_y - (mgrid_1 - max_1) * r_x +
|
||||||
(mgrid_0 - min_0) * r_y - (mgrid_1 - min_1) * r_x
|
# (mgrid_0 - min_0) * r_y - (mgrid_1 - min_1) * r_x
|
||||||
))
|
# ))
|
||||||
|
# Replace lines 413-416:
|
||||||
|
if KERNEL_AVAILABLE and _torque_kernel is not None:
|
||||||
|
torque = _torque_kernel(
|
||||||
|
r_x, r_y, mgrid_0, mgrid_1,
|
||||||
|
float(max_0), float(max_1), float(min_0), float(min_1)
|
||||||
|
)
|
||||||
|
Stor = float(cp.sum(torque))
|
||||||
|
|
||||||
self.Sn, self.Stx, self.Sty, self.Stor = Sn, Stx, Sty, Stor
|
self.Sn, self.Stx, self.Sty, self.Stor = Sn, Stx, Sty, Stor
|
||||||
output = numpy.array([Sn, Stx, Sty, Stor], dtype=numpy.float32)
|
output = numpy.array([Sn, Stx, Sty, Stor], dtype=numpy.float32)
|
||||||
@@ -430,12 +447,14 @@ class nHHD(object):
|
|||||||
cp.get_default_memory_pool().free_all_blocks()
|
cp.get_default_memory_pool().free_all_blocks()
|
||||||
|
|
||||||
def get_shear(self):
|
def get_shear(self):
|
||||||
|
if not CUPY_AVAILABLE:
|
||||||
return self.h
|
return self.h
|
||||||
|
return cp.asnumpy(self._h_gpu)
|
||||||
|
|
||||||
def get_normal(self):
|
def get_normal(self):
|
||||||
|
if not CUPY_AVAILABLE:
|
||||||
return self.d
|
return self.d
|
||||||
|
return cp.asnumpy(self._d_gpu)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -574,72 +593,72 @@ class StructuredGrid(object):
|
|||||||
|
|
||||||
return (dudx, dwdy, dudz, dvdx)
|
return (dudx, dwdy, dudz, dvdx)
|
||||||
|
|
||||||
def divcurl_gpu(self, vfield, verbose=False):
|
# def divcurl_gpu(self, vfield, verbose=False):
|
||||||
"""
|
# """
|
||||||
GPU-accelerated computation of divergence and curl of vector field.
|
# GPU-accelerated computation of divergence and curl of vector field.
|
||||||
Uses CuPy for faster gradient computation on GPU.
|
# Uses CuPy for faster gradient computation on GPU.
|
||||||
"""
|
# """
|
||||||
if not CUPY_AVAILABLE:
|
# if not CUPY_AVAILABLE:
|
||||||
if verbose:
|
# if verbose:
|
||||||
print('CuPy not available, falling back to CPU')
|
# print('CuPy not available, falling back to CPU')
|
||||||
return self.divcurl(vfield, verbose)
|
# return self.divcurl(vfield, verbose)
|
||||||
|
|
||||||
if (vfield.shape[-1] != self.dim) or (vfield.shape[0:self.dim] != self.dims):
|
# if (vfield.shape[-1] != self.dim) or (vfield.shape[0:self.dim] != self.dims):
|
||||||
raise ValueError("Dimensions of vector field should match that of the grid")
|
# raise ValueError("Dimensions of vector field should match that of the grid")
|
||||||
|
|
||||||
if verbose:
|
# if verbose:
|
||||||
print(' Computing div and curl on GPU...', end ='')
|
# print(' Computing div and curl on GPU...', end ='')
|
||||||
sys.stdout.flush()
|
# sys.stdout.flush()
|
||||||
mtimer = Timer()
|
# mtimer = Timer()
|
||||||
|
|
||||||
if self.dim == 2:
|
# if self.dim == 2:
|
||||||
# Transfer to GPU
|
# # Transfer to GPU
|
||||||
vfield_gpu = cp.asarray(vfield, dtype=cp.float32)
|
# vfield_gpu = cp.asarray(vfield, dtype=cp.float32)
|
||||||
|
|
||||||
# Compute gradients on GPU
|
# # Compute gradients on GPU
|
||||||
dudy, dudx = cp.gradient(vfield_gpu[:,:,0], self.dx[0], self.dx[1])
|
# dudy, dudx = cp.gradient(vfield_gpu[:,:,0], self.dx[0], self.dx[1])
|
||||||
dvdy, dvdx = cp.gradient(vfield_gpu[:,:,1], self.dx[0], self.dx[1])
|
# dvdy, dvdx = cp.gradient(vfield_gpu[:,:,1], self.dx[0], self.dx[1])
|
||||||
|
|
||||||
# Compute div and curl on GPU
|
# # Compute div and curl on GPU
|
||||||
div_gpu = dudx + dvdy
|
# div_gpu = dudx + dvdy
|
||||||
curl_gpu = dvdx - dudy
|
# curl_gpu = dvdx - dudy
|
||||||
|
|
||||||
# Transfer back to CPU
|
# # Transfer back to CPU
|
||||||
div = cp.asnumpy(div_gpu)
|
# div = cp.asnumpy(div_gpu)
|
||||||
curl = cp.asnumpy(curl_gpu)
|
# curl = cp.asnumpy(curl_gpu)
|
||||||
|
|
||||||
if verbose:
|
# if verbose:
|
||||||
print(' Done!', end ='')
|
# print(' Done!', end ='')
|
||||||
mtimer.end()
|
# mtimer.end()
|
||||||
|
|
||||||
return (div, curl)
|
# return (div, curl)
|
||||||
|
|
||||||
elif self.dim == 3:
|
# elif self.dim == 3:
|
||||||
# Transfer to GPU
|
# # Transfer to GPU
|
||||||
vfield_gpu = cp.asarray(vfield, dtype=cp.float32)
|
# vfield_gpu = cp.asarray(vfield, dtype=cp.float32)
|
||||||
|
|
||||||
# Compute gradients on GPU
|
# # Compute gradients on GPU
|
||||||
dudz, dudy, dudx = cp.gradient(vfield_gpu[:,:,:,0], self.dx[0], self.dx[1], self.dx[2])
|
# dudz, dudy, dudx = cp.gradient(vfield_gpu[:,:,:,0], self.dx[0], self.dx[1], self.dx[2])
|
||||||
dvdz, dvdy, dvdx = cp.gradient(vfield_gpu[:,:,:,1], self.dx[0], self.dx[1], self.dx[2])
|
# dvdz, dvdy, dvdx = cp.gradient(vfield_gpu[:,:,:,1], self.dx[0], self.dx[1], self.dx[2])
|
||||||
dwdz, dwdy, dwdx = cp.gradient(vfield_gpu[:,:,:,2], self.dx[0], self.dx[1], self.dx[2])
|
# dwdz, dwdy, dwdx = cp.gradient(vfield_gpu[:,:,:,2], self.dx[0], self.dx[1], self.dx[2])
|
||||||
|
|
||||||
# Compute div and curl components on GPU
|
# # Compute div and curl components on GPU
|
||||||
div_gpu = dudx + dvdy + dwdz
|
# div_gpu = dudx + dvdy + dwdz
|
||||||
curlu_gpu = dwdy - dvdz
|
# curlu_gpu = dwdy - dvdz
|
||||||
curlv_gpu = dudz - dwdx
|
# curlv_gpu = dudz - dwdx
|
||||||
curlw_gpu = dvdx - dudy
|
# curlw_gpu = dvdx - dudy
|
||||||
|
|
||||||
# Transfer back to CPU
|
# # Transfer back to CPU
|
||||||
div = cp.asnumpy(div_gpu)
|
# div = cp.asnumpy(div_gpu)
|
||||||
curlu = cp.asnumpy(curlu_gpu)
|
# curlu = cp.asnumpy(curlu_gpu)
|
||||||
curlv = cp.asnumpy(curlv_gpu)
|
# curlv = cp.asnumpy(curlv_gpu)
|
||||||
curlw = cp.asnumpy(curlw_gpu)
|
# curlw = cp.asnumpy(curlw_gpu)
|
||||||
|
|
||||||
if verbose:
|
# if verbose:
|
||||||
print(' Done!', end ='')
|
# print(' Done!', end ='')
|
||||||
mtimer.end()
|
# mtimer.end()
|
||||||
|
|
||||||
return (div, curlu, curlv, curlw)
|
# return (div, curlu, curlv, curlw)
|
||||||
|
|
||||||
def curl3D(self, vfield, verbose=False):
|
def curl3D(self, vfield, verbose=False):
|
||||||
|
|
||||||
@@ -1187,7 +1206,7 @@ class PoissonSolver(object):
|
|||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# GPU-accelerated solve using CuPy
|
# GPU-accelerated solve using CuPy
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
def solve_gpu(self, f, verbose=False):
|
def solve_gpu(self, f, verbose=False, return_gpu=False):
|
||||||
"""
|
"""
|
||||||
GPU-accelerated Poisson solver using CuPy.
|
GPU-accelerated Poisson solver using CuPy.
|
||||||
Falls back to CPU if CuPy is not available.
|
Falls back to CPU if CuPy is not available.
|
||||||
@@ -1241,110 +1260,150 @@ class PoissonSolver(object):
|
|||||||
# Scale by grid spacing (on GPU)
|
# Scale by grid spacing (on GPU)
|
||||||
p_gpu *= self._gpu_cache['gdx_prod']
|
p_gpu *= self._gpu_cache['gdx_prod']
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print('Poisson solver (GPU) finished')
|
||||||
|
gtimer.end()
|
||||||
|
|
||||||
# Transfer result back to CPU
|
# Transfer result back to CPU
|
||||||
p = cp.asnumpy(p_gpu)
|
if return_gpu:
|
||||||
|
return p_gpu # Return CuPy array
|
||||||
|
return cp.asnumpy(p_gpu)
|
||||||
|
# p = cp.asnumpy(p_gpu)
|
||||||
|
|
||||||
elif self.stype == 'S':
|
elif self.stype == 'S':
|
||||||
raise SyntaxError('Spatial solver not suggested for structured grids')
|
raise SyntaxError('Spatial solver not suggested for structured grids')
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# unstructured points - not optimized for GPU
|
# unstructured points - not optimized for GPU
|
||||||
elif self.ptype == 'P':
|
# elif self.ptype == 'P':
|
||||||
if verbose:
|
# if verbose:
|
||||||
print(' - GPU solver not optimized for unstructured points, using CPU')
|
# print(' - GPU solver not optimized for unstructured points, using CPU')
|
||||||
return self.solve(f, verbose)
|
# return self.solve(f, verbose)
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
if verbose:
|
|
||||||
print('Poisson solver (GPU) finished')
|
|
||||||
gtimer.end()
|
|
||||||
|
|
||||||
return p
|
# return p
|
||||||
|
|
||||||
def solve_gpu_batch(self, f_list, verbose=False):
|
def _solve_gpu_internal(self, f_gpu):
|
||||||
|
"""Internal GPU solve that accepts and returns GPU arrays."""
|
||||||
"""
|
"""
|
||||||
Batch GPU-accelerated Poisson solver for multiple inputs.
|
GPU-accelerated Poisson solver using CuPy.
|
||||||
Solves multiple Poisson equations in a single GPU call.
|
Falls back to CPU if CuPy is not available.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
f_list: List of input fields (numpy arrays)
|
f_gpu: Input field (numpy array)
|
||||||
verbose: Print timing information
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List of solutions (numpy arrays on CPU)
|
p_gpu: Solution (CuPy array)
|
||||||
"""
|
"""
|
||||||
if not CUPY_AVAILABLE:
|
|
||||||
if verbose:
|
|
||||||
print("CuPy not available, falling back to CPU solver")
|
|
||||||
return [self.solve(f, verbose) for f in f_list]
|
|
||||||
|
|
||||||
if not self.ready:
|
|
||||||
self.prepare(verbose)
|
|
||||||
|
|
||||||
gtimer = Timer()
|
|
||||||
if verbose:
|
|
||||||
print(f'\nSolving {len(f_list)} Poisson Eq. on GPU (batched)')
|
|
||||||
|
|
||||||
# Verify all inputs have same shape
|
|
||||||
fshape = f_list[0].shape
|
|
||||||
for f in f_list:
|
|
||||||
if f.shape != fshape:
|
|
||||||
raise ValueError("All input fields must have the same shape for batch processing")
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# regular grid
|
# regular grid
|
||||||
if self.ptype == 'G':
|
if self.ptype == 'G':
|
||||||
if self.gdims != fshape:
|
|
||||||
raise ValueError("Shape of function should match shape of grid")
|
|
||||||
|
|
||||||
# convolution in frequency domain using GPU
|
# convolution in frequency domain using GPU
|
||||||
if self.stype == 'F':
|
if self.stype == 'F':
|
||||||
# Initialize GPU cache if needed
|
# Initialize GPU cache if needed
|
||||||
if self._gpu_cache is None:
|
if self._gpu_cache is None:
|
||||||
if verbose:
|
|
||||||
print(' - Transferring Green\'s function to GPU...')
|
|
||||||
self._gpu_cache = {
|
self._gpu_cache = {
|
||||||
'G_gpu': cp.asarray(self.G, dtype=cp.float32),
|
'G_gpu': cp.asarray(self.G, dtype=cp.float32),
|
||||||
'gdx_prod': numpy.prod(self.gdx)
|
'gdx_prod': numpy.prod(self.gdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
results = []
|
|
||||||
|
|
||||||
# Process all inputs in batch
|
|
||||||
for i, f in enumerate(f_list):
|
|
||||||
# Transfer input to GPU
|
|
||||||
f_gpu = cp.asarray(f, dtype=cp.float32)
|
|
||||||
|
|
||||||
# Perform FFT convolution on GPU
|
# Perform FFT convolution on GPU
|
||||||
|
# CuPy's fftconvolve is GPU-accelerated
|
||||||
p_gpu = cp_signal.fftconvolve(f_gpu, self._gpu_cache['G_gpu'], mode='same')
|
p_gpu = cp_signal.fftconvolve(f_gpu, self._gpu_cache['G_gpu'], mode='same')
|
||||||
|
|
||||||
# Scale by grid spacing (on GPU)
|
# Scale by grid spacing (on GPU)
|
||||||
p_gpu *= self._gpu_cache['gdx_prod']
|
p_gpu *= self._gpu_cache['gdx_prod']
|
||||||
|
|
||||||
# Transfer result back to CPU
|
return p_gpu # Return CuPy array
|
||||||
results.append(cp.asnumpy(p_gpu))
|
|
||||||
|
|
||||||
if verbose:
|
|
||||||
print(f' - Solved {i+1}/{len(f_list)}')
|
|
||||||
|
|
||||||
if verbose:
|
# def solve_gpu_batch(self, f_list, verbose=False):
|
||||||
print('Poisson solver (GPU batch) finished')
|
# """
|
||||||
gtimer.end()
|
# Batch GPU-accelerated Poisson solver for multiple inputs.
|
||||||
|
# Solves multiple Poisson equations in a single GPU call.
|
||||||
|
|
||||||
return results
|
# Args:
|
||||||
|
# f_list: List of input fields (numpy arrays)
|
||||||
|
# verbose: Print timing information
|
||||||
|
|
||||||
elif self.stype == 'S':
|
# Returns:
|
||||||
raise SyntaxError('Spatial solver not suggested for structured grids')
|
# List of solutions (numpy arrays on CPU)
|
||||||
|
# """
|
||||||
|
# if not CUPY_AVAILABLE:
|
||||||
|
# if verbose:
|
||||||
|
# print("CuPy not available, falling back to CPU solver")
|
||||||
|
# return [self.solve(f, verbose) for f in f_list]
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# if not self.ready:
|
||||||
# unstructured points - not optimized for GPU
|
# self.prepare(verbose)
|
||||||
elif self.ptype == 'P':
|
|
||||||
if verbose:
|
|
||||||
print(' - GPU solver not optimized for unstructured points, using CPU')
|
|
||||||
return [self.solve(f, verbose) for f in f_list]
|
|
||||||
|
|
||||||
# If we reach here, it's an unsupported case
|
# gtimer = Timer()
|
||||||
raise ValueError(f"Unsupported point type '{self.ptype}' for GPU batch solver")
|
# if verbose:
|
||||||
|
# print(f'\nSolving {len(f_list)} Poisson Eq. on GPU (batched)')
|
||||||
|
|
||||||
|
# # Verify all inputs have same shape
|
||||||
|
# fshape = f_list[0].shape
|
||||||
|
# for f in f_list:
|
||||||
|
# if f.shape != fshape:
|
||||||
|
# raise ValueError("All input fields must have the same shape for batch processing")
|
||||||
|
|
||||||
|
# # ----------------------------------------------------------------------
|
||||||
|
# # regular grid
|
||||||
|
# if self.ptype == 'G':
|
||||||
|
# if self.gdims != fshape:
|
||||||
|
# raise ValueError("Shape of function should match shape of grid")
|
||||||
|
|
||||||
|
# # convolution in frequency domain using GPU
|
||||||
|
# if self.stype == 'F':
|
||||||
|
# # Initialize GPU cache if needed
|
||||||
|
# if self._gpu_cache is None:
|
||||||
|
# if verbose:
|
||||||
|
# print(' - Transferring Green\'s function to GPU...')
|
||||||
|
# self._gpu_cache = {
|
||||||
|
# 'G_gpu': cp.asarray(self.G, dtype=cp.float32),
|
||||||
|
# 'gdx_prod': numpy.prod(self.gdx)
|
||||||
|
# }
|
||||||
|
|
||||||
|
# results = []
|
||||||
|
|
||||||
|
# # Process all inputs in batch
|
||||||
|
# for i, f in enumerate(f_list):
|
||||||
|
# # Transfer input to GPU
|
||||||
|
# f_gpu = cp.asarray(f, dtype=cp.float32)
|
||||||
|
|
||||||
|
# # Perform FFT convolution on GPU
|
||||||
|
# p_gpu = cp_signal.fftconvolve(f_gpu, self._gpu_cache['G_gpu'], mode='same')
|
||||||
|
|
||||||
|
# # Scale by grid spacing (on GPU)
|
||||||
|
# p_gpu *= self._gpu_cache['gdx_prod']
|
||||||
|
|
||||||
|
# # Transfer result back to CPU
|
||||||
|
# results.append(cp.asnumpy(p_gpu))
|
||||||
|
|
||||||
|
# if verbose:
|
||||||
|
# print(f' - Solved {i+1}/{len(f_list)}')
|
||||||
|
|
||||||
|
# if verbose:
|
||||||
|
# print('Poisson solver (GPU batch) finished')
|
||||||
|
# gtimer.end()
|
||||||
|
|
||||||
|
# return results
|
||||||
|
|
||||||
|
# elif self.stype == 'S':
|
||||||
|
# raise SyntaxError('Spatial solver not suggested for structured grids')
|
||||||
|
|
||||||
|
# # ----------------------------------------------------------------------
|
||||||
|
# # unstructured points - not optimized for GPU
|
||||||
|
# elif self.ptype == 'P':
|
||||||
|
# if verbose:
|
||||||
|
# print(' - GPU solver not optimized for unstructured points, using CPU')
|
||||||
|
# return [self.solve(f, verbose) for f in f_list]
|
||||||
|
|
||||||
|
# # If we reach here, it's an unsupported case
|
||||||
|
# raise ValueError(f"Unsupported point type '{self.ptype}' for GPU batch solver")
|
||||||
|
|
||||||
def clear_gpu_cache(self):
|
def clear_gpu_cache(self):
|
||||||
"""Clear GPU cache to free GPU memory"""
|
"""Clear GPU cache to free GPU memory"""
|
||||||
|
|||||||
Reference in New Issue
Block a user