Skip to content

Commit 1092485

Browse files
authored
Merge pull request #294 from Rahul-JOON/290_Mesh_Demonstration
RodMeshSurfaceContact class init
2 parents 4340ace + 046a3f0 commit 1092485

File tree

9 files changed

+548
-8
lines changed

9 files changed

+548
-8
lines changed

elastica/contact_forces.py

Lines changed: 265 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
from elastica.typing import RodType, SystemType, AllowedContactType
44
from elastica.rod import RodBase
55
from elastica.rigidbody import Cylinder
6+
from elastica.surface import MeshSurface
67
from elastica.contact_utils import (
78
_dot_product,
89
_norm,
910
_find_min_dist,
1011
_prune_using_aabbs_rod_cylinder,
1112
_prune_using_aabbs_rod_rod,
13+
find_contact_faces_idx,
1214
)
13-
14-
from elastica._linalg import _batch_product_k_ik_to_ik
15+
from elastica.interaction import node_to_element_velocity, elements_to_nodes_inplace
16+
from elastica._linalg import _batch_product_k_ik_to_ik, _batch_dot, _batch_norm
1517
import numba
1618
import numpy as np
1719

@@ -423,6 +425,122 @@ def _calculate_contact_forces_self_rod(
423425
external_forces_rod[..., j + 1] += net_contact_force
424426

425427

428+
@numba.njit(cache=True)
429+
def _calculate_contact_forces_rod_mesh_surface(
430+
faces_normals: np.ndarray,
431+
faces_centers: np.ndarray,
432+
element_position: np.ndarray,
433+
position_idx_array: np.ndarray,
434+
face_idx_array,
435+
surface_tol: float,
436+
k: float,
437+
nu: float,
438+
radius: np.array,
439+
mass: np.array,
440+
velocity_collection: np.ndarray,
441+
external_forces: np.ndarray,
442+
) -> tuple:
443+
"""
444+
This function computes the plane force response on the element, in the
445+
case of contact. Contact model given in Eqn 4.8 Gazzola et. al. RSoS 2018 paper
446+
is used.
447+
448+
Parameters
449+
----------
450+
faces_normals: np.ndarray
451+
mesh cell's normal vectors
452+
faces_centers: np.ndarray
453+
mesh cell's center points
454+
element_position: np.ndarray
455+
rod element's center points
456+
position_idx_array: np.ndarray
457+
rod element's index array
458+
face_idx_array: np.ndarray
459+
mesh cell's index array
460+
surface_tol: float
461+
Penetration tolerance between the surface and the rod-like object
462+
k: float
463+
Contact spring constant
464+
nu: float
465+
Contact damping constant
466+
radius: np.array
467+
rod element's radius
468+
mass: np.array
469+
rod element's mass
470+
velocity_collection: np.ndarray
471+
rod element's velocity
472+
external_forces: np.ndarray
473+
rod element's external forces
474+
475+
Returns
476+
-------
477+
magnitude of the plane response
478+
"""
479+
480+
# Damping force response due to velocity towards the plane
481+
element_velocity = node_to_element_velocity(
482+
mass=mass, node_velocity_collection=velocity_collection
483+
)
484+
485+
if len(face_idx_array) > 0:
486+
element_position_contacts = element_position[:, position_idx_array]
487+
contact_face_centers = faces_centers[:, face_idx_array]
488+
normals_on_elements = faces_normals[:, face_idx_array]
489+
radius_contacts = radius[position_idx_array]
490+
element_velocity_contacts = element_velocity[:, position_idx_array]
491+
492+
else:
493+
element_position_contacts = element_position
494+
contact_face_centers = np.zeros_like(element_position)
495+
normals_on_elements = np.zeros_like(element_position)
496+
radius_contacts = radius
497+
element_velocity_contacts = element_velocity
498+
499+
# Elastic force response due to penetration
500+
501+
distance_from_plane = _batch_dot(
502+
normals_on_elements, (element_position_contacts - contact_face_centers)
503+
)
504+
plane_penetration = (
505+
-np.abs(np.minimum(distance_from_plane - radius_contacts, 0.0)) ** 1.5
506+
)
507+
elastic_force = -k * _batch_product_k_ik_to_ik(
508+
plane_penetration, normals_on_elements
509+
)
510+
511+
normal_component_of_element_velocity = _batch_dot(
512+
normals_on_elements, element_velocity_contacts
513+
)
514+
damping_force = -nu * _batch_product_k_ik_to_ik(
515+
normal_component_of_element_velocity, normals_on_elements
516+
)
517+
518+
# Compute total plane response force
519+
plane_response_force_contacts = elastic_force + damping_force
520+
521+
# Check if the rod elements are in contact with plane.
522+
no_contact_point_idx = np.where(
523+
(distance_from_plane - radius_contacts) > surface_tol
524+
)[0]
525+
# If rod element does not have any contact with plane, plane cannot apply response
526+
# force on the element. Thus lets set plane response force to 0.0 for the no contact points.
527+
plane_response_force_contacts[..., no_contact_point_idx] = 0.0
528+
529+
plane_response_forces = np.zeros_like(external_forces)
530+
for i in range(len(position_idx_array)):
531+
plane_response_forces[
532+
:, position_idx_array[i]
533+
] += plane_response_force_contacts[:, i]
534+
535+
# Update the external forces
536+
elements_to_nodes_inplace(plane_response_forces, external_forces)
537+
return (
538+
_batch_norm(plane_response_force_contacts),
539+
no_contact_point_idx,
540+
normals_on_elements,
541+
)
542+
543+
426544
class RodRodContact(NoContact):
427545
"""
428546
This class is for applying contact forces between rod-rod.
@@ -725,3 +843,148 @@ def apply_contact(
725843
self.k,
726844
self.nu,
727845
)
846+
847+
848+
class RodMeshSurfaceContactWithGridMethod(NoContact):
849+
"""
850+
This class is for applying contact forces between rod-mesh_surface.
851+
First system is always rod and second system is always mesh_surface.
852+
853+
Examples
854+
--------
855+
How to define contact between rod and mesh_surface.
856+
857+
>>> simulator.detect_contact_between(rod, mesh_surface).using(
858+
... RodMeshSurfaceContactWithGridMethod,
859+
... k=1e4,
860+
... nu=10,
861+
... surface_tol=1e-2,
862+
... )
863+
"""
864+
865+
def __init__(
866+
self, k: float, nu: float, faces_grid: dict, grid_size: float, surface_tol=1e-4
867+
):
868+
"""
869+
870+
Parameters
871+
----------
872+
k: float
873+
Stiffness coefficient between the plane and the rod-like object.
874+
nu: float
875+
Dissipation coefficient between the plane and the rod-like object.
876+
faces_grid: dict
877+
Dictionary containing the grid information of the mesh surface.
878+
grid_size: float
879+
Grid size of the mesh surface.
880+
surface_tol: float
881+
Penetration tolerance between the surface and the rod-like object.
882+
883+
"""
884+
super(RodMeshSurfaceContactWithGridMethod, self).__init__()
885+
# n_faces = faces.shape[-1]
886+
self.k = k
887+
self.nu = nu
888+
self.faces_grid = faces_grid
889+
self.grid_size = grid_size
890+
self.surface_tol = surface_tol
891+
892+
def _check_systems_validity(
893+
self,
894+
system_one: SystemType,
895+
system_two: AllowedContactType,
896+
faces_grid: dict,
897+
grid_size: float,
898+
) -> None:
899+
"""
900+
This checks the contact order and type of a SystemType object and an AllowedContactType object.
901+
For the RodMeshSurfaceContact class first_system should be a rod and second_system should be a mesh_surface;
902+
morever, the imported grid's attributes should match imported rod-mesh_surface(in contact) grid's attributes.
903+
904+
Parameters
905+
----------
906+
system_one
907+
SystemType
908+
system_two
909+
AllowedContactType
910+
"""
911+
if not issubclass(system_one.__class__, RodBase) or not issubclass(
912+
system_two.__class__, MeshSurface
913+
):
914+
raise TypeError(
915+
"Systems provided to the contact class have incorrect order/type. \n"
916+
" First system is {0} and second system is {1}. \n"
917+
" First system should be a rod, second should be a mesh surface".format(
918+
system_one.__class__, system_two.__class__
919+
)
920+
)
921+
922+
elif not faces_grid["grid_size"] == grid_size:
923+
raise TypeError(
924+
"Imported grid size does not match with the current rod-mesh_surface grid size. "
925+
)
926+
927+
elif not faces_grid["model_path"] == system_two.model_path:
928+
raise TypeError(
929+
"Imported grid's model path does not match with the current mesh_surface model path. "
930+
)
931+
932+
elif not faces_grid["surface_reorient"] == system_two.mesh_orientation:
933+
raise TypeError(
934+
"Imported grid's surface orientation does not match with the current mesh_surface rientation. "
935+
)
936+
937+
def apply_contact(
938+
self, system_one: RodType, system_two: AllowedContactType
939+
) -> tuple:
940+
"""
941+
In the case of contact with the plane, this function computes the plane reaction force on the element.
942+
943+
Parameters
944+
----------
945+
system_one: object
946+
Rod-like object.
947+
system_two: Surface
948+
Mesh surface.
949+
950+
Returns
951+
-------
952+
plane_response_force_mag : numpy.ndarray
953+
1D (blocksize) array containing data with 'float' type.
954+
Magnitude of plane response force acting on rod-like object.
955+
no_contact_point_idx : numpy.ndarray
956+
1D (blocksize) array containing data with 'int' type.
957+
Index of rod-like object elements that are not in contact with the plane.
958+
"""
959+
960+
self.mesh_surface_faces = system_two.faces
961+
self.mesh_surface_x_min = np.min(self.mesh_surface_faces[0, :, :])
962+
self.mesh_surface_y_min = np.min(self.mesh_surface_faces[1, :, :])
963+
self.mesh_surface_face_normals = system_two.face_normals
964+
self.mesh_surface_face_centers = system_two.face_centers
965+
(
966+
self.position_idx_array,
967+
self.face_idx_array,
968+
self.element_position,
969+
) = find_contact_faces_idx(
970+
self.faces_grid,
971+
self.mesh_surface_x_min,
972+
self.mesh_surface_y_min,
973+
self.grid_size,
974+
system_one.position_collection,
975+
)
976+
977+
return _calculate_contact_forces_rod_mesh_surface(
978+
self.mesh_surface_face_normals,
979+
self.mesh_surface_face_centers,
980+
self.element_position,
981+
self.position_idx_array,
982+
self.face_idx_array,
983+
self.surface_tol,
984+
self.k,
985+
self.nu,
986+
system_one.radius,
987+
system_one.mass,
988+
system_one.velocity_collection,
989+
system_one.external_forces,
990+
)

0 commit comments

Comments
 (0)