Exemplo n.º 1
0
 def __init__(self):
     bs = 32
     self.position_collection = np.random.randn(MaxDimension.value(), bs)
     self.director_collection = np.random.randn(MaxDimension.value(),
                                                MaxDimension.value(), bs)
     self.velocity_collection = np.random.randn(MaxDimension.value(), bs)
     self.omega_collection = np.random.randn(MaxDimension.value(), bs)
     self.mass = np.abs(np.random.randn(bs))
     self.external_forces = np.zeros(bs)
Exemplo n.º 2
0
def test_director_if_tangent_and_d3_are_not_same():
    """
    This test is checking the case if the tangent and d3 of the directors
    are not equal to each other.

    Returns
    -------

    """
    n_elems = 10
    start = np.array([0.0, 0.0, 0.0])
    direction = np.array([1.0, 0.0, 0.0])
    normal = np.array([0.0, 0.0, 1.0])
    base_length = 1.0
    base_radius = 0.25
    density = 1000
    nu = 0.1
    youngs_modulus = 1e6
    poisson_ratio = 0.3

    position = np.zeros((3, n_elems + 1))
    end = start + direction * base_length
    for i in range(0, 3):
        position[i, ...] = np.linspace(start[i], end[i], n_elems + 1)

    # Set the directors such that tangent and d3 are not same.
    input_directors = np.zeros(
        (MaxDimension.value(), MaxDimension.value(), n_elems))
    binormal = np.cross(direction, normal)
    normal_collection = np.repeat(binormal[:, np.newaxis], n_elems, axis=1)
    binormal_collection = np.repeat(normal[:, np.newaxis], n_elems, axis=1)
    new_direction = np.cross(binormal, normal)
    direction_collection = np.repeat(new_direction[:, np.newaxis],
                                     n_elems,
                                     axis=1)

    input_directors[0, ...] = normal_collection
    input_directors[1, ...] = binormal_collection
    input_directors[2, ...] = direction_collection

    MockRodForTest.straight_rod(
        n_elems,
        start,
        direction,
        normal,
        base_length,
        base_radius,
        density,
        nu,
        youngs_modulus,
        poisson_ratio,
        position=position,
        directors=input_directors,
    )
Exemplo n.º 3
0
def test_directors_using_input_position_array(n_elems):
    """
    This test is testing the case for which directors are computed
    using the input position array and user defined normal.
    Parameters
    ----------
    n_elems

    Returns
    -------

    """
    start = np.array([0.0, 0.0, 0.0])
    direction = np.array([1.0, 0.0, 0.0])
    normal = np.array([0.0, 0.0, 1.0])
    base_length = 1.0
    base_radius = 0.25
    density = 1000
    nu = 0.1
    youngs_modulus = 1e6
    poisson_ratio = 0.3
    # Check directors, give position as input and let allocate function to compute directors.
    input_position = np.zeros((3, n_elems + 1))
    input_position[0, :] = np.linspace(start[0], start[0] + base_length,
                                       n_elems + 1)

    correct_directors = np.zeros(
        (MaxDimension.value(), MaxDimension.value(), n_elems))
    binormal = np.cross(direction, normal)
    tangent_collection = np.repeat(direction[:, np.newaxis], n_elems, axis=1)
    normal_collection = np.repeat(normal[:, np.newaxis], n_elems, axis=1)
    binormal_collection = np.repeat(binormal[:, np.newaxis], n_elems, axis=1)

    correct_directors[0, ...] = normal_collection
    correct_directors[1, ...] = binormal_collection
    correct_directors[2, ...] = tangent_collection

    mockrod = MockRodForTest.straight_rod(
        n_elems,
        start,
        direction,
        normal,
        base_length,
        base_radius,
        density,
        nu,
        youngs_modulus,
        poisson_ratio,
        position=input_position,
    )
    test_directors = mockrod.director_collection
    assert_allclose(correct_directors, test_directors, atol=Tolerance.atol())
    def test_case_shear_torque(self):
        """
        In this test case we initialize a straight rod with two elements
        and set bending matrix to zero. This gives us opportunity decouple
        shear torque from twist and bending torques in internal torques
        equation. Then we modify node positions of second element and
        introduce artificial bending. Finally, we compute shear torque
        using internal torque function and compare with analytical value.
        This test case is for testing shear torque term,
        in internal torques equation.
        Tested function
            _compute_internal_torques

        """
        n_elem = 2
        initial, test_rod = constructor(n_elem, nu=0.0)
        position = np.zeros((MaxDimension.value(), n_elem + 1))
        position[..., 0] = np.array([0.0, 0.0, 0.0])
        position[..., 1] = np.array([0.0, 0.0, 0.5])
        position[..., 2] = np.array([0.0, -0.3, 0.9])

        test_rod.position_collection = position

        # Simplify the computations, and chose shear matrix as identity matrix.
        test_rod.shear_matrix[:] = np.repeat(np.identity(3)[:, :, np.newaxis],
                                             n_elem - 1,
                                             axis=2)

        # Internal shear stress function is tested previously
        test_rod._compute_internal_shear_stretch_stresses_from_model()

        correct_shear_torques = np.zeros((MaxDimension.value(), n_elem))
        # Correct shear torques can be computed easily.
        # Procedure:
        #       1) Q = [1., 0., 0.; 0., 1., 0.; 0., 0., 1.]
        #       2) t = [0., -0.6, 0.8]
        #       3) sigma = (eQt-d3) = [0.0, -0.6, -0.2]
        #       4) Qt = [0., -0.6, 0.8]
        #       5) torque = Qt x sigma
        # Note that this is not generic, but it does not to be, it is testing the functions.
        correct_shear_torques[..., -1] = np.array([0.3, 0.0, 0.0])

        # Set bending matrix to zero matrix, because we dont want
        # any contribution from bending on total internal torques
        test_rod.bend_matrix[:] = 0.0

        test_torques = test_rod._compute_internal_torques()

        assert_allclose(test_torques,
                        correct_shear_torques,
                        atol=Tolerance.atol())
Exemplo n.º 5
0
    def __init__(self, n_elem):
        """
        This class initialize a straight rod,
        which is for testing interaction functions.
        Parameters
        ----------
        n_elem
        """
        base_length = 1.0
        direction = np.array([0.0, 0.0, 1.0])
        start = np.array([0.0, 0.0, 0.0])

        end = start + direction * base_length
        self.n_elem = n_elem
        self.position_collection = np.zeros((MaxDimension.value(), n_elem + 1))
        for i in range(0, MaxDimension.value()):
            self.position_collection[i, ...] = np.linspace(start[i],
                                                           end[i],
                                                           num=n_elem + 1)

        self.director_collection = np.repeat(np.identity(3)[:, :, np.newaxis],
                                             n_elem,
                                             axis=2)
        self.radius = np.repeat(np.array([0.25]), n_elem, axis=0)
        self.tangents = np.repeat(direction[:, np.newaxis], n_elem, axis=1)
        self.velocity_collection = np.zeros((MaxDimension.value(), n_elem + 1))
        self.omega_collection = np.zeros((MaxDimension.value(), n_elem))
        self.external_forces = np.zeros((MaxDimension.value(), n_elem + 1))
        self.external_torques = np.zeros((MaxDimension.value(), n_elem))
        self.internal_forces = np.zeros((MaxDimension.value(), n_elem + 1))
        self.internal_torques = np.zeros((MaxDimension.value(), n_elem))
        self.lengths = np.ones(n_elem) * base_length / n_elem
Exemplo n.º 6
0
    def __init__(
        self,
        elemental_position_collection,
        dimension_collection,
        elements_per_aabb: int,
    ):
        """
        Doesn't differentiate tangent direction from the rest : potentially harmful as
        maybe you don't need to expand to radius amount in tangential direction

        :param position_collection:
        :param dimension_collection:
        :param elements_per_aabb:
        """
        n_positions = elemental_position_collection.shape[1]  # n_pos
        self.n_aabb = n_positions // elements_per_aabb
        self.n_aabb += (1 if (n_positions % elements_per_aabb) else 0
                        )  # extra if not perfectly divisible
        self.elements_per_aabb = elements_per_aabb
        self.aabb = np.empty(
            (MaxDimension.value(), 2, self.n_aabb))  # 2 for min and max

        assert dimension_collection.shape[1] == n_positions, "bad"

        # Initialize the aabbs posthaste
        self.update(elemental_position_collection, dimension_collection)
Exemplo n.º 7
0
def test_director_if_d3_cross_d2_notequal_to_d1():
    """
    This test is checking the case if the directors, d3xd2 is not equal
    to d1 and creates an AssertionError.
    Returns
    -------

    """
    n_elems = 10
    start = np.array([0.0, 0.0, 0.0])
    direction = np.array([1.0, 0.0, 0.0])
    normal = np.array([0.0, 0.0, 1.0])
    base_length = 1.0
    base_radius = 0.25
    density = 1000
    nu = 0.1
    youngs_modulus = 1e6
    poisson_ratio = 0.3
    # Check directors, give directors as input and check their validity.
    # Let the assertion fail by setting d3=d2 for the input director
    input_directors = np.zeros(
        (MaxDimension.value(), MaxDimension.value(), n_elems))
    binormal = np.cross(direction, normal)
    normal_collection = np.repeat(normal[:, np.newaxis], n_elems, axis=1)
    binormal_collection = np.repeat(binormal[:, np.newaxis], n_elems, axis=1)

    input_directors[0, ...] = normal_collection
    input_directors[1, ...] = binormal_collection
    input_directors[2, ...] = binormal_collection

    MockRodForTest.straight_rod(
        n_elems,
        start,
        direction,
        normal,
        base_length,
        base_radius,
        density,
        nu,
        youngs_modulus,
        poisson_ratio,
        directors=input_directors,
    )
Exemplo n.º 8
0
def test_compute_directors_matrix_using_user_inputs(n_elems):
    """
    This test checks the director array created by allocate function. For this
    test case we use user defined direction, normal to compute directors.
    Returns
    -------

    """
    start = np.array([0.0, 0.0, 0.0])
    direction = np.array([1.0, 0.0, 0.0])
    normal = np.array([0.0, 0.0, 1.0])
    base_length = 1.0
    base_radius = 0.25
    density = 1000
    nu = 0.1
    youngs_modulus = 1e6
    poisson_ratio = 0.3
    # Check directors, if we dont input any directors, computed ones should be valid
    correct_directors = np.zeros(
        (MaxDimension.value(), MaxDimension.value(), n_elems))
    binormal = np.cross(direction, normal)
    tangent_collection = np.repeat(direction[:, np.newaxis], n_elems, axis=1)
    normal_collection = np.repeat(normal[:, np.newaxis], n_elems, axis=1)
    binormal_collection = np.repeat(binormal[:, np.newaxis], n_elems, axis=1)

    correct_directors[0, ...] = normal_collection
    correct_directors[1, ...] = binormal_collection
    correct_directors[2, ...] = tangent_collection

    mockrod = MockRodForTest.straight_rod(
        n_elems,
        start,
        direction,
        normal,
        base_length,
        base_radius,
        density,
        nu,
        youngs_modulus,
        poisson_ratio,
    )
    test_directors = mockrod.director_collection
    assert_allclose(correct_directors, test_directors, atol=Tolerance.atol())
Exemplo n.º 9
0
def compute_geometry_analytically(n_elem):

    initial = BaseClass(n_elem)
    # Construct position array using start and direction vectors.
    # This position array will be our reference for test cases
    end = initial.start + initial.direction * initial.base_length
    position = np.zeros((MaxDimension.value(), n_elem + 1))
    for i in range(0, MaxDimension.value()):
        position[i, ...] = np.linspace(initial.start[i],
                                       end[i],
                                       num=n_elem + 1)

    # Compute geometry
    # length of each element is same we dont need to use position array for calculation of lengths
    rest_lengths = np.repeat(initial.base_length / n_elem, n_elem)

    tangents = np.repeat(initial.direction[:, np.newaxis], n_elem, axis=1)
    radius = np.repeat(initial.base_radius, n_elem)

    return position, rest_lengths, tangents, radius
Exemplo n.º 10
0
def compute_forces_analytically(n_elem, dilatation):
    internal_stress = compute_stress_analytically(n_elem, dilatation)
    # Internal forces in between elements have to be zero, because
    # we compress every element by same amount. Thus we only need
    # to compute forces at the first and last nodes. We know that
    # forces at the first and last node have to be in opposite direction
    # thus we multiply forces on last node with -1.0.
    internal_forces = np.zeros((MaxDimension.value(), n_elem + 1))
    internal_forces[..., 0] = internal_stress[..., 0] / dilatation
    internal_forces[..., -1] = -1.0 * internal_stress[..., 0] / dilatation

    return internal_forces
Exemplo n.º 11
0
    def make_from_aabb(cls, aabb_collection, scale_factor=4):
        # Make position collection and dimension collection arrays from aabb_collection
        # Wasted effort, but only once during construction
        n_aabb_from_lower_level = len(aabb_collection)
        elemental_position_collection = np.zeros(
            (MaxDimension.value(), n_aabb_from_lower_level))
        # (r,r,dl) in (d1,d2,d3) coordinates
        dimension_collection = np.zeros(
            (MaxDimension.value(), n_aabb_from_lower_level))

        for idx, aabb in enumerate(aabb_collection):
            # By design in the bottom level, there's only one AABB. So the last index is always 1
            # Also asserting herre
            assert aabb.n_aabb == 1, "Number of aabbs not 1"
            elemental_position_collection[...,
                                          idx] = 0.5 * (aabb.aabb[..., 0, 0] +
                                                        aabb.aabb[..., 1, 0])
            dimension_collection[..., idx] = 0.5 * (aabb.aabb[..., 1, 0] -
                                                    aabb.aabb[..., 0, 0])

        return cls(elemental_position_collection, dimension_collection,
                   scale_factor)
Exemplo n.º 12
0
 def _compute_internal_torques(self):
     return np.zeros((MaxDimension.value(), self.n_elem))
Exemplo n.º 13
0
def allocate(n_elements,
             start,
             direction,
             normal,
             base_length,
             base_radius,
             density,
             nu,
             youngs_modulus,
             poisson_ratio,
             alpha_c=4.0 / 3.0,
             *args,
             **kwargs):

    # sanity checks here
    assert n_elements > 1
    assert base_length > Tolerance.atol()
    assert np.sqrt(np.dot(normal, normal)) > Tolerance.atol()
    assert np.sqrt(np.dot(direction, direction)) > Tolerance.atol()

    # Set the position array
    position = np.zeros((MaxDimension.value(), n_elements + 1))
    # check if position is in kwargs, if it is use user defined position otherwise generate position
    if kwargs.__contains__("position"):
        position_temp = np.array(kwargs["position"])

        # Check the shape of the input position
        assert position_temp.shape == (MaxDimension.value(), n_elements + 1), (
            "Given position  shape is not correct, it should be " +
            str(position.shape) + " but instead " + str(position_temp.shape))
        # Check if the start position of the rod and first entry of position array are the same
        assert_allclose(
            position_temp[..., 0],
            start,
            atol=Tolerance.atol(),
            err_msg=str("First entry of position" + " (" +
                        str(position_temp[..., 0]) + " ) "
                        " is different than start " + " (" + str(start) +
                        " ) "),
        )
        position = position_temp.copy()

    else:
        end = start + direction * base_length
        for i in range(0, 3):
            position[i, ...] = np.linspace(start[i], end[i], n_elements + 1)

    # Compute rest lengths and tangents
    position_diff = position[..., 1:] - position[..., :-1]
    rest_lengths = _batch_norm(position_diff)
    tangents = position_diff / rest_lengths
    normal /= np.linalg.norm(normal)

    # Set the directors matrix
    directors = np.zeros(
        (MaxDimension.value(), MaxDimension.value(), n_elements))
    # check if directors is in kwargs, if it use user defined directors otherwise generate directors
    if kwargs.__contains__("directors"):
        directors_temp = np.array(kwargs["directors"])

        # Check the shape of input directors
        assert directors_temp.shape == (
            MaxDimension.value(),
            MaxDimension.value(),
            n_elements,
        ), (" Given directors shape is not correct, it should be " +
            str(directors.shape) + " but instead " + str(directors_temp.shape))

        # Check if d1, d2, d3 are unit vectors
        d1 = directors_temp[0, ...]
        d2 = directors_temp[1, ...]
        d3 = directors_temp[2, ...]
        assert_allclose(
            _batch_norm(d1),
            np.ones((n_elements)),
            atol=Tolerance.atol(),
            err_msg=(
                " d1 vector of input director matrix is not unit vector "),
        )
        assert_allclose(
            _batch_norm(d2),
            np.ones((n_elements)),
            atol=Tolerance.atol(),
            err_msg=(
                " d2 vector of input director matrix is not unit vector "),
        )
        assert_allclose(
            _batch_norm(d3),
            np.ones((n_elements)),
            atol=Tolerance.atol(),
            err_msg=(
                " d3 vector of input director matrix is not unit vector "),
        )

        # Check if d3xd1 = d2
        assert_allclose(
            _batch_cross(d3, d1),
            d2,
            atol=Tolerance.atol(),
            err_msg=(" d3 x d1 != d2 of input director matrix"),
        )

        # Check if computed tangents from position is the same with d3
        assert_allclose(
            tangents,
            d3,
            atol=Tolerance.atol(),
            err_msg=
            " Tangent vector computed using node positions is different than d3 vector of input directors",
        )

        directors[:] = directors_temp[:]

    else:
        # Construct directors using tangents and normal
        normal_collection = np.repeat(normal[:, np.newaxis],
                                      n_elements,
                                      axis=1)
        # Check if rod normal and rod tangent are perpendicular to each other otherwise
        # directors will be wrong!!
        assert_allclose(
            _batch_dot(normal_collection, tangents),
            0,
            atol=Tolerance.atol(),
            err_msg=(
                " Rod normal and tangent are not perpendicular to each other!"
            ),
        )
        directors[0, ...] = normal_collection
        directors[1, ...] = _batch_cross(tangents, normal_collection)
        directors[2, ...] = tangents

    # Set radius array
    radius = np.zeros((n_elements))
    # Check if the user input radius is valid
    radius_temp = np.array(base_radius)
    assert radius_temp.ndim < 2, ("Input radius shape is not correct " +
                                  str(radius_temp.shape) + " It should be " +
                                  str(radius.shape) +
                                  " or  single floating number ")
    radius[:] = radius_temp
    # Check if the elements of radius are greater than tolerance
    for k in range(n_elements):
        assert radius[k] > Tolerance.atol(), (
            " Radius has to be greater than 0" + " Check you radius input!")

    # Set density array
    density_array = np.zeros((n_elements))
    # Check if the user input density is valid
    density_temp = np.array(density)
    assert density_temp.ndim < 2, ("Input density shape is not correct " +
                                   str(density_temp.shape) + " It should be " +
                                   str(density_array.shape) +
                                   " or  single floating number ")
    density_array[:] = density_temp
    # Check if the elements of density are greater than tolerance
    for k in range(n_elements):
        assert density_array[k] > Tolerance.atol(), (
            " Density has to be greater than 0" + " Check you density input!")

    # Second moment of inertia
    A0 = np.pi * radius * radius
    I0_1 = A0 * A0 / (4.0 * np.pi)
    I0_2 = I0_1
    I0_3 = 2.0 * I0_2
    I0 = np.array([I0_1, I0_2, I0_3]).transpose()
    # Mass second moment of inertia for disk cross-section
    mass_second_moment_of_inertia = np.zeros(
        (MaxDimension.value(), MaxDimension.value(), n_elements), np.float64)

    mass_second_moment_of_inertia_temp = np.einsum("ij,i->ij", I0,
                                                   density * rest_lengths)

    for i in range(n_elements):
        np.fill_diagonal(
            mass_second_moment_of_inertia[..., i],
            mass_second_moment_of_inertia_temp[i, :],
        )
    # sanity check of mass second moment of inertia
    for k in range(n_elements):
        for i in range(0, MaxDimension.value()):
            assert mass_second_moment_of_inertia[i, i, k] > Tolerance.atol()

    # Inverse of second moment of inertia
    inv_mass_second_moment_of_inertia = np.zeros(
        (MaxDimension.value(), MaxDimension.value(), n_elements))
    for i in range(n_elements):
        # Check rank of mass moment of inertia matrix to see if it is invertible
        assert (np.linalg.matrix_rank(
            mass_second_moment_of_inertia[..., i]) == MaxDimension.value())
        inv_mass_second_moment_of_inertia[..., i] = np.linalg.inv(
            mass_second_moment_of_inertia[..., i])

    # Shear/Stretch matrix
    shear_modulus = youngs_modulus / (poisson_ratio + 1.0)
    shear_matrix = np.zeros(
        (MaxDimension.value(), MaxDimension.value(), n_elements), np.float64)
    for i in range(n_elements):
        np.fill_diagonal(
            shear_matrix[..., i],
            [
                alpha_c * shear_modulus * A0[i],
                alpha_c * shear_modulus * A0[i],
                youngs_modulus * A0[i],
            ],
        )

    # Bend/Twist matrix
    bend_matrix = np.zeros(
        (MaxDimension.value(), MaxDimension.value(), n_elements), np.float64)
    for i in range(n_elements):
        np.fill_diagonal(
            bend_matrix[..., i],
            [
                youngs_modulus * I0_1[i],
                youngs_modulus * I0_2[i],
                shear_modulus * I0_3[i],
            ],
        )
    for k in range(n_elements):
        for i in range(0, MaxDimension.value()):
            assert bend_matrix[i, i, k] > Tolerance.atol()
    # Compute bend matrix in Voronoi Domain
    bend_matrix = (bend_matrix[..., 1:] * rest_lengths[1:] +
                   bend_matrix[..., :-1] * rest_lengths[0:-1]) / (
                       rest_lengths[1:] + rest_lengths[:-1])

    # Compute volume of elements
    volume = np.pi * radius**2 * rest_lengths

    # Compute mass of elements
    mass = np.zeros(n_elements + 1)
    mass[:-1] += 0.5 * density * volume
    mass[1:] += 0.5 * density * volume

    # Set dissipation constant or nu array
    dissipation_constant_for_forces = np.zeros((n_elements))
    # Check if the user input nu is valid
    nu_temp = np.array(nu)
    assert nu_temp.ndim < 2, (
        "Input dissipation constant(nu) for forces shape is not correct " +
        str(nu_temp.shape) + " It should be " +
        str(dissipation_constant_for_forces.shape) +
        " or  single floating number ")
    dissipation_constant_for_forces[:] = nu
    # Check if the elements of dissipation constant greater than tolerance
    for k in range(n_elements):
        assert dissipation_constant_for_forces[k] >= 0.0, (
            " Dissipation constant has to be equal or greater than 0 " +
            " Check your dissipation constant(nu) input!")

    dissipation_constant_for_torques = np.zeros((n_elements))
    if kwargs.__contains__("nu_for_torques"):
        temp_nu_for_torques = np.array(kwargs["nu_for_torques"])
        assert temp_nu_for_torques.ndim < 2, (
            "Input dissipation constant(nu) for torques shape is not correct "
            + str(temp_nu_for_torques.shape) + " It should be " +
            str(dissipation_constant_for_torques.shape) +
            " or  single floating number ")
        dissipation_constant_for_torques[:] = temp_nu_for_torques

    else:
        dissipation_constant_for_torques[:] = dissipation_constant_for_forces

    # Generate rest sigma and rest kappa, use user input if defined
    # set rest strains and curvature to be  zero at start
    # if found in kwargs modify (say for curved rod)
    rest_sigma = np.zeros((MaxDimension.value(), n_elements))
    if kwargs.__contains__("rest_sigma"):
        temp_rest_sigma = np.array(kwargs["rest_sigma"])
        assert temp_rest_sigma.shape == rest_sigma.shape, (
            "Input rest sigma shape is not correct " +
            str(temp_rest_sigma.shape) + " It should be " +
            str(rest_sigma.shape))
        rest_sigma[:] = temp_rest_sigma

    rest_kappa = np.zeros((MaxDimension.value(), n_elements - 1))
    if kwargs.__contains__("rest_kappa"):
        temp_rest_kappa = np.array(kwargs["rest_kappa"])
        assert temp_rest_kappa.shape == rest_kappa.shape, (
            "Input rest kappa shape is not correct " +
            str(temp_rest_kappa.shape) + " It should be " +
            str(rest_kappa.shape))
        rest_kappa[:] = temp_rest_kappa

    # Compute rest voronoi length
    rest_voronoi_lengths = 0.5 * (rest_lengths[1:] + rest_lengths[:-1])

    # Allocate arrays for Cosserat Rod equations
    velocities = np.zeros((MaxDimension.value(), n_elements + 1))
    omegas = np.zeros((MaxDimension.value(), n_elements))
    accelerations = 0.0 * velocities
    angular_accelerations = 0.0 * omegas
    _vector_states = np.hstack(
        (position, velocities, omegas, accelerations, angular_accelerations))
    _matrix_states = directors.copy()

    internal_forces = 0.0 * accelerations
    internal_torques = 0.0 * angular_accelerations

    external_forces = 0.0 * accelerations
    external_torques = 0.0 * angular_accelerations

    lengths = np.zeros((n_elements))
    tangents = np.zeros((3, n_elements))

    dilatation = np.zeros((n_elements))
    voronoi_dilatation = np.zeros((n_elements - 1))
    dilatation_rate = np.zeros((n_elements))

    sigma = np.zeros((3, n_elements))
    kappa = np.zeros((3, n_elements - 1))

    internal_stress = np.zeros((3, n_elements))
    internal_couple = np.zeros((3, n_elements - 1))

    damping_forces = np.zeros((3, n_elements + 1))
    damping_torques = np.zeros((3, n_elements))

    return (
        n_elements,
        _vector_states,
        _matrix_states,
        radius,
        mass_second_moment_of_inertia,
        inv_mass_second_moment_of_inertia,
        shear_matrix,
        bend_matrix,
        density,
        volume,
        mass,
        dissipation_constant_for_forces,
        dissipation_constant_for_torques,
        internal_forces,
        internal_torques,
        external_forces,
        external_torques,
        lengths,
        rest_lengths,
        tangents,
        dilatation,
        dilatation_rate,
        voronoi_dilatation,
        rest_voronoi_lengths,
        sigma,
        kappa,
        rest_sigma,
        rest_kappa,
        internal_stress,
        internal_couple,
        damping_forces,
        damping_torques,
    )
Exemplo n.º 14
0
 def __init__(self, k, nu):
     super().__init__(k, nu)
     # 0 is min, 1 is max
     self.aabb_rod = np.empty((MaxDimension.value(), 2))
     self.aabb_cylinder = np.empty((MaxDimension.value(), 2))
Exemplo n.º 15
0
    def __init__(self, start, direction, normal, base_length, base_radius,
                 density):
        # rigid body does not have elements it only have one node. We are setting n_elems to
        # zero for only make code to work. _bootstrap_from_data requires n_elems to be defined
        self.n_elems = 1

        self.normal = normal.reshape(3, 1)
        self.tangents = direction.reshape(3, 1)
        self.binormal = np.cross(direction, normal).reshape(3, 1)
        self.radius = base_radius
        self.length = base_length
        self.density = density
        # This is for a rigid body cylinder
        self.volume = np.pi * base_radius * base_radius * base_length
        self.mass = np.array([self.volume * self.density])

        # Second moment of inertia
        A0 = np.pi * base_radius * base_radius
        I0_1 = A0 * A0 / (4.0 * np.pi)
        I0_2 = I0_1
        I0_3 = 2.0 * I0_2
        I0 = np.array([I0_1, I0_2, I0_3])

        # Mass second moment of inertia for disk cross-section
        mass_second_moment_of_inertia = np.zeros(
            (MaxDimension.value(), MaxDimension.value()), np.float64)
        np.fill_diagonal(mass_second_moment_of_inertia,
                         I0 * density * base_length)

        self.inv_mass_second_moment_of_inertia = np.linalg.inv(
            mass_second_moment_of_inertia).reshape(MaxDimension.value(),
                                                   MaxDimension.value(), 1)

        # position is at the center
        position = np.zeros((MaxDimension.value(), 1))
        position[:] = start.reshape(
            3, 1) + direction.reshape(3, 1) * base_length / 2

        velocities = np.zeros((MaxDimension.value(), 1))
        omegas = np.zeros((MaxDimension.value(), 1))
        accelerations = 0.0 * velocities
        angular_accelerations = 0.0 * omegas

        directors = np.zeros((MaxDimension.value(), MaxDimension.value(), 1))
        directors[0, ...] = self.normal
        directors[1, ...] = _batch_cross(self.tangents, self.normal)
        directors[2, ...] = self.tangents

        self._vector_states = np.hstack((position, velocities, omegas,
                                         accelerations, angular_accelerations))
        self._matrix_states = directors.copy()

        self.internal_forces = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)
        self.internal_torques = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)

        self.external_forces = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)
        self.external_torques = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)

        _RigidRodSymplecticStepperMixin.__init__(self)
Exemplo n.º 16
0
    def __init__(self, center, base_radius, density):
        # rigid body does not have elements it only have one node. We are setting n_elems to
        # zero for only make code to work. _bootstrap_from_data requires n_elems to be defined
        self.n_elems = 1

        self.radius = base_radius
        self.density = density
        self.length = 2 * base_radius
        # This is for a rigid body cylinder
        self.volume = 4.0 / 3.0 * np.pi * base_radius**3
        self.mass = np.array([self.volume * self.density])
        normal = np.array([1.0, 0.0, 0.0]).reshape(3, 1)
        tangents = np.array([0.0, 0.0, 1.0]).reshape(3, 1)
        binormal = _batch_cross(tangents, normal)

        # Mass second moment of inertia for disk cross-section
        mass_second_moment_of_inertia = np.zeros(
            (MaxDimension.value(), MaxDimension.value()), np.float64)
        np.fill_diagonal(mass_second_moment_of_inertia,
                         2.0 / 5.0 * self.mass * self.radius**2)

        self.mass_second_moment_of_inertia = mass_second_moment_of_inertia.reshape(
            MaxDimension.value(), MaxDimension.value(), 1)

        self.inv_mass_second_moment_of_inertia = np.linalg.inv(
            mass_second_moment_of_inertia).reshape(MaxDimension.value(),
                                                   MaxDimension.value(), 1)

        # position is at the center
        self.position_collection = np.zeros((MaxDimension.value(), 1))
        self.position_collection[:, 0] = center

        self.velocity_collection = np.zeros((MaxDimension.value(), 1))
        self.omega_collection = np.zeros((MaxDimension.value(), 1))
        self.acceleration_collection = 0.0 * self.velocity_collection
        self.alpha_collection = 0.0 * self.omega_collection

        self.director_collection = np.zeros(
            (MaxDimension.value(), MaxDimension.value(), 1))
        self.director_collection[0, ...] = normal
        self.director_collection[1, ...] = binormal
        self.director_collection[2, ...] = tangents

        self.external_forces = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)
        self.external_torques = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)
Exemplo n.º 17
0
    def test_case_bend_straight_rod(self, alpha):
        """
        In this test case we initialize a straight rod with 2 elements
        and numerically bend the rod. We modify node positions and directors
        to make a isosceles triangle. Then first we compute curvature
        between two elements and compute the angle between them.
        Finally, we compute bend twist couples and compare with
        correct solution.
        This test function tests
            _compute_bending_twist_strains
            _compute_internal_torques
            only bend_twist_couple terms.
        """

        n_elem = 2
        initial, test_rod = constructor(n_elem, nu=0.0)
        base_length = initial.base_length
        # Change the coordinates of nodes, artificially bend the rod.
        #              /\
        # ------ ==>  /  \
        #            /    \
        # Here I chose a isosceles triangle.

        length = base_length / n_elem
        position = np.zeros((MaxDimension.value(), n_elem + 1))
        position[..., 0] = np.array([0.0, 0.0, 0.0])
        position[..., 1] = length * np.array(
            [0.0, np.sin(alpha), np.cos(alpha)])
        position[..., 2] = length * np.array([0.0, 0.0, 2 * np.cos(alpha)])
        test_rod.position_collection = position

        # Set the directors manually. This is easy since we have two elements.
        directors = np.zeros(
            (MaxDimension.value(), MaxDimension.value(), n_elem))
        directors[..., 0] = np.array((
            [1.0, 0.0, 0.0],
            [0.0, np.cos(alpha), -np.sin(alpha)],
            [0.0, np.sin(alpha), np.cos(alpha)],
        ))
        directors[..., -1] = np.array((
            [1.0, 0.0, 0.0],
            [0.0, np.cos(alpha), np.sin(alpha)],
            [0, -np.sin(alpha), np.cos(alpha)],
        ))
        test_rod.director_collection = directors

        # Compute voronoi rest length. Since elements lengths are equal
        # in this test case, rest voronoi length can be easily computed
        # dividing base length to number of elements.

        rest_voronoi_length = base_length / n_elem

        # Now compute geometry and dilatation, which we need for curvature calculations.
        _compute_all_dilatations(
            test_rod.position_collection,
            test_rod.volume,
            test_rod.lengths,
            test_rod.tangents,
            test_rod.radius,
            test_rod.dilatation,
            test_rod.rest_lengths,
            test_rod.rest_voronoi_lengths,
            test_rod.voronoi_dilatation,
        )

        _compute_dilatation_rate(
            test_rod.position_collection,
            test_rod.velocity_collection,
            test_rod.lengths,
            test_rod.rest_lengths,
            test_rod.dilatation_rate,
        )

        _compute_bending_twist_strains(test_rod.director_collection,
                                       test_rod.rest_voronoi_lengths,
                                       test_rod.kappa)

        # Generalized rotation per unit length is given by rest_D_i * Kappa_i.
        # Thus in order to get the angle between two elements, we need to multiply
        # kappa with rest_D_i .  But this will give the exterior vertex angle of the
        # triangle. Think as, we rotate element 1 clockwise direction and align with
        # the element 2.
        #
        #               \
        #     /\         \ 1
        #  1 /  \ 2  ==>  \
        #   /    \         \
        #                   \ 2
        #                    \
        #
        # So for this transformation we use exterior vertex angle of isosceles triangle.
        # Exterior vertex angle can be computed easily, it is the sum of base angles
        # , since this is isosceles triangle it is 2*base_angle

        correct_angle = np.degrees(
            np.array([2 * alpha, 0.0, 0.0]).reshape(3, 1))
        test_angle = np.degrees(test_rod.kappa * test_rod.rest_voronoi_lengths)
        assert_allclose(test_angle, correct_angle, atol=Tolerance.atol())

        # Now lets test bending stress terms in internal torques equation.
        # Here we will test bend twist couple 2D and bend twist couple 3D terms of the
        # internal torques equation. Set the bending matrix to identity matrix for simplification.
        test_rod.bend_matrix[:] = np.repeat(np.identity(3)[:, :, np.newaxis],
                                            n_elem - 1,
                                            axis=2)

        # We need to compute shear stress, for internal torque equation.
        # Shear stress is not used in this test case. In order to make sure shear
        # stress do not contribute to the total torque we use assert check.
        _compute_internal_bending_twist_stresses_from_model(
            test_rod.director_collection,
            test_rod.rest_voronoi_lengths,
            test_rod.internal_couple,
            test_rod.bend_matrix,
            test_rod.kappa,
            test_rod.rest_kappa,
        )
        assert_allclose(
            test_rod.internal_stress,
            np.zeros(3 * n_elem).reshape(3, n_elem),
            atol=Tolerance.atol(),
        )

        # Make sure voronoi dilatation is 1
        assert_allclose(test_rod.voronoi_dilatation,
                        np.array([1.0]),
                        atol=Tolerance.atol())

        # Compute correct torques, first compute correct kappa.
        correct_kappa = np.radians(correct_angle / rest_voronoi_length)
        # We only need to compute bend twist couple 2D term for comparison,
        # because bend twist couple 3D term is already zero, due to cross product.
        # TODO: Extended this test for multiple elements more than 2.
        correct_torques = np.zeros((MaxDimension.value(), n_elem))
        correct_torques[..., 0] = correct_kappa[..., 0]
        correct_torques[..., -1] = -1.0 * correct_kappa[..., -1]

        _compute_internal_torques(
            test_rod.position_collection,
            test_rod.velocity_collection,
            test_rod.tangents,
            test_rod.lengths,
            test_rod.rest_lengths,
            test_rod.director_collection,
            test_rod.rest_voronoi_lengths,
            test_rod.bend_matrix,
            test_rod.rest_kappa,
            test_rod.kappa,
            test_rod.voronoi_dilatation,
            test_rod.mass_second_moment_of_inertia,
            test_rod.omega_collection,
            test_rod.internal_stress,
            test_rod.internal_couple,
            test_rod.dilatation,
            test_rod.dilatation_rate,
            test_rod.dissipation_constant_for_torques,
            test_rod.damping_torques,
            test_rod.internal_torques,
            test_rod.ghost_voronoi_idx,
        )

        assert_allclose(test_rod.internal_torques,
                        correct_torques,
                        atol=Tolerance.atol())
Exemplo n.º 18
0
    def __init__(self, start, direction, normal, base_length, base_radius,
                 density):
        # rigid body does not have elements it only have one node. We are setting n_elems to
        # zero for only make code to work. _bootstrap_from_data requires n_elems to be defined
        self.n_elems = 1

        normal = normal.reshape(3, 1)
        tangents = direction.reshape(3, 1)
        binormal = _batch_cross(tangents, normal)
        self.radius = base_radius
        self.length = base_length
        self.density = density
        # This is for a rigid body cylinder
        self.volume = np.pi * base_radius * base_radius * base_length
        self.mass = np.array([self.volume * self.density])

        # Second moment of inertia
        A0 = np.pi * base_radius * base_radius
        I0_1 = A0 * A0 / (4.0 * np.pi)
        I0_2 = I0_1
        I0_3 = 2.0 * I0_2
        I0 = np.array([I0_1, I0_2, I0_3])

        # Mass second moment of inertia for disk cross-section
        mass_second_moment_of_inertia = np.zeros(
            (MaxDimension.value(), MaxDimension.value()), np.float64)
        np.fill_diagonal(mass_second_moment_of_inertia,
                         I0 * density * base_length)

        self.mass_second_moment_of_inertia = mass_second_moment_of_inertia.reshape(
            MaxDimension.value(), MaxDimension.value(), 1)

        self.inv_mass_second_moment_of_inertia = np.linalg.inv(
            mass_second_moment_of_inertia).reshape(MaxDimension.value(),
                                                   MaxDimension.value(), 1)

        # position is at the center
        self.position_collection = np.zeros((MaxDimension.value(), 1))
        self.position_collection[:] = (
            start.reshape(3, 1) + direction.reshape(3, 1) * base_length / 2)

        self.velocity_collection = np.zeros((MaxDimension.value(), 1))
        self.omega_collection = np.zeros((MaxDimension.value(), 1))
        self.acceleration_collection = 0.0 * self.velocity_collection
        self.alpha_collection = 0.0 * self.omega_collection

        self.director_collection = np.zeros(
            (MaxDimension.value(), MaxDimension.value(), 1))
        self.director_collection[0, ...] = normal
        self.director_collection[1, ...] = binormal
        self.director_collection[2, ...] = tangents

        self.external_forces = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)
        self.external_torques = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)
Exemplo n.º 19
0
# in-plane
horizontal_direction = np.array([0.0, 0.0, 1.0]).reshape(-1, 1)
vertical_direction = np.array([1.0, 0.0, 0.0]).reshape(-1, 1)

# out-of-plane
normal = np.array([0.0, 1.0, 0.0])

total_length = 3.0
base_radius = 0.25
base_area = np.pi * base_radius ** 2
density = 5000
nu = 0.0
youngs_modulus = 1e4
poisson_ratio = 0.5

positions = np.empty((MaxDimension.value(), n_elem + 1))
dl = total_length / n_elem

# First half of positions stem from slope angle_of_inclination
first_half = np.arange(half_n_elem + 1.0).reshape(1, -1)
positions[..., : half_n_elem + 1] = origin + dl * first_half * (
    np.cos(angle_of_inclination) * horizontal_direction
    + np.sin(angle_of_inclination) * vertical_direction
)
positions[..., half_n_elem:] = positions[
    ..., half_n_elem : half_n_elem + 1
] + dl * first_half * (
    np.cos(angle_of_inclination) * horizontal_direction
    - np.sin(angle_of_inclination) * vertical_direction
)
Exemplo n.º 20
0
 def _compute_internal_forces(self):
     return np.zeros((MaxDimension.value(), self.n_elem + 1))
Exemplo n.º 21
0
    def __init__(
        self,
        n_elements,
        position,
        directors,
        rest_lengths,
        density,
        volume,
        mass_second_moment_of_inertia,
        nu,
        *args,
        **kwargs
    ):
        """
        Parameters
        ----------
        n_elements: int
        position: numpy.ndarray
            2D (dim, blocksize) array containing data with 'float' type.
            Rod node position array.
        directors: numpy.ndarray
            3D (dim, dim, blocksize) array containing data with 'float' type.
            Rod element directors array.
        rest_lengths: numpy.ndarray
            1D (blocksize) array containing data with 'float' type.
            Rod element rest lengths.
        density: numpy.ndarray
            1D (blocksize) array containing data with 'float' type.
            Rod element density.
        volume: numpy.ndarray
            1D (blocksize) array containing data with 'float' type.
            Rod element volume.
        mass_second_moment_of_inertia: numpy.ndarray
            2D (dim, blocksize) array containing data with 'float' type.
            Rod element mass second moment of inertia.
        nu: numpy.ndarray
           1D (blocksize) array containing data with 'float' type.
           Rod element dissipation constant.
        *args
            Variable length argument list.
        **kwargs
            Arbitrary keyword arguments.
        """
        velocities = np.zeros((MaxDimension.value(), n_elements + 1))
        omegas = np.zeros((MaxDimension.value(), n_elements))  # + 1e-16
        accelerations = 0.0 * velocities
        angular_accelerations = 0.0 * omegas
        self.n_elems = n_elements
        self._vector_states = np.hstack(
            (position, velocities, omegas, accelerations, angular_accelerations)
        )
        self._matrix_states = directors.copy()
        # initial set to zero; if coming through kwargs then modify
        self.rest_lengths = rest_lengths
        self.density = density
        self.volume = volume

        self.mass = np.zeros(n_elements + 1)
        self.mass[:-1] += 0.5 * self.density * self.volume
        self.mass[1:] += 0.5 * self.density * self.volume

        self.mass_second_moment_of_inertia = mass_second_moment_of_inertia

        self.inv_mass_second_moment_of_inertia = np.zeros(
            (MaxDimension.value(), MaxDimension.value(), n_elements)
        )
        for i in range(n_elements):
            # Check rank of mass moment of inertia matrix to see if it is invertible
            assert (
                np.linalg.matrix_rank(mass_second_moment_of_inertia[..., i])
                == MaxDimension.value()
            )
            self.inv_mass_second_moment_of_inertia[..., i] = np.linalg.inv(
                mass_second_moment_of_inertia[..., i]
            )

        self.nu = nu
        self.rest_voronoi_lengths = 0.5 * (
            self.rest_lengths[1:] + self.rest_lengths[:-1]
        )
        # calculated in `_compute_internal_forces_and_torques`
        self.internal_forces = 0 * accelerations
        self.internal_torques = 0 * angular_accelerations

        # will apply external force and torques externally
        self.external_forces = 0 * accelerations
        self.external_torques = 0 * angular_accelerations

        # calculated in `compute_geometry_from_state`
        self.lengths = NotImplemented
        self.tangents = NotImplemented
        self.radius = NotImplemented

        # calculated in `compute_all_dilatatation`
        self.dilatation = NotImplemented
        self.voronoi_dilatation = NotImplemented
        self.dilatation_rate = NotImplemented
Exemplo n.º 22
0
    def straight_rod(
        cls,
        n_elements,
        start,
        direction,
        normal,
        base_length,
        base_radius,
        density,
        nu,
        mass_second_moment_of_inertia,
        *args,
        **kwargs
    ):
        # sanity checks here
        assert n_elements > 1
        assert base_length > Tolerance.atol()
        assert base_radius > Tolerance.atol()
        assert density > Tolerance.atol()
        assert nu >= 0.0
        assert np.sqrt(np.dot(normal, normal)) > Tolerance.atol()
        assert np.sqrt(np.dot(direction, direction)) > Tolerance.atol()
        for i in range(0, MaxDimension.value()):
            assert mass_second_moment_of_inertia[i, i] > Tolerance.atol()

        end = start + direction * base_length
        position = np.zeros((MaxDimension.value(), n_elements + 1))
        for i in range(0, MaxDimension.value()):
            position[i, ...] = np.linspace(start[i], end[i], num=n_elements + 1)

        # compute rest lengths and tangents
        position_diff = position[..., 1:] - position[..., :-1]
        rest_lengths = np.sqrt(np.einsum("ij,ij->j", position_diff, position_diff))
        tangents = position_diff / rest_lengths
        normal /= np.sqrt(np.dot(normal, normal))

        # set directors
        # check this order once
        directors = np.zeros((MaxDimension.value(), MaxDimension.value(), n_elements))
        normal_collection = np.repeat(normal[:, np.newaxis], n_elements, axis=1)
        directors[0, ...] = normal_collection
        directors[1, ...] = _batch_cross(tangents, normal_collection)
        directors[2, ...] = tangents

        volume = np.pi * base_radius ** 2 * rest_lengths

        inertia_collection = np.repeat(
            mass_second_moment_of_inertia[:, :, np.newaxis], n_elements, axis=2
        )

        # create rod
        return cls(
            n_elements,
            position,
            directors,
            rest_lengths,
            density,
            volume,
            inertia_collection,
            nu,
            *args,
            **kwargs
        )
Exemplo n.º 23
0
    def straight_rod(
        cls,
        n_elements,
        start,
        direction,
        normal,
        base_length,
        base_radius,
        density,
        nu,
        youngs_modulus,
        poisson_ratio,
        alpha_c=4.0 / 3.0,
        *args,
        **kwargs
    ):
        """
        Call this method to initialize and generate a Cosserat rod object that is a straight rod. Future versions will contain
        methods for curvilinear rods.

        Parameters
        ----------
        n_elements: float
            Rod number of elements.
        start: numpy.ndarray
            1D (dim) array containing data with 'float' type. Start position of the rod.
        direction: numpy.ndarray
            1D (dim) array containing data with 'float' type. Direction or tangent of the rod.
        normal: numpy.ndarray
            1D (dim) array containing data with 'float' type. Normal direction of the rod.
        base_length: float
            Initial length of the rod.
        base_radius: float
            Initial radius of the rod.
        density: float
            Density of the rod.
        nu: float
            Dissipation constant of the rod.
        youngs_modulus: float
            Youngs modulus of the rod.
        poisson_ratio: float
            Poisson ratio of the rod is used to compute shear modulus.
        alpha_c: float
        *args
            Variable length argument list.
        **kwargs
            Arbitrary keyword arguments.

        Returns
        -------

        """
        # FIXME: Make sure G=E/(poisson_ratio+1.0) in wikipedia it is different
        # Shear Modulus
        shear_modulus = youngs_modulus / (poisson_ratio + 1.0)

        # Second moment of inertia
        A0 = np.pi * base_radius * base_radius
        I0_1 = A0 * A0 / (4.0 * np.pi)
        I0_2 = I0_1
        I0_3 = 2.0 * I0_2
        I0 = np.array([I0_1, I0_2, I0_3])

        # Mass second moment of inertia for disk cross-section
        mass_second_moment_of_inertia = np.zeros(
            (MaxDimension.value(), MaxDimension.value()), np.float64
        )
        np.fill_diagonal(
            mass_second_moment_of_inertia, I0 * density * base_length / n_elements
        )

        # Shear/Stretch matrix
        shear_matrix = np.zeros(
            (MaxDimension.value(), MaxDimension.value()), np.float64
        )
        np.fill_diagonal(
            shear_matrix,
            [
                alpha_c * shear_modulus * A0,
                alpha_c * shear_modulus * A0,
                youngs_modulus * A0,
            ],
        )

        # Bend/Twist matrix
        bend_matrix = np.zeros((MaxDimension.value(), MaxDimension.value()), np.float64)
        np.fill_diagonal(
            bend_matrix,
            [youngs_modulus * I0_1, youngs_modulus * I0_2, shear_modulus * I0_3],
        )

        rod = _CosseratRodBase.straight_rod(
            n_elements,
            start,
            direction,
            normal,
            base_length,
            base_radius,
            density,
            nu,
            mass_second_moment_of_inertia,
            *args,
            **kwargs
        )
        return cls(n_elements, shear_matrix, bend_matrix, rod, *args, **kwargs)
Exemplo n.º 24
0
    def test_case_compute_bending_energy(self, alpha, nu=0.0):
        """
        Similar to the previous test case test_case_bend_straight_rod.
        In this test case we initialize a straight rod with 2 elements
        and numerically bend the rod. We modify node positions and directors
        to make a isosceles triangle. Then first we compute curvature
        between two elements and compute the angle between them.
        Finally, we compute the bending energy of rod and compare with
        correct solution.
        This test function tests
            compute_bending_energy
        Parameters
        ----------
        alpha
        nu

        Returns
        -------

        """
        n_elem = 2
        initial, test_rod = constructor(n_elem, nu=0.0)
        base_length = initial.base_length
        # Change the coordinates of nodes, artificially bend the rod.
        #              /\
        # ------ ==>  /  \
        #            /    \
        # Here I chose a isosceles triangle.

        length = base_length / n_elem
        position = np.zeros((MaxDimension.value(), n_elem + 1))
        position[..., 0] = np.array([0.0, 0.0, 0.0])
        position[..., 1] = length * np.array(
            [0.0, np.sin(alpha), np.cos(alpha)])
        position[..., 2] = length * np.array([0.0, 0.0, 2 * np.cos(alpha)])
        test_rod.position_collection = position

        # Set the directors manually. This is easy since we have two elements.
        directors = np.zeros(
            (MaxDimension.value(), MaxDimension.value(), n_elem))
        directors[..., 0] = np.array((
            [1.0, 0.0, 0.0],
            [0.0, np.cos(alpha), -np.sin(alpha)],
            [0.0, np.sin(alpha), np.cos(alpha)],
        ))
        directors[..., -1] = np.array((
            [1.0, 0.0, 0.0],
            [0.0, np.cos(alpha), np.sin(alpha)],
            [0, -np.sin(alpha), np.cos(alpha)],
        ))
        test_rod.director_collection = directors

        # Compute voronoi rest length. Since elements lengths are equal
        # in this test case, rest voronoi length can be easily computed
        # dividing base length to number of elements.

        rest_voronoi_length = base_length / n_elem

        # Now compute geometry and dilatation, which we need for curvature calculations.
        _compute_all_dilatations(
            test_rod.position_collection,
            test_rod.volume,
            test_rod.lengths,
            test_rod.tangents,
            test_rod.radius,
            test_rod.dilatation,
            test_rod.rest_lengths,
            test_rod.rest_voronoi_lengths,
            test_rod.voronoi_dilatation,
        )

        _compute_dilatation_rate(
            test_rod.position_collection,
            test_rod.velocity_collection,
            test_rod.lengths,
            test_rod.rest_lengths,
            test_rod.dilatation_rate,
        )

        _compute_bending_twist_strains(test_rod.director_collection,
                                       test_rod.rest_voronoi_lengths,
                                       test_rod.kappa)

        # Generalized rotation per unit length is given by rest_D_i * Kappa_i.
        # Thus in order to get the angle between two elements, we need to multiply
        # kappa with rest_D_i .  But this will give the exterior vertex angle of the
        # triangle. Think as, we rotate element 1 clockwise direction and align with
        # the element 2.
        #
        #               \
        #     /\         \ 1
        #  1 /  \ 2  ==>  \
        #   /    \         \
        #                   \ 2
        #                    \
        #
        # So for this transformation we use exterior vertex angle of isosceles triangle.
        # Exterior vertex angle can be computed easily, it is the sum of base angles
        # , since this is isosceles triangle it is 2*base_angle

        correct_angle = np.degrees(
            np.array([2 * alpha, 0.0, 0.0]).reshape(3, 1))
        test_angle = np.degrees(test_rod.kappa * test_rod.rest_voronoi_lengths)
        assert_allclose(test_angle, correct_angle, atol=Tolerance.atol())

        # Now lets test bending stress terms in internal torques equation.
        # Here we will test bend twist couple 2D and bend twist couple 3D terms of the
        # internal torques equation. Set the bending matrix to identity matrix for simplification.
        test_rod.bend_matrix[:] = np.repeat(np.identity(3)[:, :, np.newaxis],
                                            n_elem - 1,
                                            axis=2)

        # Compute bending energy
        correct_kappa = 2 * alpha / rest_voronoi_length
        correct_bending_energy = (0.5 * correct_kappa * correct_kappa *
                                  rest_voronoi_length)
        test_bending_energy = test_rod.compute_bending_energy()
        assert_allclose(test_bending_energy,
                        correct_bending_energy,
                        atol=Tolerance.atol())
Exemplo n.º 25
0
    def __init__(self, center, base_radius, density):
        # rigid body does not have elements it only have one node. We are setting n_elems to
        # zero for only make code to work. _bootstrap_from_data requires n_elems to be defined
        self.n_elems = 1

        self.radius = base_radius
        self.density = density
        self.length = 2 * base_radius
        # This is for a rigid body cylinder
        self.volume = 4.0 / 3.0 * np.pi * base_radius**3
        self.mass = np.array([self.volume * self.density])
        self.normal = np.array([1.0, 0.0, 0.0]).reshape(3, 1)
        self.tangents = np.array([0.0, 0.0, 1.0]).reshape(3, 1)
        self.binormal = _batch_cross(self.tangents, self.normal)

        # Mass second moment of inertia for disk cross-section
        mass_second_moment_of_inertia = np.zeros(
            (MaxDimension.value(), MaxDimension.value()), np.float64)
        np.fill_diagonal(mass_second_moment_of_inertia,
                         2.0 / 5.0 * self.mass * self.radius**2)

        self.inv_mass_second_moment_of_inertia = np.linalg.inv(
            mass_second_moment_of_inertia).reshape(MaxDimension.value(),
                                                   MaxDimension.value(), 1)

        # position is at the center
        position = np.zeros((MaxDimension.value(), 1))
        position[:, 0] = center

        velocities = np.zeros((MaxDimension.value(), 1))
        omegas = np.zeros((MaxDimension.value(), 1))
        accelerations = 0.0 * velocities
        angular_accelerations = 0.0 * omegas

        directors = np.zeros((MaxDimension.value(), MaxDimension.value(), 1))
        directors[0, ...] = self.normal
        directors[1, ...] = _batch_cross(self.tangents, self.normal)
        directors[2, ...] = self.tangents
        # directors[0, ...] = [[1.0], [0.0], [0.0]]
        # directors[1, ...] = [[0.0], [1.0], [0.0]]
        # directors[2, ...] = [[0.0], [0.0], [1.0]]

        self._vector_states = np.hstack((position, velocities, omegas,
                                         accelerations, angular_accelerations))
        self._matrix_states = directors.copy()

        self.internal_forces = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)
        self.internal_torques = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)

        self.external_forces = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)
        self.external_torques = np.zeros(
            (MaxDimension.value())).reshape(MaxDimension.value(), 1)

        _RigidRodSymplecticStepperMixin.__init__(self)