From 6b2c436e83470d452fc86bdbe0eea033bc66145c Mon Sep 17 00:00:00 2001 From: Behzad Safaei <iwia103h@alex2.nhr.fau.de> Date: Fri, 7 Feb 2025 03:23:00 +0100 Subject: [PATCH] Add feature props to accessor, fix issue with non scalar types --- examples/modular/sd_sample_3_GPU.cu | 20 ++++- examples/modular/spring_dashpot.py | 18 ++-- src/pairs/code_gen/accessor.py | 120 +++++++++++++++++++++++++++ src/pairs/code_gen/cgen.py | 9 +- src/pairs/ir/device.py | 22 +++++ src/pairs/ir/features.py | 6 +- src/pairs/sim/simulation.py | 10 ++- src/pairs/transformations/devices.py | 5 +- 8 files changed, 190 insertions(+), 20 deletions(-) diff --git a/examples/modular/sd_sample_3_GPU.cu b/examples/modular/sd_sample_3_GPU.cu index 964ede0..6861c08 100644 --- a/examples/modular/sd_sample_3_GPU.cu +++ b/examples/modular/sd_sample_3_GPU.cu @@ -24,6 +24,20 @@ __global__ void change_gravitational_force(PairsAccessor ac, int idx){ printf("Force [from device] after setting = (%f, %f, %f) \n", ac.getForce(idx)[0], ac.getForce(idx)[1], ac.getForce(idx)[2]); } +void set_feature_properties(std::shared_ptr<PairsAccessor> &ac){ + ac->setTypeStiffness(0,0, 0); + ac->setTypeStiffness(0,1, 1000); + ac->setTypeStiffness(1,0, 1000); + ac->setTypeStiffness(1,1, 3000); + ac->syncTypeStiffness(); + + ac->setTypeDampingNorm(0,0, 0); + ac->setTypeDampingNorm(0,1, 20); + ac->setTypeDampingNorm(1,0, 20); + ac->setTypeDampingNorm(1,1, 10); + ac->syncTypeDampingNorm(); +} + int main(int argc, char **argv) { auto pairs_sim = std::make_shared<PairsSimulation>(); @@ -41,8 +55,10 @@ int main(int argc, char **argv) { pairs_sim->create_halfspace(1,1,1, 0, -1, 0, 0, 13); pairs_sim->create_halfspace(1,1,1, 0, 0, -1, 0, 13); - pairs::id_t pUid = pairs_sim->create_sphere(0.6, 0.6, 0.7, 0, 0, 0, 1000, 0.05, 0, 0); - pairs_sim->create_sphere(0.4, 0.4, 0.76, 2, 2, 0, 1000, 0.05, 0, 0); + pairs::id_t pUid = pairs_sim->create_sphere(0.6, 0.6, 0.7, 0, 0, 0, 1000, 0.05, 1, 0); + pairs_sim->create_sphere(0.4, 0.4, 0.76, 2, 2, 0, 1000, 0.05, 1, 0); + + set_feature_properties(ac); MPI_Allreduce(MPI_IN_PLACE, &pUid, 1, MPI_LONG_LONG_INT, MPI_SUM, MPI_COMM_WORLD); diff --git a/examples/modular/spring_dashpot.py b/examples/modular/spring_dashpot.py index 94e5ef8..3109fa5 100644 --- a/examples/modular/spring_dashpot.py +++ b/examples/modular/spring_dashpot.py @@ -48,11 +48,6 @@ def euler(i): def gravity(i): force[i][2] = force[i][2] - mass[i] * gravity_SI -gravity_SI = 9.81 -diameter = 100 # required for linkedCellWidth. TODO: set linkedCellWidth at runtime -linkedCellWidth = 1.01 * diameter - -ntypes = 1 file_name = os.path.basename(__file__) file_name_without_extension = os.path.splitext(file_name)[0] @@ -76,6 +71,10 @@ elif target == 'cpu': else: print(f"Invalid target, use {sys.argv[0]} <cpu/gpu>") +gravity_SI = 9.81 +diameter = 100 # required for linkedCellWidth. TODO: set linkedCellWidth at runtime +linkedCellWidth = 1.01 * diameter +ntypes = 2 psim.add_position('position') psim.add_property('mass', pairs.real()) @@ -83,20 +82,17 @@ psim.add_property('linear_velocity', pairs.vector()) psim.add_property('angular_velocity', pairs.vector()) psim.add_property('force', pairs.vector(), volatile=True) psim.add_property('torque', pairs.vector(), volatile=True) -psim.add_property('hydrodynamic_force', pairs.vector(), reduce=True) -psim.add_property('hydrodynamic_torque', pairs.vector(), reduce=True) -psim.add_property('old_hydrodynamic_force', pairs.vector()) -psim.add_property('old_hydrodynamic_torque', pairs.vector()) psim.add_property('radius', pairs.real()) psim.add_property('normal', pairs.vector()) psim.add_property('inv_inertia', pairs.matrix()) psim.add_property('rotation_matrix', pairs.matrix()) psim.add_property('rotation', pairs.quaternion()) + psim.add_feature('type', ntypes) psim.add_feature_property('type', 'stiffness', pairs.real(), [3000 for i in range(ntypes * ntypes)]) psim.add_feature_property('type', 'damping_norm', pairs.real(), [10.0 for i in range(ntypes * ntypes)]) -psim.add_feature_property('type', 'damping_tan', pairs.real(), [0 for i in range(ntypes * ntypes)]) -psim.add_feature_property('type', 'friction', pairs.real(), [0 for i in range(ntypes * ntypes)]) +psim.add_feature_property('type', 'damping_tan', pairs.real()) +psim.add_feature_property('type', 'friction', pairs.real()) psim.set_domain_partitioner(pairs.block_forest()) psim.pbc([False, False, False]) diff --git a/src/pairs/code_gen/accessor.py b/src/pairs/code_gen/accessor.py index cd5a986..5948b15 100644 --- a/src/pairs/code_gen/accessor.py +++ b/src/pairs/code_gen/accessor.py @@ -38,12 +38,122 @@ class PairsAcessor: self.set_property(p) self.sync_property(p) + for fp in self.sim.feature_properties: + self.get_feature_property(fp) + self.set_feature_property(fp) + self.sync_feature_property(fp) + self.utility_funcs() self.print.add_indent(-4) self.print("};") self.print("") + def get_fp_body(self, fp, device=False): + self.print.add_indent(4) + fp_name = fp.name() + f_name = fp.feature().name() + + tkw = Types.c_accessor_keyword(self.sim, fp.type()) + + if self.target.is_gpu() and device: + v = f"{fp_name}_d" + else: + v = f"ps->pobj->{fp_name}" + + idx = f"{fp.feature().nkinds()}*{f_name}1 + {f_name}2" + if Types.is_scalar(fp.type()): + self.print(f"return {v}[{idx}];") + else: + nelems = Types.number_of_elements(self.sim, fp.type()) + return_values = [f"{v}[({idx})*{nelems} + {n}]" for n in range(nelems)] + self.print(f"return {tkw}(" + ", ".join(rv for rv in return_values) + ");") + self.print.add_indent(-4) + + + def get_feature_property(self, fp): + fp_name = fp.name() + tkw = Types.c_accessor_keyword(self.sim, fp.type()) + f_name = fp.feature().name() + + splitname = f_name.split('_') + fp_name.split('_') + funcname = ''.join(word.capitalize() for word in splitname) + self.print(f"{self.host_device_attr}{tkw} get{funcname}(const size_t {f_name}1, const size_t {f_name}2) const{{") + + if self.target.is_gpu(): + self.ifdef_else("__CUDA_ARCH__", self.get_fp_body, [fp, True], self.get_fp_body, [fp, False]) + else: + self.get_fp_body(fp, False) + + self.print("}") + self.print("") + + def set_fp_body(self, fp, device=False): + self.print.add_indent(4) + fp_name = fp.name() + f_name = fp.feature().name() + tkw = Types.c_accessor_keyword(self.sim, fp.type()) + + if self.target.is_gpu() and device: + v = f"{fp_name}_d" + else: + v = f"ps->pobj->{fp_name}" + + idx = f"{fp.feature().nkinds()}*{f_name}1 + {f_name}2" + + if Types.is_scalar(fp.type()): + self.print(f"{v}[{idx}] = value;") + else: + nelems = Types.number_of_elements(self.sim, fp.type()) + set_values = [f"{v}[({idx})*{nelems} + {n}] = value[{n}];" for n in range(nelems)] + for sv in set_values: + self.print(sv) + + if self.target.is_gpu(): + flag = f"*{fp_name}_device_flag_d" if device else f"{fp_name}_host_flag" + self.print(f"{flag} = true;") + + self.print.add_indent(-4) + + + def set_feature_property(self, fp): + fp_name = fp.name() + tkw = Types.c_accessor_keyword(self.sim, fp.type()) + f_name = fp.feature().name() + + splitname = f_name.split('_') + fp_name.split('_') + funcname = ''.join(word.capitalize() for word in splitname) + + # Feature properties can only be set from host + self.print(f"{self.host_attr}void set{funcname}(const size_t {f_name}1, const size_t {f_name}2, const {tkw} &value){{") + self.set_fp_body(fp, False) + + self.print("}") + self.print("") + + def sync_feature_property(self, fp): + fp_id = fp.id() + fp_name = fp.name() + f_name = fp.feature().name() + splitname = f_name.split('_') + fp_name.split('_') + funcname = ''.join(word.capitalize() for word in splitname) + + self.print(f"{self.host_attr}void sync{funcname}(SyncMode sync_mode = HostAndDevice){{") + + if self.target.is_gpu(): + self.print.add_indent(4) + self.print(f"if ({fp_name}_host_flag) {{") + self.print(f" ps->pairs_runtime->copyFeaturePropertyToDevice({fp_id});") + self.print("}") + self.print("") + + self.print(f"{fp_name}_host_flag = false;") + self.print.add_indent(-4) + + self.print("}") + self.print("") + + def member_variables(self): self.print("PairsSimulation *ps;") @@ -66,6 +176,16 @@ class PairsAcessor: self.print(f"{tkw} *{pname}_device_flag_d;") self.print(f"{tkw} {pname}_device_flag_h = false;") self.print(f"{tkw} {pname}_host_flag = false;") + + self.print("") + self.print("//Feature properties are global") + + self.print("") + self.print("//Feature property flags") + for fp in self.sim.feature_properties: + fpname = fp.name() + tkw = Types.c_keyword(self.sim, Types.Boolean) + self.print(f"{tkw} {fpname}_host_flag = false;") self.print("") diff --git a/src/pairs/code_gen/cgen.py b/src/pairs/code_gen/cgen.py index 29e8b2f..9cfce8b 100644 --- a/src/pairs/code_gen/cgen.py +++ b/src/pairs/code_gen/cgen.py @@ -9,7 +9,7 @@ from pairs.ir.cast import Cast from pairs.ir.contexts import Contexts from pairs.ir.declaration import Decl from pairs.ir.scalars import ScalarOp -from pairs.ir.device import CopyArray, CopyContactProperty, CopyProperty, CopyVar, DeviceStaticRef, HostRef +from pairs.ir.device import CopyArray, CopyContactProperty, CopyProperty, CopyFeatureProperty, CopyVar, DeviceStaticRef, HostRef from pairs.ir.features import FeatureProperty, FeaturePropertyAccess, RegisterFeatureProperty from pairs.ir.functions import Call from pairs.ir.kernel import KernelLaunch @@ -903,6 +903,13 @@ class CGen: size = self.generate_expression(ast_node.prop().copy_size()) self.print(f"pairs_runtime->copyPropertyTo{ctx_suffix}({prop_id}, {action}, {size}); // {prop_name}") + if isinstance(ast_node, CopyFeatureProperty): + prop_id = ast_node.prop().id() + prop_name = ast_node.prop().name() + if ast_node.context() == Contexts.Device: + assert ast_node.action()==Actions.ReadOnly, "Feature properties can only be read from device." + self.print(f"pairs_runtime->copyFeaturePropertyToDevice({prop_id}); // {prop_name}") + if isinstance(ast_node, CopyVar): var_name = ast_node.variable().name() ctx_suffix = "Device" if ast_node.context() == Contexts.Device else "Host" diff --git a/src/pairs/ir/device.py b/src/pairs/ir/device.py index 4eddecf..0dfd6b9 100644 --- a/src/pairs/ir/device.py +++ b/src/pairs/ir/device.py @@ -77,7 +77,29 @@ class CopyProperty(ASTNode): def children(self): return [self._prop, self.sim.nghost, self.sim.nlocal] +class CopyFeatureProperty(ASTNode): + def __init__(self, sim, prop, ctx, action): + super().__init__(sim) + self._prop = prop + self._ctx = ctx + self._action = action + self.sim.add_statement(self) + def __str__(self): + return f"CopyFeatureProperty<{self._prop}>" + + def prop(self): + return self._prop + + def context(self): + return self._ctx + + def action(self): + return self._action + + def children(self): + return [self._prop] + class CopyContactProperty(ASTNode): def __init__(self, sim, prop, ctx, action): super().__init__(sim) diff --git a/src/pairs/ir/features.py b/src/pairs/ir/features.py index d709336..24bb365 100644 --- a/src/pairs/ir/features.py +++ b/src/pairs/ir/features.py @@ -131,9 +131,7 @@ class FeatureProperty(ASTNode): self.feature_prop_feature.nkinds()] def array_size(self): - nelems = self.feature_prop_feature.nkinds() * \ - Types.number_of_elements(self.sim, self.feature_prop_type) - return nelems * nelems + return self.feature_prop_feature.nkinds()**2 * Types.number_of_elements(self.sim, self.feature_prop_type) def __getitem__(self, expr): return FeaturePropertyAccess(self.sim, self, expr) @@ -161,7 +159,7 @@ class FeaturePropertyAccess(ASTTerm): sizes = feature_prop.sizes() layout = feature_prop.layout() - for elem in range(Types.number_of_elements(feature_prop.type())): + for elem in range(Types.number_of_elements(self.sim, feature_prop.type())): if layout == Layouts.AoS: self.vector_indexes[elem] = self.index * sizes[0] + elem elif layout == Layouts.SoA: diff --git a/src/pairs/sim/simulation.py b/src/pairs/sim/simulation.py index 208f1ad..66d5f6d 100644 --- a/src/pairs/sim/simulation.py +++ b/src/pairs/sim/simulation.py @@ -217,10 +217,18 @@ class Simulation: assert self.feature(feature_name) is None, f"Feature already defined: {feature_name}" return self.features.add(feature_name, nkinds) - def add_feature_property(self, feature_name, prop_name, prop_type, prop_data): + def add_feature_property(self, feature_name, prop_name, prop_type, prop_data=None): feature = self.feature(feature_name) assert feature is not None, f"Feature not found: {feature_name}" assert self.property(prop_name) is None, f"Property already defined: {prop_name}" + + array_size = feature.nkinds()**2 * Types.number_of_elements(self, prop_type) + + if not prop_data: + prop_data = [0 for i in range(array_size)] + else: + assert len(prop_data) == array_size, f"Incorrect array size for {prop_name}: Expected array size = {array_size}" + return self.feature_properties.add(feature, prop_name, prop_type, prop_data) def add_contact_property(self, prop_name, prop_type, prop_default, layout=Layouts.AoS): diff --git a/src/pairs/transformations/devices.py b/src/pairs/transformations/devices.py index 21898c1..d33f30e 100644 --- a/src/pairs/transformations/devices.py +++ b/src/pairs/transformations/devices.py @@ -4,7 +4,7 @@ from pairs.ir.block import Block from pairs.ir.branches import Branch, Filter from pairs.ir.cast import Cast from pairs.ir.contexts import Contexts -from pairs.ir.device import CopyArray, CopyContactProperty, CopyProperty, CopyVar, DeviceStaticRef, HostRef +from pairs.ir.device import CopyArray, CopyContactProperty, CopyProperty, CopyFeatureProperty, CopyVar, DeviceStaticRef, HostRef from pairs.ir.functions import Call_Void from pairs.ir.kernel import Kernel, KernelLaunch from pairs.ir.lit import Lit @@ -46,6 +46,9 @@ class AddDeviceCopies(Mutator): for prop, action in s.module.properties().items(): new_stmts += [CopyProperty(s.sim, prop, copy_context, action)] + for fp, action in s.module.feature_properties().items(): + new_stmts += [CopyFeatureProperty(s.sim, fp, copy_context, action)] + for contact_prop, action in s.module.contact_properties().items(): new_stmts += [CopyContactProperty(s.sim, contact_prop, copy_context, action)] -- GitLab