From 09d38e04ec2aea37a602b2a51ed0caddc9eea8f2 Mon Sep 17 00:00:00 2001 From: Songyuan Cui Date: Wed, 14 Jun 2023 15:37:49 +0800 Subject: [PATCH 1/3] feat: immersed boundary communicator interface with elasticapp --- .../cosserat_rod_cpp/__init__.py | 4 ++ .../cosserat_rod_cpp_flow_interaction.py | 60 ++++++++++++++++++ .../cosserat_rod_cpp_forcing_grids.py | 62 +++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 sopht/simulator/immersed_body/cosserat_rod_cpp/__init__.py create mode 100644 sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_flow_interaction.py create mode 100644 sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py diff --git a/sopht/simulator/immersed_body/cosserat_rod_cpp/__init__.py b/sopht/simulator/immersed_body/cosserat_rod_cpp/__init__.py new file mode 100644 index 0000000..1a3fd14 --- /dev/null +++ b/sopht/simulator/immersed_body/cosserat_rod_cpp/__init__.py @@ -0,0 +1,4 @@ +from .cosserat_rod_cpp_forcing_grids import ( + CosseratRodCPPElementCentricForcingGrid, +) +from .cosserat_rod_cpp_flow_interaction import CosseratRodCPPFlowInteraction diff --git a/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_flow_interaction.py b/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_flow_interaction.py new file mode 100644 index 0000000..f4a292e --- /dev/null +++ b/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_flow_interaction.py @@ -0,0 +1,60 @@ +import numpy as np +from sopht.simulator.immersed_body import ( + ImmersedBodyForcingGrid, + ImmersedBodyFlowInteraction, +) +from typing import Type, Optional + + +class CosseratRodCPPFlowInteraction(ImmersedBodyFlowInteraction): + """Class for Elastica++ Cosserat rod flow interaction.""" + + def __init__( + self, + cosserat_rod_simulator, + eul_grid_forcing_field: np.ndarray, + eul_grid_velocity_field: np.ndarray, + virtual_boundary_stiffness_coeff: float, + virtual_boundary_damping_coeff: float, + dx: float, + grid_dim: int, + forcing_grid_cls: Type[ImmersedBodyForcingGrid], + real_t: type = np.float64, + eul_grid_coord_shift: Optional[float] = None, + interp_kernel_width: Optional[float] = None, + enable_eul_grid_forcing_reset: bool = False, + num_threads: int | bool = False, + start_time: float = 0.0, + **forcing_grid_kwargs, + ) -> None: + """Class initialiser.""" + rod, _ = cosserat_rod_simulator.communicate() + n_elems = int(rod.n_elems) + body_flow_forces = np.zeros( + (3, n_elems + 1), + ) + body_flow_torques = np.zeros( + (3, n_elems), + ) + + forcing_grid_kwargs["cosserat_rod_simulator"] = cosserat_rod_simulator + + # initialising super class + super().__init__( + eul_grid_forcing_field, + eul_grid_velocity_field, + body_flow_forces, + body_flow_torques, + forcing_grid_cls, + virtual_boundary_stiffness_coeff, + virtual_boundary_damping_coeff, + dx, + grid_dim, + real_t, + eul_grid_coord_shift, + interp_kernel_width, + enable_eul_grid_forcing_reset, + num_threads, + start_time, + **forcing_grid_kwargs, + ) diff --git a/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py b/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py new file mode 100644 index 0000000..a92a92e --- /dev/null +++ b/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py @@ -0,0 +1,62 @@ +from elastica.interaction import node_to_element_velocity +import numpy as np +from sopht.simulator.immersed_body import ImmersedBodyForcingGrid + + +class CosseratRodCPPElementCentricForcingGrid(ImmersedBodyForcingGrid): + """Class for forcing grid at Cosserat rod element centers""" + + def __init__(self, grid_dim: int, cosserat_rod_simulator) -> None: + self.cs = cosserat_rod_simulator + num_lag_nodes = int(self.cs.n_elems) + super().__init__(grid_dim, num_lag_nodes) + + # to ensure position/velocity are consistent during initialisation + self.compute_lag_grid_position_field() + self.compute_lag_grid_velocity_field() + + def compute_lag_grid_position_field(self) -> None: + """Computes location of forcing grid for the Cosserat rod""" + rod, _ = self.cs.communicate() + rod_position_field = np.asarray(rod.position_collection) + + self.position_field[...] = ( + rod_position_field[: self.grid_dim, 1:] + + rod_position_field[: self.grid_dim, :-1] + ) / 2.0 + + def compute_lag_grid_velocity_field(self) -> None: + """Computes velocity of forcing grid points for the Cosserat rod""" + rod, _ = self.cs.communicate() + rod_velocity_field = np.asarray(rod.velocity_collection) + rod_masses = np.asarray(rod.mass) + + self.velocity_field[...] = node_to_element_velocity( + rod_masses, rod_velocity_field + )[: self.grid_dim] + + def transfer_forcing_from_grid_to_body( + self, + body_flow_forces: np.ndarray, + body_flow_torques: np.ndarray, + lag_grid_forcing_field: np.ndarray, + ) -> None: + """Transfer forcing from lagrangian forcing grid to the cosserat rod""" + # negative sign due to Newtons third law + _, body_forces_cpp = self.cs.communicate() + body_forces_cpp = np.asarray(body_forces_cpp) + body_flow_forces[...] = 0.0 + body_flow_forces[: self.grid_dim, 1:] -= 0.5 * lag_grid_forcing_field + body_flow_forces[: self.grid_dim, :-1] -= 0.5 * lag_grid_forcing_field + + body_forces_cpp[: self.grid_dim, 1:] -= 0.5 * lag_grid_forcing_field + body_forces_cpp[: self.grid_dim, :-1] -= 0.5 * lag_grid_forcing_field + + # torque from grid forcing (don't modify since set = 0 at initialisation) + # because no torques acting on element centers + + def get_maximum_lagrangian_grid_spacing(self) -> float: + """Get the maximum Lagrangian grid spacing""" + # estimated distance between consecutive elements + rod, _ = self.cs.communicate() + return np.amax(np.asarray(rod.lengths)) From d70af87b1593be3e0ff7944edae7a51445d2d2aa Mon Sep 17 00:00:00 2001 From: Songyuan Cui Date: Mon, 19 Jun 2023 22:42:46 +0800 Subject: [PATCH 2/3] fix: fix missing imports and number of element conversion --- sopht/simulator/immersed_body/__init__.py | 1 + .../cosserat_rod_cpp/cosserat_rod_cpp_flow_interaction.py | 2 +- .../cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sopht/simulator/immersed_body/__init__.py b/sopht/simulator/immersed_body/__init__.py index e402323..42ecc5f 100644 --- a/sopht/simulator/immersed_body/__init__.py +++ b/sopht/simulator/immersed_body/__init__.py @@ -3,3 +3,4 @@ from .rigid_body import * from .cosserat_rod import * from .flow_forces import FlowForces +from .cosserat_rod_cpp import * diff --git a/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_flow_interaction.py b/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_flow_interaction.py index f4a292e..447b8ac 100644 --- a/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_flow_interaction.py +++ b/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_flow_interaction.py @@ -29,7 +29,7 @@ def __init__( ) -> None: """Class initialiser.""" rod, _ = cosserat_rod_simulator.communicate() - n_elems = int(rod.n_elems) + n_elems = int(rod.n_elems[0]) body_flow_forces = np.zeros( (3, n_elems + 1), ) diff --git a/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py b/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py index a92a92e..78b646d 100644 --- a/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py +++ b/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py @@ -8,7 +8,8 @@ class CosseratRodCPPElementCentricForcingGrid(ImmersedBodyForcingGrid): def __init__(self, grid_dim: int, cosserat_rod_simulator) -> None: self.cs = cosserat_rod_simulator - num_lag_nodes = int(self.cs.n_elems) + rod, _ = self.cs.communicate() + num_lag_nodes = int(rod.n_elems[0]) super().__init__(grid_dim, num_lag_nodes) # to ensure position/velocity are consistent during initialisation From 9c5562b156e4e75a6577ba24380284da19734fb5 Mon Sep 17 00:00:00 2001 From: Songyuan Cui Date: Mon, 28 Aug 2023 11:23:53 -0500 Subject: [PATCH 3/3] fix: use python interface get_ functions instead of accessing members directly --- .../cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py b/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py index 78b646d..7603f26 100644 --- a/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py +++ b/sopht/simulator/immersed_body/cosserat_rod_cpp/cosserat_rod_cpp_forcing_grids.py @@ -19,8 +19,7 @@ def __init__(self, grid_dim: int, cosserat_rod_simulator) -> None: def compute_lag_grid_position_field(self) -> None: """Computes location of forcing grid for the Cosserat rod""" rod, _ = self.cs.communicate() - rod_position_field = np.asarray(rod.position_collection) - + rod_position_field = np.asarray(rod.get_position()) self.position_field[...] = ( rod_position_field[: self.grid_dim, 1:] + rod_position_field[: self.grid_dim, :-1] @@ -29,7 +28,7 @@ def compute_lag_grid_position_field(self) -> None: def compute_lag_grid_velocity_field(self) -> None: """Computes velocity of forcing grid points for the Cosserat rod""" rod, _ = self.cs.communicate() - rod_velocity_field = np.asarray(rod.velocity_collection) + rod_velocity_field = np.asarray(rod.get_velocity()) rod_masses = np.asarray(rod.mass) self.velocity_field[...] = node_to_element_velocity( @@ -50,6 +49,7 @@ def transfer_forcing_from_grid_to_body( body_flow_forces[: self.grid_dim, 1:] -= 0.5 * lag_grid_forcing_field body_flow_forces[: self.grid_dim, :-1] -= 0.5 * lag_grid_forcing_field + body_forces_cpp *= 0.0 body_forces_cpp[: self.grid_dim, 1:] -= 0.5 * lag_grid_forcing_field body_forces_cpp[: self.grid_dim, :-1] -= 0.5 * lag_grid_forcing_field