Beispiel #1
0
def _compute_shear_stretch_strains(
    position_collection,
    volume,
    lengths,
    tangents,
    radius,
    rest_lengths,
    rest_voronoi_lengths,
    dilatation,
    voronoi_dilatation,
    director_collection,
    sigma,
):
    # Quick trick : Instead of evaliation Q(et-d^3), use property that Q*d3 = (0,0,1), a constant
    _compute_all_dilatations(
        position_collection,
        volume,
        lengths,
        tangents,
        radius,
        dilatation,
        rest_lengths,
        rest_voronoi_lengths,
        voronoi_dilatation,
    )

    z_vector = np.array([0.0, 0.0, 1.0]).reshape(3, -1)
    sigma[:] = dilatation * _batch_matvec(director_collection,
                                          tangents) - z_vector
Beispiel #2
0
    def compute_shear_energy(self):

        sigma_diff = self.sigma - self.rest_sigma
        shear_internal_torques = _batch_matvec(self.shear_matrix, sigma_diff)

        return (0.5 * (_batch_dot(sigma_diff, shear_internal_torques) *
                       self.rest_lengths).sum())
Beispiel #3
0
    def compute_bending_energy(self):

        kappa_diff = self.kappa - self.rest_kappa
        bending_internal_torques = _batch_matvec(self.bend_matrix, kappa_diff)

        return (0.5 * (_batch_dot(kappa_diff, bending_internal_torques) *
                       self.rest_voronoi_lengths).sum())
    def test_update_acceleration(self, n_elem):
        """
        In this test case, we initialize a straight rod.
        We set correct parameters for rod mass, dilatation, mass moment of inertia
        and call the function update_accelerations and compare the angular and
        translational acceleration with the correct values.
        This test case tests,
            update_accelerations
            _update_accelerations

        """

        initial, test_rod = constructor(n_elem, nu=0.0)
        mass = test_rod.mass

        external_forces = np.zeros(3 * (n_elem + 1)).reshape(3, n_elem + 1)
        external_torques = np.zeros(3 * n_elem).reshape(3, n_elem)

        for i in range(0, n_elem):
            external_torques[..., i] = np.random.rand(3)

        for i in range(0, n_elem + 1):
            external_forces[..., i] = np.random.rand(3)

        test_rod.external_forces[:] = external_forces
        test_rod.external_torques[:] = external_torques

        # No dilatation in the rods
        dilatations = np.ones(n_elem)

        # Set mass moment of inertia matrix to identity matrix for convenience.
        # Inverse of identity = identity
        inv_mass_moment_of_inertia = np.repeat(np.identity(3)[:, :,
                                                              np.newaxis],
                                               n_elem,
                                               axis=2)
        test_rod.inv_mass_second_moment_of_inertia[:] = inv_mass_moment_of_inertia

        # Compute acceleration
        test_rod.update_accelerations(time=0)

        correct_acceleration = external_forces / mass
        assert_allclose(
            test_rod.acceleration_collection,
            correct_acceleration,
            atol=Tolerance.atol(),
        )

        correct_angular_acceleration = (
            _batch_matvec(inv_mass_moment_of_inertia, external_torques) *
            dilatations)

        assert_allclose(
            test_rod.alpha_collection,
            correct_angular_acceleration,
            atol=Tolerance.atol(),
        )
Beispiel #5
0
def test_batch_matvec(blocksize):
    input_matrix_collection = np.random.randn(3, 3, blocksize)
    input_vector_collection = np.random.randn(3, blocksize)

    test_vector_collection = _batch_matvec(input_matrix_collection,
                                           input_vector_collection)

    correct_vector_collection = [
        np.dot(input_matrix_collection[..., i],
               input_vector_collection[..., i]) for i in range(blocksize)
    ]
    correct_vector_collection = np.array(correct_vector_collection).T

    assert_allclose(test_vector_collection, correct_vector_collection)
Beispiel #6
0
def _compute_internal_bending_twist_stresses_from_model(
    director_collection,
    rest_voronoi_lengths,
    internal_couple,
    bend_matrix,
    kappa,
    rest_kappa,
):
    """
    Linear force functional
    Operates on
    B : (3,3,n) tensor and curvature kappa (3,n)

    Returns
    -------

    """
    _compute_bending_twist_strains(director_collection, rest_voronoi_lengths,
                                   kappa)  # concept : needs to compute kappa
    internal_couple[:] = _batch_matvec(bend_matrix, kappa - rest_kappa)
Beispiel #7
0
def _compute_internal_shear_stretch_stresses_from_model(
    position_collection,
    volume,
    lengths,
    tangents,
    radius,
    rest_lengths,
    rest_voronoi_lengths,
    dilatation,
    voronoi_dilatation,
    director_collection,
    sigma,
    rest_sigma,
    shear_matrix,
    internal_stress,
):
    """
    Linear force functional
    Operates on
    S : (3,3,n) tensor and sigma (3,n)

    Returns
    -------

    """
    _compute_shear_stretch_strains(
        position_collection,
        volume,
        lengths,
        tangents,
        radius,
        rest_lengths,
        rest_voronoi_lengths,
        dilatation,
        voronoi_dilatation,
        director_collection,
        sigma,
    )
    internal_stress[:] = _batch_matvec(shear_matrix, sigma - rest_sigma)
Beispiel #8
0
def _compute_internal_torques(
    position_collection,
    velocity_collection,
    tangents,
    lengths,
    rest_lengths,
    director_collection,
    rest_voronoi_lengths,
    bend_matrix,
    rest_kappa,
    kappa,
    voronoi_dilatation,
    mass_second_moment_of_inertia,
    omega_collection,
    internal_stress,
    internal_couple,
    dilatation,
    dilatation_rate,
    dissipation_constant_for_torques,
    damping_torques,
    internal_torques,
):
    # Compute \tau_l and cache it using internal_couple
    # Be careful about usage though
    _compute_internal_bending_twist_stresses_from_model(
        director_collection,
        rest_voronoi_lengths,
        internal_couple,
        bend_matrix,
        kappa,
        rest_kappa,
    )
    # Compute dilatation rate when needed, dilatation itself is done before
    # in internal_stresses
    _compute_dilatation_rate(position_collection, velocity_collection, lengths,
                             rest_lengths, dilatation_rate)

    # FIXME: change memory overload instead for the below calls!
    voronoi_dilatation_inv_cube_cached = 1.0 / voronoi_dilatation**3
    # Delta(\tau_L / \Epsilon^3)
    bend_twist_couple_2D = difference_kernel(
        internal_couple * voronoi_dilatation_inv_cube_cached)
    # \mathcal{A}[ (\kappa x \tau_L ) * \hat{D} / \Epsilon^3 ]
    bend_twist_couple_3D = quadrature_kernel(
        _batch_cross(kappa, internal_couple) * rest_voronoi_lengths *
        voronoi_dilatation_inv_cube_cached)
    # (Qt x n_L) * \hat{l}
    shear_stretch_couple = (_batch_cross(
        _batch_matvec(director_collection, tangents), internal_stress) *
                            rest_lengths)

    # I apply common sub expression elimination here, as J w / e is used in both the lagrangian and dilatation
    # terms
    # TODO : the _batch_matvec kernel needs to depend on the representation of J, and should be coded as such
    J_omega_upon_e = (
        _batch_matvec(mass_second_moment_of_inertia, omega_collection) /
        dilatation)

    # (J \omega_L / e) x \omega_L
    # Warning : Do not do micro-optimization here : you can ignore dividing by dilatation as we later multiply by it
    # but this causes confusion and violates SRP
    lagrangian_transport = _batch_cross(J_omega_upon_e, omega_collection)

    # Note : in the computation of dilatation_rate, there is an optimization opportunity as dilatation rate has
    # a dilatation-like term in the numerator, which we cancel here
    # (J \omega_L / e^2) . (de/dt)
    unsteady_dilatation = J_omega_upon_e * dilatation_rate / dilatation

    _compute_damping_torques(damping_torques, omega_collection,
                             dissipation_constant_for_torques, lengths)

    blocksize = internal_torques.shape[1]
    for i in range(3):
        for k in range(blocksize):
            internal_torques[i, k] = (bend_twist_couple_2D[i, k] +
                                      bend_twist_couple_3D[i, k] +
                                      shear_stretch_couple[i, k] +
                                      lagrangian_transport[i, k] +
                                      unsteady_dilatation[i, k] -
                                      damping_torques[i, k])
Beispiel #9
0
 def compute_rotational_energy(self):
     J_omega_upon_e = (_batch_matvec(self.mass_second_moment_of_inertia,
                                     self.omega_collection) /
                       self.dilatation)
     return 0.5 * np.einsum("ik,ik->k", self.omega_collection,
                            J_omega_upon_e).sum()
Beispiel #10
0
def anisotropic_friction(
    plane_origin,
    plane_normal,
    surface_tol,
    slip_velocity_tol,
    k,
    nu,
    kinetic_mu_forward,
    kinetic_mu_backward,
    kinetic_mu_sideways,
    static_mu_forward,
    static_mu_backward,
    static_mu_sideways,
    radius,
    tangents,
    position_collection,
    director_collection,
    velocity_collection,
    omega_collection,
    internal_forces,
    external_forces,
    internal_torques,
    external_torques,
):
    plane_response_force_mag, no_contact_point_idx = apply_normal_force_numba(
        plane_origin,
        plane_normal,
        surface_tol,
        k,
        nu,
        radius,
        position_collection,
        velocity_collection,
        internal_forces,
        external_forces,
    )

    # First compute component of rod tangent in plane. Because friction forces acts in plane not out of plane. Thus
    # axial direction has to be in plane, it cannot be out of plane. We are projecting rod element tangent vector in
    # to the plane. So friction forces can only be in plane forces and not out of plane.
    tangent_along_normal_direction = _batch_product_i_ik_to_k(plane_normal, tangents)
    tangent_perpendicular_to_normal_direction = tangents - _batch_product_i_k_to_ik(
        plane_normal, tangent_along_normal_direction
    )
    tangent_perpendicular_to_normal_direction_mag = _batch_norm(
        tangent_perpendicular_to_normal_direction
    )
    # Normalize tangent_perpendicular_to_normal_direction. This is axial direction for plane. Here we are adding
    # small tolerance (1e-10) for normalization, in order to prevent division by 0.
    axial_direction = _batch_product_k_ik_to_ik(
        1 / (tangent_perpendicular_to_normal_direction_mag + 1e-14),
        tangent_perpendicular_to_normal_direction,
    )
    element_velocity = node_to_element_pos_or_vel(velocity_collection)
    # first apply axial kinetic friction
    velocity_mag_along_axial_direction = _batch_dot(element_velocity, axial_direction)
    velocity_along_axial_direction = _batch_product_k_ik_to_ik(
        velocity_mag_along_axial_direction, axial_direction
    )

    # Friction forces depends on the direction of velocity, in other words sign
    # of the velocity vector.
    velocity_sign_along_axial_direction = np.sign(velocity_mag_along_axial_direction)
    # Check top for sign convention
    kinetic_mu = 0.5 * (
        kinetic_mu_forward * (1 + velocity_sign_along_axial_direction)
        + kinetic_mu_backward * (1 - velocity_sign_along_axial_direction)
    )
    # Call slip function to check if elements slipping or not
    slip_function_along_axial_direction = find_slipping_elements(
        velocity_along_axial_direction, slip_velocity_tol
    )
    kinetic_friction_force_along_axial_direction = -(
        (1.0 - slip_function_along_axial_direction)
        * kinetic_mu
        * plane_response_force_mag
        * velocity_sign_along_axial_direction
        * axial_direction
    )
    # If rod element does not have any contact with plane, plane cannot apply friction
    # force on the element. Thus lets set kinetic friction force to 0.0 for the no contact points.
    kinetic_friction_force_along_axial_direction[..., no_contact_point_idx] = 0.0
    elements_to_nodes_inplace(
        kinetic_friction_force_along_axial_direction, external_forces
    )

    # Now rolling kinetic friction
    rolling_direction = _batch_vec_oneD_vec_cross(axial_direction, plane_normal)
    torque_arm = _batch_product_i_k_to_ik(-plane_normal, radius)
    velocity_along_rolling_direction = _batch_dot(element_velocity, rolling_direction)
    directors_transpose = _batch_matrix_transpose(director_collection)
    # w_rot = Q.T @ omega @ Q @ r
    rotation_velocity = _batch_matvec(
        directors_transpose,
        _batch_cross(omega_collection, _batch_matvec(director_collection, torque_arm)),
    )
    rotation_velocity_along_rolling_direction = _batch_dot(
        rotation_velocity, rolling_direction
    )
    slip_velocity_mag_along_rolling_direction = (
        velocity_along_rolling_direction + rotation_velocity_along_rolling_direction
    )
    slip_velocity_along_rolling_direction = _batch_product_k_ik_to_ik(
        slip_velocity_mag_along_rolling_direction, rolling_direction
    )
    slip_velocity_sign_along_rolling_direction = np.sign(
        slip_velocity_mag_along_rolling_direction
    )
    slip_function_along_rolling_direction = find_slipping_elements(
        slip_velocity_along_rolling_direction, slip_velocity_tol
    )
    kinetic_friction_force_along_rolling_direction = -(
        (1.0 - slip_function_along_rolling_direction)
        * kinetic_mu_sideways
        * plane_response_force_mag
        * slip_velocity_sign_along_rolling_direction
        * rolling_direction
    )
    # If rod element does not have any contact with plane, plane cannot apply friction
    # force on the element. Thus lets set kinetic friction force to 0.0 for the no contact points.
    kinetic_friction_force_along_rolling_direction[..., no_contact_point_idx] = 0.0
    elements_to_nodes_inplace(
        kinetic_friction_force_along_rolling_direction, external_forces
    )
    # torque = Q @ r @ Fr
    external_torques += _batch_matvec(
        director_collection,
        _batch_cross(torque_arm, kinetic_friction_force_along_rolling_direction),
    )

    # now axial static friction
    nodal_total_forces = _batch_vector_sum(internal_forces, external_forces)
    element_total_forces = nodes_to_elements(nodal_total_forces)
    force_component_along_axial_direction = _batch_dot(
        element_total_forces, axial_direction
    )
    force_component_sign_along_axial_direction = np.sign(
        force_component_along_axial_direction
    )
    # check top for sign convention
    static_mu = 0.5 * (
        static_mu_forward * (1 + force_component_sign_along_axial_direction)
        + static_mu_backward * (1 - force_component_sign_along_axial_direction)
    )
    max_friction_force = (
        slip_function_along_axial_direction * static_mu * plane_response_force_mag
    )
    # friction = min(mu N, pushing force)
    static_friction_force_along_axial_direction = -(
        np.minimum(np.fabs(force_component_along_axial_direction), max_friction_force)
        * force_component_sign_along_axial_direction
        * axial_direction
    )
    # If rod element does not have any contact with plane, plane cannot apply friction
    # force on the element. Thus lets set static friction force to 0.0 for the no contact points.
    static_friction_force_along_axial_direction[..., no_contact_point_idx] = 0.0
    elements_to_nodes_inplace(
        static_friction_force_along_axial_direction, external_forces
    )

    # now rolling static friction
    # there is some normal, tangent and rolling directions inconsitency from Elastica
    total_torques = _batch_matvec(
        directors_transpose, (internal_torques + external_torques)
    )
    # Elastica has opposite defs of tangents in interaction.h and rod.cpp
    total_torques_along_axial_direction = _batch_dot(total_torques, axial_direction)
    force_component_along_rolling_direction = _batch_dot(
        element_total_forces, rolling_direction
    )
    noslip_force = -(
        (
            radius * force_component_along_rolling_direction
            - 2.0 * total_torques_along_axial_direction
        )
        / 3.0
        / radius
    )
    max_friction_force = (
        slip_function_along_rolling_direction
        * static_mu_sideways
        * plane_response_force_mag
    )
    noslip_force_sign = np.sign(noslip_force)
    static_friction_force_along_rolling_direction = (
        np.minimum(np.fabs(noslip_force), max_friction_force)
        * noslip_force_sign
        * rolling_direction
    )
    # If rod element does not have any contact with plane, plane cannot apply friction
    # force on the element. Thus lets set plane static friction force to 0.0 for the no contact points.
    static_friction_force_along_rolling_direction[..., no_contact_point_idx] = 0.0
    elements_to_nodes_inplace(
        static_friction_force_along_rolling_direction, external_forces
    )
    external_torques += _batch_matvec(
        director_collection,
        _batch_cross(torque_arm, static_friction_force_along_rolling_direction),
    )