Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
 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
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
    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
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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)}>"
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
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)}>"
Ejemplo n.º 9
0
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))
Ejemplo n.º 10
0
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)
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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)
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
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)
Ejemplo n.º 15
0
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)