Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Magnetic elastica #75

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
a2176aa
initial commit:magnetic elastica under constant B
armantekinalp Apr 17, 2022
ab58d49
initial commit: magnetic beam analytical solution case
armantekinalp Apr 17, 2022
94a0f50
fix path magnetic_beam_comparison
armantekinalp Apr 17, 2022
563b00a
fix:beam comparison analytical solution multiprocessing error
Apr 17, 2022
56cc813
refac: comment for separating magnetic forcing and field info
bhosale2 Apr 19, 2022
9621137
feat: generic magnetic forcing class: apply torques
bhosale2 Apr 19, 2022
9ff755d
feat: generic magnetic forcing class: apply forces
bhosale2 Apr 19, 2022
b6cecc3
enhancement:add magnetic rod.
armantekinalp Apr 20, 2022
9982cf6
refac: remove forces and cleanup torques
bhosale2 Apr 20, 2022
81d614c
feat: magnetic field classes
bhosale2 Apr 20, 2022
3c60ddc
refac: replace tile with broadcast
bhosale2 Apr 20, 2022
0942e89
docs: docs for magnetic field and magnetic forcing class
bhosale2 Apr 20, 2022
ff8a9e9
update:MagneticRod kwargs
armantekinalp Apr 20, 2022
e24029d
black formatting
armantekinalp Apr 20, 2022
1171e6a
update:Magnetic rod simulation and forces
armantekinalp Apr 20, 2022
7d61158
refac: rename comparison -> phase space validation
bhosale2 Apr 20, 2022
59045a4
feat: ramping for oscillation magnetic field
bhosale2 Apr 20, 2022
ebe67a3
refac: factor out rampup code
bhosale2 Apr 20, 2022
5f47510
refac: remove analytical soln copy and cleanup soln
bhosale2 Apr 20, 2022
d896890
feat: base magnetic field class
bhosale2 Apr 20, 2022
66ab493
feat: simple magnetic beam bending illustration case
bhosale2 Apr 21, 2022
f0e9d73
fmt: remove old code
bhosale2 Apr 21, 2022
7546a74
:bug: fix: ramp up factor function
bhosale2 Apr 22, 2022
e21e131
feat: base test file for magnetic forces
bhosale2 Apr 22, 2022
453896f
feat: tests for all magnetic forces kernels
bhosale2 Apr 22, 2022
e5be4e2
enhancement:magnetic rod under rotating magnetic field
armantekinalp Apr 22, 2022
81ae6a8
docs: remove time round comment
bhosale2 Apr 22, 2022
c19944b
refac: move around files
bhosale2 Apr 22, 2022
895a05a
feat: cilia carpet file
bhosale2 Apr 25, 2022
e6c3be0
modify test params for cleaner videos
bhosale2 Apr 26, 2022
0bd06b5
enhancement:Test cases for magnetic rod and its wrappers
armantekinalp Apr 26, 2022
7cf41f8
fmt:black
armantekinalp Apr 26, 2022
df13269
Merge branch 'magnetic_elastica' of https://github.com/armantekinalp/…
armantekinalp Apr 26, 2022
95e2da8
feat: add main pytest call
bhosale2 Apr 26, 2022
48ced0c
docs: add doctsring at file top
bhosale2 Apr 26, 2022
b2abb13
refac: remove unwanted import
bhosale2 Apr 26, 2022
17f2e03
Organize Magnetic rod example folder
armantekinalp Apr 26, 2022
8d2d9eb
Rename MagneticElastica as MagneticRodCases
armantekinalp Apr 26, 2022
d519523
clean up the magnetic rod cases, add documentation and change import
armantekinalp Apr 26, 2022
72677f2
update:Example readme file is updated and magnetic rod cases are added
armantekinalp Apr 26, 2022
9e563c7
enhancement:Magneticmilipede initial commit
armantekinalp May 11, 2022
a0706ba
feat : track velocity in magnetic example
tp5uiuc May 24, 2022
e20f2b8
Merge pull request #1 from armantekinalp/magnetic_example_track_velocity
armantekinalp May 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions elastica/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@
from elastica.timestepper import *
from elastica.restart import *
from elastica.reset_functions_for_block_structure import *
from elastica.rod.magnetic_rod import *
from elastica.magnetic_forces import *
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,337 @@
import numpy as np
import numba
from numba import njit
from elastica.joint import FreeJoint
from elastica._linalg import _batch_norm, _batch_matvec


def get_connection_vector_for_perpendicular_rods(
rod_one,
rod_two,
rod_one_index,
rod_two_index,
):
"""
This function computes the connection vectors in from rod one to rod two and rod two to rod one.
Here we are assuming rod two tip is connected with rod one. Becareful with rod orders.
Parameters
----------
rod_one : rod object
rod_two : rod object
rod_one_index : int
rod_two_index : int

Returns
-------

"""

# Compute rod element positions
rod_one_element_position = 0.5 * (
rod_one.position_collection[..., 1:] + rod_one.position_collection[..., :-1]
)
rod_one_element_position = rod_one_element_position[:, rod_one_index]
rod_two_element_position = 0.5 * (
rod_two.position_collection[..., 1:] + rod_two.position_collection[..., :-1]
)
rod_two_element_position = rod_two_element_position[:, rod_two_index]

# Lets get the distance between rod elements
distance_vector_rod_one_to_rod_two = (
rod_two_element_position - rod_one_element_position
)
distance_vector_rod_one_to_rod_two_norm = np.linalg.norm(
distance_vector_rod_one_to_rod_two
)
distance_vector_rod_one_to_rod_two /= distance_vector_rod_one_to_rod_two_norm

distance_vector_rod_two_to_rod_one = -distance_vector_rod_one_to_rod_two

rod_one_direction_vec_in_material_frame = (
rod_one.director_collection[:, :, rod_one_index]
@ distance_vector_rod_one_to_rod_two
)

rod_two_direction_vec_in_material_frame = (
rod_two.director_collection[:, :, rod_two_index]
@ distance_vector_rod_two_to_rod_one
)

offset_btw_rods = distance_vector_rod_one_to_rod_two_norm - (
rod_one.radius[rod_one_index] + rod_two.lengths[rod_two_index] / 2
)

return (
rod_one_direction_vec_in_material_frame,
rod_two_direction_vec_in_material_frame,
offset_btw_rods,
)


class PerpendicularRodsConnection(FreeJoint):
"""
This is a connection class to connect two perpendicular rod elements.
We are connecting rod two tip element with rod one.
"""

def __init__(
self,
k,
nu,
k_repulsive,
kt,
rod_one_direction_vec_in_material_frame,
rod_two_direction_vec_in_material_frame,
offset_btw_rods,
**kwargs,
):

super().__init__(np.array(k), np.array(nu))

self.k_repulsive = np.array(k_repulsive)
self.kt = kt

self.offset_btw_rods = np.array(offset_btw_rods)

self.rod_one_direction_vec_in_material_frame = np.array(
rod_one_direction_vec_in_material_frame
).T
self.rod_two_direction_vec_in_material_frame = np.array(
rod_two_direction_vec_in_material_frame
).T

def apply_forces(self, rod_one, index_one, rod_two, index_two):
(self.rod_one_rd2, self.rod_two_ld3, self.spring_force,) = self._apply_forces(
self.k,
self.nu,
self.k_repulsive,
index_one,
index_two,
self.rod_one_direction_vec_in_material_frame,
self.rod_two_direction_vec_in_material_frame,
self.offset_btw_rods,
rod_one.director_collection,
rod_two.director_collection,
rod_one.position_collection,
rod_two.position_collection,
rod_one.radius,
rod_two.lengths,
rod_one.dilatation,
rod_two.dilatation,
rod_one.velocity_collection,
rod_two.velocity_collection,
rod_one.external_forces,
rod_two.external_forces,
)

@staticmethod
@njit(cache=True)
def _apply_forces(
k,
nu,
k_repulsive,
index_one,
index_two,
rod_one_direction_vec_in_material_frame,
rod_two_direction_vec_in_material_frame,
rest_offset_btw_rods,
rod_one_director_collection,
rod_two_director_collection,
rod_one_position_collection,
rod_two_position_collection,
rod_one_radius,
rod_two_lengths,
rod_one_dilatation,
rod_two_dilatation,
rod_one_velocity_collection,
rod_two_velocity_collection,
rod_one_external_forces,
rod_two_external_forces,
):

rod_one_to_rod_two_connection_vec = (
rod_one_director_collection[:, :, index_one].T
@ rod_one_direction_vec_in_material_frame
).reshape(3)
rod_two_to_rod_one_connection_vec = (
rod_two_director_collection[:, :, index_two].T
@ rod_two_direction_vec_in_material_frame
).reshape(3)

# Compute element positions
rod_one_element_position = 0.5 * (
rod_one_position_collection[:, index_one]
+ rod_one_position_collection[:, index_one + 1]
)
rod_two_element_position = 0.5 * (
rod_two_position_collection[:, index_two]
+ rod_two_position_collection[:, index_two + 1]
)

# If there is an offset between rod one and rod two surface, then it should change as a function of dilatation.
offset_rod_one = (
0.5 * rest_offset_btw_rods / np.sqrt(rod_one_dilatation[index_one])
)
offset_rod_two = 0.5 * rest_offset_btw_rods * rod_two_dilatation[index_two]

# Compute vector r*d2 (radius * connection vector) for each rod and element
rod_one_rd2 = rod_one_to_rod_two_connection_vec * (
rod_one_radius[index_one] + offset_rod_one
)
# Compute vector ld3 (radius * connection vector) for rod two to one
rod_two_ld3 = rod_two_to_rod_one_connection_vec * (
rod_two_lengths[index_two] / 2 + offset_rod_two
)

# Compute connection points on the rod surfaces
surface_position_rod_one = rod_one_element_position + rod_one_rd2
surface_position_rod_two = rod_two_element_position + rod_two_ld3

# Compute spring force between two rods
distance_vector = surface_position_rod_two - surface_position_rod_one
np.round_(distance_vector, 12, distance_vector)
spring_force = k * (distance_vector)

# Damping force
rod_one_element_velocity = 0.5 * (
rod_one_velocity_collection[:, index_one]
+ rod_one_velocity_collection[:, index_one + 1]
)
rod_two_element_velocity = 0.5 * (
rod_two_velocity_collection[:, index_two]
+ rod_two_velocity_collection[:, index_two + 1]
)

relative_velocity = rod_two_element_velocity - rod_one_element_velocity

distance = np.linalg.norm(distance_vector)

if distance >= 1e-12:
normalized_distance_vector = distance_vector / distance
else:
normalized_distance_vector = np.zeros(
3,
)

normal_relative_velocity_vector = (
np.dot(relative_velocity, normalized_distance_vector)
* normalized_distance_vector
)

damping_force = -nu * normal_relative_velocity_vector

# Compute the total force
total_force = spring_force + damping_force

# Compute contact forces. Contact forces are applied in the case one rod penetrates to the other, in that case
# we apply a repulsive force.
center_distance = rod_two_element_position - rod_one_element_position
center_distance_unit_vec = center_distance / np.linalg.norm(center_distance)
penetration = np.linalg.norm(center_distance) - (
rod_one_radius[index_one]
+ offset_rod_one
+ rod_two_lengths[index_two] / 2
+ offset_rod_two
)

round(penetration, 12)
# Contact present only if rods penetrate to each other
if penetration < 0:
# Hertzian contact
contact_force = (
-k_repulsive * np.abs(penetration) ** (1.5) * center_distance_unit_vec
)
else:
contact_force = np.zeros(
3,
)

# Add contact forces
total_force += contact_force

# Re-distribute forces from elements to nodes.
rod_one_external_forces[..., index_one] += 0.5 * total_force
rod_one_external_forces[..., index_one + 1] += 0.5 * total_force
rod_two_external_forces[..., index_two] -= 0.5 * total_force
rod_two_external_forces[..., index_two + 1] -= 0.5 * total_force

return (
rod_one_rd2,
rod_two_ld3,
spring_force,
)

def apply_torques(self, rod_one, index_one, rod_two, index_two):
self._apply_torques(
self.spring_force,
self.rod_one_rd2,
self.rod_two_ld3,
index_one,
index_two,
rod_one.director_collection,
rod_two.director_collection,
rod_one.external_torques,
rod_two.external_torques,
rod_one.radius,
rod_one.position_collection,
rod_two.position_collection,
self.kt,
)

@staticmethod
@njit(cache=True)
def _apply_torques(
spring_force,
rod_one_rd2,
rod_two_ld3,
index_one,
index_two,
rod_one_director_collection,
rod_two_director_collection,
rod_one_external_torques,
rod_two_external_torques,
rod_one_radius,
rod_one_position_collection,
rod_two_position_collection,
kt,
):
# Compute torques due to the connection forces
torque_on_rod_one = np.cross(rod_one_rd2, spring_force)
torque_on_rod_two = np.cross(rod_two_ld3, -spring_force)

# new method
# We want to make sure rod one and rod two stays perpendicular to each other all the time. So we are
# adding a torque spring to bring rods perpendicular to each other if they deform.
# Compute element positions
rod_one_element_position = 0.5 * (
rod_one_position_collection[:, index_one]
+ rod_one_position_collection[:, index_one + 1]
)
rod_two_element_position = 0.5 * (
rod_two_position_collection[:, index_two]
+ rod_two_position_collection[:, index_two + 1]
)
# Moment arm is in the direction for rod two tangent.
moment_arm_dir = rod_two_ld3 / np.linalg.norm(rod_two_ld3)
moment_arm = rod_one_radius[index_one] * moment_arm_dir + rod_two_ld3
# Starting from rod two element center compute, the target position for rod one element.
rod_one_target_element_position = rod_two_element_position + moment_arm
# If rod one element position is different than target position then apply a torque to bring it back.
distance_vector = rod_one_element_position - rod_one_target_element_position
np.round_(distance_vector, 12, distance_vector)
torque_spring_force = kt * (distance_vector)
spring_torque = np.cross(moment_arm, torque_spring_force)

torque_on_rod_one -= spring_torque
torque_on_rod_two += spring_torque

#
torque_on_rod_one_material_frame = (
rod_one_director_collection[:, :, index_one] @ torque_on_rod_one
)
torque_on_rod_two_material_frame = (
rod_two_director_collection[:, :, index_two] @ torque_on_rod_two
)

rod_one_external_torques[..., index_one] += torque_on_rod_one_material_frame
rod_two_external_torques[..., index_two] += torque_on_rod_two_material_frame
Loading