def replace(self, gl_object): if not gl_object.get_fixed(): state = gl_object.__getstate__() state.pop("name", None) state.pop("transformation", None) new = self.get_new(gl_object.transformation.t) if(self.current_object == "Fragment"): #fragments are inserted at frames - have no refs target_object = new.children[1] else: target_object = new for reference in gl_object.references[::-1]: if not reference.check_target(target_object): return parent = gl_object.parent import copy primitive.Add(new, parent) if(self.current_object == "Fragment"): # rotation Bond = context.application.plugins.get_node("Bond") if len(gl_object.references) == 1 and isinstance(gl_object.references[0].parent, Bond): bond1 = gl_object.references[0].parent direction1 = bond1.shortest_vector_relative_to(parent) if bond1.children[0].target != gl_object: direction1 *= -1 bond2 = new.children[0].references[0].parent direction2 = bond2.shortest_vector_relative_to(parent) if bond2.children[0].target != target_object: direction2 *= -1 axis = numpy.cross(direction2, direction1) if numpy.linalg.norm(axis) < 1e-8: axis = random_orthonormal(direction1) angle = compute_angle(direction1, direction2) rotation = Rotation() rotation.set_rotation_properties(angle,axis,False) primitive.Transform(new, rotation) else: bond1 = None # tranlsation translation = Translation() pos_old = new.children[1].get_frame_relative_to(parent).t pos_new = gl_object.transformation.t translation.t = pos_new - pos_old primitive.Transform(new, translation) if bond1 != None: # bond length old_length = numpy.linalg.norm(direction1) new_length = bonds.get_length(new.children[1].number, bond1.get_neighbor(gl_object).number) translation = Translation() translation.t = -direction1/old_length*(new_length-old_length) primitive.Transform(new, translation) for reference in gl_object.references[::-1]: reference.set_target(target_object) primitive.Delete(gl_object) if(self.current_object == "Fragment"): primitive.Delete(new.children[0]) # get rid of frame UnframeAbsolute = context.application.plugins.get_action("UnframeAbsolute") UnframeAbsolute([new])
def add_cell_vector(self, vector, norm_threshold=1e-6, volume_threshold=1e-6): active, inactive = self.get_active_inactive() if len(active) == 3: raise ValueError("The unit cell already has three axes.") norm_vector = numpy.linalg.norm(vector) if norm_vector < norm_threshold: raise ValueError( "The norm of the proposed vector must be significantly larger than zero." ) if len(active) == 0: # Add the vector self.cell[:, 0] = vector self.cell_active[0] = True # Make sure that the unused vectors are not linearly dependent direction = vector / norm_vector self.cell[:, 1] = random_orthonormal(direction) self.cell[:, 2] = numpy.cross(self.cell[:, 0], self.cell[:, 1]) # update self.update_reciproke() return a = self.cell[:, active[0]] norm_a = numpy.linalg.norm(a) if len(active) == 1: if 1 - abs(numpy.dot(a, vector) / (norm_vector * norm_a)) < volume_threshold: raise ValueError( "Can not add the vector since it is colinear with the existing unit cell axis." ) else: # Add the vector self.cell[:, inactive[0]] = vector self.cell_active[inactive[0]] = True # Make sure that the unused vector is not linearly dependent self.cell[:, 2] = numpy.cross(self.cell[:, 0], self.cell[:, 1]) # update self.update_reciproke() return b = self.cell[:, active[1]] norm_b = numpy.linalg.norm(b) if len(active) == 2: backup = self.cell[:, inactive[0]].copy() self.cell[:, inactive[0]] = vector if abs( numpy.linalg.det(self.cell) / (norm_vector * norm_a * norm_b)) < volume_threshold: self.cell[:, inactive[0]] = backup raise ValueError( "Can not add the vector since it is linearly dependent on the existing unit cell axes." ) else: self.cell_active[inactive[0]] = True self.update_reciproke() return True
def zmat_to_cart(zmat): """Converts a ZMatrix back to cartesian coordinates.""" numbers = zmat["number"] N = len(numbers) coordinates = numpy.zeros((N, 3), float) # special cases for the first coordinates coordinates[1, 2] = zmat["distance"][1] if zmat["rel1"][2] == 1: sign = -1 else: sign = 1 coordinates[2, 2] = zmat["distance"][2] * sign * numpy.cos(zmat["angle"][2]) coordinates[2, 1] = zmat["distance"][2] * sign * numpy.sin(zmat["angle"][2]) coordinates[2] += coordinates[2 - zmat["rel1"][2]] ref0 = 3 for (number, distance, rel1, angle, rel2, dihed, rel3) in zmat[3:]: ref1 = ref0 - rel1 ref2 = ref0 - rel2 ref3 = ref0 - rel3 if ref1 < 0: ref1 = 0 if ref2 < 0: ref2 = 0 if ref3 < 0: ref3 = 0 # define frame axes origin = coordinates[ref1] new_z = coordinates[ref2] - origin norm_z = numpy.linalg.norm(new_z) if norm_z < 1e-15: new_z = numpy.array([0, 0, 1], float) else: new_z /= numpy.linalg.norm(new_z) new_x = coordinates[ref3] - origin new_x -= numpy.dot(new_x, new_z) * new_z norm_x = numpy.linalg.norm(new_x) if norm_x < 1e-15: new_x = random_orthonormal(new_z) else: new_x /= numpy.linalg.norm(new_x) # we must make our axes frame left handed due to the poor IUPAC # definition of the sign of a dihedral angle. new_y = -numpy.cross(new_z, new_x) # coordinates of new atom: x = distance * numpy.cos(dihed) * numpy.sin(angle) y = distance * numpy.sin(dihed) * numpy.sin(angle) z = distance * numpy.cos(angle) coordinates[ref0] = origin + x * new_x + y * new_y + z * new_z # loop ref0 += 1 return numbers, coordinates
def zmat_to_cart(zmat): """Converts a ZMatrix back to cartesian coordinates.""" numbers = zmat["number"] N = len(numbers) coordinates = numpy.zeros((N, 3), float) # special cases for the first coordinates coordinates[1, 2] = zmat["distance"][1] if zmat["rel1"][2] == 1: sign = -1 else: sign = 1 coordinates[2, 2] = zmat["distance"][2]*sign*numpy.cos(zmat["angle"][2]) coordinates[2, 1] = zmat["distance"][2]*sign*numpy.sin(zmat["angle"][2]) coordinates[2] += coordinates[2-zmat["rel1"][2]] ref0 = 3 for (number, distance, rel1, angle, rel2, dihed, rel3) in zmat[3:]: ref1 = ref0 - rel1 ref2 = ref0 - rel2 ref3 = ref0 - rel3 if ref1 < 0: ref1 = 0 if ref2 < 0: ref2 = 0 if ref3 < 0: ref3 = 0 # define frame axes origin = coordinates[ref1] new_z = coordinates[ref2] - origin norm_z = numpy.linalg.norm(new_z) if norm_z < 1e-15: new_z = numpy.array([0, 0, 1], float) else: new_z /= numpy.linalg.norm(new_z) new_x = coordinates[ref3] - origin new_x -= numpy.dot(new_x, new_z)*new_z norm_x = numpy.linalg.norm(new_x) if norm_x < 1e-15: new_x = random_orthonormal(new_z) else: new_x /= numpy.linalg.norm(new_x) # we must make our axes frame left handed due to the poor IUPAC # definition of the sign of a dihedral angle. new_y = -numpy.cross(new_z, new_x) # coordinates of new atom: x = distance*numpy.cos(dihed)*numpy.sin(angle) y = distance*numpy.sin(dihed)*numpy.sin(angle) z = distance*numpy.cos(angle) coordinates[ref0] = origin + x*new_x + y*new_y + z*new_z # loop ref0 += 1 return numbers, coordinates
def get_transformation(self, molecule): atom1, atom2, atom3 = self.hinge_atoms center = molecule.coordinates[atom2] a = molecule.coordinates[atom1] - molecule.coordinates[atom2] b = molecule.coordinates[atom3] - molecule.coordinates[atom2] axis = numpy.cross(a, b) norm = numpy.linalg.norm(axis) if norm < 1e-5: # We suppose that atom3 is part of the affected atoms axis = random_orthonormal(a) else: axis /= numpy.linalg.norm(axis) angle = numpy.random.uniform(-self.max_amplitude, self.max_amplitude) return rotation_around_center(center, angle, axis)
def get_transformation(self, coordinates): """Construct a transformation object""" atom1, atom2, atom3 = self.hinge_atoms center = coordinates[atom2] a = coordinates[atom1] - coordinates[atom2] b = coordinates[atom3] - coordinates[atom2] axis = np.cross(a, b) norm = np.linalg.norm(axis) if norm < 1e-5: # We suppose that atom3 is part of the affected atoms axis = random_orthonormal(a) else: axis /= np.linalg.norm(axis) angle = np.random.uniform(-self.max_amplitude, self.max_amplitude) return Complete.about_axis(center, angle, axis)
def get_transformation(self, coordinates): """Construct a transformation object""" atom1, atom2, atom3 = self.hinge_atoms center = coordinates[atom2] a = coordinates[atom1] - coordinates[atom2] b = coordinates[atom3] - coordinates[atom2] axis = numpy.cross(a, b) norm = numpy.linalg.norm(axis) if norm < 1e-5: # We suppose that atom3 is part of the affected atoms axis = random_orthonormal(a) else: axis /= numpy.linalg.norm(axis) angle = numpy.random.uniform(-self.max_amplitude, self.max_amplitude) return Complete.about_axis(center, angle, axis)
def update_reciproke(self, auto_threshold=1e-6): active, inactive = self.get_active_inactive() if len(active) == 0: self.cell_reciproke = numpy.zeros((3, 3), float) return elif len(active) == 1: temp = copy.deepcopy(self.cell) if sum(temp[:,inactive[0]]**2) < auto_threshold: temp[:, inactive[0]] = random_orthonormal(temp[:, active[0]]) if sum(temp[:,inactive[1]]**2) < auto_threshold: temp[:, inactive[1]] = numpy.cross(temp[:, inactive[0]], temp[:, active[0]]) elif len(active) == 2: temp = copy.deepcopy(self.cell) if sum(temp[:,inactive[0]]**2) < auto_threshold: temp[:, inactive[0]] = numpy.cross(temp[:, active[0]], temp[:, active[1]]) elif len(active) == 3: temp = self.cell self.cell_reciproke = numpy.transpose(self.cell_active*numpy.transpose(numpy.linalg.inv(temp)))
def add_cell_vector(self, vector, norm_threshold=1e-6, volume_threshold=1e-6): active, inactive = self.get_active_inactive() if len(active) == 3: raise ValueError("The unit cell already has three axes.") norm_vector = numpy.linalg.norm(vector) if norm_vector < norm_threshold: raise ValueError("The norm of the proposed vector must be significantly larger than zero.") if len(active) == 0: # Add the vector self.cell[:,0] = vector self.cell_active[0] = True # Make sure that the unused vectors are not linearly dependent direction = vector/norm_vector self.cell[:,1] = random_orthonormal(direction) self.cell[:,2] = numpy.cross(self.cell[:,0], self.cell[:,1]) # update self.update_reciproke() return a = self.cell[:,active[0]] norm_a = numpy.linalg.norm(a) if len(active) == 1: if 1 - abs(numpy.dot(a, vector) / (norm_vector * norm_a)) < volume_threshold: raise ValueError("Can not add the vector since it is colinear with the existing unit cell axis.") else: # Add the vector self.cell[:,inactive[0]] = vector self.cell_active[inactive[0]] = True # Make sure that the unused vector is not linearly dependent self.cell[:,2] = numpy.cross(self.cell[:,0], self.cell[:,1]) # update self.update_reciproke() return b = self.cell[:,active[1]] norm_b = numpy.linalg.norm(b) if len(active) == 2: backup = self.cell[:,inactive[0]].copy() self.cell[:,inactive[0]] = vector if abs(numpy.linalg.det(self.cell) / (norm_vector * norm_a * norm_b)) < volume_threshold: self.cell[:,inactive[0]] = backup raise ValueError("Can not add the vector since it is linearly dependent on the existing unit cell axes.") else: self.cell_active[inactive[0]] = True self.update_reciproke() return True
def update_reciproke(self, auto_threshold=1e-6): active, inactive = self.get_active_inactive() if len(active) == 0: self.cell_reciproke = numpy.zeros((3, 3), float) return elif len(active) == 1: temp = copy.deepcopy(self.cell) if sum(temp[:, inactive[0]]**2) < auto_threshold: temp[:, inactive[0]] = random_orthonormal(temp[:, active[0]]) if sum(temp[:, inactive[1]]**2) < auto_threshold: temp[:, inactive[1]] = numpy.cross(temp[:, inactive[0]], temp[:, active[0]]) elif len(active) == 2: temp = copy.deepcopy(self.cell) if sum(temp[:, inactive[0]]**2) < auto_threshold: temp[:, inactive[0]] = numpy.cross(temp[:, active[0]], temp[:, active[1]]) elif len(active) == 3: temp = self.cell self.cell_reciproke = numpy.transpose( self.cell_active * numpy.transpose(numpy.linalg.inv(temp)))
def add_hydrogens(atom): existing_bonds = list(atom.yield_bonds()) num_bonds = len(existing_bonds) bond_length = bonds.get_length(atom.number, 1, BOND_SINGLE) if num_bonds == 0: H = Atom(name="auto H", number=1) H.transformation.t = atom.transformation.t + numpy.array([0,bond_length,0]) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) existing_bonds.append(bond) num_bonds = 1 used_valence = 0 oposite_direction = numpy.zeros(3, float) for bond in existing_bonds: shortest_vector = bond.shortest_vector_relative_to(atom.parent) if bond.children[1].target == atom: shortest_vector *= -1 oposite_direction -= shortest_vector if bond.bond_type == BOND_SINGLE: used_valence += 1 elif bond.bond_type == BOND_DOUBLE: used_valence += 2 elif bond.bond_type == BOND_TRIPLE: used_valence += 3 oposite_direction /= numpy.linalg.norm(oposite_direction) num_hydrogens = valence_el(atom.number) - 2*lone_pairs(atom.number) - used_valence if num_hydrogens <= 0: return hybride_count = num_hydrogens + lone_pairs(atom.number) + num_bonds - (used_valence - num_bonds) num_sites = num_hydrogens + lone_pairs(atom.number) rotation = Rotation() rotation.set_rotation_properties(2*math.pi / float(num_sites), oposite_direction, False) opening_key = (hybride_count, num_sites) opening_angle = self.opening_angles.get(opening_key) if opening_angle is None: return if num_bonds == 1: first_bond = existing_bonds[0] other_atom = first_bond.children[0].target if other_atom == atom: other_atom = first_bond.children[1].target other_bonds = [bond for bond in other_atom.yield_bonds() if bond != first_bond] if len(other_bonds) > 0: normal = other_bonds[0].shortest_vector_relative_to(atom.parent) normal -= numpy.dot(normal, oposite_direction) * oposite_direction normal /= numpy.linalg.norm(normal) if other_bonds[0].children[0].target == other_atom: normal *= -1 else: normal = random_orthonormal(oposite_direction) elif num_bonds == 2: normal = numpy.cross(oposite_direction, existing_bonds[0].shortest_vector_relative_to(atom.parent)) normal /= numpy.linalg.norm(normal) elif num_bonds == 3: normal = random_orthonormal(oposite_direction) else: return h_pos = bond_length*(oposite_direction*math.cos(opening_angle) + normal*math.sin(opening_angle)) for i in range(num_hydrogens): H = Atom(name="auto H", number=1) H.transformation.t = atom.transformation.t + h_pos primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) h_pos = rotation.vector_apply(h_pos)