def find_instruction_order(instructions: OrderedMultiDict): starting_instruction = set(instructions.keys(multi=True)).difference( set(instructions.values(multi=True))) worker_manager = WorkerManager(offset=0) instruction_order = IndexedSet() current_instructions = list() for index, value in enumerate(sorted(starting_instruction)): current_instructions.append(value) worker_manager.add(value) current_instructions = sorted(current_instructions) while current_instructions: index = -1 for i, current_instruction in enumerate(current_instructions): if not { key for key, value in instructions.items(multi=True) if value == current_instruction }.difference(instruction_order): instruction_order.add(current_instruction) worker_manager.remove(current_instruction) for n, value in enumerate( instructions.getlist(current_instruction)): if value not in current_instructions: current_instructions.append(value) index = i break if index != -1: current_instructions.pop(index) current_instructions = sorted(current_instructions) for instruction in current_instructions: worker_manager.add(instruction) print("".join(instruction_order)) print(worker_manager.start_time)
def get_ancestor_nodes(self, add_self: bool = False) -> IndexedSet: """ Returns a list of all connected ancestors of this node. :param add_self: if True, adds this node to the set of returned nodes. :return: a Set of nodes that are ancestors of this node. """ nodes = self._node.get_ancestor_nodes(add_self=add_self) out = IndexedSet() for n in nodes: out.add(n.get_container()) return out
def store_selection(self): """Remember to call it on "ok", to persist history of selected paths.""" print("Storing selection...") if self.stkey_last_path: sect, key = self.stkey_last_path.split('.') store_put(sect, key, self.path) if self.stkey_paths_hist: sect, key = self.stkey_paths_hist.split('.') try: paths_hist = IndexedSet(store_get(sect, key, [])) paths_hist.add(self.path) paths_hist = list( reversed(paths_hist)) # earlier elements at list-head except: paths_hist = [self.path] store_put(sect, key, paths_hist)
def get_ancestor_nodes(self, add_self: bool = False) -> IndexedSet: """ Returns a list of all connected ancestors of this node. :param add_self: if True, adds this node to the set of returned nodes. :return: a Set of nodes that are ancestors of this node. """ out = IndexedSet() if add_self: out.add(self) for socket in self.get_input_sockets(): if socket.is_connected(): for node in [n for n in socket.get_connected_nodes()]: out.update(node.get_ancestor_nodes(add_self=True)) return out
def test_iset_index_method(): original_list = list(range(8, 20)) + list(range(8)) indexed_list = IndexedSet() for i in original_list: indexed_list.add(i) for i in original_list: index = indexed_list.index(i) # if we're removing them in order, the target value should always be at index 0 assert index == 0 indexed_list.pop(index) indexed_list = IndexedSet(range(10)) for i in reversed(range(10)): if i % 2: continue index = indexed_list.index(i) assert i == indexed_list.pop(index) indexed_list = IndexedSet(range(32)) for i in list(indexed_list): if i % 3: index = indexed_list.index(i) assert i == indexed_list.pop(index) indexed_list = IndexedSet(range(10)) for i in range(10): if i < 3: continue index = indexed_list.index(i) assert i == indexed_list.pop(index) indexed_list = IndexedSet(range(32)) for i in list(indexed_list): if i % 3: index = indexed_list.index(i) assert i == indexed_list.pop(index)
class Topology(object): """A topology. A topology represents a chemical structure wherein lie the collection of sites which together form a chemical structure containing connections (gmso.Bond, gmso.Angle and gmso.Dihedral (along with their associated types). A topology is the fundamental data structure in GMSO, from which we can gather various information about the chemical structure and apply a forcefield before converting the structure into a format familiar to various simulation engines. Parameters ---------- name : str, optional, default='Topology' A name for the Topology. box : gmso.Box, optional, default=None A gmso.Box object bounding the topology Attributes ---------- typed : bool True if the topology is typed combining_rule : str, ['lorentz', 'geometric'] The combining rule for the topology, can be either 'lorentz' or 'geometric' scaling_factors : dict A collection of scaling factors used in the forcefield n_sites : int Number of sites in the topology n_connections : int Number of connections in the topology (Bonds, Angles, Dihedrals, Impropers) n_bonds : int Number of bonds in the topology n_angles: int Number of angles in the topology n_dihedrals : int Number of dihedrals in the topology n_impropers : int Number of impropers in the topology n_subtops : int Number of subtopolgies in the topology connections : tuple of gmso.Connection objects A collection of bonds, angles, dihedrals, and impropers in the topology bonds : tuple of gmso.Bond objects A collection of bonds in the topology angles : tuple of gmso.Angle objects A collection of angles in the topology dihedrals : tuple of gmso.Dihedral objects A collection of dihedrals in the topology impropers : tuple of gmso.Improper objects A collection of impropers in the topology connection_types : tuple of gmso.Potential objects A collection of BondTypes, AngleTypes, DihedralTypes, and ImproperTypes in the topology atom_types : tuple of gmso.AtomType objects A collection of AtomTypes in the topology bond_types : tuple of gmso.BondType objects A collection of BondTypes in the topology angle_types : tuple of gmso.AngleType objects A collection go AngleTypes in the topology dihedral_types : tuple of gmso.DihedralType objects A collection of DihedralTypes in the topology improper_types : tuple of gmso.ImproperType objects A collection of ImproperTypes in the topology atom_type_expressions : list of gmso.AtomType.expression objects A collection of all the expressions for the AtomTypes in topology connection_type_expressions : list of gmso.Potential.expression objects A collection of all the expressions for the Potential objects in the topology that represent a connection type bond_type_expressions : list of gmso.BondType.expression objects A collection of all the expressions for the BondTypes in topology angle_type_expressions : list of gmso.AngleType.expression objects A collection of all the expressions for the AngleTypes in topology dihedral_type_expressions : list of gmso.DihedralType.expression objects A collection of all the expression for the DihedralTypes in the topology improper_type_expressions : list of gmso.ImproperType.expression objects A collection of all the expression for the ImproperTypes in the topology See Also -------- gmso.SubTopology : A topology within a topology """ def __init__(self, name="Topology", box=None): self.name = name self._box = box self._sites = IndexedSet() self._typed = False self._connections = IndexedSet() self._bonds = IndexedSet() self._angles = IndexedSet() self._dihedrals = IndexedSet() self._impropers = IndexedSet() self._subtops = IndexedSet() self._atom_types = {} self._atom_types_idx = {} self._connection_types = {} self._bond_types = {} self._bond_types_idx = {} self._angle_types = {} self._angle_types_idx = {} self._dihedral_types = {} self._dihedral_types_idx = {} self._improper_types = {} self._improper_types_idx = {} self._combining_rule = 'lorentz' self._scaling_factors = { "vdw_12": 0.0, "vdw_13": 0.0, "vdw_14": 0.5, "coul_12": 0.0, "coul_13": 0.0, "coul_14": 0.5, } self._set_refs = { ATOM_TYPE_DICT: self._atom_types, BOND_TYPE_DICT: self._bond_types, ANGLE_TYPE_DICT: self._angle_types, DIHEDRAL_TYPE_DICT: self._dihedral_types, IMPROPER_TYPE_DICT: self._improper_types, } self._index_refs = { ATOM_TYPE_DICT: self._atom_types_idx, BOND_TYPE_DICT: self._bond_types_idx, ANGLE_TYPE_DICT: self._angle_types_idx, DIHEDRAL_TYPE_DICT: self._dihedral_types_idx, IMPROPER_TYPE_DICT: self._improper_types_idx } self._unique_connections = {} @property def name(self): return self._name @name.setter def name(self, name): self._name = str(name) if name else 'Topology' @property def box(self): return self._box @box.setter def box(self, box): self._box = box @property def typed(self): return self._typed @typed.setter def typed(self, typed): self._typed = typed @property def combining_rule(self): return self._combining_rule @combining_rule.setter def combining_rule(self, rule): if rule not in ['lorentz', 'geometric']: raise GMSOError('Combining rule must be `lorentz` or `geometric`') self._combining_rule = rule @property def scaling_factors(self): return self._scaling_factors @scaling_factors.setter def scaling_factors(self, scaling_factors): expected_items = [ "vdw_12", "vdw_13", "vdw_14", "coul_12", "coul_13", "coul_14" ] if not isinstance(scaling_factors, dict): raise GMSOError("Scaling factors should be a dictionary") for item in expected_items: if item not in scaling_factors.keys(): raise GMSOError( f"Expected {expected_items} as keys in the scaling factors" ) for val in scaling_factors.values(): if val < 0.0 or val > 1.0: raise GMSOError( "Scaling factors should be between 0.0 and 1.0") self._scaling_factors = scaling_factors @property def positions(self): xyz = np.empty(shape=(self.n_sites, 3)) * u.nm for i, site in enumerate(self._sites): xyz[i, :] = site.position return xyz @property def n_sites(self): return len(self.sites) @property def n_connections(self): return len(self.connections) @property def n_bonds(self): return len(self.bonds) @property def n_angles(self): return len(self.angles) @property def n_dihedrals(self): return len(self.dihedrals) @property def n_impropers(self): return len(self.impropers) @property def subtops(self): return self._subtops @property def n_subtops(self): return len(self._subtops) @property def sites(self): return tuple(self._sites) @property def connections(self): return tuple(self._connections) @property def bonds(self): return tuple(self._bonds) @property def angles(self): return tuple(self._angles) @property def dihedrals(self): return tuple(self._dihedrals) @property def impropers(self): return tuple(self._impropers) @property def atom_types(self): return tuple(self._atom_types.values()) @property def connection_types(self): return tuple(self._connection_types.values()) @property def bond_types(self): return tuple(self._bond_types.values()) @property def angle_types(self): return tuple(self._angle_types.values()) @property def dihedral_types(self): return tuple(self._dihedral_types.values()) @property def improper_types(self): return tuple(self._improper_types.values()) @property def atom_type_expressions(self): return list(set([atype.expression for atype in self.atom_types])) @property def connection_type_expressions(self): return list( set([contype.expression for contype in self.connection_types])) @property def bond_type_expressions(self): return list(set([btype.expression for btype in self.bond_types])) @property def angle_type_expressions(self): return list(set([atype.expression for atype in self.angle_types])) @property def dihedral_type_expressions(self): return list(set([atype.expression for atype in self.dihedral_types])) @property def improper_type_expressions(self): return list(set([atype.expression for atype in self.improper_types])) def add_site(self, site, update_types=True): """Add a site to the topology This method will add a site to the existing topology, since sites are stored in an indexed set, adding redundant site will have no effect. If the update_types parameter is set to true (default behavior), this method will also check if there is an gmso.AtomType associated with the site and it to the topology's AtomTypes collection. Parameters ----------- site : gmso.core.Site Site to be added to this topology update_types : (bool), default=True If true, add this site's atom type to the topology's set of AtomTypes """ self._sites.add(site) if update_types and site.atom_type: site.atom_type.topology = self if site.atom_type in self._atom_types: site.atom_type = self._atom_types[site.atom_type] else: self._atom_types[site.atom_type] = site.atom_type self._atom_types_idx[site.atom_type] = len( self._atom_types) - 1 self.is_typed(updated=False) def update_sites(self): """Update the sites of the topology. This method will update the sites in the topology based on the connection members, For example- if you add a bond to a topology, without adding the constituent sites, this method can be called to add the sites which are the connection members of the bond as shown below. >>> import gmso >>> site1 = gmso.Site(name='MySite1') >>> site2 = gmso.Site(name='MySite2') >>> bond1 = gmso.Bond(name='site1-site2', connection_members=[site1, site2]) >>> this_topology = gmso.Topology('TwoSitesTopology') >>> this_topology.add_connection(bond1) >>> this_topology.update_sites() See Also -------- gmso.Topology.add_site : Add a site to the topology. gmso.Topology.add_connection : Add a Bond, an Angle or a Dihedral to the topology. gmso.Topology.update_topology : Update the entire topology. """ for connection in self.connections: for member in connection.connection_members: if member not in self._sites: self.add_site(member) def add_connection(self, connection, update_types=True): """Add a gmso.Connection object to the topology. This method will add a gmso.Connection object to the topology, it can be used to generically include any Connection object i.e. Bond or Angle or Dihedral to the topology. According to the type of object added, the equivalent collection in the topology is updated. For example- If you add a Bond, this method will update topology.connections and topology.bonds object. Additionally, if update_types is True (default behavior), it will also update any Potential objects associated with the connection. Parameters ---------- connection : one of gmso.Connection, gmso.Bond, gmso.Angle, gmso.Dihedral, or gmso.Improper object update_types : bool, default=True If True also add any Potential object associated with connection to the topology. Returns _______ gmso.Connection The Connection object or equivalent Connection object that is in the topology """ # Check if an equivalent connection is in the topology equivalent_members = connection._equivalent_members_hash() if equivalent_members in self._unique_connections: warnings.warn('An equivalent connection already exists. ' 'Providing the existing equivalent Connection.') connection = self._unique_connections[equivalent_members] for conn_member in connection.connection_members: if conn_member not in self.sites: self.add_site(conn_member) self._connections.add(connection) self._unique_connections.update({equivalent_members: connection}) if isinstance(connection, Bond): self._bonds.add(connection) if isinstance(connection, Angle): self._angles.add(connection) if isinstance(connection, Dihedral): self._dihedrals.add(connection) if isinstance(connection, Improper): self._impropers.add(connection) if update_types: self.update_connection_types() return connection def identify_connections(self): _identify_connections(self) def update_connection_types(self): """Update the connection types based on the connection collection in the topology. This method looks into all the connection objects (Bonds, Angles, Dihedrals, Impropers) to check if any Potential object (BondType, AngleType, DihedralType, ImproperType) is not in the topology's respective collection and will add those objects there. See Also -------- gmso.Topology.update_atom_types : Update atom types in the topology. """ for c in self.connections: if c.connection_type is None: warnings.warn( 'Non-parametrized Connection {} detected'.format(c)) elif not isinstance(c.connection_type, ParametricPotential): raise GMSOError('Non-Potential {} found' 'in Connection {}'.format( c.connection_type, c)) elif c.connection_type not in self._connection_types: c.connection_type.topology = self self._connection_types[c.connection_type] = c.connection_type if isinstance(c.connection_type, BondType): self._bond_types[c.connection_type] = c.connection_type self._bond_types_idx[c.connection_type] = len( self._bond_types) - 1 if isinstance(c.connection_type, AngleType): self._angle_types[c.connection_type] = c.connection_type self._angle_types_idx[c.connection_type] = len( self._angle_types) - 1 if isinstance(c.connection_type, DihedralType): self._dihedral_types[c.connection_type] = c.connection_type self._dihedral_types_idx[c.connection_type] = len( self._dihedral_types) - 1 if isinstance(c.connection_type, ImproperType): self._improper_types[c.connection_type] = c.connection_type self._improper_types_idx[c.connection_type] = len( self._improper_types) - 1 elif c.connection_type in self.connection_types: if isinstance(c.connection_type, BondType): c.connection_type = self._bond_types[c.connection_type] if isinstance(c.connection_type, AngleType): c.connection_type = self._angle_types[c.connection_type] if isinstance(c.connection_type, DihedralType): c.connection_type = self._dihedral_types[c.connection_type] if isinstance(c.connection_type, ImproperType): c.connection_type = self._improper_types[c.connection_type] def update_atom_types(self): """Update atom types in the topology This method checks all the sites in the topology which have an associated AtomType and if that AtomType is not in the topology's AtomTypes collection, it will add it there. See Also: --------- gmso.Topology.update_connection_types : Update the connection types based on the connection collection in the topology """ for site in self._sites: if site.atom_type is None: warnings.warn('Non-parametrized site detected {}'.format(site)) elif not isinstance(site.atom_type, AtomType): raise GMSOError( 'Non AtomType instance found in site {}'.format(site)) elif site.atom_type not in self._atom_types: site.atom_type.topology = self self._atom_types[site.atom_type] = site.atom_type self._atom_types_idx[site.atom_type] = len( self._atom_types) - 1 elif site.atom_type in self._atom_types: site.atom_type = self._atom_types[site.atom_type] self.is_typed(updated=True) def add_subtopology(self, subtop, update=True): """Add a sub-topology to this topology This methods adds a gmso.Core.SubTopology object to the topology All the sites in this sub-topology are added to the collection of current sites in this topology. Parameters ---------- subtop : gmso.SubTopology The sub-topology object to be added. update : bool, default=True See Also -------- gmso.SubTopology : A topology within a topology """ self._subtops.add(subtop) subtop.parent = self self._sites.union(subtop.sites) if update: self.update_topology() def is_typed(self, updated=False): if not updated: self.update_connection_types() self.update_atom_types() if len(self.atom_types) > 0 or len(self.connection_types) > 0: self._typed = True else: self._typed = False return self._typed def update_angle_types(self): """Uses gmso.Topology.update_connection_types to update AngleTypes in the topology. This method is an alias for gmso.Topology.update_connection_types. See Also -------- gmso.Topology.update_connection_types : Update the connection types based on the connection collection in the topology. """ self.update_connection_types() def update_bond_types(self): """Uses gmso.Topology.update_connection_types to update BondTypes in the topology. This method is an alias for gmso.Topology.update_connection_types. See Also -------- gmso.Topology.update_connection_types : Update the connection types based on the connection collection in the topology. """ self.update_connection_types() def update_dihedral_types(self): """Uses gmso.Topology.update_connection_types to update DihedralTypes in the topology. This method is an alias for gmso.Topology.update_connection_types. See Also -------- gmso.Topology.update_connection_types : Update the connection types based on the connection collection in the topology. """ self.update_connection_types() def update_improper_types(self): """Uses gmso.Topology.update_connection_types to update ImproperTypes in the topology. This method is an alias for gmso.Topology.update_connection_types. See Also -------- gmso.Topology.update_connection_types : Update the connection types based on the connection collection in the topology. """ self.update_connection_types() def update_topology(self): """Update the entire topology""" self.update_sites() self.update_atom_types() self.update_connection_types() self.is_typed(updated=True) def _get_bonds_for(self, site): """Return a list of bonds in this Topology that the site is a part of""" bonds = [] for bond in self.bonds: if site in bond.connection_members: bonds.append(bond) return bonds def _get_angles_for(self, site): """Return a list of angles in this Topology that the site is a part of""" angles = [] for angle in self.angles: if site in angle.connection_members: angles.append(angle) return angles def _get_dihedrals_for(self, site): """Return a list of dihedrals in this Topology that the site is a part of""" dihedrals = [] for dihedral in self.dihedrals: if site in dihedral.connection_members: dihedrals.append(dihedral) return dihedrals def get_index(self, member): """Get index of a member in the topology Parameters ---------- member : gmso Topology objects The member to for which to return index for. `member` can be of type gmso.Site, gmso.Bond, gmso.Angle, gmso.Dihedral, gmso.Improper, gmso.AtomType, gmso.BondType, gmso.AngleType, gmso.DihedralType or gmso.ImproperType. Returns ------- int The index of the member in the topology's collection objects """ refs = { Atom: self._sites, Bond: self._bonds, Angle: self._angles, Dihedral: self._dihedrals, Improper: self._impropers, AtomType: self._atom_types_idx, BondType: self._bond_types_idx, AngleType: self._angle_types_idx, DihedralType: self._dihedral_types_idx, ImproperType: self._improper_types_idx } member_type = type(member) if member_type not in refs.keys(): raise TypeError( f'Cannot index member of type {member_type.__name__}') try: index = refs[member_type].index(member) except AttributeError: index = refs[member_type][member] return index def _reindex_connection_types(self, ref): if ref not in self._index_refs: raise GMSOError( f'cannot reindex {ref}. It should be one of ' f'{ANGLE_TYPE_DICT}, {BOND_TYPE_DICT}, ' f'{ANGLE_TYPE_DICT}, {DIHEDRAL_TYPE_DICT}, {IMPROPER_TYPE_DICT}' ) for i, ref_member in enumerate(self._set_refs[ref].keys()): self._index_refs[ref][ref_member] = i def __repr__(self): return f"<Topology {self.name}, {self.n_sites} sites,\n " \ f"{self.n_connections} connections,\n " \ f"{len(self.connection_types)} potentials,\n " \ f"id: {id(self)}>" def __str__(self): return f"<Topology {self.name}, {self.n_sites} sites, id: {id(self)}>"
class SubTopology(object): """A sub-topology i.e. topology within a topology This class provides a hierarchical topological representation to the topology as it imperative with many chemical structures to have separation of layers/ boundaries. A sub-topology can be added to a gmso.Topology object which will be the parent of the sub-topology. Parameters ---------- name : str, optional, default='Sub-Topology' Name of the sub-topology parent : gmso.Topology, optional, default=None The parent topology of this SubTopology Attributes ---------- sites : IndexedSet of gmso.Site objects Collection of sites within this sub-topology n_sites : int Number of sites withing this sub-topology """ def __init__(self, name="Sub-Topology", parent=None): if name is not None: self._name = str(name) if parent is None: self._parent = parent else: self._parent = _validate_parent(parent) self._sites = IndexedSet() @property def name(self): return self._name @name.setter def name(self, name): self._name = str(name) @property def sites(self): return self._sites @property def n_sites(self): return len(self.sites) @property def parent(self): return self._parent @parent.setter def parent(self, parent): warnings.warn( 'Setting a parent is potentially dangerous. Consider using ' 'Topology.add_subtopology instead') if parent is None: raise NotImplementedError( 'Setting parents to None is not yet supported') self._parent = _validate_parent(parent) def add_site(self, site): """Add a site to this sub-topology This method adds a site to the sub-topology. If the sub-topology has a parent, the site will also be added to the parent topology. Parameters ---------- site : gmso.Site The site to be added to this sub-topology Raises ------ TypeError If the parameter site is not of type topology.Site """ site = _validate_site_addability(site) if site in self.sites: warnings.warn("Redundantly adding Site {}".format(site)) self._sites.add(site) if self.parent: self.parent.add_site(site) def __repr__(self): descr = list('<') descr.append(self.name + ' ') descr.append('{:d} sites, '.format(self.n_sites)) descr.append('id: {}>'.format(id(self))) return ''.join(descr)
class SubTopology(object): """A sub-topology i.e. topology within a topology This class provides a hierarchical topological representation to the topology as it imperative with many chemical structures to have separation of layers/ boundaries. A sub-topology can be added to a gmso.Topology object which will be the parent of the sub-topology. Parameters ---------- name : str, optional, default='Sub-Topology' Name of the sub-topology parent : gmso.Topology, optional, default=None The parent topology of this SubTopology Attributes ---------- sites : IndexedSet of gmso.Site objects Collection of sites within this sub-topology n_sites : int Number of sites withing this sub-topology """ def __init__(self, name="Sub-Topology", parent=None): if name is not None: self._name = str(name) if parent is None: self._parent = parent else: self._parent = _validate_parent(parent) self._sites = IndexedSet() @property def name(self): return self._name @name.setter def name(self, name): self._name = str(name) @property def sites(self): return self._sites @property def n_sites(self): return len(self.sites) @property def parent(self): return self._parent @parent.setter def parent(self, parent): warnings.warn( 'Setting a parent is potentially dangerous. Consider using ' 'Topology.add_subtopology instead') if parent is None: raise NotImplementedError( 'Setting parents to None is not yet supported') self._parent = _validate_parent(parent) def add_site(self, site, update_types=True): """Add a site to this sub-topology This method adds a site to the sub-topology. If the sub-topology has a parent, the site will also be added to the parent topology. If the update_types parameter is set to true (default behavior), this method will also check if there is an gmso.AtomType associated with the site and it to the sub-topology's AtomTypes collection. Parameters ---------- site : gmso.Atom The site to be added to this sub-topology update_types : (bool), default=True If true, add this site's atom type to the sub-topology's set of AtomTypes Raises ------ TypeError If the parameter site is not of type topology.Site """ site = _validate_site_addability(site) if site in self.sites: warnings.warn("Redundantly adding Site {}".format(site)) self._sites.add(site) if self.parent: self.parent.add_site(site, update_types=update_types) def __repr__(self): return f"<SubTopology {self.name},\n " \ f"{self.n_sites} sites,\n " \ f"id: {id(self)}>" def __str__(self): return f"<SubTopology {self.name}, " \ f"{self.n_sites} sites, " \ f"id: {id(self)}>"
class Site(object): """An interaction site object in the topology hierarchy. Site is the object that represents any general interaction site in a molecular simulation. Sites have been designed to be as general as possible, making no assumptions about representing atoms or beads, or having mass or charge. That is, a Site can represent an atom in an atomistic system, a bead in a coarse-grained system, and much more. Parameters ---------- name : str, optional, default='Site' Name of the site position : unyt array or numpy array or list, optional, default=None The position of the site in Cartesian space. If a unyt array is not passed, units are assumed to be in 'nm'. charge : unyt quantity or float, optional, default=None The charge of the site. Unyt quantities are converted to units of elementary charge, float values are assumed to be in units of elementary charge. If no value is passed, site attempts to grab a charge from site.atom_type. mass : unyt quantity or float, optional, default=None The mass of the site. Unyt quantities are converted to units of g/mol, float values are assumed to be in units of g/mol. If no value is passed, site attempts to grab a mass from site.atom_type. element : 'Element' object, optional, default=None The element of the site represented by the `Element` object. See `element.py` for more information. atom_type : 'AtomType' object, optional, default=None The atom type of the site containing functional forms, interaction parameters, and other properties such as mass and charge. See `atom_type.py` for more information. Attributes ---------- connections : IndexedSet Set that contains connection information for the site n_connections : int Number of connections for the site """ def __init__(self, name='Site', position=None, charge=None, mass=None, element=None, atom_type=None): if name is not None: self.name = str(name) if name is None: # Guardrail against checking deliberate None self.name = 'Site' if position is None: self.position = u.nm * np.zeros(3) else: self.position = _validate_position(position) self._element = element self._atom_type = _validate_atom_type(atom_type) self._charge = _validate_charge(charge) self._mass = _validate_mass(mass) self._connections = IndexedSet() def add_connection(self, connection): connection = _validate_connection(self, connection) if connection: self._connections.add(connection) @property def element(self): return self._element @element.setter def element(self, element): self._element = element @property def connections(self): return self._connections @property def n_connections(self): return len(self._connections) @property def charge(self): if self._charge is not None: return self._charge elif self.atom_type is not None: return self.atom_type.charge else: return None @charge.setter def charge(self, charge): self._charge = _validate_charge(charge) @property def mass(self): if self._mass is not None: return self._mass elif self.atom_type is not None: return self.atom_type.mass else: return None @mass.setter def mass(self, mass): self._mass = _validate_mass(mass) @property def atom_type(self): return self._atom_type @atom_type.setter def atom_type(self, val): val = _validate_atom_type(val) self._atom_type = val def __repr__(self): return "<Site {}, id {}>".format(self.name, id(self))
class BuffBot: #configure buff bot def __init__(self, logfile, buffs, regex): print(regex) self.logfile = logfile self.bufflist = buffs self.rx = re.compile(regex) self.windowName = "EverQuest" self.buffqueue = IndexedSet() #display configuration def displayconfig(self): print("Bot Configuration") print("Reading Log file: ", self.logfile) print("Current Buff list") for key in self.bufflist: print("buff trigger:", key, "\tgem slot: ", self.bufflist[key][0], '\tcast time:', self.bufflist[key][1]) # Log matches Process def MatchFound(self, name, request): try: print(str(dt.datetime.now())[0:19], name, "requested", request) self.ProcessRequest(name, request, self.bufflist[request]) except KeyError: #ignore non-buff requests. print( str(dt.datetime.now())[0:19], "Unknown request from", name, "ignored:", request) pass # build the macro for this buff and execute it. def ProcessRequest(self, name, request, spellinfo): gem = spellinfo[0] casttime = spellinfo[1] castline = [ "/tar " + name, "/cast " + str(gem), "/cast " + str(gem), "/cast " + str(gem) ] print( str(dt.datetime.now())[0:19], 'casting', request, 'on', name, 'from spell slot', gem, 'with cast time ', casttime, 'seconds.') for lines in castline: self.enterline(lines) time.sleep(0.5) time.sleep(float(casttime)) self.enterline("/sit") print(str(dt.datetime.now())[0:19], "Request completed.") # copy each line to clipboard and paste into EQ. def enterline(self, cmd): cb.copy(cmd) kb.press("enter", _pause=False) kb.keyDown("ctrl", _pause=False) kb.press("v", _pause=False) kb.keyUp("ctrl", _pause=False) kb.press("enter", _pause=False) # main thread, wait for a valid buff request then add it to the queue. def listen(self): with open(self.logfile) as f: f.seek(0, os.SEEK_END) print(str(dt.datetime.now())[0:19], "Listening for buff requests") time.sleep(2) while True: txt = '' line = f.readline() if line: s = line.split() # get rid of time stamps txt = " ".join(s[5:]) txt = txt.lower() m = self.rx.match(txt) if m: self.addQueue(m.group(1), m.group(2)) time.sleep(0.1) # If queue isn't empty get to work. def monitorQueue(self): print(str(dt.datetime.now())[0:19], "Monitoring Queue") while (True): while (len(self.buffqueue)): print( str(dt.datetime.now())[0:19], "Processing Queue: ", str(len(self.buffqueue)) + " items left.") rq = self.buffqueue.pop(0) self.MatchFound(rq[0], rq[1]) time.sleep(0.25) # Add an request to queue. def addQueue(self, name, req): print( str(dt.datetime.now())[0:19], "Adding " + name + " to queue for: " + req) request = (name, req) self.buffqueue.add(request) print(self.buffqueue)
def get_traceroute_path(atlas_src, result): global PORT global PORT2 global PREFIX hop_ips = [] hop_avg_rtts = [] # Get the IPs from the traceroute success = False num_hops = len(result) for hop_num, hop_result in enumerate(result): per_hop_results = hop_result["result"] no_result = True ips = [] rtts = [] for per_hop_result in per_hop_results: rtt_exists = per_hop_result.get("rtt", None) from_exists = per_hop_result.get("from", None) if rtt_exists and from_exists: no_result = False rtts.append(per_hop_result["rtt"]) ips.append(per_hop_result["from"]) if (hop_num + 1) == num_hops: success = True if no_result: continue if check_equal_list(ips): hop_ips.append(ips[0]) else: log_message( "Different IP for at least one IP for hop {}".format(hop_num)) hop_ips.append(ips[0]) avg_rtt = sum(rtts) / len(rtts) hop_avg_rtts.append(avg_rtt) log_message("Found {} total valid (non-empty) hops".format(len(hop_ips))) # Get the ASNs for each IP from Team Cymru hop_asns = IndexedSet() hop_rtts = dict() for hop_ip, hop_rtt in zip(hop_ips, hop_avg_rtts): hop_asn = get_asn_from_ip(hop_ip) if hop_asn: hop_asns.add(hop_asn) hop_rtts[hop_asn] = hop_rtt if len(hop_asns) != 0: # Deal with the first AS not resolving in some cases corrected_path = False revised_hop_asns = IndexedSet() revised_hop_rtts = dict() if hop_asns[0] != atlas_src.asn: revised_hop_asns.add(atlas_src.asn) revised_hop_rtts[atlas_src.asn] = 0.0 for hop_asn in hop_asns: revised_hop_asns.add(hop_asn) revised_hop_rtts[hop_asn] = hop_rtts[hop_asn] corrected_path = True if corrected_path: hop_asns = revised_hop_asns hop_rtts = revised_hop_rtts log_message("Found path: {}".format("->".join( list([str(x) for x in hop_asns])))) else: success = False return hop_asns, num_hops, list(hop_rtts.values()), success
class GNodeSocket(QGraphicsWidget): edge_started = pyqtSignal(GEdge) edge_released = pyqtSignal(GEdge) connection_changed = pyqtSignal(object, object) # Socket, Edge or None position_changed = pyqtSignal(QPointF) INPUT = SocketType.INPUT OUTPUT = SocketType.OUTPUT def __init__(self, parent_node: 'GShaderNode', socket: NodeSocket): super().__init__(parent=parent_node) self._socket = socket self._socket.set_container(self) self._parent_g_node = parent_node self._connected_g_edges = IndexedSet() # Define socket properties self._circle_connected_brush = QColor( 255, 130, 0, 255) if self._socket.type() == NodeSocket.INPUT else QColor( 130, 255, 0, 255) self._circle_disconnected_brush = QColor( 102, 50, 0, 255) if self._socket.type() == NodeSocket.INPUT else QColor( 50, 102, 0, 255) self._circle_hover_brush = QColor( 170, 130, 0, 255) if self._socket.type() == NodeSocket.INPUT else QColor( 130, 170, 0, 255) self._border_connected_brush = QPen(QColor(255, 255, 255, 255)) self._border_disconnected_brush = QPen(Qt.black) self._circle_brush = self._circle_disconnected_brush self._border_brush = self._border_disconnected_brush self._bbox = QRectF(0, 0, 10, 10) self._moving_edge = False self._current_edge = None self._layout = QGraphicsLinearLayout(Qt.Horizontal) self._init_socket() def _init_socket(self): self.setLayout(self._layout) self.setFlag(QGraphicsItem.ItemIsMovable, False) self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, True) self.setAcceptHoverEvents(True) def type(self) -> SocketType: return self._socket.type() def value(self): return self._socket.value() def set_value(self, value: typing.Any): self._socket.set_value(value) def save_value(self): """Saves the value of this socket internally. This value can be reassigned by calling 'restore_value()'.""" self._socket.save_value() def restore_value(self): """Restores the value of this socket to the last saved value.""" self._socket.restore_value() def parent_node(self) -> 'GShaderNode': return self._parent_g_node def get_size(self): """Returns a tuple with the width,height of this socket.""" return self._bbox.right(), self._bbox.bottom() def get_backend_socket(self) -> NodeSocket: return self._socket def get_connected_nodes(self) -> typing.List['GShaderNode']: return [n.get_container() for n in self._socket.get_connected_nodes()] def get_connected_sockets(self) -> typing.List['GNodeSocket']: return [ s.get_container() for s in self._socket.get_connected_sockets() ] def set_index(self, index: int): self._socket.set_index(index) def get_index(self) -> int: return self._socket.get_index() def is_connected(self) -> bool: return self._socket.is_connected() def boundingRect(self): return self._bbox def paint(self, painter: QPainter, option, widget=None): painter.setPen(self._border_brush) painter.setBrush(self._circle_brush) painter.drawEllipse(self._bbox) def get_scene_pos(self) -> QPointF: """Gets the center position of this socket in scene coordinates.""" pos = self.scenePos() pos.setX(pos.x() + self._bbox.right() / 2) pos.setY(pos.y() + self._bbox.bottom() / 2) return pos def connect_to(self, socket: 'GNodeSocket') -> GEdge: """ Connects this GNodeSocket to another GNodeSocket. :param other_socket: Other GNodeSocket to connect this socket to. :return: the GEdge that was created between the sockets, or the old GEdge if there already exists a connection. """ edge = self._socket.connect_to(socket.get_backend_socket()) # Only emit change event for input sockets, as nothing really changed for the output socket (at least not for the node as a whole) if self.type() == SocketType.INPUT: self.connection_changed.emit(self, edge) elif socket.type() == SocketType.INPUT: socket.connection_changed.emit(socket, edge) return GEdge.from_edge(edge) def get_connected_edges(self) -> IndexedSet: return self._connected_g_edges def add_connecting_edge(self, edge: GEdge): self._connected_g_edges.add(edge) def remove_connected_edge(self, gedge: GEdge): assert gedge in self._connected_g_edges self._connected_g_edges.remove(gedge) def label(self) -> str: return self._socket.label() def __eq__(self, other): if isinstance(other, GNodeSocket): return self._socket.__eq__(other.get_backend_socket()) return False def __hash__(self): return self._socket.__hash__() def __str__(self): return self._socket.__str__() # -------- Event Handling --------- # ---------------------------------- def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent): # self._circle_brush = self._circle_hover_brush self._border_brush = self._border_connected_brush self.update() event.accept() def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent): # self._circle_brush = self._circle_connected_brush if self.connected else self._circle_disconnected_brush if not self.is_connected(): self._border_brush = self._border_disconnected_brush self.update() event.accept() def mousePressEvent(self, event: QGraphicsSceneMouseEvent): # Needs to be reimplemented to be able to catch mouseMoveEvent, but it does not need to do anything event.accept() def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent): if event.buttons() == Qt.LeftButton: if not self._moving_edge: if self.type() == self.INPUT: edge = GEdge(destination=self) else: edge = GEdge(source=self) self._current_edge = edge self._moving_edge = True self.edge_started.emit(edge) else: self._current_edge.set_tip_pos(event.scenePos()) event.accept() else: event.ignore() def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent): if self._current_edge: # Prevent release without dragging self.edge_released.emit(self._current_edge) self._current_edge = None self._moving_edge = False def itemChange(self, change, value): if change == self.ItemScenePositionHasChanged: for edge in self._connected_g_edges: edge.update_edge() return super().itemChange(change, value)
class NodeSocket(GraphElement): INPUT: SocketType = SocketType.INPUT OUTPUT: SocketType = SocketType.OUTPUT def __init__(self, parent_node: 'Node', socket_type: SocketType, dtype: DataType = None, label: str = "", container=None): super().__init__(container) assert isinstance(socket_type, SocketType), "socket_type must be of enum type SocketType!" self._parent_g_node = parent_node self._type = socket_type self._dtype = dtype self._label = label self._id = uuid.uuid4() self._index = -1 self._value = None self._saved_value = None self._connected = False self._connected_sockets = IndexedSet() self._connected_edges = IndexedSet() def connect_to(self, other_socket: 'NodeSocket') -> Edge: """ Connects this NodeSocket to another NodeSocket. :param other_socket: Other NodeSocket to connect this socket to. :return: the Edge that was created between the sockets, or the old Edge if there already exists a connection. """ assert self.id() != other_socket.id(), "Can not connect socket to itself!" assert self.type() != other_socket.type(), "Can not connect sockets of the same type!" # Check if this socket is already connected to 'socket' if other_socket in self._connected_sockets: edge = self.find_connecting_edge(other_socket) return edge # Create a directed edge if self.type() == NodeSocket.OUTPUT: edge = Edge(source=self, destination=other_socket) else: edge = Edge(source=other_socket, destination=self) self._connected_edges.add(edge) other_socket._connected_edges.add(edge) self._connected_sockets.add(other_socket) other_socket._connected_sockets.add(self) self._connected = True other_socket._connected = True return edge def disconnect_all(self): """Disconnects this NodeSocket from all other connected sockets.""" for other in self._connected_sockets: self.disconnect_from(other) def disconnect_from(self, other_socket: 'NodeSocket'): """ Disconnects this NodeSocket from another NodeSocket. :param other_socket: Another NodeSocket to disconnect from. """ assert other_socket in self._connected_sockets, "Can not disconnect from a socket that is not connected!" edge = self.find_connecting_edge(other_socket) if edge is not None: self._connected_sockets.remove(other_socket) other_socket._connected_sockets.remove(self) self._connected_edges.remove(edge) other_socket._connected_edges.remove(edge) del edge self._connected = False other_socket._connected = False else: raise RuntimeError("Sockets are indicated as connected but could not find their connecting Edge. The Node Graph is corrupt!") def get_parent_node(self) -> 'Node': return self._parent_g_node def get_connected_nodes(self) -> typing.List['Node']: """ Returns each node that is connected to this NodeSocket. :return: A list of Nodes connected to this NodeSocket. """ nodes = [] for socket in self._connected_sockets: nodes.append(socket.get_parent_node()) return nodes def get_connected_sockets(self) -> list: """ Returns each NodeSocket that this NodeSocket is connected to. :return: A list of NodeSockets connected to this NodeSocket. """ return list(self._connected_sockets) def set_index(self, index): """Set the index of this socket. This value is used to figure out what input/output this socket corresponds to.""" self._index = index def get_index(self) -> int: """Gets the index of this socket.""" return self._index def set_value(self, value: typing.Any): self._value = value def save_value(self): """Saves the value of this socket internally. This value can be reassigned by calling 'restore_value()'.""" self._saved_value = self._value.detach().clone() def restore_value(self): """Restores the value of this socket to the last saved value.""" self._value = self._saved_value.detach().clone() def value(self) -> typing.Any: return self._value def label(self) -> str: return self._label def is_connected(self) -> bool: """Returns a boolean indicating whether this socket is connected to any other sockets with an Edge.""" return self._connected def type(self) -> SocketType: """Returns the type of this NodeSocket indicating whether it accepts input or returns output.""" return self._type def dtype(self) -> DataType: return self._dtype def find_connecting_edge(self, other_socket: 'NodeSocket') -> typing.Union[None, Edge]: for edge in self._connected_edges: if edge.connects(self, other_socket): return edge return None
class Topology(object): """A topology. A topology represents a chemical structure wherein lie the collection of sites which together form a chemical structure containing connections (gmso.Bond, gmso.Angle and gmso.Dihedral (along with their associated types). A topology is the fundamental data structure in GMSO, from which we can gather various information about the chemical structure and apply a forcefield before converting the structure into a format familiar to various simulation engines. Parameters ---------- name : str, optional, default='Topology' A name for the Topology. box : gmso.Box, optional, default=None A gmso.Box object bounding the topology Attributes ---------- typed : bool True if the topology is typed combining_rule : str, ['lorentz', 'geometric'] The combining rule for the topology, can be either 'lorentz' or 'geometric' n_sites : int Number of sites in the topology n_connections : int Number of connections in the topology (Bonds, Angles, Dihedrals) n_bonds : int Number of bonds in the topology n_angles: int Number of angles in the topology n_dihedrals : int Number of dihedrals in the topology n_subtops : int Number of subtopolgies in the topology connections : tuple of gmso.Connection objects A collection of bonds, angles and dihedrals in the topology bonds : tuple of gmso.Bond objects A collection of bonds in the topology dihedrals : tuple of gmso.Dihedral objects A collection of dihedrals in the topology connection_types : tuple of gmso.Potential objects A collection of BondTypes, AngleTypes and DihedralTypes in the topology atom_types : tuple of gmso.AtomType objects A collection of AtomTypes in the topology bond_types : tuple of gmso.BondType objects A collection of BondTypes in the topology angle_types : tuple of gmso.AngleType objects A collection go AngleTypes in the topology dihedral_types : tuple of gmso.DihedralType objects A collection of DihedralTypes in the topology atom_type_expressions : list of gmso.AtomType.expression objects A collection of all the expressions for the AtomTypes in topology connection_type_expressions : list of gmso.Potential.expression objects A collection of all the expressions for the Potential objects in the topology that represent a connection type bond_type_expressions : list of gmso.BondType.expression objects A collection of all the expressions for the BondTypes in topology angle_type_expressions : list of gmso.AngleType.expression objects A collection of all the expressions for the AngleTypes in topology dihedral_type_expressions : list of gmso.DihedralType.expression objects A collection of all the expression for the DihedralTypes in the topology See Also -------- gmso.SubTopology : A topology within a topology """ def __init__(self, name="Topology", box=None): if name is not None: self._name = name else: self._name = "Topology" self._box = box self._sites = IndexedSet() self._typed = False self._connections = IndexedSet() self._bonds = IndexedSet() self._angles = IndexedSet() self._dihedrals = IndexedSet() self._subtops = IndexedSet() self._atom_types = {} self._connection_types = {} self._bond_types = {} self._angle_types = {} self._dihedral_types = {} self._combining_rule = 'lorentz' self._set_refs = { ATOM_TYPE_DICT: self._atom_types, BOND_TYPE_DICT: self._bond_types, ANGLE_TYPE_DICT: self._angle_types, DIHEDRAL_TYPE_DICT: self._dihedral_types, } @property def name(self): return self._name @name.setter def name(self, name): self._name = str(name) if name else None @property def box(self): return self._box @box.setter def box(self, box): self._box = box @property def typed(self): return self._typed @typed.setter def typed(self, typed): self._typed = typed @property def combining_rule(self): return self._combining_rule @combining_rule.setter def combining_rule(self, rule): if rule not in ['lorentz', 'geometric']: raise GMSOError('Combining rule must be `lorentz` or `geometric`') self._combining_rule = rule @property def positions(self): xyz = np.empty(shape=(self.n_sites, 3)) * u.nm for i, site in enumerate(self._sites): xyz[i, :] = site.position return xyz @property def n_sites(self): return len(self.sites) @property def n_connections(self): return len(self.connections) @property def n_bonds(self): return len(self.bonds) @property def n_angles(self): return len(self.angles) @property def n_dihedrals(self): return len(self.dihedrals) @property def subtops(self): return self._subtops @property def n_subtops(self): return len(self._subtops) @property def sites(self): return tuple(self._sites) @property def connections(self): return tuple(self._connections) @property def bonds(self): return tuple(self._bonds) @property def angles(self): return tuple(self._angles) @property def dihedrals(self): return tuple(self._dihedrals) @property def atom_types(self): return tuple(self._atom_types.values()) @property def connection_types(self): return tuple(self._connection_types.values()) @property def bond_types(self): return tuple(self._bond_types.values()) @property def angle_types(self): return tuple(self._angle_types.values()) @property def dihedral_types(self): return tuple(self._dihedral_types.values()) @property def atom_type_expressions(self): return list(set([atype.expression for atype in self.atom_types])) @property def connection_type_expressions(self): return list(set([contype.expression for contype in self.connection_types])) @property def bond_type_expressions(self): return list(set([btype.expression for btype in self.bond_types])) @property def angle_type_expressions(self): return list(set([atype.expression for atype in self.angle_types])) @property def dihedral_type_expressions(self): return list(set([atype.expression for atype in self.dihedral_types])) def add_site(self, site, update_types=True): """Add a site to the topology This method will add a site to the existing topology, since sites are stored in an indexed set, adding redundant site will have no effect. If the update_types parameter is set to true (default behavior), this method will also check if there is an gmso.AtomType associated with the site and it to the topology's AtomTypes collection. Parameters ----------- site : gmso.core.Site Site to be added to this topology update_types : (bool), default=True If true, add this site's atom type to the topology's set of AtomTypes """ self._sites.add(site) if update_types and site.atom_type: site.atom_type.topology = self site.atom_type = self._atom_types.get(site.atom_type, site.atom_type) self._atom_types[site.atom_type] = site.atom_type self.is_typed(updated=False) def update_sites(self): """Update the sites of the topology. This method will update the sites in the topology based on the connection members, For example- if you add a bond to a topology, without adding the constituent sites, this method can be called to add the sites which are the connection members of the bond as shown below. >>> import gmso >>> site1 = gmso.Site(name='MySite1') >>> site2 = gmso.Site(name='MySite2') >>> bond1 = gmso.Bond(name='site1-site2', connection_members=[site1, site2]) >>> this_topology = gmso.Topology('TwoSitesTopology') >>> this_topology.add_connection(bond1) >>> this_topology.update_sites() See Also -------- gmso.Topology.update_connections : Update the connections in the topology to reflect any added sites connections gmso.Topology.add_site : Add a site to the topology. gmso.Topology.add_connection : Add a Bond, an Angle or a Dihedral to the topology. gmso.Topology.update_topology : Update the entire topology. """ for connection in self.connections: for member in connection.connection_members: if member not in self._sites: self.add_site(member) def add_connection(self, connection, update_types=True): """Add a gmso.Connection object to the topology. This method will add a gmso.Connection object to the topology, it can be used to generically include any Connection object i.e. Bond or Angle or Dihedral to the topology. According to the type of object added, the equivalent collection in the topology is updated. For example- If you add a Bond, this method will update topology.connections and topology.bonds object. Additionally, if update_types is True (default behavior), it will also update any Potential objects associated with the connection. Parameters ---------- connection : one of gmso.Connection, gmso.Bond, gmso.Angle or gmso.Dihedral object update_types : bool, default=True If True also add any Potential object associated with connection to the topology. """ for conn_member in connection.connection_members: if conn_member not in self.sites: self.add_site(conn_member) self._connections.add(connection) if isinstance(connection, Bond): self._bonds.add(connection) if isinstance(connection, Angle): self._angles.add(connection) if isinstance(connection, Dihedral): self._dihedrals.add(connection) if update_types: self.update_connection_types() def update_connections(self, update_types=False): """Update the topology's connections(bonds, angles, dihedrals) from its sites. This method takes all the sites in the current topology and if any connection (Bond, Angle, Dihedral) is present in the site but not in the topology's connection collection, this method will add to that collection. If update_types is True (default behavior is False), this method will also add the Potential objects(AtomType, BondType, AngleType, DihedralType) to the topology's respective collection. Parameters ---------- update_types : bool, default=False See Also -------- gmso.Topology.update_sites : Update the sites in the topology to reflect any added connection's sites gmso.Topology.add_connection : Add a Bond, an Angle or a Dihedral to the topology. gmso.Topology.add_site : Add a site to the topology. gmso.Topology.update_connection_types : Update the connection types based on the connection collection in the topology. gmso.Topology.update_topology : Update the entire topology. """ for site in self.sites: for conn in site.connections: if conn not in self.connections: self.add_connection(conn, update_types=False) if update_types: self.update_connection_types() self.is_typed() def update_bonds(self, update_types=False): """Uses gmso.Topology.update_connections to update bonds in the topology. This method is an alias for gmso.Topology.update_connections. See Also -------- gmso.Topology.update_connections : Update all the Bonds, Angles and Dihedrals in the topology. """ self.update_connections(update_types) def update_angles(self, update_types=False): """Uses gmso.Topology.update_connections to update angles in the topology. This method is an alias for gmso.Topology.update_connections. See Also -------- gmso.Topology.update_connections : Update all the Bonds, Angles and Dihedrals in the topology. """ self.update_connections(update_types) def update_dihedrals(self, update_types=False): """Uses gmso.Topology.update_connections to update dihedrals in the topology. This method is an alias for gmso.Topology.update_connections. See Also -------- gmso.Topology.update_connections : Update all the Bonds, Angles and Dihedrals in the topology. """ self.update_connections(update_types) def update_connection_types(self): """Update the connection types based on the connection collection in the topology. This method looks into all the connection objects (Bonds, Angles, Dihedrals) to check if any Potential object (BondType, AngleType, DihedralType) is not in the topology's respective collection and will add those objects there. See Also -------- gmso.Topology.update_atom_types : Update atom types in the topology. """ for c in self.connections: if c.connection_type is None: warnings.warn('Non-parametrized Connection {} detected'.format(c)) elif not isinstance(c.connection_type, Potential): raise GMSOError('Non-Potential {} found' 'in Connection {}'.format(c.connection_type, c)) elif c.connection_type not in self._connection_types: c.connection_type.topology = self self._connection_types[c.connection_type] = c.connection_type if isinstance(c.connection_type, BondType): self._bond_types[c.connection_type] = c.connection_type if isinstance(c.connection_type, AngleType): self._angle_types[c.connection_type] = c.connection_type if isinstance(c.connection_type, DihedralType): self._dihedral_types[c.connection_type] = c.connection_type elif c.connection_type in self.connection_types: if isinstance(c.connection_type, BondType): c.connection_type = self._bond_types[c.connection_type] if isinstance(c.connection_type, AngleType): c.connection_type = self._angle_types[c.connection_type] if isinstance(c.connection_type, DihedralType): c.connection_type = self._dihedral_types[c.connection_type] def update_atom_types(self): """Update atom types in the topology This method checks all the sites in the topology which have an associated AtomType and if that AtomType is not in the topology's AtomTypes collection, it will add it there. See Also: --------- gmso.Topology.update_connection_types : Update the connection types based on the connection collection in the topology """ for site in self._sites: if site.atom_type is None: warnings.warn('Non-parametrized site detected {}'.format(site)) elif not isinstance(site.atom_type, AtomType): raise GMSOError('Non AtomType instance found in site {}'.format(site)) elif site.atom_type not in self._atom_types: site.atom_type.topology = self self._atom_types[site.atom_type] = site.atom_type elif site.atom_type in self._atom_types: site.atom_type = self._atom_types[site.atom_type] self.is_typed(updated=True) def add_subtopology(self, subtop): """Add a sub-topology to this topology This methods adds a gmso.Core.SubTopology object to the topology All the sites in this sub-topology are added to the collection of current sites in this topology. Parameters ---------- subtop : gmso.SubTopology The sub-topology object to be added. See Also -------- gmso.SubTopology : A topology within a topology """ self._subtops.add(subtop) subtop.parent = self self._sites.union(subtop.sites) def is_typed(self, updated=False): if not updated: self.update_connection_types() self.update_atom_types() if len(self.atom_types) > 0 or len(self.connection_types) > 0: self._typed = True else: self._typed = False return self._typed def update_angle_types(self): """Uses gmso.Topology.update_connection_types to update AngleTypes in the topology. This method is an alias for gmso.Topology.update_connection_types. See Also -------- gmso.Topology.update_connection_types : Update the connection types based on the connection collection in the topology. """ self.update_connection_types() def update_bond_types(self): """Uses gmso.Topology.update_connection_types to update BondTypes in the topology. This method is an alias for gmso.Topology.update_connection_types. See Also -------- gmso.Topology.update_connection_types : Update the connection types based on the connection collection in the topology. """ self.update_connection_types() def update_dihedral_types(self): """Uses gmso.Topology.update_connection_types to update DihedralTypes in the topology. This method is an alias for gmso.Topology.update_connection_types. See Also -------- gmso.Topology.update_connection_types : Update the connection types based on the connection collection in the topology. """ self.update_connection_types() def update_topology(self): """Update the entire topology""" self.update_sites() self.update_connections() self.update_atom_types() self.update_connection_types() self.is_typed(updated=True) def __repr__(self): descr = list('<') descr.append(self.name + ' ') descr.append('{:d} sites, '.format(self.n_sites)) descr.append('{:d} connections, '.format(self.n_connections)) descr.append('id: {}>'.format(id(self))) return ''.join(descr)
class GLSLCode: def __init__(self, code: typing.List[str], filename: str, primary_function: str): self.code = code self._generated_code = [] self._node_num = -1 self._reset = False self.filename = filename self.shader_name = self.filename.split("/")[-1].split(".")[0] self.functions = [] self.imports = IndexedSet() self.connected_code = IndexedSet() self.parse_time = 0 self._primary_func_name = primary_function self.primary_function: GLSLFunction = None self._uniforms = [] self._arguments_to_add_to_primary = [] # Track line numbers of important parts of code self._functions_start_line = -1 self._primary_func_start_line = -1 self._parse() self._set_primary_function() self._add_args_to_primary() def __str__(self): return "GLSLCode of {}".format(self.filename) def _parse(self): start_time = time.time() code_iterator = iter(self.code) for line_i, line in enumerate(code_iterator): if "/*" in line: while "*/" not in line: line = next(code_iterator) # Consume lines line_i += 1 # --- Import Statement --- match = REG_GLSL_IMPORT.match(line) if match: needed_file = match.group(1) self.imports.add(needed_file) continue # --- in or uniform --- match = REG_GLSL_IN_OR_UNIFORM.match(line) if match: arg = match.groupdict()[IN_UNIFORM_ARGUMENT] self._arguments_to_add_to_primary.append(arg) continue # --- Function --- match = REG_FUNCTION.match(line) if match: if not self._functions_start_line > 0: self._functions_start_line = line_i # Track which line the function definitions start on if not self._primary_func_start_line > 0: func_name = match.groupdict()[FUNC_FUNCTION_NAME] if func_name == self._primary_func_name: self._primary_func_start_line = line_i func_code = [line] opening_curly_brace = 0 closing_curly_brace = 0 # Opening brace of primary_function might not be on the same line as the primary_function title! while not ("{" in line): line = next(code_iterator) func_code.append(line) opening_curly_brace += 1 while opening_curly_brace > closing_curly_brace: line = next(code_iterator) line_i += 1 func_code.append(line) if "}" in line: closing_curly_brace += 1 if "{" in line: opening_curly_brace += 1 self.functions.append(GLSLFunction(func_code, self)) self.parse_time = time.time() - start_time def _add_args_to_primary(self): for arg_code in self._arguments_to_add_to_primary: self.primary_function.arguments.append( GLSLArgument(arg_code, self.primary_function)) def _set_primary_function(self): name = self._primary_func_name for f in self.functions: if f.function_name == name: self.primary_function = f return raise KeyError("No primary_function with title {} in {}".format( name, self.filename)) def reset(self, node_num: int): """ Resets all connections for this code. :param node_num: The number of the node containing the code object. """ self._node_num = node_num self._generated_code = [] self.connected_code = set() self._uniforms = [] for func in self.functions: func.reset() self._reset = True def get_node_num(self) -> int: return self._node_num def get_primary_function(self) -> 'GLSLFunction': return self.primary_function def get_modified_arg_name(self, arg: str, number: int = None) -> str: arg = self.primary_function.get_argument(arg) if arg: return arg.get_modified_name(number) return None def connect(self, argument: str, other_code: 'GLSLCode', out_arg: str = None): """ Connect the argument 'argument' of this other_code's primary_function to the output of the primary_function of the other_code in 'other_code'. If the primary function to be connected is using inout variables, specify the 'out_arg' as well. :param argument: The title of the argument that is to be replaced by a call to another primary_function. :param other_code: The other_code that will be called and whose return value will replace the argument :param out_arg: The name of the 'out' variable in the primary function. """ self.connected_code.add(other_code) for sub_code in other_code.connected_code: self.connected_code.add(sub_code) self.primary_function.connect(argument, other_code, out_arg) def get_uniforms(self) -> typing.List[typing.Tuple[str, str]]: return self._uniforms def generate_code(self) -> str: if not self._reset: _logger.error( "generate_code() called before the code has been reset. Call reset() first." ) return "" # Step 1, copy all lines from the code up until the first function self._generated_code = self.code[0:self._functions_start_line].copy() # Step 2, insert uniforms for all connected nodes for each unconnected argument for needed_code in self.connected_code: needed_code._reset = False prim_func = needed_code.get_primary_function() for arg in prim_func.arguments: if not arg.is_connected( ) and arg.name != "frag_pos" and not arg.is_output(): self._generated_code.append(arg.get_uniform_string() + "\n") self._uniforms.append((arg.type, arg.modified_name)) # Step 3, handle imports all_imports = IndexedSet([ im for n in self.connected_code for im in n.imports ]) # Convert to set to remove double imports for import_file in all_imports: print("Import File: {}".format(import_file)) libcode = generate_comment_line( import_file) + "\n" + get_import_code(import_file) self._generated_code.append(libcode + "\n") # Step 4, import code from connected nodes var_declarations = [] function_calls = [] added_function_defs = [] _add_calls(self, var_declarations, function_calls, added_function_defs, self._generated_code) # Step 5, generate calls to connected functions in own primary function self.primary_function.add_calls(function_calls, var_declarations) primary_code = "".join(self.primary_function.generated_code) self._generated_code.append(primary_code) self._reset = False return "".join(self._generated_code)