def test_bounding_circle_radius_random_hull_rotation(points, rotation): """Test that rotating vertices does not change the bounding radius.""" assume(not np.all(rotation == 0)) hull = ConvexHull(points) poly = Polygon(points[hull.vertices]) rotation = rowan.normalize(rotation) rotated_vertices = rowan.rotate(rotation, poly.vertices) poly_rotated = Polygon(rotated_vertices) circle = poly.minimal_bounding_circle rotated_circle = poly_rotated.minimal_bounding_circle assert np.isclose(circle.radius, rotated_circle.radius)
def test_ap(self): """Test finding quaternion to rotate antiparallel vectors onto each other""" # For this test, there are multiple quaternions that would effect the # correct rotation, so rather than checking for a specific one we check # that the appropriate rotation results from applying the quaternion vec1 = np.array([1, 0, 0]) vec2 = np.array([0, 1, 0]) quat = rowan.vector_vector_rotation(vec1, vec2) self.assertTrue( np.allclose( rowan.rotate(quat, vec1), vec2/np.linalg.norm(vec2, axis=-1) )) vec1 = np.array([1, 0, 0]) vec2 = np.array([[0, 1, 0], [2, 0, 0], [-2, 0, 0]]) quat = rowan.vector_vector_rotation(vec1, vec2) self.assertTrue( np.allclose( rowan.rotate(quat, vec1), vec2/np.linalg.norm(vec2, axis=-1)[:, np.newaxis] )) vec1 = np.array([0, 1, 0]) vec2 = np.array([[0, 0, 1], [0, 2, 0], [0, -2, 0]]) quat = rowan.vector_vector_rotation(vec1, vec2) self.assertTrue( np.allclose( rowan.rotate(quat, vec1), vec2/np.linalg.norm(vec2, axis=-1)[:, np.newaxis] ))
def testfun(random_quat): assume(not np.all(random_quat == 0)) random_quat = rowan.normalize(random_quat) rotated_points = rowan.rotate(random_quat, square_points) r = 1 poly = ConvexSpheropolygon(rotated_points, radius=r) hull = ConvexHull(square_points[:, :2]) cap_area = np.pi * r * r edge_area = np.sum( np.linalg.norm(square_points - np.roll(square_points, 1, 0), axis=1), axis=0 ) sphero_area = cap_area + edge_area assert np.isclose(poly.signed_area, hull.volume + sphero_area)
def _calc_shift(orientations: np.ndarray, molecule: Molecule) -> np.ndarray: """A function to calculate the shift of large particle positions to COM positions. The positions defined in the Trimer classes are for the center of the 'large' particle since this was the output from the packing algorithm. This offsets these values to be on the center of mass, taking into account the orientation of the molecule. Args: orientations: Orientation of particles in degrees molecule: The molecule for which the shift is occurring. """ pos_shift = molecule.get_relative_positions()[0] orient_quat = rowan.from_euler(np.deg2rad(orientations), 0, 0) return rowan.rotate(orient_quat, pos_shift)
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_rotational_invariance(self): classes = [[(-1., 0, 0), (1., 0, 0)], [(0, 1, 0), (0, -1, 0)]] num_classes = len(classes) num_data = 128 train_ins = np.tile(classes, (num_data, 1, 1)) train_ins += np.random.normal(scale=.1, size=train_ins.shape) # sort by R to learn permutation invariance rsqs = np.linalg.norm(train_ins, axis=-1) for (neighborhood, rsq) in zip(train_ins, rsqs): neighborhood[:] = neighborhood[np.argsort(rsq)] train_ins, test_ins = train_ins[:num_data], train_ins[num_data:] train_outs = np.tile(np.arange(num_classes), num_data // 2) quats = rowan.random.rand(test_ins.shape[0]) test_ins = rowan.rotate(quats[:, np.newaxis], test_ins) model = keras.models.Sequential() model.add( pythia.learned.bonds.InertiaRotation( 4, input_shape=train_ins.shape[1:])) model.add(pythia.learned.spherical_harmonics.SphericalHarmonics(2)) model.add(pythia.learned.spherical_harmonics.NeighborAverage()) model.add( pythia.learned.spherical_harmonics.ComplexProjection(4, 'abs')) model.add(keras.layers.Activation('relu')) model.add(keras.layers.Flatten()) model.add(keras.layers.BatchNormalization()) model.add(keras.layers.Dense(num_classes, activation='softmax')) model.compile('sgd', 'categorical_crossentropy', metrics=['accuracy']) net_train_outs = keras.utils.to_categorical(train_outs, num_classes) train_history = model.fit(train_ins, net_train_outs, validation_data=(test_ins, net_train_outs), epochs=100, verbose=0, shuffle=True) self.assertLess(np.mean(train_history.history['val_accuracy']), .55)
def to_view(bod, view_orientation, image_size): lin_grid = np.linspace(-1, 1, image_size) x, y = np.meshgrid(lin_grid, lin_grid) y = -y r2 = x**2 + y**2 z = np.sqrt(np.clip(1 - r2, 0, None)) xyz = np.dstack((x, y, z)) xyz = rowan.rotate(rowan.inverse(view_orientation), xyz) x, y, z = xyz[:, :, 0], xyz[:, :, 1], xyz[:, :, 2] view = np.zeros((image_size, image_size)) phi = np.arccos(z) theta = np.arctan2(y, x) % (2 * np.pi) num_theta_bins, num_phi_bins = bod.nbins theta_bin_edges, phi_bin_edges = bod.bin_edges theta_bins = np.trunc(theta / (2 * np.pi) * num_theta_bins).astype(int) phi_bins = np.trunc(phi / np.pi * num_phi_bins).astype(int) view = bod.bond_order[theta_bins, phi_bins] view[r2 > 1] = np.nan return view
def test_rotational_invariance(self): box = freud.box.Box.cube(10) positions = np.array([[0, 0, 0], [-1, -1, 0], [-1, 1, 0], [1, -1, 0], [1, 1, 0], [-1, 0, -1], [-1, 0, 1], [1, 0, -1], [1, 0, 1], [0, -1, -1], [0, -1, 1], [0, 1, -1], [0, 1, 1]]) query_point_indices = np.zeros(len(positions) - 1) point_indices = np.arange(1, len(positions)) nlist = freud.locality.NeighborList.from_arrays( len(positions), len(positions), query_point_indices, point_indices, np.full(len(query_point_indices), np.sqrt(2))) q6 = freud.order.Steinhardt(6) w6 = freud.order.Steinhardt(6, wl=True) q6.compute((box, positions), neighbors=nlist) q6_unrotated_order = q6.particle_order[0] w6.compute((box, positions), neighbors=nlist) w6_unrotated_order = w6.particle_order[0] for i in range(10): np.random.seed(i) quat = rowan.random.rand() positions_rotated = rowan.rotate(quat, positions) # Ensure Q6 is rotationally invariant q6.compute((box, positions_rotated), neighbors=nlist) npt.assert_allclose(q6.particle_order[0], q6_unrotated_order, rtol=1e-5) npt.assert_allclose(q6.particle_order[0], PERFECT_FCC_Q6, rtol=1e-5) # Ensure W6 is rotationally invariant w6.compute((box, positions_rotated), neighbors=nlist) npt.assert_allclose(w6.particle_order[0], w6_unrotated_order, rtol=1e-5) npt.assert_allclose(w6.particle_order[0], PERFECT_FCC_W6, rtol=1e-5)
def transform(self, coords, sensor_labels): sensor_order = {s: idx for idx, s in enumerate(sensor_labels)} ref_cols = [ sensor_order[self._nasion], sensor_order[self._right], sensor_order[self._left] ] if len(coords.shape) == 2: coords = np.expand_dims(coords, axis=0) for n in np.arange(coords.shape[0]): try: q, t = rowan.mapping.davenport( coords[n, ref_cols, :], self.fixed_ref ) coords[n,:,:] = rowan.rotate(q, coords[n,:,:]) + t except Exception as e: warnings.warn( 'Caught an error and emitting NaNs.\nError message:\n' + \ str(e) ) coords[n,:,:] = np.nan return coords
def test_init_crystal_position(crystal_params): if isinstance(crystal_params.crystal, (TrimerP2)): return with crystal_params.temp_context(cell_dimensions=1): snap = init_from_crystal(crystal_params, equilibration=False, minimize=False) crys = crystal_params.crystal num_mols = get_num_mols(snap) mol_positions = crystal_params.molecule.get_relative_positions() positions = np.concatenate([ pos + rowan.rotate(orient, mol_positions) for pos, orient in zip(crys.positions, crys.get_orientations()) ]) box = np.array([snap.box.Lx, snap.box.Ly, snap.box.Lz]) if crys.molecule.rigid: sim_pos = snap.particles.position[num_mols:] % box else: sim_pos = snap.particles.position % box init_pos = positions % box assert_allclose(sim_pos, init_pos)
def f(self, s, a): # input: # s, nd array, (n,) # a, nd array, (m,1) # output # dsdt, nd array, (n,1) dsdt = np.zeros(self.n) q = s[6:10] omega = s[10:] # get input a = np.reshape(a, (self.m, )) eta = np.dot(self.B0, a) f_u = np.array([0, 0, eta[0]]) tau_u = np.array([eta[1], eta[2], eta[3]]) # dynamics # dot{p} = v dsdt[0:3] = s[3:6] # mv = mg + R f_u dsdt[3:6] = self.g + rowan.rotate(q, f_u) / self.mass # dot{R} = R S(w) # to integrate the dynamics, see # https://www.ashwinnarayan.com/post/how-to-integrate-quaternions/, and # https://arxiv.org/pdf/1604.08139.pdf qnew = rowan.calculus.integrate(q, omega, self.ave_dt) qnew = rowan.normalize(qnew) if qnew[0] < 0: qnew = -qnew # transform qnew to a "delta q" that works with the usual euler integration dsdt[6:10] = (qnew - q) / self.ave_dt # mJ = Jw x w + tau_u dsdt[10:] = self.inv_J * (np.cross(self.J * omega, omega) + tau_u) return dsdt.reshape((len(dsdt), 1))
def render(self, rotation=(1, 0, 0, 0), **kwargs): positions = rowan.rotate(rotation, self.positions) orientations = rowan.multiply(rotation, rowan.normalize(self.orientations)) rotations = np.degrees(rowan.to_euler(orientations)) lines = [] for (pos, rot, col, alpha) in zip(positions, rotations, self.colors[:, :3], 1 - self.colors[:, 3]): lines.append( 'sphere {{ ' '0, 1 scale<{a}, {b}, {c}> ' 'rotate <{rot[2]}, {rot[1]}, {rot[0]}> ' 'translate <{pos[0]}, {pos[1]}, {pos[2]}> ' 'pigment {{ color <{col[0]}, {col[1]}, {col[2]}> transmit {alpha} }} ' '}}'.format(a=self.a, b=self.b, c=self.c, pos=pos, rot=rot, col=col, alpha=alpha)) return lines
def test_single_quaternion(self): """Testing trivial rotations""" self.assertTrue(np.all(rowan.rotate(one, one_vector) == one_vector))
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
(3, [0, 0, 1]), (-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
def update_arrays(self): if not self._dirty_attributes: return # TODO make number of facets a configurable attribute thetas = np.linspace(0, 2 * np.pi, 10, endpoint=False) # to begin, pretend we are constructing a unit circle in the # xy plane; multiply xy by each line radius and z by each line # length before rotating and translating appropriately image = np.array( [np.cos(thetas), np.sin(thetas), np.zeros_like(thetas)]).T image = np.concatenate([image, image + (0, 0, 1)], axis=0) # indices to make all triangles for a given Line object image_indices = np.tile( np.arange(2 * len(thetas))[:, np.newaxis], (1, 3)) image_indices[:len(thetas), 1] += 1 image_indices[:len(thetas), 2] += len(thetas) image_indices[len(thetas):, 1] += 1 - len(thetas) image_indices[len(thetas):, 2] += 1 image_indices[len(thetas) - 1, 1] -= len(thetas) image_indices[-1, 1:] -= len(thetas) cap_indices = np.array( list( geometry.fanTriangleIndices( np.arange(len(thetas))[np.newaxis]))) image_indices = np.concatenate( [image_indices, cap_indices[:, ::-1], len(thetas) + cap_indices], axis=0) # normal vectors for each Line segment normals = self.end_points - self.start_points lengths = np.linalg.norm(normals, axis=-1, keepdims=True) normals /= lengths # find quaternion to rotate (0, 0, 1) into the direction each # Line is pointing quats = rowan.vector_vector_rotation( np.array([0, 0, 1.])[np.newaxis, :], normals) # Nlines*Nvertices*3 vertices = np.tile(image[np.newaxis], (len(quats), 1, 1)) # set xy according to line width vertices[:, :, :2] *= self.widths[:, np.newaxis, np.newaxis] * 0.5 # set z according to line length vertices[:, :, 2] *= lengths[:, np.newaxis, 0] # rotate appropriately vertices = rowan.rotate(quats[:, np.newaxis], vertices) # translation by start_points will happen in _finalize_primitive_arrays # these are incorrect normals, but this looks to be the most # straightforward way to have the normals get serialized normals = vertices - self.start_points[:, np.newaxis] colors = np.tile(self.colors[:, np.newaxis], (1, len(image), 1)) positions = np.tile(self.start_points[:, np.newaxis], (1, len(image), 1)) self._finalize_primitive_arrays(positions, None, colors, vertices, normals, image_indices)
def write(self, trajectory, file=sys.stdout): """Serialize a trajectory into pos-format and write it to file. :param trajectory: The trajectory to serialize :type trajectory: :class:`~garnett.trajectory.Trajectory` :param file: A file-like object.""" def _write(msg, end='\n'): file.write(msg + end) for i, frame in enumerate(trajectory): # data section if frame.data is not None: header_keys = frame.data_keys _write('#[data] ', end='') _write(' '.join(header_keys)) columns = list() for key in header_keys: columns.append(frame.data[key]) rows = np.array(columns).transpose() for row in rows: _write(' '.join(row)) _write('#[done]') # boxMatrix and rotation box_matrix = np.array(frame.box.get_box_matrix()) if self._rotate and frame.view_rotation is not None: for i in range(3): box_matrix[:, i] = rowan.rotate(frame.view_rotation, box_matrix[:, i]) if frame.view_rotation is not None and not self._rotate: angles = rowan.to_euler(frame.view_rotation, axis_type='extrinsic', convention='xyz') * 180 / math.pi _write('rotation ' + ' '.join((str(_num(_)) for _ in angles))) _write('boxMatrix ', end='') _write(' '.join((str(_num(v)) for v in box_matrix.flatten()))) # shape defs try: if len(frame.types) != len(frame.type_shapes): raise ValueError( "Unequal number of types and type_shapes.") for name, type_shape in zip(frame.types, frame.type_shapes): _write('def {} "{}"'.format(name, type_shape.pos_string)) except AttributeError: # If AttributeError is raised because the frame does not contain # shape information, fill them all with the default shape for name in frame.types: logger.info("No shape defined for '{}'. " "Using fallback definition.".format(name)) _write('def {} "{}"'.format( name, DEFAULT_SHAPE_DEFINITION.pos_string)) # Orientations must be provided for all particles # If the frame does not have orientations, identity quaternions are used orientation = getattr(frame, 'orientation', np.array([[1, 0, 0, 0]] * frame.N)) for typeid, pos, rot in zip(frame.typeid, frame.position, orientation): name = frame.types[typeid] _write(name, end=' ') try: shapedef = frame.shapedef.get(name) except AttributeError: shapedef = DEFAULT_SHAPE_DEFINITION if self._rotate and frame.view_rotation is not None: pos = rowan.rotate(frame.view_rotation, pos) rot = rowan.multiply(frame.view_rotation, rot) if isinstance(shapedef, SphereShape): _write(' '.join((str(_num(v)) for v in pos))) elif isinstance(shapedef, ArrowShape): # The arrow shape actually has two position vectors of # three elements since it has start.{x,y,z} and end.{x,y,z}. # That is, "rot" is not an accurate variable name, since it # does not represent a quaternion. _write(' '.join( (str(_num(v)) for v in chain(pos, rot[:3])))) else: _write(' '.join((str(_num(v)) for v in chain(pos, rot)))) _write('eof') logger.debug("Wrote frame {}.".format(i + 1)) logger.info("Wrote {} frames.".format(i + 1))
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 check_rotation_invariance(quat): rotated_poly = ConvexPolyhedron(rowan.rotate(quat, poly.vertices)) assert sphere_isclose(poly_insphere, rotated_poly.insphere, atol=1e-4)
def quatrot(q, v): """Rotates the given vector array v by the quaternions in the array q""" return rowan.rotate(q, v)
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 calculateNematicAndPlot(orientations,points,clusterOrient,latent, name): # To show orientations, we use arrows rotated by the quaternions. arrowheads = rowan.rotate(orientations, [1, 0, 0]) (a,b,c)=(np.min(points[:,0]), np.min(points[:,1]), np.min(points[:,2])) (x,y,z)=(np.max(points[:,0]), np.max(points[:,1]), np.max(points[:,2])) boxSize=(x-a)*(y-b)*(z-c) ls=boxSize**(1/3.0) fig = plt.figure(figsize=(8,4)) ax = fig.add_subplot(121, projection='3d') ax.plot(centroids[:,0],centroids[:,1],centroids[:,2],'k.',ms=0.5) ax.plot(points[:,0],points[:,1],points[:,2],'b.') ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') ax.view_init(elev=105., azim=165) ax = fig.add_subplot(122, projection='3d') factor=1 ax.quiver3D(points[:, 0], points[:, 1], points[:, 2], factor*arrowheads[:, 0], factor*arrowheads[:, 1], factor*arrowheads[:, 2],length=ls/5.0, arrow_length_ratio=0.4, color='b', lw=1) nop = freud.order.Nematic([1, 0, 0]) nop.compute(orientations) #print("The value of the order parameter is {}.".format(nop.order)) center=np.mean(points,axis=0) ''' eig = np.linalg.eig(nop.nematic_tensor) rotation=R.from_dcm(eig[1]) arrowheads = rowan.rotate(rotation.as_quat(), [1, 0, 0]) #ax.quiver3D(center[0], center[1], center[2],arrowheads[0], arrowheads[1], arrowheads[2],length=20, color='k', lw=2) print(name, np.max(eig[0]),eig[1], rotation.as_dcm()) ''' ''' arrowheads = rowan.rotate(clusterOrient[[0,1,2,3]], [1, 0, 0]) factor=ls/50.0 latent=[12,6,3] ax.quiver3D(center[0], center[1], center[2],arrowheads[0], arrowheads[1], arrowheads[2],length=latent[0]*factor, color='b', lw=1) ax.quiver3D(center[0], center[1], center[2],-arrowheads[0], -arrowheads[1], -arrowheads[2],length=latent[0]*factor, color='b', lw=1) arrowheads = rowan.rotate(clusterOrient[[4,5,6,7]], [1, 0, 0]) ax.quiver3D(center[0], center[1], center[2],arrowheads[0], arrowheads[1], arrowheads[2],length=latent[1]*factor, color='m', lw=1) ax.quiver3D(center[0], center[1], center[2],-arrowheads[0], -arrowheads[1], -arrowheads[2],length=latent[1], color='m', lw=1) arrowheads = rowan.rotate(clusterOrient[[8,9,10,11]], [1, 0, 0]) ax.quiver3D(center[0], center[1], center[2],arrowheads[0], arrowheads[1], arrowheads[2],length=latent[2]*factor, color='k', lw=1) ax.quiver3D(center[0], center[1], center[2],-arrowheads[0], -arrowheads[1], -arrowheads[2],length=latent[2]*factor, color='k', lw=1) ''' t=' [' t1=[] axes = [[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 0], [1, 0, 1], [0, 1, 1], [1, 1, 1]] for ax1 in axes: nop = freud.order.Nematic(ax1) nop.compute(orientations) #print("For axis {}, the value of the order parameter is {:0.3f}.".format(ax1, nop.order)) t=t+' %0.2f,'%nop.order t1.append(nop.order) t=t[0:-1]+']' fig.suptitle("Cluster "+str(name)+''+'', fontsize=16); ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') ax.view_init(elev=105., azim=165) plt.savefig('Nematic/cluster'+str(name)+'.png') #plt.show() plt.close() return t1
def f(self, X, Z, A, t, logentry=None): # x = X[0] # y = X[1] # z = X[2] # xdot = X[3] # ydot = X[4] # zdot = X[5] # qw = X[6] # qx = X[7] # qy = X[8] # qz = X[9] # wx = X[10] # wy = X[11] # wz = X[12] a = Z**2 # a = A J = self.J dxdt = np.zeros(13) q = X[6:10] q = rowan.normalize(q) omega = X[10:] B0 = self.B0 * self.params['C_T'] # B0 = self.B0 eta = np.dot(B0, a) f_u = np.array([0, 0, eta[0]]) tau_u = np.array([eta[1], eta[2], eta[3]]) dxdt[0:3] = X[3:6] # dxdt[3:6] = -9.81 + rowan.rotate(q, f_u) / self.params['m'] dxdt[3:6] = np.array([0., 0., -9.81 ]) + rowan.rotate(q, f_u) / self.params['m'] Vinf = self.params['Vwind_mean'] - np.linalg.norm(X[3:6]) Vinf_B = rowan.rotate(rowan.inverse(q), Vinf) Vz_B = np.array([0.0, 0.0, Vinf_B[2]]) Vs_B = Vinf_B - Vz_B alpha = np.arcsin(np.linalg.norm(Vz_B) / np.linalg.norm(Vinf_B)) n = np.sqrt( np.multiply(a, self.B0[0, :]) / (self.params['C_T'] * self.params['rho'] * self.params['D']**4)) Fs_B = (Vs_B / np.linalg.norm(Vs_B) ) * self.params['C_s'] * self.params['rho'] * sum( n**self.params['k1']) * (np.linalg.norm(Vinf)**( 2 - self.params['k1'])) * (self.params['D']**( 2 + self.params['k1'])) * ( (np.pi / 2)**2 - alpha**2) * (alpha + self.params['k2']) # Fs_B = [0,0,0] dxdt[3:6] += rowan.rotate(q, Fs_B) / self.mass qnew = rowan.calculus.integrate(q, omega, self.ave_dt) if qnew[0] < 0: qnew = -qnew dxdt[6:10] = (qnew - q) / self.ave_dt dxdt[10:] = 1 / J * (np.cross(J * omega, omega) + tau_u) dxdt[10:] += 1 / J * np.cross( np.array([0.0, 0.0, self.params['D'] / 4]), Fs_B) euler_o = rowan.to_euler(q, 'xyz') if logentry is not None: logentry['f_u'] = f_u logentry['tau'] = tau_u logentry['euler_o'] = euler_o return dxdt.reshape((len(dxdt), 1))
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 translation(self): controls = self._backend_objects['controls'] translation = -np.array(controls.target) translation = rowan.rotate(self.rotation, translation) translation[2] += self.z_offset return translation
def check_position(central_position, central_orientation, constituent_position, local_position): d_pos = rowan.rotate(central_orientation, local_position) assert np.allclose(central_position + d_pos, constituent_position)
def test_zero_beta(self): """Check cases where beta is 0.""" # Since the Euler calculations are all done using matrices, it's easier # to construct the test cases by directly using matrices as well. We # assume gamma is 0 since, due to gimbal lock, only either alpha+gamma # or alpha-gamma is a relevant parameter, and we just scan the other # possible values. The actual function is defined such that gamma will # always be zero in those cases. We define the matrices using lambda # functions to support sweeping a range of values for alpha and beta, # specifically to test cases where signs flip e.g. cos(0) vs cos(pi). # These sign flips lead to changes in the rotation angles that must be # tested. mats_euler_intrinsic = [ ('xzx', 'intrinsic', lambda alpha, beta: [[np.cos(beta), 0, 0], [0, np.cos(beta) * np.cos(alpha), -np.sin(alpha)], [0, np.cos(beta) * np.sin(alpha), np.cos(alpha)]]), ('xyx', 'intrinsic', lambda alpha, beta: [[np.cos(beta), 0, 0], [0, np.cos(alpha), -np.cos(beta) * np.sin(alpha)], [0, np.sin(alpha), np.cos(beta) * np.cos(alpha)]]), ('yxy', 'intrinsic', lambda alpha, beta: [[np.cos(alpha), 0, np.cos(beta) * np.sin(alpha)], [0, np.cos(beta), 0], [-np.sin(alpha), 0, np.cos(beta) * np.cos(alpha)]]), ('yzy', 'intrinsic', lambda alpha, beta: [[np.cos(beta) * np.cos(alpha), 0, np.sin(alpha)], [0, np.cos(beta), 0], [-np.cos(beta) * np.sin(alpha), 0, np.cos(alpha)]]), ('zyz', 'intrinsic', lambda alpha, beta: [[ np.cos(beta) * np.cos(alpha), -np.sin(alpha), 0 ], [np.cos(beta) * np.sin(alpha), np.cos(beta), 0], [0, 0, np.cos(beta)]]), ('zxz', 'intrinsic', lambda alpha, beta: [[np.cos(alpha), -np.cos(beta) * np.sin(alpha), 0], [np.sin(alpha), np.cos(beta) * np.cos(beta), 0], [0, 0, np.cos(beta)]]), ] mats_tb_intrinsic = [ ('xzy', 'intrinsic', lambda alpha, beta: [[0, -np.sin( beta), 0], [np.sin(beta) * np.cos(alpha), 0, -np.sin( alpha)], [np.sin(beta) * np.sin(alpha), 0, np.cos(alpha)]]), ('xyz', 'intrinsic', lambda alpha, beta: [[0, 0, np.sin( beta)], [np.sin(beta) * np.sin(alpha), np.cos(alpha), 0 ], [-np.sin(beta) * np.cos(alpha), np.sin(alpha), 0]]), ('yxz', 'intrinsic', lambda alpha, beta: [[np.cos(alpha), np.sin(beta) * np.sin(alpha), 0], [0, 0, -np.sin(beta)], [-np.sin(alpha), np.sin(beta) * np.cos(alpha), 0]]), ('yzx', 'intrinsic', lambda alpha, beta: [[0, -np.sin(beta) * np.cos(alpha), np.sin(alpha)], [np.sin(beta), 0, 0], [0, np.sin(beta) * np.sin(alpha), np.cos(alpha)]]), ('zyx', 'intrinsic', lambda alpha, beta: [[ 0, -np.sin(alpha), np.sin(beta) * np.cos(alpha) ], [0, np.cos(alpha), np.sin(beta) * np.sin(alpha)], [-np.sin(beta), 0, 0]]), ('zxy', 'intrinsic', lambda alpha, beta: [[ np.cos(alpha), 0, np.sin(beta) * np.sin(alpha) ], [np.sin(alpha), 0, -np.sin(beta) * np.cos(alpha)], [0, -1, 0]]), ] # Extrinsic rotations can be tested identically to intrinsic rotations # in the case of proper Euler angles. mats_euler_extrinsic = [(m[0], 'extrinsic', m[2]) for m in mats_euler_intrinsic] # For Tait-Bryan angles, extrinsic rotations axis order must be # reversed (since axes 1 and 3 are not identical), but more # importantly, due to the sum/difference of alpha and gamma that # arises, we need to test the negative of alpha to catch the dangerous # cases. In practice we get the same results since we're sweeping alpha # values in the tests below, but it's useful to set this up precisely. mats_tb_extrinsic = [(m[0][::-1], 'extrinsic', lambda alpha, beta: m[2] (-alpha, beta)) for m in mats_tb_intrinsic] # Since angle representations may not be unique, checking that # quaternions are equal may not work. Instead we perform rotations and # check that they are identical. For simplicity, we rotate the # simplest vector with all 3 components (otherwise tests won't catch # the problem because there's no component to rotate). test_vector = [1, 1, 1] mats_intrinsic = (mats_euler_intrinsic, mats_tb_intrinsic) mats_extrinsic = (mats_euler_extrinsic, mats_tb_extrinsic) # The beta angles are different for proper Euler angles and Tait-Bryan # angles because the relevant beta terms will be sines and cosines, # respectively. all_betas = ((0, np.pi), (np.pi / 2, -np.pi / 2)) alphas = (0, np.pi / 2, np.pi, 3 * np.pi / 2) for mats in (mats_intrinsic, mats_extrinsic): for betas, mat_set in zip(all_betas, mats): for convention, axis_type, mat_func in mat_set: quaternions = [] for beta in betas: for alpha in alphas: mat = mat_func(alpha, beta) if np.linalg.det(mat) == -1: # Some of these will be improper rotations. continue quat = rowan.from_matrix(mat) quaternions.append(quat) euler = rowan.to_euler(quat, convention, axis_type) converted = rowan.from_euler(*euler, convention=convention, axis_type=axis_type) correct_rotation = rowan.rotate(quat, test_vector) test_rotation = rowan.rotate( converted, test_vector) self.assertTrue(np.allclose(correct_rotation, test_rotation, atol=1e-6), msg=""" Failed for convention {}, axis type {}, alpha = {}, beta = {}. Expected quaternion: {}. Calculated: {}. Expected vector: {}. Calculated vector: {}.""".format( convention, axis_type, alpha, beta, quat, converted, correct_rotation, test_rotation)) # For completeness, also test with broadcasting. quaternions = np.asarray(quaternions).reshape(-1, 4) all_euler = rowan.to_euler(quaternions, convention, axis_type) converted = rowan.from_euler(all_euler[..., 0], all_euler[..., 1], all_euler[..., 2], convention, axis_type) self.assertTrue( np.allclose(rowan.rotate(quaternions, test_vector), rowan.rotate(converted, test_vector), atol=1e-6))
def rotate_vectors(quaternion, vector): return rowan.rotate(quaternion, vector)
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 read(self): "Read the frame data from the stream." self.stream.seek(self.start) i = line = None def _assert(assertion): assert i is not None assert line is not None if not assertion: raise ParserError("Failed to read line #{}: {}.".format( i, line)) monotype = False raw_frame = _RawFrameData() raw_frame.view_rotation = None for i, line in enumerate(self.stream): if _is_comment(line): continue if line.startswith('#'): if line.startswith('#[data]'): _assert(raw_frame.data is None) raw_frame.data_keys, raw_frame.data, j = \ self._read_data_section(line, self.stream) i += j else: raise ParserError(line) else: tokens = line.rstrip().split() if not tokens: continue # empty line elif tokens[0] in TOKENS_SKIP: continue # skip these lines if tokens[0] == 'eof': logger.debug("Reached end of frame.") break elif tokens[0] == 'def': definition, data, end = line.strip().split('"') _assert(len(end) == 0) name = definition.split()[1] type_shape = self._parse_shape_definition(data) if name not in raw_frame.types: raw_frame.types.append(name) raw_frame.type_shapes.append(type_shape) else: typeid = raw_frame.type_shapes.index(name) raw_frame.type_shapes[typeid] = type_shape warnings.warn( "Redefinition of type '{}'.".format(name)) elif tokens[0] == 'shape': # monotype data = line.strip().split('"')[1] raw_frame.types.append(self.default_type) type_shape = self._parse_shape_definition(data) raw_frame.type_shapes.append(type_shape) _assert(len(raw_frame.type_shapes) == 1) monotype = True elif tokens[0] in ('boxMatrix', 'box'): if len(tokens) == 10: raw_frame.box = np.array( [self._num(v) for v in tokens[1:]]).reshape((3, 3)) elif len(tokens) == 4: raw_frame.box = np.array([[self._num(tokens[1]), 0, 0], [0, self._num(tokens[2]), 0], [0, 0, self._num(tokens[3])] ]).reshape((3, 3)) elif tokens[0] == 'rotation': euler_angles = np.array([float(t) for t in tokens[1:]]) euler_angles *= np.pi / 180 raw_frame.view_rotation = rowan.from_euler( *euler_angles, axis_type='extrinsic', convention='xyz') else: # assume we are reading positions now if not monotype: name = tokens[0] if name not in raw_frame.types: raw_frame.types.append(name) type_shape = self._parse_shape_definition(' '.join( tokens[:3])) raw_frame.type_shapes.append(type_shape) else: name = self.default_type typeid = raw_frame.types.index(name) type_shape = raw_frame.type_shapes[typeid] if len(tokens) == 7 and isinstance(type_shape, ArrowShape): xyz = tokens[-6:-3] quat = tokens[-3:] + [0] elif len(tokens) >= 7: xyz = tokens[-7:-4] quat = tokens[-4:] elif len(tokens) >= 3: xyz = tokens[-3:] quat = None else: raise ParserError(line) raw_frame.typeid.append(typeid) raw_frame.position.append([self._num(v) for v in xyz]) if quat is None: raw_frame.orientation.append(quat) else: raw_frame.orientation.append( [self._num(v) for v in quat]) # Perform inverse rotation to recover original coordinates if raw_frame.view_rotation is not None: pos = rowan.rotate(rowan.inverse(raw_frame.view_rotation), raw_frame.position) else: pos = np.asarray(raw_frame.position) # If all the z coordinates are close to zero, set box dimension to 2 if np.allclose(pos[:, 2], 0.0, atol=1e-7): raw_frame.box_dimensions = 2 # If no valid orientations have been added, the array should be empty if all([quat is None for quat in raw_frame.orientation]): raw_frame.orientation = [] else: # Replace values of None with an identity quaternion for i in range(len(raw_frame.orientation)): if raw_frame.orientation[i] is None: raw_frame.orientation[i] = [1, 0, 0, 0] return raw_frame