Example #1
0
    def __init__(self, proto, port_labels=("up", "down"), n=2):
        if n < 1:
            raise Exception('n must be 1 or more')
        super(Polymer, self).__init__()

        for label in port_labels:
            assert_port_exists(label, proto)

        last_part = None
        for _ in range(n):
            this_part = clone(proto)
            self.add(this_part, 'monomer[$]')
            if last_part is None:
                first_part = this_part
            else:
                # Transform this part, such that it's bottom port is rotated
                # and translated to the last part's top port.


                equivalence_transform(this_part, this_part.labels[port_labels[1]],
                                      last_part.labels[port_labels[0]])
            last_part = this_part

        # Hoist the last part's top port to be the top port of the polymer.
        self.add(last_part.labels[port_labels[0]], port_labels[0], containment=False)

        # Hoist the first part's bottom port to be the bottom port of the polymer.
        self.add(first_part.labels[port_labels[1]], port_labels[1], containment=False)
Example #2
0
    def apply_to_compound(self, guest, guest_port_name='down', host=None,
                          backfill=None, backfill_port_name='up'):
        """Attach copies of a guest Compound to Ports on a host Compound.

        Parameters
        ----------
        guest
        guest_port_name
        host
        backfill
        backfill_port_name

        Returns
        -------

        """
        n_ports = len(host.available_ports())
        assert n_ports >= self.points.shape[0], "Not enough ports for pattern."

        assert_port_exists(guest_port_name, guest)
        box = host.boundingbox
        pattern = self.points * box.lengths + box.mins

        port_positions = np.empty(shape=(n_ports, 3))
        port_list = list()
        for port_idx, port in enumerate(host.available_ports()):
            port_positions[port_idx, :] = port['up']['middle'].pos
            port_list.append(port)

        used_ports = set()  # Keep track of used ports for backfilling.
        guests = []
        for point in pattern:
            closest_point_idx = np.argmin(host.min_periodic_distance(point, port_positions))
            closest_port = port_list[closest_point_idx]
            used_ports.add(closest_port)

            # Attach the guest to the closest port.
            new_guest = clone(guest)
            equivalence_transform(new_guest, new_guest.labels[guest_port_name], closest_port)
            guests.append(new_guest)

            # Move the port as far away as possible (simpler than removing it).
            # There may well be a more elegant/efficient way of doing this.
            port_positions[closest_point_idx, :] = np.array([np.inf, np.inf, np.inf])

        backfills = []
        if backfill:
            assert_port_exists(backfill_port_name, backfill)
            # Attach the backfilling Compound to unused ports.
            for port in port_list:
                if port not in used_ports:
                    new_backfill = clone(backfill)
                    # Might make sense to have a backfill_port_name option...
                    equivalence_transform(
                        new_backfill, new_backfill.labels[backfill_port_name], port)
                    backfills.append(new_backfill)
        return guests, backfills
Example #3
0
    def __init__(self, monomers, n, sequence="A", port_labels=("up", "down")):
        if n < 1:
            raise ValueError("n must be 1 or more")
        super(Polymer, self).__init__()
        if isinstance(monomers, Compound):
            monomers = (monomers, )
        for monomer in monomers:
            for label in port_labels:
                assert_port_exists(label, monomer)

        unique_seq_ids = sorted(set(sequence))

        if len(monomers) != len(unique_seq_ids):
            raise ValueError(
                "Number of monomers passed to `Polymer` class must match "
                "number of unique entries in the specified sequence.")

        # 'A': monomer_1, 'B': monomer_2....
        seq_map = dict(zip(unique_seq_ids, monomers))

        last_part = None
        for n_added, seq_item in enumerate(it.cycle(sequence)):
            this_part = clone(seq_map[seq_item])
            self.add(this_part, "monomer[$]")
            if last_part is None:
                first_part = this_part
            else:
                # Transform this part, such that it's bottom port is rotated
                # and translated to the last part's top port.
                force_overlap(
                    this_part,
                    this_part.labels[port_labels[1]],
                    last_part.labels[port_labels[0]],
                )
            last_part = this_part
            if n_added == n * len(sequence) - 1:
                break

        # Hoist the last part's top port to be the top port of the polymer.
        self.add(last_part.labels[port_labels[0]],
                 port_labels[0],
                 containment=False)

        # Hoist the first part's bottom port to the bottom port of the polymer.
        self.add(first_part.labels[port_labels[1]],
                 port_labels[1],
                 containment=False)
Example #4
0
    def __init__(self, monomers, n, sequence='A', port_labels=('up', 'down')):
        if n < 1:
            raise ValueError('n must be 1 or more')
        super(Polymer, self).__init__()
        if isinstance(monomers, Compound):
            monomers = (monomers,)
        for monomer in monomers:
            for label in port_labels:
                assert_port_exists(label, monomer)

        unique_seq_ids = sorted(set(sequence))

        if len(monomers) != len(unique_seq_ids):
            raise ValueError('Number of monomers passed to `Polymer` class must'
                             ' match number of unique entries in the specified'
                             ' sequence.')

        # 'A': monomer_1, 'B': monomer_2....
        seq_map = dict(zip(unique_seq_ids, monomers))

        last_part = None
        for n_added, seq_item in enumerate(it.cycle(sequence)):
            this_part = clone(seq_map[seq_item])
            self.add(this_part, 'monomer[$]')
            if last_part is None:
                first_part = this_part
            else:
                # Transform this part, such that it's bottom port is rotated
                # and translated to the last part's top port.
                force_overlap(this_part,
                              this_part.labels[port_labels[1]],
                              last_part.labels[port_labels[0]])
            last_part = this_part
            if n_added == n * len(sequence) - 1:
                break

        # Hoist the last part's top port to be the top port of the polymer.
        self.add(last_part.labels[port_labels[0]], port_labels[0], containment=False)

        # Hoist the first part's bottom port to be the bottom port of the polymer.
        self.add(first_part.labels[port_labels[1]], port_labels[1], containment=False)
Example #5
0
    def apply_to_compound(
        self,
        guest,
        guest_port_name="down",
        host=None,
        backfill=None,
        backfill_port_name="up",
        scale=True,
    ):
        """Attach copies of a guest Compound to Ports on a host Compound.

        Parameters
        ----------
        guest : mb.Compound
            The Compound prototype to be applied to the host Compound
        guest_port_name : str, optional, default='down'
            The name of the port located on `guest` to attach to the host
        host : mb.Compound, optional, default=None
            A Compound with available ports to add copies of `guest` to
        backfill : mb.Compound, optional, default=None
            A Compound to add to the remaining available ports on `host` after
            clones of `guest` have been added for each point in the pattern
        backfill_port_name : str, optional, default='up'
            The name of the port located on `backfill` to attach to the host
        scale : bool, optional, default=True
            Scale the points in the pattern to the lengths of the `host`'s
            `boundingbox` and shift them by the hosts mins

        Returns
        -------
        guests : list of mb.Compound
            List of inserted guest compounds on host compound
        backfills : list of mb.Compound
            List of inserted backfill compounds on host compound
        """
        n_ports = len(host.available_ports())
        assert n_ports >= self.points.shape[0], "Not enough ports for pattern."
        assert_port_exists(guest_port_name, guest)
        box = host.get_boundingbox()
        if scale:
            self.scale(box.lengths)
            self.points += host.mins
        pattern = self.points
        port_positions = np.empty(shape=(n_ports, 3))
        port_list = list()
        for port_idx, port in enumerate(host.available_ports()):
            port_positions[port_idx, :] = port["up"]["middle"].pos
            port_list.append(port)
        used_ports = set()  # Keep track of used ports for backfilling.
        guests = []
        for point in pattern:
            closest_point_idx = np.argmin(
                host.min_periodic_distance(point, port_positions))
            closest_port = port_list[closest_point_idx]
            used_ports.add(closest_port)

            # Attach the guest to the closest port.
            new_guest = clone(guest)
            force_overlap(new_guest, new_guest.labels[guest_port_name],
                          closest_port)
            guests.append(new_guest)

            # Move the port as far away as possible (simpler than removing it).
            # There may well be a more elegant/efficient way of doing this.
            port_positions[closest_point_idx, :] = np.array(
                [np.inf, np.inf, np.inf])
        backfills = []
        if backfill:
            assert_port_exists(backfill_port_name, backfill)
            # Attach the backfilling Compound to unused ports.
            for port in port_list:
                if port not in used_ports:
                    new_backfill = clone(backfill)
                    # Might make sense to have a backfill_port_name option...
                    force_overlap(
                        new_backfill,
                        new_backfill.labels[backfill_port_name],
                        port,
                    )
                    backfills.append(new_backfill)
        return guests, backfills
Example #6
0
 def test_assert_port_exists(self, ch2):
     assert_port_exists("up", ch2)
     with pytest.raises(ValueError):
         assert_port_exists("dog", ch2)
Example #7
0
 def test_assert_port_exists(self, ch2):
     assert_port_exists('up', ch2)
     with pytest.raises(ValueError):
         assert_port_exists('dog', ch2)
Example #8
0
    def apply_to_compound(self,
                          guest,
                          guest_port_name='down',
                          host=None,
                          backfill=None,
                          backfill_port_name='up'):
        """Attach copies of a guest Compound to Ports on a host Compound.

        Parameters
        ----------
        guest
        guest_port_name
        host
        backfill
        backfill_port_name

        Returns
        -------

        """
        n_ports = len(host.available_ports())
        assert n_ports >= self.points.shape[0], "Not enough ports for pattern."

        assert_port_exists(guest_port_name, guest)
        box = host.boundingbox
        pattern = self.points * box.lengths + box.mins

        port_positions = np.empty(shape=(n_ports, 3))
        port_list = list()
        for port_idx, port in enumerate(host.available_ports()):
            port_positions[port_idx, :] = port['up']['middle'].pos
            port_list.append(port)

        used_ports = set()  # Keep track of used ports for backfilling.
        guests = []
        for point in pattern:
            closest_point_idx = np.argmin(
                host.min_periodic_distance(point, port_positions))
            closest_port = port_list[closest_point_idx]
            used_ports.add(closest_port)

            # Attach the guest to the closest port.
            new_guest = clone(guest)
            force_overlap(new_guest, new_guest.labels[guest_port_name],
                          closest_port)
            guests.append(new_guest)

            # Move the port as far away as possible (simpler than removing it).
            # There may well be a more elegant/efficient way of doing this.
            port_positions[closest_point_idx, :] = np.array(
                [np.inf, np.inf, np.inf])

        backfills = []
        if backfill:
            assert_port_exists(backfill_port_name, backfill)
            # Attach the backfilling Compound to unused ports.
            for port in port_list:
                if port not in used_ports:
                    new_backfill = clone(backfill)
                    # Might make sense to have a backfill_port_name option...
                    force_overlap(new_backfill,
                                  new_backfill.labels[backfill_port_name],
                                  port)
                    backfills.append(new_backfill)
        return guests, backfills
Example #9
0
 def test_assert_port_exists(self, ch2):
     assert_port_exists('up', ch2)
     with pytest.raises(ValueError):
         assert_port_exists('dog', ch2)
Example #10
0
    def build(self, n, sequence="A", add_hydrogens=True):
        """Connect one or more components in a specified sequence.

        Uses the compounds that are stored in Polymer.monomers and
        Polymer.end_groups.

        Parameters
        ----------
        n : int
            The number of times to replicate the sequence.
        sequence : str, optional, default 'A'
            A string of characters where each unique character represents one
            repetition of a monomer. Characters in `sequence` are assigned to
            monomers in the order they appear in `Polymer.monomers`.
            The characters in `sequence` are assigned to the compounds in the
            in the order that they appear in the Polymer.monomers list.
            For example, 'AB' where 'A'corresponds to the first compound
            added to Polymer.monomers and 'B' to the second compound.
        add_hydrogens : bool, default True
            If True and an end group compound is None, then the head or tail
            of the polymer will be capped off with hydrogen atoms. If end group
            compounds exist, then they will be used.
            If False and an end group compound is None, then the head or tail
            port will be exposed in the polymer.
        """
        if n < 1:
            raise ValueError("n must be 1 or more")
        n_monomers = n * len(sequence)

        for monomer in self._monomers:
            for label in self._port_labels:
                assert_port_exists(label, monomer)

        unique_seq_ids = sorted(set(sequence))

        if len(self._monomers) != len(unique_seq_ids):
            raise ValueError(
                "Number of monomers passed to `Polymer` class must match "
                "number of unique entries in the specified sequence.")

        # 'A': monomer_1, 'B': monomer_2....
        seq_map = dict(zip(unique_seq_ids, self._monomers))
        last_part = None
        for n_added, seq_item in enumerate(it.cycle(sequence)):
            this_part = clone(seq_map[seq_item])
            self.add(this_part, "monomer[$]")
            if last_part is None:
                first_part = this_part
            else:
                # Transform this part, such that its bottom port is rotated
                # and translated to the last parts top port.
                force_overlap(
                    this_part,
                    this_part.labels[self._port_labels[0]],
                    last_part.labels[self._port_labels[1]],
                )
            last_part = this_part
            if n_added == n * len(sequence) - 1:
                break

        # Add the end groups
        head = self["monomer[0]"]  # First monomer
        tail = self["monomer[{}]".format(n_monomers - 1)]  # Last monomer
        if not head["up"].used:
            head_port = head["up"]
        else:
            head_port = None
        if not tail["down"].used:
            tail_port = tail["down"]
        else:
            tail_port = None

        head_tail = [head_port, tail_port]

        for i, compound in enumerate(self._end_groups):
            if compound is not None:
                if self._headtail[i] is not None:
                    head_tail[i].update_separation(self._headtail[i])
                self.add(compound)
                force_overlap(compound, compound.labels["up"], head_tail[i])
            else:
                if add_hydrogens:
                    hydrogen = H()
                    # Defaut to 1/2 H-C bond len
                    head_tail[i].update_separation(0.0547)
                    hydrogen["up"].update_separation(0.0547)
                    self.add(hydrogen)
                    force_overlap(hydrogen, hydrogen["up"], head_tail[i])
                else:
                    # if None, hoist port to polymer level
                    self.add(head_tail[i],
                             self._port_labels[i],
                             containment=False)

        for port in self.all_ports():
            if port not in self.available_ports():
                self.remove(port)
Example #11
0
    def apply_to_compound(self, guest, guest_port_name='down', host=None,
                          backfill=None, backfill_port_name='up', scale=True):
        """Attach copies of a guest Compound to Ports on a host Compound.

        Parameters
        ----------
        guest : mb.Compound
            The Compound prototype to be applied to the host Compound
        guest_port_name : str, optional, default='down'
            The name of the port located on `guest` to attach to the host
        host : mb.Compound, optional, default=None
            A Compound with available ports to add copies of `guest` to
        backfill : mb.Compound, optional, default=None
            A Compound to add to the remaining available ports on `host`
            after clones of `guest` have been added for each point in the
            pattern
        backfill_port_name : str, optional, default='up'
            The name of the port located on `backfill` to attach to the host
        scale : bool, optional, default=True
            Scale the points in the pattern to the lengths of the `host`'s
            `boundingbox` and shift them by the `boundingbox`'s mins

        Returns
        -------

        """
        n_ports = len(host.available_ports())
        assert n_ports >= self.points.shape[0], "Not enough ports for pattern."
        assert_port_exists(guest_port_name, guest)
        box = host.boundingbox
        if scale:
            self.scale(box.lengths)
            self.points += box.mins
        pattern = self.points
        port_positions = np.empty(shape=(n_ports, 3))
        port_list = list()
        for port_idx, port in enumerate(host.available_ports()):
            port_positions[port_idx, :] = port['up']['middle'].pos
            port_list.append(port)
        used_ports = set()  # Keep track of used ports for backfilling.
        guests = []
        for point in pattern:
            closest_point_idx = np.argmin(host.min_periodic_distance(point, port_positions))
            closest_port = port_list[closest_point_idx]
            used_ports.add(closest_port)

            # Attach the guest to the closest port.
            new_guest = clone(guest)
            force_overlap(new_guest, new_guest.labels[guest_port_name], closest_port)
            guests.append(new_guest)

            # Move the port as far away as possible (simpler than removing it).
            # There may well be a more elegant/efficient way of doing this.
            port_positions[closest_point_idx, :] = np.array([np.inf, np.inf, np.inf])
        backfills = []
        if backfill:
            assert_port_exists(backfill_port_name, backfill)
            # Attach the backfilling Compound to unused ports.
            for port in port_list:
                if port not in used_ports:
                    new_backfill = clone(backfill)
                    # Might make sense to have a backfill_port_name option...
                    force_overlap(new_backfill,
                                  new_backfill.labels[backfill_port_name],
                                  port)
                    backfills.append(new_backfill)
        return guests, backfills