def test_case_compute_all_dilatations(self, n_elem, dilatation): """ This test case, tests compute_all_dilatations function by comparing with analytical solution. :param n_elem: :param dilatation: :return: """ initial, test_rod = constructor(n_elem) ( dilatation_collection, voronoi_dilatation, lengths, rest_voronoi_lengths, ) = compute_all_dilatations_analytically(n_elem, dilatation) test_rod.position_collection *= dilatation # Compute dilatation using compute_all_dilatations # Compute geometry again because node positions changed. # But compute geometry will be done inside compute_all_dilatations. _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, ) assert_allclose(test_rod.lengths, lengths, atol=Tolerance.atol()) assert_allclose(test_rod.rest_voronoi_lengths, rest_voronoi_lengths, atol=Tolerance.atol()) assert_allclose(test_rod.dilatation, dilatation_collection, atol=Tolerance.atol()) assert_allclose(test_rod.voronoi_dilatation, voronoi_dilatation, atol=Tolerance.atol())
def test_case_compute_rotational_energy(self, n_elem, nu=0): """ This function tests compute rotational energy function. We take an initial input energy for the rod and compute the angular velocity and set the angular velocity of rod elements. We call compute_rotational_energy function and compare the result with output energy. Here we are using mass moment of inertia corresponding to z velocity. Note here we are only setting the z velocity of the rod, y and x velocity are zero. Parameters ---------- n_elem nu Returns ------- """ initial, test_rod = constructor(n_elem, nu) input_energy = 10 omega = np.sqrt( 2 * input_energy / (test_rod.mass_second_moment_of_inertia[2, 2, 0] * n_elem)) test_rod.omega_collection[..., :] = np.array([0.0, 0.0, omega]).reshape(3, 1) _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, ) output_energy = test_rod.compute_rotational_energy() assert_allclose(output_energy, input_energy, atol=Tolerance.atol())
def test_case_compute_dilatation_rate(self, n_elem, dilatation): """ This test case tests compute_dilatation_rate function by comparing with analytical calculation. This function depends on the compute_all_dilatations. :param n_elem: :param dilatation: :return: """ initial, test_rod = constructor(n_elem) dilatation_rate, velocity = compute_dilatation_rate_analytically( n_elem, dilatation) # Set velocity vector in test_rod to the computed velocity vector above, # since we need to initialize velocity for dilatation_rate test_rod.velocity_collection = velocity _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, ) assert_allclose(test_rod.dilatation_rate, dilatation_rate, atol=Tolerance.atol())
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())
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())