def add_new(self, position, parent): new = self.get_new(position) if self.current_object == "Fragment": result = new.children[1] primitive.Add(new, parent) UnframeAbsolute = context.application.plugins.get_action( "UnframeAbsolute") UnframeAbsolute([new]) #return result else: primitive.Add(new, parent) return new
def do(self, cutoff): cache = context.application.cache parent = cache.common_root def iter_translation_nodes(nodes): for node in nodes: if isinstance(node, GLTransformationMixin) and \ isinstance(node.transformation, Translation) and \ self.allow_node(node): yield node if isinstance(node, ContainerMixin): for subnode in iter_translation_nodes(node.children): yield subnode nodes = [] coordinates = [] for node in iter_translation_nodes(cache.nodes_without_children): nodes.append(node) coordinates.append(node.get_frame_up_to(parent).t) coordinates = numpy.array(coordinates) unit_cell = None if isinstance(parent, context.application.plugins.get_node("Universe")): unit_cell = parent.cell vector_counter = 1 for i0, i1, delta, distance in PairSearchIntra(coordinates, cutoff, unit_cell): vector = self.get_vector(nodes[i0], nodes[i1], distance) if vector is not None: vector.name += " %i" % vector_counter vector_counter += 1 primitive.Add(vector, common_parent([nodes[i0], nodes[i1]]))
def do(self): Atom = context.application.plugins.get_node("Atom") Tetraeder = context.application.plugins.get_node("Tetraeder") def iter_all_tetra(nodes): for node in nodes: if isinstance(node, Atom) and node.num_bonds() == 4: yield node elif isinstance(node, ContainerMixin): for tetra in iter_all_tetra(node.children): yield tetra cache = context.application.cache if isinstance(cache.node, GLContainerMixin): parent = cache.node else: parent = cache.common_parent if parent is None: parent = context.application.model.universe for tetra in iter_all_tetra(cache.nodes_without_children): primitive.Add( Tetraeder(targets=list(tetra.iter_neighbors()), color=tetra.get_color()), parent, )
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_vertices: atoms = [graph.molecule.atoms[i] for i in group] new_positions = self.calc_new_positions(group, atoms, graph, parent) if new_positions is None: # this happens for groups of atoms that are inherently periodic. continue 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( atom.get_parentframe_up_to(parent).inv * 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 list(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 fn(): FileNew = context.application.plugins.get_action("FileNew") FileNew() universe = context.application.model.root[0] context.application.action_manager.record_primitives = False # add some test objects Box = context.application.plugins.get_node("Box") Frame = context.application.plugins.get_node("Frame") box1 = Box() primitive.Add(box1, universe) frame = Frame() primitive.Add(frame, universe) box2 = Box() primitive.Add(box2, frame) # test nodes_without_children context.application.main.select_nodes([box1, box2]) assert context.application.cache.nodes_without_children == [box1, box2]
def do(self, NewParentClass): cache = context.application.cache old_parent = cache.parent new_parent = NewParentClass() nodes = list(cache.nodes) primitive.Add(new_parent, old_parent, index=cache.lowest_index) for victim in nodes: primitive.Move(victim, new_parent) return new_parent
def do(self): cache = context.application.cache parent = cache.node unit_cell = None if isinstance(parent, UnitCell): unit_cell = parent Atom = context.application.plugins.get_node("Atom") atoms = [] coordinates = [] for child in parent.children: if isinstance(child, Atom): atoms.append(child) coordinates.append(child.transformation.t) coordinates = numpy.array(coordinates) cf = ClusterFactory() for i0, i1, delta, distance in PairSearchIntra( coordinates, periodic.max_radius * 0.4, unit_cell): atom0 = atoms[i0] atom1 = atoms[i1] if atom0.number == atom1.number: if distance < periodic[atom0.number].vdw_radius * 0.4: cf.add_related(atom0, atom1) clusters = cf.get_clusters() del cf # define the new singles singles = [] for cluster in clusters: number = iter(cluster.items).next().number single = Atom(name="Single " + periodic[number].symbol) single.set_number(number) singles.append((single, list(cluster.items))) # calculate their positions for single, overlappers in singles: # in the following algorithm, we suppose that the cluster of # atoms is small compared to the parent's periodic sizes # (if the parent is a periodic system) first_pos = overlappers[0].transformation.t delta_to_mean = numpy.zeros(3, float) for atom in overlappers[1:]: delta_to_mean += parent.shortest_vector(atom.transformation.t - first_pos) delta_to_mean /= float(len(overlappers)) single.set_transformation(Translation(first_pos + delta_to_mean)) # modify the model for single, overlappers in singles: lowest_index = min([atom.get_index() for atom in overlappers]) primitive.Add(single, parent, index=lowest_index) for atom in overlappers: while len(atom.references) > 0: primitive.SetTarget(atom.references[0], single) primitive.Delete(atom)
def do(self): victim = context.application.cache.node Frame = context.application.plugins.get_node("Frame") frame = Frame(name="Frame of " + victim.name) primitive.Add(frame, victim.parent, index=victim.get_index()) primitive.Transform(frame, victim.transformation) primitive.Move(victim, frame) primitive.SetProperty(victim, "transformation", victim.Transformation())
def add_hydrogens(atom): existing_bonds = list(atom.iter_bonds()) bond_length = bonds.get_length(atom.number, 1, BOND_SINGLE) num_hydrogens = self.parameters.num_hydrogens if len(existing_bonds) == 0: t = atom.transformation.t + numpy.array([0, bond_length, 0]) H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) existing_bonds.append(bond) num_hydrogens -= 1 main_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 main_direction += shortest_vector main_direction /= numpy.linalg.norm(main_direction) normal = random_orthonormal(main_direction) rotation = Rotation.from_properties( 2 * numpy.pi / float(num_hydrogens), main_direction, False) h_pos = bond_length * ( main_direction * numpy.cos(self.parameters.valence_angle) + normal * numpy.sin(self.parameters.valence_angle)) for i in range(num_hydrogens): t = atom.transformation.t + h_pos H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) h_pos = rotation * h_pos
def do(self): cache = context.application.cache originals = cache.nodes parent = cache.parent highest_index = cache.highest_index serialized = StringIO.StringIO() dump_to_file(serialized, originals) serialized.seek(0) duplicates = load_from_file(serialized) for duplicate in duplicates: highest_index += 1 primitive.Add(duplicate, parent, index=highest_index)
def connect(self, gl_object1, gl_object2): try: new = context.application.plugins.get_node( self.vector_store.get_value( self.cb_vector.get_active_iter(), 0))(targets=[gl_object1, gl_object2]) except TargetError: return if (self.vector_store.get_value(self.cb_vector.get_active_iter(), 0) == "Bond"): new.set_bond_type( self.bondtype_store.get_value( self.cb_bondtype.get_active_iter(), 1)) primitive.Add(new, common_parent([gl_object1.parent, gl_object2.parent]))
def do(self): cache = context.application.cache parent = cache.common_parent while not parent.check_add(Point): parent = parent.parent vector_sum = numpy.zeros(3, float) num_vectors = 0 for node in cache.nodes: if isinstance(node, GLTransformationMixin) and \ isinstance(node.transformation, Translation): vector_sum += node.get_frame_relative_to(parent).t num_vectors += 1 point = Point(name="Average", transformation=Translation(vector_sum / num_vectors)) primitive.Add(point, parent)
def connect_springs(self): model, iter = self.tree_selection.get_selected() Spring = context.application.plugins.get_node("Spring") if self.cb_inverse.get_active() and len(model.get_value(iter, 3)[3]) > 0: springs = [ Spring(targets=[node1(), node2()]) for node1, node2 in model.get_value(iter, 3)[3] ] else: springs = [ Spring(targets=[node1(), node2()]) for node1, node2 in model.get_value(iter, 3)[2] ] parent = common_parent([self.frame1, self.frame2]) for spring in springs: primitive.Add(spring, parent) return springs
def do(self): Atom = context.application.plugins.get_node("Atom") cache = context.application.cache for spring in list(cache.nodes): atom1, atom2 = spring.get_targets() if isinstance(atom1, Atom) and isinstance(atom2, Atom): t = 0.5 * (atom1.get_frame_relative_to(spring.parent).t + atom2.get_frame_relative_to(spring.parent).t) replacement = Atom(name="Merge of %s and %s" % (atom1.name, atom2.name), number=max([atom1.number, atom2.number]), transformation=Translation(t)) primitive.Add(replacement, spring.parent, spring.get_index()) atoms = set([atom1, atom2]) for atom in atoms: while len(atom.references) > 0: primitive.SetTarget(atom.references[0], replacement) primitive.Delete(spring) for atom in atoms: primitive.Delete(atom)
def do(self): cache = context.application.cache nodes = cache.nodes primitive.Add(Plane(targets=nodes), cache.common_parent, index=cache.highest_index + 1)
def do(self): cache = context.application.cache geometry_nodes = [[], []] def get_parameters(node, point_type, geometry_index, filter_expression, radius_expression): template = "An exception occured in the %%s expression\nfor the %s points of geometry %i." % ( point_type, geometry_index + 1) try: is_type = filter_expression(node) except Exception: raise UserError(template % "filter") if is_type: try: radius = radius_expression(node) except Exception: raise UserError(template % "radius") geometry_nodes[geometry_index].append(node) return True, radius else: return False, None def read_geometry(frame, geometry_index, connect_description, repulse_description): coordinates = [] connect_masks = [] radii = [] for child in frame.children: if isinstance(child, GLTransformationMixin) and \ isinstance(child.transformation, Translation): is_connect, radius = get_parameters( child, "connecting", geometry_index, *connect_description) if is_connect: coordinates.append(child.transformation.t) connect_masks.append(True) radii.append(radius) else: is_repulse, radius = get_parameters( child, "repulsive", geometry_index, *repulse_description) if is_repulse: coordinates.append(child.transformation.t) connect_masks.append(False) radii.append(radius) return Geometry( numpy.array(coordinates, float), numpy.array(connect_masks, bool), numpy.array(radii, float), ) inp = {} inp["geometry1"] = read_geometry(cache.nodes[0], 0, self.parameters.connect_description1, self.parameters.repulse_description1) if len(cache.nodes) == 2: inp["geometry2"] = read_geometry( cache.nodes[1], 1, self.parameters.connect_description2, self.parameters.repulse_description2) else: inp["geometry2"] = None inp["action_radius"] = self.parameters.action_radius inp["hit_tolerance"] = self.parameters.hit_tolerance if not isinstance(self.parameters.allow_inversions, Undefined): inp["allow_rotations"] = True inp["allow_inversions"] = self.parameters.allow_inversions inp["minimum_triangle_area"] = self.parameters.minimum_triangle_size**2 else: inp["allow_rotations"] = False if isinstance(self.parameters.rotation2, Undefined): inp["rotation2"] = Rotation.identity() else: inp["rotation2"] = self.parameters.rotation2 if inp["allow_rotations"]: connections = self.triangle_report_dialog.run(inp) else: connections = self.pair_report_dialog.run(inp) if connections is not None and len(connections) > 0: if len(cache.nodes) == 1: frame1 = cache.nodes[0] Duplicate = context.application.plugins.get_action("Duplicate") Duplicate() frame2 = cache.nodes[0] geometry_nodes[1] = geometry_nodes[0] else: frame1, frame2 = cache.nodes conscan_results = ConscanResults( targets=[frame1, frame2], connections=[( float(connection.quality), connection.transformation, [(geometry_nodes[0][first].get_index(), geometry_nodes[1][second].get_index()) for first, second in connection.pairs], [(geometry_nodes[0][second].get_index(), geometry_nodes[1][first].get_index()) for first, second in connection.pairs if connection.invertible], ) for connection in connections], ) primitive.Add(conscan_results, context.application.model.folder)
def do(self): primitive.Add(SavedSelection(targets=context.application.cache.nodes), context.application.model.folder)
def add_hydrogens(atom): existing_bonds = list(atom.iter_bonds()) num_bonds = len(existing_bonds) bond_length = bonds.get_length(atom.number, 1, BOND_SINGLE) if num_bonds == 0: t = atom.transformation.t + numpy.array([0, bond_length, 0]) H = Atom(name="auto H", number=1, transformation=Translation(t)) 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.from_properties( 2 * numpy.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.iter_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 * numpy.cos(opening_angle) + normal * numpy.sin(opening_angle)) for i in range(num_hydrogens): t = atom.transformation.t + h_pos H = Atom(name="auto H", number=1, transformation=Translation(t)) primitive.Add(H, atom.parent) bond = Bond(name="aut H bond", targets=[atom, H]) primitive.Add(bond, atom.parent) h_pos = rotation * h_pos
def do(self): cache = context.application.cache nodes = cache.nodes primitive.Add(self.new_connector(nodes[0], nodes[1]), cache.common_parent, index=cache.highest_index + 1)
def extend_to_cluster(axis, interval): if (interval is None) or isinstance(interval, Undefined): return assert universe.cell.active[axis] interval.sort() index_min = int(numpy.floor(interval[0])) index_max = int(numpy.ceil(interval[1])) old_points = [ node for node in universe.children if (isinstance(node, GLTransformationMixin) and isinstance(node.transformation, Translation)) ] if len(old_points) == 0: return old_connections = [ node for node in universe.children if (isinstance(node, ReferentMixin) and reduce( (lambda x, y: x and y), (isinstance(child, SpatialReference) for child in node.children), True, )) ] # replication of the points new_points = {} for old_point in old_points: # determine the wrapped position old_pos = old_point.transformation.t.copy() old_frac = universe.cell.to_fractional(old_pos) old_index = numpy.floor(old_frac).astype(int) old_pos -= universe.cell.to_cartesian(old_index) old_frac -= old_index del old_index # make copies for cell_index in xrange(index_min, index_max): position = old_pos + universe.cell.matrix[:, axis] * cell_index if (old_frac[axis] + cell_index < interval[0]) or ( old_frac[axis] + cell_index > interval[1]): continue state = old_point.__getstate__() state["transformation"] = state[ "transformation"].copy_with(t=position) new_point = old_point.__class__(**state) new_points[(old_point, cell_index)] = new_point new_connections = [] # replication of the connections for cell_index in xrange(index_min - 1, index_max + 1): for connection in old_connections: old_target0 = connection.children[0].target new_target0 = new_points.get((old_target0, cell_index)) if new_target0 is None: continue new_targets = [new_target0] for reference in connection.children[1:]: abort = True old_target1 = reference.target for offset in 0, 1, -1: new_target1 = new_points.get( (old_target1, cell_index + offset)) if new_target1 is not None: delta = new_target0.transformation.t - new_target1.transformation.t if vector_acceptable( delta, universe.cell.matrix[:, axis]): new_targets.append(new_target1) abort = False break if abort: break if abort: del new_targets continue state = connection.__getstate__() state["targets"] = new_targets new_connections.append(connection.__class__(**state)) # remove the existing points and connections for node in old_connections: primitive.Delete(node) del old_connections for node in old_points: primitive.Delete(node) del old_points # remove the periodicity new_active = universe.cell.active.copy() new_active[axis] = False new_cell = universe.cell.copy_with(active=new_active) primitive.SetProperty(universe, "cell", new_cell) # add the new nodes for node in new_points.itervalues(): primitive.Add(node, universe) for connection in new_connections: primitive.Add(connection, universe)
def do(self): # create the repetitions vector repetitions = [] if hasattr(self.parameters, "repetitions_a"): repetitions.append(self.parameters.repetitions_a) else: repetitions.append(1) if hasattr(self.parameters, "repetitions_b"): repetitions.append(self.parameters.repetitions_b) else: repetitions.append(1) if hasattr(self.parameters, "repetitions_c"): repetitions.append(self.parameters.repetitions_c) else: repetitions.append(1) repetitions = numpy.array(repetitions, int) # serialize the positioned children universe = context.application.model.universe positioned = [ node for node in universe.children if (isinstance(node, GLTransformationMixin) and isinstance(node.transformation, Translation)) ] if len(positioned) == 0: return serialized = StringIO.StringIO() dump_to_file(serialized, positioned) # create the replica's # replicate the positioned objects new_children = {} for cell_index in iter_all_positions(repetitions): cell_index = numpy.array(cell_index) cell_hash = tuple(cell_index) serialized.seek(0) nodes = load_from_file(serialized) new_children[cell_hash] = nodes for node in nodes: t = node.transformation.t + numpy.dot(universe.cell.matrix, cell_index) new_transformation = node.transformation.copy_with(t=t) node.set_transformation(new_transformation) # forget about serialized stuff serialized.close() del serialized new_connectors = [] # replicate the objects that connect these positioned objects for cell_index in iter_all_positions(repetitions): cell_index = numpy.array(cell_index) cell_hash = tuple(cell_index) for connector in universe.children: # Only applicable to ReferentMixin with only SpatialReference # children if not isinstance(connector, ReferentMixin): continue skip = False for reference in connector.children: if not isinstance(reference, SpatialReference): skip = True break if skip: continue # first locate the new first target for this cell_index first_target_orig = connector.children[0].target first_target_index = positioned.index(first_target_orig) first_target = new_children[cell_hash][first_target_index] assert first_target is not None new_targets = [first_target] for reference in connector.children[1:]: # then find the other new targets, taking into account # periodicity other_target_orig = reference.target shortest_vector = universe.shortest_vector( (other_target_orig.transformation.t - first_target_orig.transformation.t)) translation = first_target.transformation.t + shortest_vector other_target_pos = translation other_cell_index = numpy.floor( universe.cell.to_fractional(other_target_pos)).astype( int) other_cell_index %= repetitions other_cell_hash = tuple(other_cell_index) other_target_index = positioned.index(other_target_orig) other_cell_children = new_children.get(other_cell_hash) assert other_cell_children is not None other_target = other_cell_children[other_target_index] assert other_target is not None new_targets.append(other_target) state = connector.__getstate__() state["targets"] = new_targets new_connectors.append(connector.__class__(**state)) # remove the existing nodes while len(universe.children) > 0: primitive.Delete(universe.children[0]) del positioned # multiply the cell matrix and reset the number of repetitions new_matrix = universe.cell * repetitions primitive.SetProperty(universe, "cell", new_matrix) primitive.SetProperty(universe, "repetitions", numpy.array([1, 1, 1], int)) # add the new nodes for nodes in new_children.itervalues(): for node in nodes: primitive.Add(node, universe) for connector in new_connectors: primitive.Add(connector, universe)
def replace(self, gl_object): if not gl_object.get_fixed(): new = self.get_new(gl_object.transformation.t) # select a target object, i.e. the one the will be connected with # the bonds/vectors/... of the replaced object if (self.current_object == "Fragment"): # fragments are inserted as frames # take atom with index 1 as target target_object = new.children[1] else: target_object = new # check if all connections to the replaced object are applicable # to the new object. if not, then do not perform the replacement # and return early. for reference in gl_object.references[::-1]: if not reference.check_target(target_object): return # add the new object parent = gl_object.parent primitive.Add(new, parent) if (self.current_object == "Fragment"): # Fix the rotation and translation of the molecular 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.from_properties(angle, axis, False) primitive.Transform(new, rotation) else: bond1 = None # Tranlsation pos_old = new.children[1].get_frame_relative_to(parent).t pos_new = gl_object.transformation.t translation = Translation(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(-direction1 / old_length * (new_length - old_length)) primitive.Transform(new, translation) # let the references to the replaced object point to the new object for reference in gl_object.references[::-1]: try: primitive.SetTarget(reference, target_object) except primitive.PrimitiveError: primitive.Delete(reference.parent) # delete the replaced object primitive.Delete(gl_object) if (self.current_object == "Fragment"): # Delete the first atom in the fragment primitive.Delete(new.children[0]) # Unframe the fragment UnframeAbsolute = context.application.plugins.get_action( "UnframeAbsolute") UnframeAbsolute([new])
def do(self, AddModelObject): primitive.Add(AddModelObject(), context.application.cache.node)
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.cell.active_inactive lengths, angles = universe.cell.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.cell.active_inactive a = universe.cell.matrix[:, active[0]] b = universe.cell.matrix[:, active[1]] c = numpy.cross(a, b) tmp_cell = UnitCell(numpy.array([a, b, c]).transpose()) rotation = tmp_cell.alignment_a return [(atom.number, rotation * atom.get_absolute_frame().t) for atom in iter_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 iter_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 iter_pattern(): for number, coordinate in pattern: yield number, coordinate.copy() # first delete everything the universe: while len(universe.children) > 0: primitive.Delete(universe.children[0]) # 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_matrix = numpy.array([ [rot_a[0], rot_b[0], 0], [rot_a[1], rot_b[1], 0], [0, 0, 10 * angstrom], ], float) big_cell = UnitCell( big_matrix, numpy.array([True, periodic_tube, False], bool)) primitive.SetProperty(universe, "cell", big_cell) for p in iter_translations(): for number, coordinate in iter_pattern(): coordinate[:2] += p coordinate[:2] = numpy.dot(rotation, coordinate[:2]) translation = Translation(coordinate) primitive.Add( Atom(number=number, transformation=translation), universe) else: tube_length = numpy.linalg.norm(big_b) big_matrix = numpy.diag([radius * 2, radius * 2, tube_length]) big_cell = UnitCell( big_matrix, numpy.array([False, False, periodic_tube], bool)) primitive.SetProperty(universe, "cell", big_cell) for p in iter_translations(): for number, coordinate in iter_pattern(): coordinate[:2] += p coordinate[:2] = numpy.dot(rotation, coordinate[:2]) translation = Translation( numpy.array([ (radius + coordinate[2]) * numpy.cos(coordinate[0] / radius), (radius + coordinate[2]) * numpy.sin(coordinate[0] / radius), coordinate[1], ])) primitive.Add( Atom(number=number, transformation=translation), universe)