def test_nontrivial_orientations(self): """Ensure that orientations are applied to the right particles.""" box = self.get_cubic_box(6) points = np.array([[-1.0, 0.0, 0.0]], dtype=np.float32) query_points = np.array([[0.9, 0.1, 0.0]], dtype=np.float32) for angles in ([0], [np.pi / 4]): for query_angles in ([0.01], [np.pi / 4 + 0.01]): max_width = 2 nbins = 4 self.limits = (max_width, ) * 2 self.bins = (nbins, nbins, nbins) pmft = freud.pmft.PMFTXYT(max_width, max_width, nbins) pmft.compute((box, points), angles, query_points, query_angles) query_orientation = rowan.from_axis_angle([0, 0, 1], query_angles[0]) orientation = rowan.from_axis_angle([0, 0, 1], angles[0]) assert tuple(np.asarray(np.where( pmft.bin_counts)).flatten()) == self.get_bin( query_points[0], points[0], query_orientation, orientation) assert np.sum(pmft.bin_counts) == 1
def test_disordered(self): # do not need positions, just orientations N = 1000 axes = np.zeros(shape=(N, 3), dtype=np.float32) angles = np.zeros(shape=N, dtype=np.float32) # pick axis at random ax_list = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 0], [1, 0, 1], [0, 1, 1], [1, 1, 1]], dtype=np.float32) ax_list /= np.linalg.norm(ax_list, axis=-1)[:, np.newaxis] for i in range(N): axes[i] = ax_list[i % ax_list.shape[0]] # generate disordered orientations np.random.seed(0) angles = np.random.uniform(low=np.pi / 4.0, high=np.pi / 2.0, size=N) orientations = rowan.from_axis_angle(axes, angles) # create cubatic object cubatic = freud.order.Cubatic(5.0, 0.001, 0.95, 10) cubatic.compute(orientations) # get the op op = cubatic.order pop = cubatic.particle_order op_max = np.nanmax(pop) npt.assert_array_less(op, 0.3, err_msg="Cubatic Order is > 0.3") npt.assert_array_less( op_max, 0.2, err_msg="per particle order parameter value is too high")
def test_convex_polygon_distance_to_surface_unit_area_ngon_rotated(shape, ): """Check shape distance consistency with the final edge wraparound.""" theta = np.linspace(0, 2 * np.pi, 1000000) # Try a positive rotation. verts = rowan.rotate(rowan.from_axis_angle([0, 0, 1], 0.1), shape.vertices) shape = ConvexPolygon(verts) distance = shape.distance_to_surface(theta) assert_distance_to_surface_2d(shape, theta, distance) # Now try a negative rotation. verts = rowan.rotate(rowan.from_axis_angle([0, 0, 1], -0.2), shape.vertices) shape = ConvexPolygon(verts) distance = shape.distance_to_surface(theta) assert_distance_to_surface_2d(shape, theta, distance)
def test_equivalent(self): """Perform a rotation and ensure that we can recover it""" # Test on an octahedron points = [[1, 0, 0], [-1, 0, 0], [0, 1, 0], [0, -1, 0], [0, 0, 1], [0, 0, -1]] # This is just a selected subset eq = [ from_axis_angle([0, 0, 1], a) for a in [0, np.pi / 2, np.pi, 3 * np.pi / 2] ] np.random.seed(0) rotation = random.rand(1) translation = np.random.rand(1, 3) transformed_points = rotate(rotation, points) + translation q, t = mapping.procrustes(points, transformed_points, equivalent_quaternions=eq) # Sort the points in a deterministic manner for comparison recovered_points = rotate(q, points) + t ordered_recovered_points = recovered_points[np.argsort( recovered_points[:, 0])] ordered_transformed_points = transformed_points[np.argsort( transformed_points[:, 0])] self.assertTrue( np.allclose(ordered_recovered_points, ordered_transformed_points))
def test_icp_exact(self): """Ensure that ICP is exact for corresponding inputs""" # Note that we do not bother to test the non-unique matching since we # know it provides very poor results. np.random.seed(0) # First test using unique matching, which should work for i in range(2, 6): num_points = 2**i points = np.random.rand(num_points, 3) rotation = from_axis_angle([0.3, 0.3, 0.3], 0.3) translation = np.random.rand(1, 3) transformed_points = rotate(rotation, points) + translation q, t, indices = mapping.icp(points, transformed_points, return_indices=True) q = from_matrix(q) # In the case of just two points, the mapping is not unique, # so we don't check the mapping itself, just the result. if i > 1: self.assertTrue( np.logical_or( np.allclose(rotation, q), np.allclose(rotation, -q), )) self.assertTrue(np.allclose(translation, t)) self.assertTrue( np.allclose(transformed_points, rotate(q, points[indices]) + t))
def test_icp_mismatched(self): """See how ICP works for non-corresponding inputs. Have set some reasonable threshold for testing purposes.""" np.random.seed(0) # First test using unique matching, which should work for i in range(2, 6): num_points = 2**i points = np.random.rand(num_points, 3) rotation = from_axis_angle([0.3, 0.3, 0.3], 0.3) translation = np.random.rand(1, 3) permutation = np.random.permutation(num_points) transformed_points = rotate(rotation, points[permutation]) + translation q, t, indices = mapping.icp(points, transformed_points, return_indices=True) q = from_matrix(q) deltas = transformed_points - (rotate(q, points[indices]) + t) norms = np.linalg.norm(deltas, axis=-1) # This is purely a heuristic, since we can't guarantee exact matches self.assertTrue(np.mean(norms) < 0.5)
def s3_unitcell(f): """Open solid unit cell Args ---- f : float The patch offset statepoint parameter Assumes f = ±1 corresponds to the patch on the vertex """ import hoomd N = 2 a1 = np.array([1, 0, 0]) theta = np.deg2rad(60) a2 = np.array([np.cos(theta), np.sin(theta), 0]) a3 = np.array([0, 0, 1]) pos = [[0, 0, 0], 1 / 3 * (a1 + a2)] x = f * 90 orientations = np.deg2rad(np.array([-30 + x, 150 + x])) orientations = [rowan.from_axis_angle(a3, t) for t in orientations] return hoomd.lattice.unitcell(N, a1, a2, a3, dimensions=2, position=pos, orientation=orientations)
def test_distance_to_surface_unit_area_ngon_vertex_distance(shape, ): """Check that the actual distances are computed correctly.""" distances = np.linalg.norm(shape.vertices - shape.center, axis=-1) theta = np.linspace(0, 2 * np.pi, shape.num_vertices + 1) assert np.allclose(shape.distance_to_surface(theta)[:-1], distances) # Try a positive rotation. verts = rowan.rotate(rowan.from_axis_angle([0, 0, 1], 0.1), shape.vertices) shape = ConvexPolygon(verts) assert np.allclose(shape.distance_to_surface(theta + 0.1)[:-1], distances) # Now try a negative rotation. verts = rowan.rotate(rowan.from_axis_angle([0, 0, 1], -0.2), shape.vertices) shape = ConvexPolygon(verts) assert np.allclose(shape.distance_to_surface(theta - 0.1)[:-1], distances)
def test_single(self): """Test rotation about an axis.""" v = np.array([1, 0, 0]) theta = np.pi quats = rowan.from_axis_angle(v, theta) self.assertTrue(quats.shape[:-1] == v.shape[:-1]) self.assertTrue(np.allclose(quats, np.array([0, 1, 0, 0])))
def quaternion_from_axis_angle(x, y, z, theta): """Generate a quaternion from axis [x, y, z] and angle theta.""" if x == y == z == 0: return np.array([1, 0, 0, 0]) axis = np.array([x, y, z]) axis /= np.linalg.norm(axis) return rowan.from_axis_angle(axis, theta)
def orbit(self, yaw=0, pitch=0, roll=0, factor=-0.0025, slight=False): """Orbit the camera about the look_at point.""" if slight: factor = factor * 0.1 basis = numpy.array(self._start_camera.basis) q1 = rowan.from_axis_angle(basis[1, :], factor * yaw) q2 = rowan.from_axis_angle(basis[0, :], factor * pitch) q3 = rowan.from_axis_angle(basis[2, :], factor * roll) q = rowan.multiply(q2, rowan.multiply(q1, q3)) v = self._start_camera.position - self._start_camera.look_at v = rowan.rotate(q, v) self.camera.position = self._start_camera.look_at + v self.camera.up = rowan.rotate(q, basis[1, :])
def test_ordered(self): # do not need positions, just orientations N = 1000 axes = np.zeros(shape=(N, 3), dtype=np.float32) angles = np.zeros(shape=N, dtype=np.float32) axes[:, 2] = 1.0 # generate similar angles np.random.seed(1030) angles = np.random.uniform(low=0.0, high=0.05, size=N) orientations = rowan.from_axis_angle(axes, angles) # create cubatic object t_initial = 5.0 t_final = 0.001 scale = 0.95 n_replicates = 10 cop = freud.order.Cubatic(t_initial, t_final, scale, n_replicates) # Test access with self.assertRaises(AttributeError): cop.order with self.assertRaises(AttributeError): cop.orientation with self.assertRaises(AttributeError): cop.particle_order with self.assertRaises(AttributeError): cop.global_tensor with self.assertRaises(AttributeError): cop.cubatic_tensor cop.compute(orientations) # Test access cop.order cop.orientation cop.particle_order cop.global_tensor cop.cubatic_tensor # Test values of the OP self.assertAlmostEqual(cop.order, 1, places=2, msg="Cubatic Order is not approx. 1") self.assertGreater(np.nanmin(cop.particle_order), 0.9, msg="Per particle order parameter value is too low") # Test attributes self.assertAlmostEqual(cop.t_initial, t_initial) self.assertAlmostEqual(cop.t_final, t_final) self.assertAlmostEqual(cop.scale, scale) # Test shapes for the tensor since we can't ensure values. self.assertEqual(cop.orientation.shape, (4, )) self.assertEqual(cop.cubatic_tensor.shape, (3, 3, 3, 3)) self.assertEqual(cop.global_tensor.shape, (3, 3, 3, 3))
def test_multiple_vectors(self): """Test multiple vectors against an angle.""" v = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) theta = np.pi quats = rowan.from_axis_angle(v, theta) self.assertTrue(quats.shape[:-1] == v.shape[:-1]) self.assertTrue( np.allclose(quats, np.array([[0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])))
def get_bin(query_point, point, query_point_angle, point_angle): r_ij = point - query_point orientation = rowan.from_axis_angle([0, 0, 1], -point_angle) rot_r_ij = rowan.rotate(orientation, r_ij) xy_bins = np.floor( (rot_r_ij[:2] + [maxX, maxY]) / [dx, dy]).astype(np.int32) angle_bin = np.floor( ((query_point_angle - np.arctan2(-r_ij[1], -r_ij[0])) % (2. * np.pi)) / dT).astype(np.int32) return [xy_bins[0], xy_bins[1], angle_bin]
def test_ordered(self): # do not need positions, just orientations N = 1000 axes = np.zeros(shape=(N, 3), dtype=np.float32) angles = np.zeros(shape=N, dtype=np.float32) axes[:, 2] = 1.0 # generate similar angles np.random.seed(1030) angles = np.random.uniform(low=0.0, high=0.05, size=N) orientations = rowan.from_axis_angle(axes, angles) # create cubatic object t_initial = 5.0 t_final = 0.001 scale = 0.95 n_replicates = 10 cop = freud.order.Cubatic(t_initial, t_final, scale, n_replicates) # Test access with pytest.raises(AttributeError): cop.order with pytest.raises(AttributeError): cop.orientation with pytest.raises(AttributeError): cop.particle_order with pytest.raises(AttributeError): cop.global_tensor with pytest.raises(AttributeError): cop.cubatic_tensor cop.compute(orientations) # Test access cop.order cop.orientation cop.particle_order cop.global_tensor cop.cubatic_tensor # Test values of the OP assert round(abs(cop.order - 1), 2) == 0, "Cubatic Order is not approx. 1" assert (np.nanmin(cop.particle_order) > 0.9), "Per particle order parameter value is too low" # Test attributes assert round(abs(cop.t_initial - t_initial), 7) == 0 assert round(abs(cop.t_final - t_final), 7) == 0 assert round(abs(cop.scale - scale), 7) == 0 # Test shapes for the tensor since we can't ensure values. assert cop.orientation.shape == (4, ) assert cop.cubatic_tensor.shape == (3, 3, 3, 3) assert cop.global_tensor.shape == (3, 3, 3, 3)
def gen_sym_quats(group): """Generate symmetric quaternions for a set of point groups.""" operations = symgroups[group] quats = [] for operation in operations: qtemp = rowan.from_axis_angle(axes=operation[1], angles=2 * np.pi / operation[0]) quats.append(qtemp.tolist()) quats.append(rowan.multiply([-1, 0, 0, 0], qtemp).tolist()) return quats
def test_multiple(self): """Test multiple vectors against multiple angles""" v = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) theta = np.array([np.pi, np.pi / 2, np.pi / 3]) quats = rowan.from_axis_angle(v, theta) self.assertTrue(quats.shape[:-1] == v.shape[:-1]) self.assertTrue( np.allclose( quats, np.array([[0, 1, 0, 0], [np.sqrt(2) / 2, 0, np.sqrt(2) / 2, 0], [np.sqrt(3) / 2, 0, 0, 1 / 2]])))
def test_inertia_tensor(square): """Test the inertia tensor calculation.""" square.center = (0, 0, 0) assert np.sum(square.inertia_tensor > 1e-6) == 1 assert square.inertia_tensor[2, 2] == 1 / 6 # Validate yz plane. rotation = rowan.from_axis_angle([0, 1, 0], np.pi / 2) rotated_verts = rowan.rotate(rotation, square.vertices) rotated_square = ConvexPolygon(rotated_verts) assert np.sum(rotated_square.inertia_tensor > 1e-6) == 1 assert rotated_square.inertia_tensor[0, 0] == 1 / 6 # Validate xz plane. rotation = rowan.from_axis_angle([1, 0, 0], np.pi / 2) rotated_verts = rowan.rotate(rotation, square.vertices) rotated_square = ConvexPolygon(rotated_verts) assert np.sum(rotated_square.inertia_tensor > 1e-6) == 1 assert rotated_square.inertia_tensor[1, 1] == 1 / 6 # Validate translation along each axis. delta = 2 area = square.area for i in range(3): translation = [0] * 3 translation[i] = delta translated_verts = square.vertices + translation translated_square = ConvexPolygon(translated_verts) offdiagonal_tensor = translated_square.inertia_tensor.copy() diag_indices = np.diag_indices(3) offdiagonal_tensor[diag_indices] = 0 assert np.sum(offdiagonal_tensor > 1e-6) == 0 expected_diagonals = [0, 0, 1 / 6] for j in range(3): if i != j: expected_diagonals[j] += area * delta * delta assert np.allclose( np.diag(translated_square.inertia_tensor), expected_diagonals )
def test_complex(self): """Test higher dimensions and broadcasting.""" # Various ways of producing the same output expected_output = (np.array([ [0, 1, 0, 0], [np.sqrt(2) / 2, 0, np.sqrt(2) / 2, 0], [np.sqrt(3) / 2, 0, 0, 1 / 2], ])[np.newaxis, np.newaxis, ...].repeat(2, axis=0).repeat(2, axis=1)) # Matching array shapes (no broadcasing at all) v = (np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])[np.newaxis, np.newaxis, ...].repeat(2, axis=0).repeat(2, axis=1)) theta = (np.array([np.pi, np.pi / 2, np.pi / 3])[np.newaxis, np.newaxis, ...].repeat(2, axis=0).repeat(2, axis=1)) quats = rowan.from_axis_angle(v, theta) self.assertTrue(quats.shape[:-1] == v.shape[:-1]) self.assertTrue(np.allclose(quats, expected_output)) # Broadcasting in theta theta_reduced = theta[0, :, ...] quats = rowan.from_axis_angle(v, theta_reduced) self.assertTrue(quats.shape[:-1] == v.shape[:-1]) self.assertTrue(np.allclose(quats, expected_output)) # Broadcasting in v v_reduced = v[:, 0, ...] quats = rowan.from_axis_angle(v_reduced, theta) self.assertTrue(quats.shape[:-1] == v.shape[:-1]) self.assertTrue(np.allclose(quats, expected_output)) # Broadcasting in both quats = rowan.from_axis_angle(v_reduced[:, np.newaxis, ...], theta_reduced[np.newaxis, :, ...]) self.assertTrue(quats.shape[:-1] == v.shape[:-1]) self.assertTrue(np.allclose(quats, expected_output))
def test_quaternions(self): """Test that using quaternions as angles works.""" boxSize = 8 box = freud.box.Box.square(boxSize) points = np.array([[0, 0, 0]], dtype=np.float32) query_points = np.array([[1.1, 0.0, 0.0], [-1.2, 0.0, 0.0], [0.0, 1.3, 0.0], [0.0, -1.4, 0.0]], dtype=np.float32) angles = np.array([0.0] * points.shape[0], dtype=np.float32) query_angles = np.array([0.0] * query_points.shape[0], dtype=np.float32) orientations = rowan.from_axis_angle([0, 0, 1], angles) query_orientations = rowan.from_axis_angle([0, 0, 1], query_angles) max_width = 3 nbins = 3 pmft = freud.pmft.PMFTXY(max_width, max_width, nbins) pmft.compute((box, points), orientations, query_points, neighbors={ 'mode': 'nearest', 'num_neighbors': 1 }) # Now every point in query_points will find the origin as a neighbor. npt.assert_array_equal(pmft.bin_counts, [[0, 1, 0], [1, 0, 1], [0, 1, 0]]) # Now there will be only one neighbor for the single point. pmft.compute((box, query_points), query_orientations, points, neighbors={ 'mode': 'nearest', 'num_neighbors': 1 }) npt.assert_array_equal(pmft.bin_counts, [[0, 0, 0], [0, 0, 0], [0, 1, 0]])
def test_perfect(self): """Test perfectly aligned systems with different molecular axes""" N = 10000 axes = np.zeros(shape=(N, 3), dtype=np.float32) angles = np.zeros(shape=N, dtype=np.float32) axes[:, 0] = 1.0 orientations = rowan.from_axis_angle(axes, angles) # Test for parallel to molecular axis u = np.array([1, 0, 0]) op_parallel = freud.order.Nematic(u) # Test access with self.assertRaises(AttributeError): op_parallel.order with self.assertRaises(AttributeError): op_parallel.director with self.assertRaises(AttributeError): op_parallel.particle_tensor with self.assertRaises(AttributeError): op_parallel.nematic_tensor op_parallel.compute(orientations) # Test access op_parallel.order op_parallel.director op_parallel.particle_tensor op_parallel.nematic_tensor self.assertTrue(op_parallel.order == 1) npt.assert_equal(op_parallel.director, u) npt.assert_equal( op_parallel.nematic_tensor, np.diag([1, -0.5, -0.5])) npt.assert_equal( op_parallel.nematic_tensor, np.mean( op_parallel.particle_tensor, axis=0)) # Test for perpendicular to molecular axis u = np.array([0, 1, 0]) op_perp = freud.order.Nematic(u) op_perp.compute(orientations) self.assertEqual(op_perp.order, 1) npt.assert_equal(op_perp.director, u) npt.assert_equal( op_perp.nematic_tensor, np.diag([-0.5, 1, -0.5]))
def test_orientation_with_fewer_query_points(self): """The orientations should be associated with the query points if they are provided. Ensure that this works when the number of points and query points differ.""" L = 8 box = freud.box.Box.cube(L) # Don't place the points at exactly distances of 0/1 apart to avoid any # ambiguity when the distances fall on the bin boundaries. points = np.array([[0.1, 0.1, 0], [0.89, 0.89, 0]], dtype=np.float32) points2 = np.array([[1, 0, 0]], dtype=np.float32) angles = np.array([np.deg2rad(0)] * points.shape[0], dtype=np.float32) quats = rowan.from_axis_angle([0, 0, 1], angles) angles2 = np.array([np.deg2rad(0)] * points2.shape[0], dtype=np.float32) quats2 = rowan.from_axis_angle([0, 0, 1], angles2) def points_to_set(bin_counts): """Extract set of unique occupied bins from pmft bin counts.""" return set(zip(*np.asarray(np.where(bin_counts)).tolist())) max_width = 3 cells_per_unit_length = 4 nbins = max_width * cells_per_unit_length pmft = freud.pmft.PMFTXYZ(max_width, max_width, max_width, nbins) # There should be two nonzero bins: # dx=-0.9, dy=0.1: bin (4, 6). # dx=-0.11, dy=0.89: bin (5, 7). pmft.compute( (box, points), quats2, points2, neighbors={ "mode": "nearest", "num_neighbors": 2 }, ) npt.assert_array_equal(points_to_set(pmft.bin_counts), {(4, 6, 6), (5, 7, 6)}) # Now the sets of points are swapped, so: # dx=0.9, dy=-0.1: bin (7, 5). # dx=0.11, dy=-0.89: bin (6, 4). pmft.compute( (box, points2), quats, points, neighbors={ "mode": "nearest", "num_neighbors": 2 }, ) npt.assert_array_equal(points_to_set(pmft.bin_counts), {(7, 5, 6), (6, 4, 6)}) # Apply a rotation to whichever point is provided as a query_point by # 45 degrees (easiest to picture if you think of each point as a # square). angles2 = np.array([np.deg2rad(45)] * points2.shape[0], dtype=np.float32) quats2 = rowan.from_axis_angle([0, 0, 1], angles2) # Determine the relative position of the point when points2 is rotated # by 45 degrees. Since we're undoing the orientation of the orientation # of the particle, we have to conjugate the quaternion. bond_vector = rowan.rotate(rowan.conjugate(quats2), points - points2) bins = ((bond_vector + max_width) * cells_per_unit_length / 2).astype(int) bins = {tuple(x) for x in bins} pmft.compute( (box, points), quats2, points2, neighbors={ "mode": "nearest", "num_neighbors": 2 }, ) npt.assert_array_equal(points_to_set(pmft.bin_counts), bins)
def test_orientation_with_query_points(self): """The orientations should be associated with the query points if they are provided.""" L = 8 box = freud.box.Box.cube(L) # Don't place the points at exactly distances of 0/1 apart to avoid any # ambiguity when the distances fall on the bin boundaries. points = np.array([[0.1, 0.1, 0]], dtype=np.float32) points2 = np.array([[1, 0, 0]], dtype=np.float32) angles = np.array([np.deg2rad(0)] * points2.shape[0], dtype=np.float32) quats = rowan.from_axis_angle([0, 0, 1], angles) max_width = 3 cells_per_unit_length = 4 nbins = max_width * cells_per_unit_length pmft = freud.pmft.PMFTXYZ(max_width, max_width, max_width, nbins) # In this case, the only nonzero bin should be in the bin corresponding # to dx=-0.9, dy=0.1, which is (4, 6). pmft.compute( (box, points), quats, points2, neighbors={ "mode": "nearest", "num_neighbors": 1 }, ) npt.assert_array_equal( np.asarray(np.where(pmft.bin_counts)).squeeze(), (4, 6, 6)) # Now the sets of points are swapped, so dx=0.9, dy=-0.1, which is # (7, 5). pmft.compute( (box, points2), quats, points, neighbors={ "mode": "nearest", "num_neighbors": 1 }, ) npt.assert_array_equal( np.asarray(np.where(pmft.bin_counts)).squeeze(), (7, 5, 6)) # Apply a rotation to whichever point is provided as a query_point by # 45 degrees (easiest to picture if you think of each point as a # square). angles = np.array([np.deg2rad(45)] * points2.shape[0], dtype=np.float32) quats = rowan.from_axis_angle([0, 0, 1], angles) # Determine the relative position of the point when points2 is rotated # by 45 degrees. Since we're undoing the orientation of the orientation # of the particle, we have to conjugate the quaternion. bond_vector = rowan.rotate(rowan.conjugate(quats), points - points2) bins = ((bond_vector + max_width) * cells_per_unit_length / 2).astype(int) pmft.compute( (box, points), quats, points2, neighbors={ "mode": "nearest", "num_neighbors": 1 }, ) npt.assert_array_equal( np.asarray(np.where(pmft.bin_counts)).squeeze(), bins.squeeze()) # If we swap the order of the points, the angle should no longer # matter. bond_vector = rowan.rotate(rowan.conjugate(quats), points2 - points) bins = ((bond_vector + max_width) * cells_per_unit_length / 2).astype(int) pmft.compute( (box, points2), quats, points, neighbors={ "mode": "nearest", "num_neighbors": 1 }, ) npt.assert_array_equal( np.asarray(np.where(pmft.bin_counts)).squeeze(), bins.squeeze())
(-3, [0, 0, 1]), (2, [1, 0, 0]), (2, [np.cos(np.pi / 3), np.sin(np.pi / 3), 0]), (2, [-np.cos(np.pi / 3), np.sin(np.pi / 3), 0]), ] # http://www-history.mcs.st-and.ac.uk/~john/geometry/Lectures/L10.html # Generated by hand # Some variables to clean up the icosohedral group icodi = np.pi - np.arctan(2) # the dihedral angle picodi = -np.pi / 2 + np.arctan(2) # pi/2 - the dihedral angle # a face vector that will be used a lot face1 = [np.cos(picodi), 0, -np.sin(picodi)] # the vector to the crown edges lat_edge = rowan.rotate( rowan.from_axis_angle(axes=[0, 1, 0], angles=icodi / 2), [0, 0, 1]) # the vector to the mid-latitude edges crown_edge = rowan.rotate( rowan.from_axis_angle(axes=face1, angles=2 * 2 * np.pi / 5), lat_edge) crown_vert = [-0.850651, 0.0, 1.11352] # A vertex in the top pentagon equi_vert = rowan.rotate( rowan.from_axis_angle(axes=face1, angles=1 * 2 * np.pi / 5), crown_vert) icosohedral = [ (1, [1, 0, 0]), # first face pair (5, [0, 0, 1]), (5 / 2, [0, 0, 1]), (5 / 3, [0, 0, 1]), (5 / 4, [0, 0, 1]), # second face pair
omega = np.radians(np.arange(0,365,5)) ro_y = {} yi = 0 for v in tqdm(pf_xyz): ro_h = {} for fi,fam in enumerate(pf.symHKL): axis = np.cross(fam,v) angle = np.arccos(np.dot(fam,v)) q0 = rowan.from_axis_angle(axis, angle) q0_n = rowan.normalize(q0) q1_n = [rowan.normalize(rowan.from_axis_angle(h, omega)) for h in fam] # eu = np.zeros((len(omega),3,fam.shape[0])) eu = [] for i,(qA,qB) in enumerate(zip(q0_n,q1_n)): qF = rowan.multiply(qA, qB) with np.errstate(divide='ignore'): temp = np.array((qF[:,1]/qF[:,0],qF[:,2]/qF[:,0],qF[:,3]/qF[:,0])).T ax = ro2ax(temp)
def initialize(job): import hoomd import hoomd.hpmc import scipy.spatial """Sets up the system and sets default values for writers and stuf """ # get sp params f = job.sp.patch_offset n_e = job.sp.n_edges n_ge = job.sp.n_guest_edges gar = job.sp.guest_aspect_ratio host_guest_area_ratio = job.sp.host_guest_area_ratio n_repeats = job.sp.n_repeats seed = job.sp.replica # initialize the hoomd context msg_fn = job.fn('init-hoomd.log') hoomd.context.initialize('--mode=cpu --msg-file={}'.format(msg_fn)) # figure out shape vertices and patch locations xs = np.array([np.cos(n * 2 * np.pi / n_e) for n in range(n_e)]) ys = np.array([np.sin(n * 2 * np.pi / n_e) for n in range(n_e)]) zs = np.zeros_like(ys) vertices = np.vstack((xs, ys, zs)).T A_particle = scipy.spatial.ConvexHull(vertices[:, :2]).volume vertices = vertices - np.mean(vertices, axis=0) vertex_vertex_vectors = np.roll(vertices, -1, axis=0) - vertices half_edge_locations = vertices + 0.5 * vertex_vertex_vectors patch_locations = half_edge_locations + f * (vertices - half_edge_locations) # make the guest particles xs = np.array([np.cos(n * 2 * np.pi / n_ge) for n in range(n_ge)]) ys = np.array([np.sin(n * 2 * np.pi / n_ge) for n in range(n_ge)]) zs = np.zeros_like(ys) guest_vertices = np.vstack((xs, ys, zs)).T rot_quat = rowan.from_axis_angle([0, 0, 1], 2 * np.pi / n_ge / 2) guest_vertices = rowan.rotate(rot_quat, guest_vertices) guest_vertices[:, 0] *= gar target_guest_area = A_particle * host_guest_area_ratio current_guest_area = scipy.spatial.ConvexHull(guest_vertices[:, :2]).volume guest_vertices *= np.sqrt(target_guest_area / current_guest_area) # save everything into the job doc that we need to if hoomd.comm.get_rank() == 0: job.doc.vertices = vertices job.doc.patch_locations = patch_locations job.doc.A_particle = A_particle job.doc.guest_vertices = guest_vertices hoomd.comm.barrier() # build the system if job.sp.initial_config in ('open', 's3'): uc = job._project.s3_unitcell(job.sp.patch_offset) system = hoomd.init.create_lattice(uc, n_repeats) else: raise NotImplementedError('Initialization not implemented.') # restart writer; period=None since we'll just call write_restart() at end restart_writer = hoomd.dump.gsd(filename=job.fn('restart.gsd'), group=hoomd.group.all(), truncate=True, period=None, phase=0) # set up the integrator with the shape info mc = hoomd.hpmc.integrate.convex_polygon(seed=seed, d=0, a=0) mc.shape_param.set('A', vertices=vertices[:, :2]) total_particle_area = len(system.particles) * A_particle phi = total_particle_area / system.box.get_volume() sf = np.sqrt(phi / job.sp.phi) hoomd.update.box_resize( Lx=system.box.Lx * sf, Ly=system.box.Ly * sf, period=None, ) restart_writer.dump_shape(mc) restart_writer.dump_state(mc) mc.set_params(d=0.1, a=0.5) # save everything into the job doc that we need to if hoomd.comm.get_rank() == 0: job.doc.mc_d = {x: 0.05 for x in system.particles.types} job.doc.mc_a = {x: 0.1 for x in system.particles.types} job.doc.vertices = vertices job.doc.patch_locations = patch_locations job.doc.A_particle = A_particle for k, v in job._project.required_defaults: job.doc.setdefault(k, v) os.system('cp {} {}'.format(job.fn('restart.gsd'), job.fn('init.gsd'))) hoomd.comm.barrier() restart_writer.write_restart() if hoomd.comm.get_rank() == 0: os.system('cp {} {}'.format(job.fn('restart.gsd'), job.fn('init.gsd'))) hoomd.comm.barrier() return
fibre_e[fi] = {} fibre_q[fi] = {} nn_gridPts[fi] = {} nn_gridDist[fi] = {} for yi in trange(len(xyz_pf)): y = xyz_pf[yi] # for fi,fam in enumerate(pf.symHKL): axis = np.cross(fam, y) angle = np.arccos(np.dot(fam, y)) q0 = quat.from_axis_angle(axis, angle) q0_n = quat.normalize(q0) q1_n = [quat.normalize(quat.from_axis_angle(h, omega)) for h in fam] # eu = np.zeros((len(omega),3,fam.shape[0])) eu2 = [] qfib = np.zeros((len(q1_n[0]), len(q0_n), 4)) for sym_eq, (qA, qB) in enumerate(zip(q0_n, q1_n)): temp = quat.multiply(qA, qB) qfib[:, sym_eq, :] = temp
def calcFibre(symHKL, yset, qgrid, omega, rad, tree, euc_rad): fibre_e = {} fibre_q = {} nn_gridPts = {} nn_gridDist = {} egrid_trun = {} for fi, fam in enumerate(tqdm(symHKL)): fibre_e[fi] = {} fibre_q[fi] = {} nn_gridPts[fi] = {} nn_gridDist[fi] = {} egrid_trun[fi] = {} """ set proper iterator """ if isinstance(yset, dict): it = yset[fi] else: it = yset q1_n = [quat.from_axis_angle(h, omega) for h in fam] for yi, y in enumerate(it): axis = np.cross(fam, y) angle = np.arccos(np.dot(fam, y)) q0_n = quat.from_axis_angle(axis, angle) # q0_n = quat.normalize(q0) qfib = np.zeros((len(q1_n[0]), len(q0_n), 4)) for sym_eq, (qA, qB) in enumerate(zip(q0_n, q1_n)): temp = quat.multiply(qA, qB) qfib[:, sym_eq, :] = temp phi1, Phi, phi2 = quat2eu(qfib) phi1 = np.where(phi1 < 0, phi1 + 2 * np.pi, phi1) #brnng back to 0 - 2pi Phi = np.where(Phi < 0, Phi + np.pi, Phi) #brnng back to 0 - pi phi2 = np.where(phi2 < 0, phi2 + 2 * np.pi, phi2) #brnng back to 0 - 2pi eu_fib = np.stack((phi1, Phi, phi2), axis=2) eu_fib = np.reshape(eu_fib, (eu_fib.shape[0] * eu_fib.shape[1], eu_fib.shape[2])) #new method fz = (eu_fib[:, 0] <= od._phi1max) & ( eu_fib[:, 1] <= od._Phimax) & (eu_fib[:, 2] <= od._phi2max) fz_idx = np.nonzero(fz) fibre_e[fi][yi] = eu_fib[fz] fib_idx = np.unravel_index(fz_idx[0], (qfib.shape[0], qfib.shape[1])) fibre_q[fi][yi] = qfib[fib_idx] # """ reduce geodesic query size """ # qfib_pos = np.copy(qfib[fib_idx]) # qfib_pos[qfib_pos[:,0] < 0] *= -1 # query = np.concatenate(tree.query_radius(qfib_pos,euc_rad)) # query_uni = np.unique(query) # qgrid_trun = qgrid[query_uni] # qgrid_trun_idx = np.arange(len(qgrid))[query_uni] #store indexes to retrieve original grid pts later """ distance calc """ temp = quatMetricNumba(qgrid, qfib[fib_idx]) """ find tube """ tube = (temp <= rad) temp = np.column_stack((np.argwhere(tube)[:, 0], temp[tube])) """ round very small values """ temp = np.round(temp, decimals=7) """ move values at zero to very small (1E-5) """ temp[:, 1] = np.where(temp[:, 1] == 0, 1E-5, temp[:, 1]) """ sort by min distance """ temp = temp[np.argsort(temp[:, 1], axis=0)] """ return unique pts (first in list) """ uni_pts = np.unique(temp[:, 0], return_index=True) nn_gridPts[fi][yi] = uni_pts[0].astype(int) nn_gridDist[fi][yi] = temp[uni_pts[1], 1] # egrid_trun[fi][yi] = bungeAngs[query_uni] return nn_gridPts, nn_gridDist, fibre_e
def _calcFibreHDF5(hfam, yset, omega, qgrid, od, h5fname, h5gname): fibre_e = {} fibre_q = {} nn_gridPts = {} nn_gridDist = {} f = _h5.File(h5fname, 'r+') grp = f.create_group(h5gname) fib_grp = grp.create_group('fibre') dist_grp = grp.create_group('dist') for yi, y in enumerate(yset): axis = _np.cross(hfam, y) angle = _np.arccos(_np.dot(hfam, y)) q0 = _quat.from_axis_angle(axis, angle) q1 = [_quat.from_axis_angle(h, omega) for h in hfam] qfib = _np.zeros((len(q1[0]), len(q0), 4)) for sym_eq, (qA, qB) in enumerate(zip(q0, q1)): temp = _quat.multiply(qA, qB) qfib[:, sym_eq, :] = temp phi1, Phi, phi2 = _quat2eu(qfib) phi1 = _np.where(phi1 < 0, phi1 + 2 * _np.pi, phi1) #brnng back to 0 - 2pi Phi = _np.where(Phi < 0, Phi + _np.pi, Phi) #brnng back to 0 - pi phi2 = _np.where(phi2 < 0, phi2 + 2 * _np.pi, phi2) #brnng back to 0 - 2pi eu_fib = _np.stack((phi1, Phi, phi2), axis=2) eu_fib = _np.reshape( eu_fib, (eu_fib.shape[0] * eu_fib.shape[1], eu_fib.shape[2])) #new method fz = (eu_fib[:, 0] < od._phi1max) & (eu_fib[:, 1] < od._Phimax) & ( eu_fib[:, 2] < od._phi2max) fz_idx = _np.nonzero(fz) fibre_e[yi] = eu_fib[fz] fib_idx = _np.unravel_index(fz_idx[0], (qfib.shape[0], qfib.shape[1])) fibre_q[yi] = qfib[fib_idx] """ distance calc """ temp = quatMetricNumba(qgrid, qfib[fib_idx]) fib_grp.create_dataset(str(yi), data=fibre_q[yi], compression="gzip", compression_opts=9) dist_grp.create_dataset(str(yi), data=temp, compression="gzip", compression_opts=9) # """ find tube """ # tube = (temp <= rad) # temp = _np.column_stack((_np.argwhere(tube)[:,0],temp[tube])) # """ round very small values """ # temp = _np.round(temp, decimals=7) # """ move values at zero to very small (1E-5) """ # temp[:,1] = _np.where(temp[:,1] == 0, 1E-5, temp[:,1]) # """ sort by min distance """ # temp = temp[_np.argsort(temp[:,1],axis=0)] # """ return unique pts (first in list) """ # uni_pts = _np.unique(temp[:,0],return_index=True) # nn_gridPts[yi] = uni_pts[0] # nn_gridDist[yi] = temp[uni_pts[1],1] # return nn_gridPts, nn_gridDist, fibre_e, fibre_q f.close() return
azi = np.linspace(0,2*pi,azi_n) pol = np.ones((len(azi)))*pol x = np.sin(pol) * np.cos(azi) y = np.sin(pol) * np.sin(azi) z = np.cos(pol) pts.append(np.array((x,y,z)).T) xyz_sphere = np.vstack(pts) # %% ## secondary rotations # offset_rots = quat.from_euler(np.deg2rad([0,0,0,0,0]), np.deg2rad([0,2,2,2,2]), np.deg2rad([0,5,10,15,20]),convention='zxz',axis_type='intrinsic') offset_rots = quat.from_axis_angle(np.array(([-1,1,0],[-1,1,0],[1,-1,0],[-1,-1,2])), np.deg2rad([0,8,8,-8])) cphi = np.cos(phi/2) sphi = np.sin(phi/2) q0 = {} q = {} qf = {} fibre_e = {} fibre_q = {} tube_e = {} nn_gridPts = {} nn_gridDist = {}