diff --git a/src/sfg_walberla/api.py b/src/sfg_walberla/api.py index dd50def1eb7d99ac86f2fc28b48cb2c1272dc277..2c8caeef9bb6715af061e5d508a79cfdeddd4566 100644 --- a/src/sfg_walberla/api.py +++ b/src/sfg_walberla/api.py @@ -149,13 +149,59 @@ class StructuredBlockForest(AugExpr): return AugExpr(uint_t).bind("{}.getLevel({})", self, block) -class GhostLayerFieldPtr(SrcField): +class GenericWalberlaField(SrcField): + """Common base class for GhostLayerField and GpuField defining their shared interface.""" + def __init__( + self, + element_type: PsType, + field_type: PsCustomType, + ptr: bool = False, + ref: bool = False + ): + if ptr and ref: + raise ValueError("At most one of `ptr` and `ref` may be true.") + + self._element_type = element_type + self._field_type = field_type + + if ptr: + obj_type = PsPointerType(self._field_type) + elif ref: + obj_type = Ref(self._field_type) + else: + obj_type = self._field_type + + super().__init__(obj_type) + + @property + def _a(self) -> AugExpr: + """Member access""" + if isinstance(self.dtype, PsPointerType): + return AugExpr.format("{}->", self) + else: + return AugExpr.format("{}.", self) + + @property + def field_type(self) -> PsCustomType: + return self._field_type + + def get_extraction(self) -> IFieldExtraction: + return GhostLayerFieldExtraction(self, None) + + def cloneUninitialized(self) -> AugExpr: + return AugExpr.format("{}cloneUninitialized()", self._a) + + def swapDataPointers(self, other: AugExpr) -> AugExpr: + return AugExpr.format("{}swapDataPointers({});", self._a, other) + + +class GhostLayerFieldPtr(GenericWalberlaField): @staticmethod def create(field: Field): if field.index_dimensions > 1: raise ValueError( - "Cannot map fields with more than one index dimension to nfield::FieldBuffer." + "Cannot map fields with more than one index dimension to field::GhostLayerField." ) element_type = field.dtype @@ -168,31 +214,56 @@ class GhostLayerFieldPtr(SrcField): element_type: UserTypeSpec, fsize: int, ): - self._element_type = create_type(element_type) - - elmt_type_str = self._element_type.c_string() - self._field_type = PsCustomType( + element_type = create_type(element_type) + elmt_type_str = element_type.c_string() + field_type = PsCustomType( f"walberla::field::GhostLayerField< {elmt_type_str}, {fsize} >" ) - super().__init__(PsPointerType(self._field_type)) - - @property - def field_type(self) -> PsCustomType: - return self._field_type - - def get_extraction(self) -> IFieldExtraction: - return GhostLayerFieldExtraction(self, None) - + super().__init__( + element_type, + field_type, + ptr=True + ) + @property def required_includes(self) -> set[SfgHeaderInclude]: return {SfgHeaderInclude("field/GhostLayerField.h")} - def cloneUninitialized(self) -> AugExpr: - return AugExpr.format("{}->cloneUninitialized()", self) - def swapDataPointers(self, other: GhostLayerFieldPtr) -> AugExpr: - return AugExpr.format("{}->swapDataPointers({});", self, other) +class GpuFieldPtr(GenericWalberlaField): + @staticmethod + def create(field: Field): + if field.index_dimensions > 1: + raise ValueError( + "Cannot map fields with more than one index dimension to gpu::GpuField." + ) + + element_type = field.dtype + fsize = field.index_shape[0] if field.index_shape else 1 + + return GpuFieldPtr(element_type, fsize).var(field.name) + + def __init__( + self, + element_type: UserTypeSpec, + fsize: int, + ): + element_type = create_type(element_type) + elmt_type_str = element_type.c_string() + field_type = PsCustomType( + f"walberla::gpu::GpuField< {elmt_type_str} >" + ) + + super().__init__( + element_type, + field_type, + ptr=True + ) + + @property + def required_includes(self) -> set[SfgHeaderInclude]: + return {SfgHeaderInclude("gpu/GpuField.h")} class GhostLayerFieldExtraction(IFieldExtraction): diff --git a/src/sfg_walberla/postprocessing.py b/src/sfg_walberla/postprocessing.py index 603ffb099d6b16b42daf7573a05bee02735ad844..d4a911f1318cca77802af0d321a0e01d89b8c8ab 100644 --- a/src/sfg_walberla/postprocessing.py +++ b/src/sfg_walberla/postprocessing.py @@ -44,7 +44,7 @@ class CaptureToClass(SfgDeferredNode): members_block.append_member(member) captured.append(var) statements.append( - SfgStatements(f"{var.dtype} {var.name} {{ {member} }};", [var], []) + SfgStatements(f"{var.dtype.c_string()} {var.name} {{ {member} }};", [var], []) ) cls.append_visibility_block(members_block) diff --git a/src/sfg_walberla/sweep.py b/src/sfg_walberla/sweep.py index a3998d72e731fdacc00db805bc81f4b074f8c722..1f094eae0e24603deac25b753d1b59b525c54e1d 100644 --- a/src/sfg_walberla/sweep.py +++ b/src/sfg_walberla/sweep.py @@ -8,15 +8,17 @@ import sympy as sp from pystencilssfg.composer.custom import CustomGenerator from pystencilssfg.composer.class_composer import SfgClassComposer -from pystencilssfg.ir import SfgMethod +from pystencilssfg.ir import SfgMethod, SfgKernelHandle from pystencils import ( Assignment, AssignmentCollection, CreateKernelConfig, Field, + Target, ) from pystencils.types import PsType, constify, deconstify, PsCustomType +from pystencils.backend.kernelfunction import GpuKernelFunction from pystencilssfg import SfgComposer from pystencilssfg.lang import ( @@ -29,8 +31,11 @@ from pystencilssfg.lang import ( strip_ptr_ref, ) from .api import ( + uint_t, StructuredBlockForest, + GenericWalberlaField, GhostLayerFieldPtr, + GpuFieldPtr, IBlockPtr, BlockDataID, Vector2, @@ -249,7 +254,7 @@ class BlockforestParameters: @dataclass class FieldInfo: field: Field - glfield_ptr: GhostLayerFieldPtr + glfield_ptr: GenericWalberlaField data_id: BlockDataID @@ -314,10 +319,25 @@ class ShadowFieldCache: def _getter(self, orig_name: str) -> str: return f"_getShadow_{orig_name}" - def _cache_ptr(self, orig: GhostLayerFieldPtr) -> str: + def _cache_ptr(self, orig: GenericWalberlaField) -> str: return f"shadow_{str(orig)}_" +class CudaInvocation: + def __init__(self, khandle: SfgKernelHandle): + self._khandle = khandle + + gpu_func = khandle.get_kernel_function() + assert isinstance(gpu_func, GpuKernelFunction) + self._gpu_func = gpu_func + + self._block_size = Vector3(uint_t).var("gpuBlockSize") + + def render(self, sfg: SfgComposer): + pass + + + def combine_vectors(scalars: set[SfgVar]) -> dict[SrcVector, tuple[SfgVar, ...]]: """Attempt to combine vector component symbols into vectors. @@ -408,6 +428,15 @@ class Sweep(CustomGenerator): self._assignments = AssignmentCollection(assignments) self._gen_config = config + if self._gen_config.target == Target.CUDA: + self._glfield_type = GpuFieldPtr + elif self._gen_config.target.is_cpu(): + self._glfield_type = GhostLayerFieldPtr + else: + raise ValueError( + f"Cannot generate sweep for target {self._gen_config.target}" + ) + # Map from shadow field to shadowed field self._shadow_fields: dict[Field, Field] = dict() @@ -431,7 +460,7 @@ class Sweep(CustomGenerator): all_fields: dict[str, FieldInfo] = { f.name: FieldInfo( - f, GhostLayerFieldPtr.create(f), BlockDataID().var(f"{f.name}Id") + f, self._glfield_type.create(f), BlockDataID().var(f"{f.name}Id") ) for f in khandle.fields } @@ -464,6 +493,10 @@ class Sweep(CustomGenerator): for s in sorted(parameters, key=lambda p: p.name): props.add_property(s, setter=True, getter=True) + # GPU invocation + if self._gen_config.target == Target.CUDA: + + sfg.klass(self._name)( *props.render(sfg), sfg.public(