def test_3d_array(self): """Multiplying higher dimensional arrays of quaternions""" num_reps = 20 expanded_shape = (int(num_reps / 5), 5, 4) zeros = np.reshape(np.repeat(zero[np.newaxis, :], num_reps, axis=0), expanded_shape) ones = np.reshape(np.repeat(one[np.newaxis, :], num_reps, axis=0), expanded_shape) expected_product_zeros = np.reshape( np.repeat(np.array([0, 0, 0, 0])[np.newaxis, :], num_reps, axis=0), expanded_shape) expected_product_ones = np.reshape( np.repeat(np.array([1, 0, 0, 0])[np.newaxis, :], num_reps, axis=0), expanded_shape) # Zeros product = rowan.multiply(zeros, zeros) self.assertTrue(np.all(product == expected_product_zeros)) # Ones product = rowan.multiply(ones, ones) self.assertTrue(np.all(product == expected_product_ones)) # Complex random array num_reps = input1.shape[0] expanded_shape = (int(num_reps / 5), 5, 4) product = rowan.multiply(np.reshape(input1, expanded_shape), np.reshape(input2, expanded_shape)) self.assertTrue( np.allclose(product, np.reshape(stored_product, expanded_shape)))
def test_single_quaternion(self): """Simplest case of quaternion multiplication""" # Multiply zeros product = rowan.multiply(zero, zero) self.assertTrue(np.all(product == np.array([0, 0, 0, 0]))) # Multiply ones product = rowan.multiply(one, one) self.assertTrue(np.all(product == np.array([1, 0, 0, 0])))
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 render(self, rotation=(1, 0, 0, 0), translation=(0, 0, 0), **kwargs): (positions, orientations, colors) = mesh.unfoldProperties( [self.positions, self.orientations, self.colors]) positions = rowan.rotate(rotation, positions) positions += translation orientations = rowan.multiply(rotation, rowan.normalize(orientations)) rotations = np.degrees(rowan.to_euler(orientations)) lines = [] for (pos, rot, col, alpha) in zip(positions, rotations, colors[:, :3], 1 - 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 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 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_divide(self): """Ensure division works""" shapes = [(4, ), (5, 4), (5, 5, 4), (5, 5, 5, 4)] np.random.seed(0) for shape_i in shapes: x = np.random.random_sample(shape_i) for shape_j in shapes: y = np.random.random_sample(shape_j) self.assertTrue( np.allclose(rowan.divide(x, y), rowan.multiply(x, rowan.inverse(y))))
def test_2d_array(self): """Multiplying arrays of quaternions""" zeros = np.repeat(zero[np.newaxis, :], 10, axis=0) ones = np.repeat(one[np.newaxis, :], 10, axis=0) # Multiply zeros product = rowan.multiply(zeros, zeros) self.assertTrue( np.all(product == np.repeat( np.array([0, 0, 0, 0])[np.newaxis, :], 10, axis=0))) # Multiply ones product = rowan.multiply(ones, ones) self.assertTrue( np.all(product == np.repeat( np.array([1, 0, 0, 0])[np.newaxis, :], 10, axis=0))) # Complex random array product = rowan.multiply(input1, input2) self.assertTrue(np.allclose(product, stored_product))
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_broadcast(self): """Ensure broadcasting works""" # Multiply zeros, simple shape check shape = (45, 3, 13, 4) many_zeros = np.zeros(shape) product = rowan.multiply(many_zeros, zero) self.assertTrue(product.shape == shape) # Two nonconforming array sizes with self.assertRaises(ValueError): rowan.multiply(many_zeros, np.repeat(zero[np.newaxis, :], 2, axis=0)) # Require broadcasting in multiple dimensions zeros_A = np.zeros((1, 1, 3, 8, 1, 4)) zeros_B = np.zeros((3, 5, 1, 1, 9, 4)) shape = (3, 5, 3, 8, 9, 4) product = rowan.multiply(zeros_A, zeros_B) self.assertTrue(product.shape == shape) # Test some actual products num_first = 2 num_second = 5 i1 = input1[:num_first, np.newaxis, :] i2 = input1[np.newaxis, :num_second, :] product = rowan.multiply(i1, i2) for i in range(num_first): for j in range(num_second): single_prod = rowan.multiply(i1[i, 0, :], i2[0, j, :]) self.assertTrue(np.all(product[i, j, :] == single_prod))
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_power(self): """Ensure that quaternion power behaves correctly.""" self.assertTrue(np.all(rowan.power(one, 0) == one)) self.assertTrue(np.all(rowan.power(one, 1) == one)) self.assertTrue(np.all(rowan.power(one, 10) == one)) self.assertTrue(np.all(rowan.power(zero, 0) == one)) self.assertTrue(np.all(rowan.power(zero, 1) == zero)) self.assertTrue(np.all(rowan.power(zero, 10) == zero)) np.random.seed(0) shapes = [(4,), (1, 4), (3, 4, 4), (12, 7, 3, 4)] max_power = 8 for shape in shapes: x = np.random.random_sample(shape) cur_ans = x for i in range(1, max_power + 1): self.assertTrue( np.allclose(rowan.power(x, i), cur_ans), msg="Failed for shape {}".format(shape), ) cur_ans = rowan.multiply(cur_ans, x)
def calcFibre(symHKL, yset, qgrid, phi, rad, tree, euc_rad, quatSymOps): cphi = np.cos(phi / 2) sphi = np.sin(phi / 2) q0 = {} q = {} qf = {} axis = {} omega = {} 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] = {} q0[fi] = {} q[fi] = {} axis[fi] = {} omega[fi] = {} """ set proper iterator """ if isinstance(yset, dict): it = yset[fi] else: it = yset for yi, y in enumerate(it): axis[fi][yi] = np.cross(fam, y) axis[fi][yi] = axis[fi][yi] / np.linalg.norm(axis[fi][yi], axis=-1) omega[fi][yi] = np.arccos(np.dot(fam, y)) q0[fi][yi] = np.hstack([ np.cos(omega[fi][yi] / 2), np.sin(omega[fi][yi] / 2) * axis[fi][yi] ]) q[fi][yi] = np.hstack([ cphi[:, np.newaxis], np.tile(y, (len(cphi), 1)) * sphi[:, np.newaxis] ]) qf[yi] = quat.multiply(q[fi][yi], q0[fi][yi]) # qfib = quat.multiply(quatSymOps, qf[yi]) qfib = quat.multiply(qf[yi], quatSymOps) qfib = qfib.transpose((1, 0, 2)) phi1, Phi, phi2 = quat2eu(qfib) """ old way """ # axis[fi][yi] = np.cross(fam,y) # axis[fi][yi] = axis[fi][yi] / np.linalg.norm(axis[fi][yi],axis=1)[:,None] # omega[fi][yi] = np.arccos(np.dot(fam,y)) # q0[fi][yi] = {} # q[fi][yi] = {} # qf[yi] = {} # qfib = np.zeros((len(phi),len(fam),4)) # for hi,HxY in enumerate(axis[fi][yi]): # q0[fi][yi][hi] = np.hstack( [ np.cos(omega[fi][yi][hi]/2), np.sin(omega[fi][yi][hi]/2) * HxY ] ) # q[fi][yi][hi] = np.hstack( [ cphi[:, np.newaxis], np.tile( y, (len(cphi),1) ) * sphi[:, np.newaxis] ] ) # qf[yi][hi] = quat.multiply(q[fi][yi][hi], q0[fi][yi][hi]) # for qi in range(qf[yi][hi].shape[0]): # qfib[qi,hi,:] = qf[yi][hi][qi,:] # 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) #pull only unique points? - not sure why there are repeated points, something with symmetry for certain hkls #should only be ~73 points per path, but three fold symmetry is also present fibre_e[fi][yi], uni_path_idx = np.unique(eu_fib[fz], return_index=True, axis=0) fib_idx = np.unravel_index(fz_idx[0], (qfib.shape[0], qfib.shape[1])) fibre_q[fi][yi] = qfib[fib_idx][uni_path_idx] """ euclidean distance calculation - KDTree """ qfib_pos = np.copy(qfib[fib_idx][uni_path_idx]) qfib_pos[qfib_pos[:, 0] < 0] *= -1 # returns tuple - first array are points, second array is distances query = tree.query_radius(qfib_pos, euc_rad, return_distance=True) # concatenate arrays query = np.column_stack([np.concatenate(ar) for ar in query]) # round very small values query = np.round(query, decimals=7) # move values at zero to very small (1E-5) query[:, 1] = np.where(query[:, 1] == 0, 1E-5, query[:, 1]) # sort by minimum distance - unique function takes first appearance of index query_sort = query[np.argsort(query[:, 1], axis=0)] # return unique points uni_pts = np.unique(query_sort[:, 0], return_index=True) nn_gridPts[fi][yi] = uni_pts[0].astype(int) nn_gridDist[fi][yi] = query_sort[uni_pts[1], 1] """ geodesic distance calculation - dot product """ # """ 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_trun,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] = qgrid_trun_idx[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, axis, omega
def render(self, rotation=(1, 0, 0, 0), name_suffix='', illo_id='illo', ambient_light=0.4, directional_light=[], stroke=False, outline=False, **kwargs): # in the zdog coordinate system, x is to the right, y is down, # and z is toward you lines = [] stroke = stroke or 'false' (vertices, faces) = geometry.convexHull(self.vertices) face_normals = [] face_paths = [] for face in faces: r01 = vertices[face[1]] - vertices[face[0]] r12 = vertices[face[2]] - vertices[face[1]] normal = np.cross(r01, r12) normal /= np.linalg.norm(normal) face_normals.append(normal) path = ', '.join('{{x: {}, y: {}, z: {}}}'.format(*v) for v in vertices[face] * (1, -1, 1)) face_paths.append(path) face_normals = np.array(face_normals, dtype=np.float32) orientations_euler = rowan.to_euler(self.orientations, convention='xyz', axis_type='intrinsic') particles = zip(*mesh.unfoldProperties([ self.positions * (1, -1, 1), self.orientations, -orientations_euler, self.colors * 255 ])) for i, (position, orientation, eulers, color) in enumerate(particles): group_index = 'convexPoly_{}_{}'.format(name_suffix, i) # full rotation to apply to vectors from base orientation # to final scene orientation full_rotation = rowan.multiply(rotation, orientation) lines.append(""" let {group_index} = new Zdog.Group({{ addTo: {illo_id}, rotate: {{x: {angle[0]}, y: {angle[1]}, z: {angle[2]}}}, translate: {{x: {pos[0]}, y: {pos[1]}, z: {pos[2]}}}, updateSort: true, }});""".format(group_index=group_index, illo_id=illo_id, angle=eulers, pos=position)) for (face_path, normal) in zip(face_paths, face_normals): rotated_normal = rowan.rotate(full_rotation, normal) light = ambient_light for direction in directional_light: light += max(0, -np.dot(rotated_normal, direction)) light = np.clip(light, 0, 1) (r, g, b) = map(int, light * color[:3]) # RGB components are 0-255, A component is a float 0-1 face_color = '"rgba({}, {}, {}, {})"'.format( r, g, b, color[3] / 255) lines.append(""" new Zdog.Shape({{ addTo: {group_index}, color: {face_color}, path: [{path}], fill: true, backface: true, stroke: {stroke}, }}); """.format(group_index=group_index, face_color=face_color, path=face_path, stroke=stroke)) if outline: outline_color = '"rgba(0, 0, 0, {})"'.format(color[3] / 255) lines.append(""" new Zdog.Shape({{ addTo: {group_index}, color: {color}, path: [{path}], fill: false, stroke: {stroke}, }}); """.format(group_index=group_index, color=outline_color, path=face_path, stroke=outline)) return lines
def check_orientation(central_orientation, constituent_orientation, local_orientation): expected_orientation = rowan.normalize( rowan.multiply(central_orientation, local_orientation)) assert np.allclose(expected_orientation, local_orientation)
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 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
def calcFibre(symHKL, yset, qgrid, phi, rad, tree, euc_rad): cphi = np.cos(phi / 2) sphi = np.sin(phi / 2) q0 = {} q = {} qf = {} axis = {} omega = {} 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] = {} q0[fi] = {} q[fi] = {} axis[fi] = {} omega[fi] = {} """ set proper iterator """ if isinstance(yset, dict): it = yset[fi] else: it = yset for yi, y in enumerate(it): axis[fi][yi] = np.cross(fam, y) axis[fi][yi] = axis[fi][yi] / np.linalg.norm(axis[fi][yi], axis=1)[:, None] omega[fi][yi] = np.arccos(np.dot(fam, y)) q0[fi][yi] = {} q[fi][yi] = {} qf[yi] = {} qfib = np.zeros((len(phi), len(fam), 4)) for hi, HxY in enumerate(axis[fi][yi]): q0[fi][yi][hi] = quat.normalize( np.hstack([ np.cos(omega[fi][yi][hi] / 2), np.sin(omega[fi][yi][hi] / 2) * HxY ])) q[fi][yi][hi] = quat.normalize( np.hstack([ cphi[:, np.newaxis], np.tile(y, (len(cphi), 1)) * sphi[:, np.newaxis] ])) qf[yi][hi] = quat.multiply(q[fi][yi][hi], q0[fi][yi][hi]) for qi in range(qf[yi][hi].shape[0]): qfib[qi, hi, :] = qf[yi][hi][qi, :] 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 fib_idx = np.unravel_index(fz_idx[0], (qfib.shape[0], qfib.shape[1])) fibre_q[fi][yi] = qfib """ 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_trun, 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] = qgrid_trun_idx[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, axis, omega
## h onto y q0[fi][yi] = np.hstack( [ np.cos(omega/2), np.sin(omega/2) * axis ] ) ## around y q[fi][yi] = np.hstack( [ cphi[:, np.newaxis], np.tile( y, (len(cphi),1) ) * sphi[:, np.newaxis] ] ) fibre_e[fi][yi] = {} fibre_q[fi][yi] = {} tube_e[fi][yi] = {} qf[fi][yi] = {} ## loop through for oi,offset in enumerate(offset_rots): #q0 then offset q0_t = quat.multiply(offset[None,:], q0[fi][yi]) ## full fiber qf[fi][yi][oi] = quat.multiply(q[fi][yi], q0_t) qfib = quat.multiply(qf[fi][yi][oi], quatSymOps) qfib = qfib.transpose((1,0,2)) 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 ) #unique values based on rectilinear bunge box eu_fib = np.reshape( eu_fib, (eu_fib.shape[0]*eu_fib.shape[1], eu_fib.shape[2]) ) #new method
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
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 calcFibre(h, pf_y, omega): fibre_e = {} fibre_q = {} nn_gridPts = {} nn_gridDist = {} h_y = np.cross(h, pf_y) h_yAng = np.arccos(np.dot(pf_y, h)) qh_y = np.column_stack((np.cos(h_yAng/2),np.sin(h_yAng/2)[:,None]*h_y)) qh_y = quat.normalize(qh_y) qy_om = np.column_stack((np.cos(omega/2), np.sin(omega/2))) # qh_y = quat.vector_vector_rotation(h,pf_y) # qh_om = quat.from_axis_angle(h,omega) for yi,y in enumerate(pf_y): qy_om = np.column_stack((np.cos(omega/2), np.sin(omega/2)[:,None]*np.tile(y,(len(omega),1)))) qy_om = quat.normalize(qy_om) qfib = quat.multiply(qy_om,qh_y[yi,:]) """ reshape for tiling, simplify?? """ qfib = qfib.T.reshape((1,4,len(omega)),order='F') qfib = np.tile(qfib,(quatSymOps.shape[1],1,1)) qfib = qfib.transpose((2,0,1)) """ apply symmetry """ qfib = quat.multiply(quatSymOps,qfib) """ symmetry ops, then filter by given maximum bunge angles """ 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]) ) 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].reshape((len(fz_idx[0]),4)) """ distance calc """ temp = quatMetricNumba(qgrid,qfib[fib_idx]) """ find tube """ tube = (temp <= rad) temp = np.column_stack((np.argwhere(tube)[:,0],temp[tube])) """ 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
axis[fi] = {} omega[fi] = {} """ set proper iterator """ if isinstance(xyz_pf,dict): it = xyz_pf[fi] else: it = xyz_pf for yi,y in enumerate(it): axis[fi][yi] = np.cross(fam,y) axis[fi][yi] = axis[fi][yi] / np.linalg.norm(axis[fi][yi],axis=-1) omega[fi][yi] = np.arccos(np.dot(fam,y)) q0[fi][yi] = np.hstack( [ np.cos(omega[fi][yi]/2), np.sin(omega[fi][yi]/2) * axis[fi][yi] ] ) q[fi][yi] = np.hstack( [ cphi[:, np.newaxis], np.tile( y, (len(cphi),1) ) * sphi[:, np.newaxis] ] ) qf[fi][yi] = quat.multiply(q[fi][yi], q0[fi][yi]) q_tubeTest[fi][yi] = quat.multiply(qf[fi][yi],quat.from_axis_angle((1,1,0), theta)) # qfib = quat.multiply(quatSymOps, qf[yi]) qfib = quat.multiply(qf[fi][yi], quatSymOps) qfib = qfib.transpose((1,0,2)) phi1, Phi, phi2 = quat2eu(qfib) qtube = quat.multiply(q_tubeTest[fi][yi], quatSymOps) qtube = qtube.transpose((1,0,2)) phi1_t, Phi_t, phi2_t = quat2eu(qtube) # q0[fi][yi] = {} # q[fi][yi] = {}
def _regularize_box(position, velocity, orientation, angmom, box_matrix, dtype=None, dimensions=3): """Convert box into a right-handed coordinate frame with only upper triangular entries. Also convert corresponding positions and orientations.""" # First use QR decomposition to compute the new basis Q, R = np.linalg.qr(box_matrix) Q = Q.astype(dtype) R = R.astype(dtype) if not np.allclose(Q[:dimensions, :dimensions], np.eye(dimensions)): # If Q is not the identity matrix, then we will be # changing data, so we have to copy. This only causes # actual failures for non-writeable GSD frames, but could # cause unexpected data corruption for other cases position = np.copy(position) if orientation is not None: orientation = np.copy(orientation) if velocity is not None: velocity = np.copy(velocity) if angmom is not None: angmom = np.copy(angmom) # Since we'll be performing a quaternion operation, # we have to ensure that Q is a pure rotation sign = np.linalg.det(Q) Q = Q * sign R = R * sign # First rotate positions, velocities. # Since they are vectors, we can use the matrix directly. # Conveniently, instead of transposing Q we can just reverse # the order of multiplication here position = position.dot(Q) if velocity is not None: velocity = velocity.dot(Q) # For orientations and angular momenta, we use the quaternion quat = rowan.from_matrix(Q.T) if orientation is not None: for i in range(orientation.shape[0]): orientation[i, :] = rowan.multiply(quat, orientation[i, :]) if angmom is not None: for i in range(angmom.shape[0]): angmom[i, :] = rowan.multiply(quat, angmom[i, :]) # Now we have to ensure that the box is right-handed. We # do this as a second step to avoid introducing reflections # into the rotation matrix before making the quaternion signs = np.diag( np.diag(np.where(R < 0, -np.ones(R.shape), np.ones(R.shape)))) box = R.dot(signs) position = position.dot(signs) if velocity is not None: velocity = velocity.dot(signs) else: box = box_matrix # Construct the box Lx, Ly, Lz = np.diag(box).flatten().tolist() xy = box[0, 1] / Ly xz = box[0, 2] / Lz yz = box[1, 2] / Lz box = Box(Lx=Lx, Ly=Ly, Lz=Lz, xy=xy, xz=xz, yz=yz, dimensions=dimensions) return position, velocity, orientation, angmom, box
def quatquat(qi, qj): """Multiplies the quaternions in the array qi by those in qj""" return rowan.multiply(qi, qj)
print("Diagonal Inertia Inverse:\n ", DiagonalInertiaInverse) #Frame changes print("To Body COM Frame", ToBodyFrame(ToCOM(Position[0]))) print("Original Position[0]", ToOrigin(ToLabFrame(ToBodyFrame(ToCOM( Position[0]))))) #note correct order of frame changes #Solve for OmegaDot and Omega Omega = [0, 0, 1] #Torque = [0,0,1] #gets confused with "Torque" function later #Rotation with quaternions print("Omega as Quat:\n", l_m.VectorToQuat(Omega)) print("New Principal Basis:\n", RungeKuttaOrient(Omega, PrincipalBasis)) print("Multiply:\n", l_m.QuatMultiply(l_m.VectorToQuat(Omega), np.array([1, 2, 3, 4]))) print("Alternate Multiply:\n", quat.multiply(l_m.VectorToQuat(Omega), np.array([1, 2, 3, 4]))) #actual sim stuff Step = 0 #QUESTION all this initializatios stuff should be in a function #Initial Conditions; data to be stored through simulation InitialOmega = np.array( [0, 0, 0] ) #This seems to be a good inital condition so that things don't weirdly drift off in y direction (about 10^-9m scale) #[0,0,0.01] was used because in an earlier version of the simulation, they would just annihilate AllOmegas = np.zeros((TotalSteps + 1, 3)) AllOmegas[0] = InitialOmega #print("All Omegas:\n", AllOmegas) AllHCPositions = [] AllHCPositions.append(1 * HydrodymanicCenter)
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) om = ax2om(ax) eu.append(om2eu(om)) ro_h[fi] = np.vstack(eu) ro_y[yi] = ro_h yi += 1 # %%
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 get_basis_vectors(self, base_positions, base_type=[], base_quaternions=None, is_complete=False, apply_orientation=False): """Get the basis vectors for the defined crystall structure. :param base_positions: N by 3 np array of the Wyckoff postions :type base_positions: np.ndarray :param base_type: a list of string for particle type name :type base_type: list :param base_quaternions: N by 4 np array of quaternions, default None :type base_quaternions: np.ndarray :param is_complete: bool value to indicate if the positions are complete postions in a unitcell :type is_complete: bool :param apply_orientations: bool value to indicate if the space group symmetry should be applied to orientatioin :type apply_orientations: bool :return: basis_vectors :rtype: np.ndarray """ # check input accuracy if not isinstance(base_positions, np.ndarray) or len(base_positions.shape) == 1 \ or base_positions.shape[1] != 3: raise ValueError( 'base_positions must be an numpy array of shape Nx3') if apply_orientation: if not isinstance(base_quaternions, np.ndarray) or len(base_quaternions.shape) == 1 \ or base_quaternions.shape[1] != 4: raise ValueError( 'base_quaternions must be an numpy array of shape Nx4') if len(base_type): if not isinstance(base_type, list) or len(base_type) != base_positions.shape[0] \ or not all(isinstance(i, str) for i in base_type): raise ValueError( 'base_type must contain a list of type name the same length' 'as the number of basis positions') else: base_type = ['A'] * base_positions.shape[0] threshold = 1e-6 reflection_exist = False for i in range(0, len(self.rotations)): # Generate the new set of positions from the base pos = wrap(self.rotations[i].dot(base_positions.T).T + self.translations[i]) if apply_orientation: if np.linalg.det(self.rotations[i]) == 1: quat_rotate = rowan.from_matrix(self.rotations[i], require_orthogonal=False) quat = rowan.multiply(quat_rotate, base_quaternions) else: quat = base_quaternions reflection_exist = True if i == 0: positions = pos type_list = copy.deepcopy(base_type) if apply_orientation: quaternions = quat else: pos_comparisons = pos - positions[:, np.newaxis, :] norms = np.linalg.norm(pos_comparisons, axis=2) comps = np.all(norms > threshold, axis=0) positions = np.append(positions, pos[comps], axis=0) type_list += [x for x, y in zip(base_type, comps) if y] if apply_orientation: quaternions = np.append(quaternions, quat[comps], axis=0) if norms.min() < threshold: print( 'Orientation quaterions may have multiple values for the same ' 'particle postion under the symmetry operation for this space group ' 'and is not well defined, only the first occurance is used.' ) if reflection_exist: print( 'Warning: reflection operation is included in this space group, ' 'and is ignored for quaternion calculation.') if is_complete and len(positions) != len(base_positions): raise ValueError( 'the complete basis postions vector does not match the space group ' 'chosen. More positions are generated based on the symmetry operation ' 'within the provided space group') if apply_orientation: return wrap(positions), type_list, quaternions else: return wrap(positions), type_list
# brass_y2[:,:,yi] = by HxY[yi] = np.cross(symHKL[0],by) HxY[yi] = HxY[yi] / np.linalg.norm(HxY[yi],axis=1)[:,None] ome[yi] = np.arccos(np.dot(symHKL[0],by)) q0[yi] = {} q[yi] = {} qf[yi] = {} fibre_marc[yi] = np.zeros_like(brassFibre[yi]) for hi,h in enumerate(HxY[yi]): q0[yi][hi] = quat.normalize(np.hstack( [ np.cos(ome[yi][hi]/2), np.sin(ome[yi][hi]/2) * h ] )) q[yi][hi] = quat.normalize(np.hstack( [ cphi[:, np.newaxis], np.tile( by, (len(cphi),1) ) * sphi[:, np.newaxis] ] )) qf[yi][hi] = quat.multiply(q[yi][hi], q0[yi][hi]) for qi in range(qf[yi][hi].shape[0]): fibre_marc[yi][qi,hi,:] = qf[yi][hi][qi,:] phi1, Phi, phi2 = quat2eu(fibre_marc[yi]) 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 ) fibre_marc_e[yi] = np.reshape( eu_fib, (eu_fib.shape[0]*eu_fib.shape[1], eu_fib.shape[2]) ) #new method eu_fib = np.reshape( eu_fib, (eu_fib.shape[0]*eu_fib.shape[1], eu_fib.shape[2]) ) #new method