def do(self): cache = context.application.cache graph = create_molecular_graph(cache.nodes) parent = cache.node Frame = context.application.plugins.get_node("Frame") for group in graph.independent_nodes: atoms = [graph.molecule.atoms[i] for i in group] new_positions = self.calc_new_positions(group, atoms, graph, parent) frame = Frame(name=chemical_formula(atoms)[1]) primitive.Add(frame, parent, index=0) for node, atom in zip(group, atoms): primitive.Move(atom, frame) new_position = new_positions[node] translation = Translation() translation.t = atom.get_parentframe_up_to(parent).vector_apply_inverse(new_position) primitive.SetProperty(atom, "transformation", translation) for atom in atoms: # take a copy of the references since they are going to be # refreshed (changed and reverted) during the loop. for reference in copy.copy(atom.references): referent = reference.parent if referent.parent != frame: has_to_move = True for child in referent.children: if child.target.parent != frame: has_to_move = False break if has_to_move: primitive.Move(referent, frame)
def coords_to_zeobuilder(org_coords, opt_coords, atoms, parent, graph=None): if graph == None: atomgroups = [numpy.arange(len(atoms))] else: atomgroups = graph.independent_nodes for group in atomgroups: group_org = org_coords[group] group_opt = opt_coords[group] # Transform the guessed geometry as to overlap with the original geometry transf = superpose(group_org, group_opt) group_opt = numpy.dot(group_opt, transf.r.transpose()) + transf.t # Put coordinates of guessed geometry back into Zeobuilder model for i,atomindex in enumerate(group): translation = Translation() atom = graph.molecule.atoms[atomindex] # Make sure atoms in subframes are treated properly transf = atom.parent.get_frame_relative_to(parent) org_pos = atom.transformation.t opt_pos = transf.vector_apply_inverse(group_opt[i]) translation = Translation() translation.t = opt_pos - org_pos primitive.Transform(atom, translation)
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 get_transformation(self, molecule): atom1, atom2 = self.hinge_atoms direction = molecule.coordinates[atom1] - molecule.coordinates[atom2] direction /= numpy.linalg.norm(direction) direction *= numpy.random.uniform(-self.max_amplitude, self.max_amplitude) result = Translation() result.t[:] = direction return result
def reset(self): config = context.application.configuration self.rotation_center = Translation() self.rotation = Rotation() self.eye = Translation() self.eye.t[2] = config.viewer_distance self.opening_angle = config.opening_angle self.window_size = config.window_size self.window_depth = config.window_depth
def get_transformation(self, molecule): atom1, atom2, atom3, atom4 = self.hinge_atoms a = molecule.coordinates[atom1] - molecule.coordinates[atom2] a /= numpy.linalg.norm(a) b = molecule.coordinates[atom3] - molecule.coordinates[atom4] b /= numpy.linalg.norm(b) direction = 0.5*(a+b) direction *= numpy.random.uniform(-self.max_amplitude, self.max_amplitude) result = Translation() result.t[:] = direction return result
def get_transformation(self, molecule): atom1, atom2, atom3, atom4 = self.hinge_atoms a = molecule.coordinates[atom1] - molecule.coordinates[atom2] a /= numpy.linalg.norm(a) b = molecule.coordinates[atom3] - molecule.coordinates[atom4] b /= numpy.linalg.norm(b) direction = 0.5 * (a + b) direction *= numpy.random.uniform(-self.max_amplitude, self.max_amplitude) result = Translation() result.t[:] = direction return result
def coords_to_zeobuilder(org_coords, opt_coords, atoms, parent): # Transform the guessed geometry as to overlap with the original geometry transf = superpose(org_coords, opt_coords) opt_coords = numpy.dot(opt_coords, transf.r.transpose()) + transf.t # Put coordinates of guess geometry back into Zeobuilder model for i in xrange(len(atoms)): translation = Translation() atom = atoms[i] # Make sure atoms in subframes are treated properly transf = atom.parent.get_frame_relative_to(parent) org_pos = atom.transformation.t opt_pos = transf.vector_apply_inverse(opt_coords[i]) translation.t = opt_pos - org_pos primitive.Transform(atom, translation)
def do(self): cache = context.application.cache for node in cache.nodes: translation = Translation() child_translations = [] translated_children = [] for child in node.children: if isinstance(child, GLTransformationMixin) and isinstance(child.transformation, Translation): if child.get_fixed(): translated_children = [] break translated_children.append(child) child_translations.append(child.transformation) if len(translated_children) > 0: translation.t = calculate_center(child_translations) CenterAlignBase.do(self, node, translated_children, translation)
def get_transformation(self, coordinates): """Construct a transformation object""" atom1, atom2 = self.hinge_atoms direction = coordinates[atom1] - coordinates[atom2] direction /= np.linalg.norm(direction) direction *= np.random.uniform(-self.max_amplitude, self.max_amplitude) result = Translation(direction) return result
def get_transformation(self, coordinates): """Construct a transformation object""" atom1, atom2, atom3, atom4 = self.hinge_atoms a = coordinates[atom1] - coordinates[atom2] a /= np.linalg.norm(a) b = coordinates[atom3] - coordinates[atom4] b /= np.linalg.norm(b) direction = 0.5 * (a + b) direction *= np.random.uniform(-self.max_amplitude, self.max_amplitude) result = Translation(direction) return result
def do(self): cache = context.application.cache for node in cache.nodes: translation = Translation() translated_children = [] for child in node.children: if isinstance(child, GLTransformationMixin) and isinstance(child.transformation, Translation): if child.get_fixed(): translated_children = [] break translated_children.append(child) if len(translated_children) == 0: continue mass, com = compute_center_of_mass(yield_particles(node)) if mass == 0.0: continue translation.t = com CenterAlignBase.do(self, node, translated_children, translation)
def do(self): cache = context.application.cache # Translate where possible, if necessary translated_nodes = cache.translated_nodes if len(translated_nodes) > 0: if isinstance(cache.last, GLTransformationMixin) and \ isinstance(cache.last.transformation, Translation): absolute_inversion_center = cache.last.get_absolute_frame().t else: absolute_inversion_center = numpy.zeros(3, float) victims_by_parent = list_by_parent(cache.transformed_nodes) for parent, victims in victims_by_parent.iteritems(): local_inversion_center = parent.get_absolute_frame().vector_apply_inverse(absolute_inversion_center) for victim in victims: translation = Translation() translation.t = 2 * (local_inversion_center - victim.transformation.t) primitive.Transform(victim, translation) # Apply an inversion rotation where possible r = Rotation() r.inversion_rotation() for victim in cache.rotated_nodes: primitive.Transform(victim, r, after=False)
def do(self): cache = context.application.cache translation = Translation() translation.t = copy.deepcopy(cache.node.transformation.t) CenterAlignBase.do(self, cache.parent, cache.translated_neighbors, translation)
def randomize(self, center): self.transform(Translation(-np.array(center))) self.transform(Rotation.random()) self.transform(Translation(center))
def rotate(self, center, axis, angle): self.transform(Translation(-np.array(center))) self.transform(Rotation.from_properties(angle, axis, invert=False)) self.transform(Translation(center))
def translate(self, vector): self.transform(Translation(vector))
def do(self): # the indices (n,m) that define the tube, see e.g. the wikipedia page # about nanotubes for the interpretation of these indices: # http://en.wikipedia.org/wiki/Carbon_nanotube n = self.parameters.n m = self.parameters.m periodic_tube = isinstance(self.parameters.tube_length, Undefined) universe = context.application.model.universe def define_flat(): "Reads and converts the unit cell vectors from the current model." # some parts of the algorithm have been arranged sub functions like # these, to reduce the number of local variables in self.do. This # should also clarify the code. active, inactive = universe.get_active_inactive() lengths, angles = universe.get_parameters() a = lengths[active[0]] b = lengths[active[1]] theta = angles[inactive[0]] return ( numpy.array([a, 0], float), numpy.array([b*numpy.cos(theta), b*numpy.sin(theta)], float) ) flat_a, flat_b = define_flat() def create_pattern(): "Read the atom positions and transform them to the flat coordinates" active, inactive = universe.get_active_inactive() tmp_cell = UnitCell() tmp_cell.add_cell_vector(universe.cell[:,active[0]]) tmp_cell.add_cell_vector(universe.cell[:,active[1]]) r = tmp_cell.calc_align_rotation_matrix() return [ (atom.number, numpy.dot(r, atom.get_absolute_frame().t)) for atom in yield_atoms([universe]) ] pattern = create_pattern() def define_big_periodic(): "Based on (n,m) calculate the size of the periodic sheet (that will be folded into a tube)." big_a = n*flat_a - m*flat_b norm_a = numpy.linalg.norm(big_a) radius = norm_a/(2*numpy.pi) big_x = big_a/norm_a big_y = numpy.array([-big_x[1], big_x[0]], float) big_b = None stack_vector = flat_b - flat_a*numpy.dot(big_x, flat_b)/numpy.dot(big_x, flat_a) stack_length = numpy.linalg.norm(stack_vector) nominator = numpy.linalg.norm(stack_vector - flat_b) denominator = numpy.linalg.norm(flat_a) fraction = nominator/denominator stack_size = 1 while True: repeat = fraction*stack_size if stack_length*stack_size > self.parameters.max_length: break if abs(repeat - round(repeat))*denominator < self.parameters.max_error: big_b = stack_vector*stack_size break stack_size += 1 if big_b is None: raise UserError("Could not create a periodic tube shorter than the given maximum length.") rotation = numpy.array([big_x, big_y], float) return big_a, big_b, rotation, stack_vector, stack_size, radius def define_big_not_periodic(): "Based on (n,m) calculate the size of the non-periodic sheet (that will be folded into a tube)." big_a = n*flat_a - m*flat_b norm_a = numpy.linalg.norm(big_a) radius = norm_a/(2*numpy.pi) big_x = big_a/norm_a big_y = numpy.array([-big_x[1], big_x[0]], float) stack_vector = flat_b - flat_a*numpy.dot(big_x, flat_b)/numpy.dot(big_x, flat_a) stack_length = numpy.linalg.norm(stack_vector) stack_size = int(self.parameters.tube_length/stack_length) big_b = stack_vector*stack_size rotation = numpy.array([big_x, big_y], float) return big_a, big_b, rotation, stack_vector, stack_size, radius if periodic_tube: big_a, big_b, rotation, stack_vector, stack_size, radius = define_big_periodic() else: big_a, big_b, rotation, stack_vector, stack_size, radius = define_big_not_periodic() def yield_translations(): "Yields the indices of the periodic images that are part of the tube." to_fractional = numpy.linalg.inv(numpy.array([big_a, big_b]).transpose()) col_len = int(numpy.linalg.norm(big_a + m*stack_vector)/numpy.linalg.norm(flat_a))+4 shift = numpy.dot(stack_vector - flat_b, flat_a)/numpy.linalg.norm(flat_a)**2 for row in xrange(-m-1, stack_size+1): col_start = int(numpy.floor(row*shift))-1 for col in xrange(col_start, col_start+col_len): p = col*flat_a + row*flat_b i = numpy.dot(to_fractional, p) if (i >= 0).all() and (i < 1-1e-15).all(): yield p #yield p, (i >= 0).all() and (i < 1).all() def yield_pattern(): for number, coordinate in pattern: yield number, coordinate.copy() # first delete everything the universe: for child in copy.copy(universe.children): primitive.Delete(child) # add the new atoms Atom = context.application.plugins.get_node("Atom") if self.parameters.flat: rot_a = numpy.dot(rotation, big_a) rot_b = numpy.dot(rotation, big_b) big_cell = numpy.array([ [rot_a[0], rot_b[0], 0], [rot_a[1], rot_b[1], 0], [0, 0, 10*angstrom], ], float) primitive.SetProperty(universe, "cell", big_cell) primitive.SetProperty(universe, "cell_active", numpy.array([True, periodic_tube, False], bool)) for p in yield_translations(): for number, coordinate in yield_pattern(): coordinate[:2] += p coordinate[:2] = numpy.dot(rotation, coordinate[:2]) translation = Translation() translation.t[:] = coordinate primitive.Add(Atom(number=number, transformation=translation), universe) else: tube_length = numpy.linalg.norm(big_b) primitive.SetProperty(universe, "cell", numpy.diag([radius*2, radius*2, tube_length])) primitive.SetProperty(universe, "cell_active", numpy.array([False, False, periodic_tube], bool)) for p in yield_translations(): for number, coordinate in yield_pattern(): coordinate[:2] += p coordinate[:2] = numpy.dot(rotation, coordinate[:2]) translation = Translation() translation.t[0] = (radius+coordinate[2])*numpy.cos(coordinate[0]/radius) translation.t[1] = (radius+coordinate[2])*numpy.sin(coordinate[0]/radius) translation.t[2] = coordinate[1] primitive.Add(Atom(number=number, transformation=translation), universe)
class Camera(object): def __init__(self): # register configuration settings: default camera from zeobuilder.gui import fields from zeobuilder.gui.fields_dialogs import DialogFieldInfo config = context.application.configuration config.register_setting( "viewer_distance", 100.0*angstrom, DialogFieldInfo("Default Viewer", (1, 0), fields.faulty.Length( label_text="Distance from origin", attribute_name="viewer_distance", low=0.0, low_inclusive=True, )), ) config.register_setting( "opening_angle", 0.0, DialogFieldInfo("Default Viewer", (1, 1), fields.faulty.MeasureEntry( measure="Angle", label_text="Camera opening angle", attribute_name="opening_angle", low=0.0, low_inclusive=True, high=0.5*numpy.pi, high_inclusive=False, show_popup=False, )), ) config.register_setting( "window_size", 25*angstrom, DialogFieldInfo("Default Viewer", (1, 2), fields.faulty.Length( label_text="Window size", attribute_name="window_size", low=0.0, low_inclusive=False, )), ) config.register_setting( "window_depth", 200.0*angstrom, DialogFieldInfo("Default Viewer", (1, 3), fields.faulty.Length( label_text="Window depth", attribute_name="window_depth", low=0.0, low_inclusive=False, )), ) self.reset() def reset(self): config = context.application.configuration self.rotation_center = Translation() self.rotation = Rotation() self.eye = Translation() self.eye.t[2] = config.viewer_distance self.opening_angle = config.opening_angle self.window_size = config.window_size self.window_depth = config.window_depth def get_znear(self): if self.opening_angle > 0.0: return 0.5*self.window_size/numpy.tan(0.5*self.opening_angle) else: return 0.0 znear = property(get_znear) # coordinate transformations def eye_to_camera(self, vector_e): tmp = numpy.ones(2, float) znear = self.znear if znear > 0: return -vector_e[:2]/vector_e[2]/self.window_size*znear else: return vector_e[:2]/self.window_size def camera_window_to_eye(self, vector_c): tmp = numpy.zeros(3, float) tmp[:2] = vector_c*self.window_size znear = self.znear if znear > 0: tmp[2] = -self.znear else: tmp[2] = -self.window_size/3.0 return tmp def model_to_eye(self, vector_m): scene = context.application.scene tmp = scene.model_center.vector_apply_inverse(vector_m) tmp = self.rotation_center.vector_apply_inverse(tmp) tmp = self.rotation.vector_apply_inverse(tmp) tmp[2] -= self.znear tmp = self.eye.vector_apply_inverse(tmp) return tmp def eye_to_model(self, vector_e): scene = context.application.scene tmp = self.eye.vector_apply(vector_e) tmp[2] += self.znear tmp = self.rotation.vector_apply(tmp) tmp = self.rotation_center.vector_apply(tmp) tmp = scene.model_center.vector_apply(tmp) return tmp def model_to_camera(self, vector_m): return self.eye_to_camera(self.model_to_eye(vector_m)) def camera_window_to_model(self, vector_c): return self.eye_to_model(self.camera_window_to_eye(vector_c)) def object_to_depth(self, gl_object): result = -self.model_to_eye(gl_object.get_absolute_frame().t)[2] return result def object_to_camera(self, gl_object): return self.eye_to_camera(self.model_to_eye(gl_object.get_absolute_frame().t)) def object_to_eye(self, gl_object): return self.model_to_eye(gl_object.get_absolute_frame().t) def object_eye_rotation(self, gl_object): """ Returns a matrix that consists of the x, y and z axes of the eye frame in the coordinates of the parent frame of the given object. """ if hasattr(gl_object, "parent") and \ isinstance(gl_object.parent, GLTransformationMixin): parent_matrix = gl_object.parent.get_absolute_frame().r else: parent_matrix = numpy.identity(3, float) result = numpy.dot(self.rotation.r.transpose(), parent_matrix).transpose() return result def depth_to_scale(self, depth): """ transforms a depth into a scale (au/camcoords)""" znear = self.znear if znear > 0: return depth/znear*self.window_size else: return self.window_size def vector_in_plane(self, r, p_m): """Returns a vector at camera position r in a plane (through p, orthogonal to viewing direction) Arguments r -- a two-dimensional vector in camera coordinates p_m -- a three-dimensional vector in model coordinates Returns rp -- a three-dimensional vector in model coordinates that lies at the intersection of a plane and a line. The plane is orthogonal to the viewing direction and goes through the point p. The line connects the eye (eye_m below) with the point r (r_m below) in the camera window. """ eye_m = self.eye_to_model(numpy.zeros(3, float)) r_m = self.camera_window_to_model(r) center_m = self.camera_window_to_model(numpy.zeros(2, float)) normal = (eye_m - center_m) normal /= numpy.linalg.norm(normal) if self.znear > 0: # the line is defined as r = eye_m + d*t, where t = -infinity ... infinity d = eye_m - r_m # t at the intersection: t = -numpy.dot(eye_m - p_m, normal)/numpy.dot(d, normal) return eye_m + d*t else: # the line is defined as r = r_m + d*t, where t = -infinity ... infinity d = normal # t at the intersection: t = -numpy.dot(r_m - p_m, normal)/numpy.dot(d, normal) return r_m + d*t