def bounding_circle(self): """tuple[float, float]: Get the center and radius of the bounding circle.""" # noqa: E501 if not MINIBALL: raise ImportError("The miniball module must be installed. It can " "be installed as an extra with coxeter (e.g. " "with pip install coxeter[bounding_sphere], or " "directly from PyPI using pip install miniball.") # The algorithm in miniball involves solving a linear system and # can therefore occasionally be somewhat unstable. Applying a # random rotation will usually fix the issue. max_attempts = 10 attempt = 0 current_rotation = [1, 0, 0, 0] vertices = self.vertices while attempt < max_attempts: attempt += 1 try: center, r2 = miniball.get_bounding_ball(vertices) break except np.linalg.LinAlgError: current_rotation = rowan.random.rand(1) vertices = rowan.rotate(current_rotation, vertices) if attempt == max_attempts: raise RuntimeError("Unable to solve for a bounding sphere.") # The center must be rotated back to undo any rotation. center = rowan.rotate(rowan.conjugate(current_rotation), center) return Circle(np.sqrt(r2), center)
def minimal_bounding_sphere(self): """:class:`~.Sphere`: Get the polyhedron's bounding sphere.""" if not MINIBALL: raise ImportError( "The miniball module must be installed. It can " "be installed as an extra with coxeter (e.g. " 'with "pip install coxeter[bounding_sphere]") or ' 'directly from PyPI using "pip install miniball".' ) # The algorithm in miniball involves solving a linear system and # can therefore occasionally be somewhat unstable. Applying a # random rotation will usually fix the issue. max_attempts = 10 attempt = 0 current_rotation = [1, 0, 0, 0] vertices = self.vertices while attempt < max_attempts: attempt += 1 try: center, r2 = miniball.get_bounding_ball(vertices) break except np.linalg.LinAlgError: current_rotation = rowan.random.rand(1) vertices = rowan.rotate(current_rotation, vertices) else: raise RuntimeError("Unable to solve for a bounding sphere.") # The center must be rotated back to undo any rotation. center = rowan.rotate(rowan.conjugate(current_rotation), center) return Sphere(np.sqrt(r2), center)
def return_correlation(l, initial_q, orientations): """Compute the rotational autocorrelation.""" calc_quats = rowan.multiply(rowan.conjugate(initial_q), orientations) ref_quats = rowan.multiply(rowan.conjugate(initial_q), initial_q) ref_angles = quat_to_greek(ref_quats) calc_angles = quat_to_greek(calc_quats) f_of_t = 0 for m1 in np.arange(-l / 2, l / 2 + 1): for m2 in np.arange(-l / 2, l / 2 + 1): ref_y = hypersphere_harmonic(ref_angles, l, m1, m2) calc_y = hypersphere_harmonic(calc_angles, l, m1, m2) f_of_t += (ref_y.conjugate() * calc_y) return f_of_t.real
def get_bin(self, query_point, point, query_point_orientation, point_orientation): r_ij = point - query_point rot_r_ij = rowan.rotate(rowan.conjugate(query_point_orientation), r_ij) limits = np.asarray(self.limits) return tuple( np.floor((rot_r_ij[:2] + limits) * np.asarray(self.bins) / (2 * limits)).astype(np.int32))
def test_conjugate(self): """Test quaternion conjugation.""" np.random.seed(0) shapes = [(4,), (5, 4), (5, 5, 4), (5, 5, 5, 4)] for shape in shapes: quats = np.random.random_sample(shape) quats_conj = quats.copy() quats_conj[..., 1:] *= -1 self.assertTrue(np.all(rowan.conjugate(quats) == quats_conj))
def rotation(self, value): camera = self._backend_objects['camera'] # rotating a scene by q is equivalent to rotating the camera's # position and up vector by q* conj = rowan.conjugate(value) camera_distance = np.linalg.norm(camera.position) camera.position = rowan.rotate(conj, [0, 0, camera_distance]).tolist() camera.up = rowan.rotate(conj, [0, 1, 0]).tolist() self._backend_objects['controls'].exec_three_obj_method('update')
def rotation(self): camera = self._backend_objects['camera'] norm_out = np.array(camera.position) norm_out /= np.linalg.norm(norm_out) norm_up = np.array(camera.up) norm_up -= np.dot(norm_up, norm_out) * norm_out norm_up /= np.linalg.norm(norm_up) norm_right = np.cross(norm_up, norm_out) rotation_matrix = np.array([norm_right, norm_up, norm_out]).T result = rowan.from_matrix(rotation_matrix, require_orthogonal=False) result = rowan.conjugate(result) return result
def get_bin(self, query_point, point, query_point_orientation, point_orientation): r_ij = point - query_point rot_r_ij = rowan.rotate(rowan.conjugate(query_point_orientation), r_ij) limits = np.asarray(self.limits) xy_bins = tuple( np.floor((rot_r_ij[:2] + limits) * np.asarray(self.bins[:2]) / (2 * limits)).astype(np.int32)) point_angle = rowan.geometry.angle(point_orientation) angle_bin = np.floor( ((point_angle - np.arctan2(-r_ij[1], -r_ij[0])) % TWO_PI) * self.bins[2] / TWO_PI).astype(np.int32) return xy_bins + (angle_bin, )
def test_slerp_prime(self): """Test spherical linear interpolation derivative.""" self.assertTrue(np.all(interpolate.slerp_prime(one, one, 0) == zero)) self.assertTrue(np.all(interpolate.slerp_prime(one, one, 1) == zero)) self.assertTrue(np.all(interpolate.slerp_prime(one, one, 0.5) == zero)) self.assertTrue( np.allclose( interpolate.slerp_prime(root_two, one, 0.5), rowan.multiply( interpolate.slerp(root_two, one, 0.5), rowan.log(rowan.multiply(rowan.conjugate(root_two), one)), ), ) )
def _update_camera(self): camera = self._backend_objects['camera'] (width, height) = self.size.astype(np.float32) dz = np.sqrt(np.sum(self.size**2)) * self._clip_scale conj = rowan.conjugate(self.rotation) translation = rowan.rotate(conj, [0, 0, 1 + dz / 2]) camera.left = -width / 2 camera.right = width / 2 camera.top = height / 2 camera.bottom = -height / 2 camera.far = 1 + dz camera.up = rowan.rotate(conj, (0, 1, 0)).tolist() camera.position = translation.tolist()
def compute_rotational_ke(snapshot: Snapshot) -> float: """Compute the kinetic energy of the rotational degrees of freedom. Args: snapshot: (Snapshot): Simulation snapshot from which to compute the kinetic energy Returns: The total rotational kinetic energy of the snapshot. """ num_mols = get_num_mols(snapshot) angmom = snapshot.particles.angmom[:num_mols] moment_inertia = snapshot.particles.moment_inertia[:num_mols] momentum = rowan.multiply( 0.5 * rowan.conjugate(snapshot.particles.orientation[:num_mols]), angmom )[:, 1:] mask = moment_inertia > 0 return np.sum(0.5 * np.square(momentum[mask]) / moment_inertia[mask])
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())
def translation(self, value): controls = self._backend_objects['controls'] value = np.asarray(value) - (0, 0, self.z_offset) value = rowan.rotate(rowan.conjugate(self.rotation), value) controls.target = (-value).tolist() controls.exec_three_obj_method('update')
def render(self): """Render this Scene object.""" # Remove existing fresnel geometries from the scene for geometry in self._geometries: geometry.remove() # Clear the list of fresnel geometries self._geometries = [] # Add fresnel scene geometries from plato scene primitives for prim in self._primitives: geometry = prim.render(self._fresnel_scene) self._geometries.append(geometry) # Set up the camera camera_up = rowan.rotate(rowan.conjugate(self.rotation), [0, 1, 0]) camera_position = rowan.rotate(rowan.conjugate(self.rotation), -self.translation) camera_look_at = camera_position + rowan.rotate(rowan.conjugate(self.rotation), [0, 0, -1]) camera_height = self.size[1]/self.zoom try: orthographic_camera = fresnel.camera.Orthographic except AttributeError: # Support fresnel < 0.13.0 orthographic_camera = fresnel.camera.orthographic self._fresnel_scene.camera = orthographic_camera( position=camera_position, look_at=camera_look_at, up=camera_up, height=camera_height) # Set up lights lights = [] if 'ambient_light' in self.enabled_features: config = self.get_feature_config('ambient_light') magnitude = config.get('value', 0.25) if magnitude > 0: lights.append(fresnel.light.Light(direction=(0, 0, 1), color=(magnitude, magnitude, magnitude), theta=np.pi)) if 'directional_light' in self.enabled_features: config = self.get_feature_config('directional_light') directions = config.get('value', (.25, .5, -1)) directions = np.atleast_2d(directions).astype(np.float32) for direction in directions: magnitude = np.linalg.norm(direction) if magnitude > 0: lights.append(fresnel.light.Light(direction=-direction, color=(magnitude, magnitude, magnitude), theta=0.7)) if len(lights) > 0: self._fresnel_scene.lights = lights # Set up tracer if 'pathtracer' in self.enabled_features: # Use path tracer if enabled config = self.get_feature_config('pathtracer') tracer = self._path_tracer samples = config.get('samples', 64) def render_function(scene, **kwargs): return tracer.sample(scene, samples, **kwargs) else: # Use preview tracer by default tracer = self._preview_tracer tracer.anti_alias = 'antialiasing' in self.enabled_features render_function = tracer.render self._output = render_function(self._fresnel_scene)
def quatconj(q): """Returns the conjugate of a quaternion array q*""" return -rowan.conjugate(q)