From 0668608d36b0a748514d4afbb426f5f3b80fc4a3 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Tue, 2 Feb 2021 22:51:46 -0700 Subject: [PATCH 01/55] added a couple new functions, moved existing code into build function --- mbuild/lib/recipes/polymer.py | 62 ++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 830a06cc4..b31f97aa3 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -26,19 +26,23 @@ class Polymer(Compound): The names of the two ports to use to connect copies of proto. """ - def __init__(self, monomers, n, sequence='A', port_labels=('up', 'down')): + def __init__(self): + super(Polymer, self).__init__() + self.monomers = [] + self.port_labels = [] + + def build(self, n, sequence='A'): 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: + if isinstance(self.monomers, Compound): + self.monomers = (self.monomers,) + for monomer, port_label in zip(self.monomers, self.port_labels): for label in port_labels: assert_port_exists(label, monomer) unique_seq_ids = sorted(set(sequence)) - if len(monomers) != len(unique_seq_ids): + 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.') @@ -68,8 +72,44 @@ def __init__(self, monomers, n, sequence='A', port_labels=('up', 'down')): # 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) -if __name__ == "__main__": - from mbuild.lib.moieties import CH2 - ch2 = CH2() - poly = Polymer(ch2, n=13, port_labels=("up", "down")) - poly.save('polymer.mol2') + + def add_monomer(self, monomer, bonding_indices, + port_labels, separation, orientation=None, + replace=True): + "" + + "" + if self.port_labels: + if not sorted(set(port_labels)) == sorted(set(self.port_labels)): + raise ValueError("The port labels given for each" + + "monomer must match. The previous" + + "port labels used were {}".format(self.port_labels) + ) + else: + self.port_labels.append(*port_labels) + + for idx, label in zip(bonding_indices, port_labels): + _add_port(self, monomer, label, idx, separation, orientation, replace) + + self.monomers.append(monomer) + + + def _add_port(self, monomer, label, atom_idx, separation, orientation=None, replace=True): + "" + "" + if replace: + bonds = [bond for bond in monomer.bonds()] + atom_bonds = [b for b in bonds if monomer[atom_idx] in b] + + for atom_pair in atom_bonds: + for atom in atom_pair: + if atom.name == 'H': + orientation = atom.pos - monomer[atom_idx].pos + monomer.remove(atom) + + port = mb.Port(anchor = monomer[atom_idx], + orientation=orientation, + separation=separation/2 + ) + monomer.add(port, label=label) + From 3edbabb5b7b182acc74fa3b38dc9d97b551ef28c Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Tue, 2 Feb 2021 23:40:50 -0700 Subject: [PATCH 02/55] added some doc strings --- mbuild/lib/recipes/polymer.py | 47 +++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index b31f97aa3..db47a48b7 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -36,19 +36,18 @@ def build(self, n, sequence='A'): raise ValueError('n must be 1 or more') if isinstance(self.monomers, Compound): self.monomers = (self.monomers,) - for monomer, port_label in zip(self.monomers, self.port_labels): - for label in port_labels: + 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, monomers)) + seq_map = dict(zip(unique_seq_ids, self.monomers)) last_part = None for n_added, seq_item in enumerate(it.cycle(sequence)): @@ -76,9 +75,43 @@ def build(self, n, sequence='A'): def add_monomer(self, monomer, bonding_indices, port_labels, separation, orientation=None, replace=True): - "" + """ + Add an mBuild compound to self.monomers which will be used to build the polymer. + Call this function for each unique monomer to be used in the polymer. + - "" + Parameters + ---------- + monomer : mb.Compound + A compound of an individual monomer + bonding_indices : list of int of length 2 + The particle indicies of monomer that represent the polymer + bonding sites. You can specify the indices of particles that will + be replaced by the polymer bond, or indices of particles that act + as the bonding sites. See the 'replace' parameter notes. + port_labels : list of str of length 2 + Labels given to the two ports added to monomer. + Ex.) ['head', 'tail'] or ['A', 'B'] + The same port labels must be used for any subsequent + monomer created using add_monomer() + separation : float, units nm + The bond length desired at the monomer-monomer bonding site. + (separation / 2) is used to set the length of each port + orientation : array-like, shape=(3,), default=None + Vector along which to orient the port + If replace = True, then the orientation of the bond + between the particle being removed and the anchor particle + is used. + replace : Bool, required, default=True + If True, then the particles identified by bonding_indices + will be removed and ports are added to the particles they + were initially bonded to. Only use replace=True in the case + that bonding_indices point to hydrogen atoms bonded to the + desired monomer-monomer bonding site particles. + If False, then the particles identified by bonding_indices + will have ports added, and no particles are removed from + the monomer compound. + """ if self.port_labels: if not sorted(set(port_labels)) == sorted(set(self.port_labels)): raise ValueError("The port labels given for each" + @@ -86,7 +119,7 @@ def add_monomer(self, monomer, bonding_indices, "port labels used were {}".format(self.port_labels) ) else: - self.port_labels.append(*port_labels) + self.port_labels.append(port_labels*) for idx, label in zip(bonding_indices, port_labels): _add_port(self, monomer, label, idx, separation, orientation, replace) From 7463549a2df0c54bb2b51f00a636b8be4d08be7d Mon Sep 17 00:00:00 2001 From: chrisjonesbsu Date: Wed, 3 Feb 2021 12:19:54 -0700 Subject: [PATCH 03/55] added a default value to the port labels --- mbuild/lib/recipes/polymer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index db47a48b7..c5e645d57 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -72,8 +72,8 @@ def build(self, n, sequence='A'): self.add(first_part.labels[port_labels[1]], port_labels[1], containment=False) - def add_monomer(self, monomer, bonding_indices, - port_labels, separation, orientation=None, + def add_monomer(self, monomer, bonding_indices, separation, + port_labels=['A', 'B'], orientation=None, replace=True): """ Add an mBuild compound to self.monomers which will be used to build the polymer. @@ -89,14 +89,14 @@ def add_monomer(self, monomer, bonding_indices, bonding sites. You can specify the indices of particles that will be replaced by the polymer bond, or indices of particles that act as the bonding sites. See the 'replace' parameter notes. - port_labels : list of str of length 2 + separation : float, units nm + The bond length desired at the monomer-monomer bonding site. + (separation / 2) is used to set the length of each port + port_labels : list of str of length 2, default=['A', 'B'] Labels given to the two ports added to monomer. Ex.) ['head', 'tail'] or ['A', 'B'] The same port labels must be used for any subsequent monomer created using add_monomer() - separation : float, units nm - The bond length desired at the monomer-monomer bonding site. - (separation / 2) is used to set the length of each port orientation : array-like, shape=(3,), default=None Vector along which to orient the port If replace = True, then the orientation of the bond @@ -133,7 +133,7 @@ def _add_port(self, monomer, label, atom_idx, separation, orientation=None, repl if replace: bonds = [bond for bond in monomer.bonds()] atom_bonds = [b for b in bonds if monomer[atom_idx] in b] - + for atom_pair in atom_bonds: for atom in atom_pair: if atom.name == 'H': From 8b80df7401754c4312d1319db1a77abd2fcb0061 Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Fri, 5 Feb 2021 14:41:20 -0700 Subject: [PATCH 04/55] reworked the _add_port function --- mbuild/lib/recipes/polymer.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index c5e645d57..e444707f5 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -131,16 +131,14 @@ def _add_port(self, monomer, label, atom_idx, separation, orientation=None, repl "" "" if replace: - bonds = [bond for bond in monomer.bonds()] - atom_bonds = [b for b in bonds if monomer[atom_idx] in b] - - for atom_pair in atom_bonds: - for atom in atom_pair: - if atom.name == 'H': - orientation = atom.pos - monomer[atom_idx].pos - monomer.remove(atom) + atom_bonds = [bond for bond in monomer.bonds() if monomer[atom_idx] in bond][0] + anchor_particle = [p for p in atom_bonds if p != monomer[atom_idx]][0] + orientation = monomer[atom_idx].pos - anchor_particle.pos + compound.remove(monomer[atom_idx]) + else: + anchor_particle = monomer[atom_idx] - port = mb.Port(anchor = monomer[atom_idx], + port = mb.Port(anchor = anchor_particle, orientation=orientation, separation=separation/2 ) From 25161efa5b6678a843df2128f4b6135f97470055 Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Fri, 5 Feb 2021 16:41:47 -0700 Subject: [PATCH 05/55] saving my progress with the add_end_groups function --- mbuild/lib/recipes/polymer.py | 66 +++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index e444707f5..b39ded4c0 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -1,5 +1,6 @@ import itertools as it +from mbuild.port import Port from mbuild.compound import Compound from mbuild.coordinate_transform import force_overlap from mbuild.utils.validation import assert_port_exists @@ -30,10 +31,12 @@ def __init__(self): super(Polymer, self).__init__() self.monomers = [] self.port_labels = [] + self.N = None def build(self, n, sequence='A'): if n < 1: raise ValueError('n must be 1 or more') + self.N = n if isinstance(self.monomers, Compound): self.monomers = (self.monomers,) for monomer in self.monomers: @@ -59,17 +62,17 @@ def build(self, n, sequence='A'): # 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]]) + this_part.labels[self.port_labels[1]], + last_part.labels[self.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) + self.add(last_part.labels[self.port_labels[0]], self.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) + self.add(first_part.labels[self.port_labels[1]], self.port_labels[1], containment=False) def add_monomer(self, monomer, bonding_indices, separation, @@ -119,28 +122,47 @@ def add_monomer(self, monomer, bonding_indices, separation, "port labels used were {}".format(self.port_labels) ) else: - self.port_labels.append(port_labels*) + self.port_labels.extend(port_labels) for idx, label in zip(bonding_indices, port_labels): - _add_port(self, monomer, label, idx, separation, orientation, replace) + _add_port(monomer, label, idx, separation, orientation, replace) self.monomers.append(monomer) + def add_end_groups(self, compound, bond_index, separation, orientation=None, replace=True): + """ + """ + head = self['monomer[0]'] # First monomer group + tail = self['monomer[{}]'.format(self.N - 1)] # Last monomer group - def _add_port(self, monomer, label, atom_idx, separation, orientation=None, replace=True): - "" - "" - if replace: - atom_bonds = [bond for bond in monomer.bonds() if monomer[atom_idx] in bond][0] - anchor_particle = [p for p in atom_bonds if p != monomer[atom_idx]][0] - orientation = monomer[atom_idx].pos - anchor_particle.pos - compound.remove(monomer[atom_idx]) - else: - anchor_particle = monomer[atom_idx] - - port = mb.Port(anchor = anchor_particle, - orientation=orientation, - separation=separation/2 - ) - monomer.add(port, label=label) + for label in self.port_labels: + if not head[label].used: + head_port = head[label] + if not tail[label].used: + tail_port = tail[label] + + compound_2 = clone(compound) + add_port(compound, 'head', bond_index, separation, orientation, replace) + add_port(compound_2, 'tail', bond_index, separation, orientation, replace) + + + + + +def _add_port(compound, label, atom_idx, separation, orientation=None, replace=True): + """ + """ + if replace: + atom_bonds = [bond for bond in compound.bonds() if compound[atom_idx] in bond][0] + anchor_particle = [p for p in atom_bonds if p != compound[atom_idx]][0] + orientation = compound[atom_idx].pos - anchor_particle.pos + compound.remove(compound[atom_idx]) + else: + anchor_particle = compound[atom_idx] + + port = Port(anchor = anchor_particle, + orientation=orientation, + separation=separation/2 + ) + compound.add(port, label=label) From e0f5224584f4d03ba144a14230d79077c15817d7 Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Fri, 5 Feb 2021 23:52:42 -0700 Subject: [PATCH 06/55] made progress with end group funcitonality --- mbuild/lib/recipes/polymer.py | 46 +++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index b39ded4c0..9dca6bf20 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -4,6 +4,7 @@ from mbuild.compound import Compound from mbuild.coordinate_transform import force_overlap from mbuild.utils.validation import assert_port_exists +from mbuild.lib.atoms import H from mbuild import clone @@ -31,12 +32,11 @@ def __init__(self): super(Polymer, self).__init__() self.monomers = [] self.port_labels = [] - self.N = None + self.end_groups = [] def build(self, n, sequence='A'): if n < 1: raise ValueError('n must be 1 or more') - self.N = n if isinstance(self.monomers, Compound): self.monomers = (self.monomers,) for monomer in self.monomers: @@ -74,7 +74,30 @@ def build(self, n, sequence='A'): # Hoist the first part's bottom port to be the bottom port of the polymer. self.add(first_part.labels[self.port_labels[1]], self.port_labels[1], containment=False) + # Add the end groups + head = self['monomer[0]'] # First monomer group + tail = self['monomer[{}]'.format(n - 1)] # Last monomer group + for label in self.port_labels: + if not head[label].used: + head_port = head[label] + if not tail[label].used: + tail_port = tail[label] + + if not self.end_groups: + self.end_groups.extend([H(), H()]) + for compound in self.end_groups: + self.add(compound) + + force_overlap(self.end_groups[0], + self.end_groups[0].labels['up'], + head_port + ) + force_overlap(self.end_groups[1], + self.end_groups[1].labels['up'], + tail_port + ) + def add_monomer(self, monomer, bonding_indices, separation, port_labels=['A', 'B'], orientation=None, replace=True): @@ -132,21 +155,12 @@ def add_monomer(self, monomer, bonding_indices, separation, def add_end_groups(self, compound, bond_index, separation, orientation=None, replace=True): """ """ - head = self['monomer[0]'] # First monomer group - tail = self['monomer[{}]'.format(self.N - 1)] # Last monomer group - - for label in self.port_labels: - if not head[label].used: - head_port = head[label] - if not tail[label].used: - tail_port = tail[label] - compound_2 = clone(compound) - add_port(compound, 'head', bond_index, separation, orientation, replace) - add_port(compound_2, 'tail', bond_index, separation, orientation, replace) - - - + print('adding ports') + _add_port(compound, 'up', bond_index, separation, orientation, replace) + _add_port(compound_2, 'up', bond_index, separation, orientation, replace) + print('finished adding ports') + self.end_groups.extend([compound, compound_2]) def _add_port(compound, label, atom_idx, separation, orientation=None, replace=True): From 02b3f440ab7b758b3b65d70f0c7cf93bbe41622b Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Fri, 5 Feb 2021 23:58:14 -0700 Subject: [PATCH 07/55] removing a couple print statements --- mbuild/lib/recipes/polymer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 9dca6bf20..88f4ee8be 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -156,10 +156,8 @@ def add_end_groups(self, compound, bond_index, separation, orientation=None, rep """ """ compound_2 = clone(compound) - print('adding ports') _add_port(compound, 'up', bond_index, separation, orientation, replace) _add_port(compound_2, 'up', bond_index, separation, orientation, replace) - print('finished adding ports') self.end_groups.extend([compound, compound_2]) From 17819aeeffdb8c4afcd29d21fbf45e5627b1c305 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Thu, 11 Feb 2021 09:57:55 -0700 Subject: [PATCH 08/55] added 2 new attributes to port class --- mbuild/lib/recipes/polymer.py | 10 ++++++++++ mbuild/port.py | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 88f4ee8be..add8c0771 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -87,6 +87,16 @@ def build(self, n, sequence='A'): self.end_groups.extend([H(), H()]) for compound in self.end_groups: self.add(compound) + + # Update head_port and tail_port orientation and separation before using force_overlap + # Goal is for the orientation and separation in head and tail ports to match + # what was given in the ports created in the self.end_groups compounds + + head_port.orientation = -self.end_groups[0].labels['up'].orientation + head_port.separation = self.end_groups[0].labels['up'].separation + + tail_port.orientation = -self.end_groups[1].labels['up'].orientation + tail_port.separation = self.end_groups[1].labels['up'].separation force_overlap(self.end_groups[0], self.end_groups[0].labels['up'], diff --git a/mbuild/port.py b/mbuild/port.py index 20fb0a562..14efb8e1b 100755 --- a/mbuild/port.py +++ b/mbuild/port.py @@ -39,11 +39,12 @@ class Port(Compound): def __init__(self, anchor=None, orientation=None, separation=0): super(Port, self).__init__(name='Port', port_particle=True) self.anchor = anchor - + self.separation = separtaion default_direction = np.array([0, 1, 0]) if orientation is None: orientation = [0, 1, 0] orientation = np.asarray(orientation) + self.orientation = orientation up = Compound(name='subport', port_particle=True) up.add(Particle(name='G', pos=[0.005, 0.0025, -0.0025], From c1b1575460d86110ccc6c4acbfcd51658988bd07 Mon Sep 17 00:00:00 2001 From: chrisjonesbsu Date: Thu, 11 Feb 2021 13:35:29 -0700 Subject: [PATCH 09/55] removed changes from port.py, commented out orientation and separation lines --- mbuild/lib/recipes/polymer.py | 9 ++++----- mbuild/port.py | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index add8c0771..e0dd2ebb9 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -92,11 +92,10 @@ def build(self, n, sequence='A'): # Goal is for the orientation and separation in head and tail ports to match # what was given in the ports created in the self.end_groups compounds - head_port.orientation = -self.end_groups[0].labels['up'].orientation - head_port.separation = self.end_groups[0].labels['up'].separation - - tail_port.orientation = -self.end_groups[1].labels['up'].orientation - tail_port.separation = self.end_groups[1].labels['up'].separation + #head_port.orientation = -self.end_groups[0].labels['up'].orientation + #head_port.separation = self.end_groups[0].labels['up'].separation + #tail_port.orientation = -self.end_groups[1].labels['up'].orientation + #tail_port.separation = self.end_groups[1].labels['up'].separation force_overlap(self.end_groups[0], self.end_groups[0].labels['up'], diff --git a/mbuild/port.py b/mbuild/port.py index 14efb8e1b..47303c76d 100755 --- a/mbuild/port.py +++ b/mbuild/port.py @@ -39,12 +39,11 @@ class Port(Compound): def __init__(self, anchor=None, orientation=None, separation=0): super(Port, self).__init__(name='Port', port_particle=True) self.anchor = anchor - self.separation = separtaion + default_direction = np.array([0, 1, 0]) if orientation is None: orientation = [0, 1, 0] orientation = np.asarray(orientation) - self.orientation = orientation up = Compound(name='subport', port_particle=True) up.add(Particle(name='G', pos=[0.005, 0.0025, -0.0025], From 658e1f82c8e50036fc148536fbdba2d8a09158b3 Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Wed, 17 Feb 2021 23:11:31 -0700 Subject: [PATCH 10/55] changed method of removing hydrogens, added n_monomers var for finding last monomer --- mbuild/lib/recipes/polymer.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index e0dd2ebb9..eda6e213f 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -37,6 +37,8 @@ def __init__(self): def build(self, n, sequence='A'): if n < 1: raise ValueError('n must be 1 or more') + n_monomers = n*len(sequence) + if isinstance(self.monomers, Compound): self.monomers = (self.monomers,) for monomer in self.monomers: @@ -76,7 +78,7 @@ def build(self, n, sequence='A'): # Add the end groups head = self['monomer[0]'] # First monomer group - tail = self['monomer[{}]'.format(n - 1)] # Last monomer group + tail = self['monomer[{}]'.format(n_monomers - 1)] # Last monomer group for label in self.port_labels: if not head[label].used: head_port = head[label] @@ -113,7 +115,6 @@ def add_monomer(self, monomer, bonding_indices, separation, """ Add an mBuild compound to self.monomers which will be used to build the polymer. Call this function for each unique monomer to be used in the polymer. - Parameters ---------- @@ -134,9 +135,9 @@ def add_monomer(self, monomer, bonding_indices, separation, monomer created using add_monomer() orientation : array-like, shape=(3,), default=None Vector along which to orient the port - If replace = True, then the orientation of the bond - between the particle being removed and the anchor particle - is used. + If replace = True, and orientation = None, + the orientation of the bond between the particle being + removed and the anchor particle is used. replace : Bool, required, default=True If True, then the particles identified by bonding_indices will be removed and ports are added to the particles they @@ -158,6 +159,11 @@ def add_monomer(self, monomer, bonding_indices, separation, for idx, label in zip(bonding_indices, port_labels): _add_port(monomer, label, idx, separation, orientation, replace) + if replace: + remove_atom1 = monomer[bonding_indices[0]] + remove_atom2 = monomer[bonding_indices[1]] + monomer.remove(remove_atom1) + monomer.remove(remove_atom2) self.monomers.append(monomer) @@ -167,6 +173,10 @@ def add_end_groups(self, compound, bond_index, separation, orientation=None, rep compound_2 = clone(compound) _add_port(compound, 'up', bond_index, separation, orientation, replace) _add_port(compound_2, 'up', bond_index, separation, orientation, replace) + if replace: + compound.remove(compound[bond_index]) + compound_2.remove(compound_2[bond_index]) + self.end_groups.extend([compound, compound_2]) @@ -176,8 +186,8 @@ def _add_port(compound, label, atom_idx, separation, orientation=None, replace=T if replace: atom_bonds = [bond for bond in compound.bonds() if compound[atom_idx] in bond][0] anchor_particle = [p for p in atom_bonds if p != compound[atom_idx]][0] - orientation = compound[atom_idx].pos - anchor_particle.pos - compound.remove(compound[atom_idx]) + if not orientation: + orientation = compound[atom_idx].pos - anchor_particle.pos else: anchor_particle = compound[atom_idx] From 7c2c178c35d903f0fea863f7c536af94a09e9680 Mon Sep 17 00:00:00 2001 From: chrisjonesbsu Date: Mon, 22 Feb 2021 10:32:39 -0700 Subject: [PATCH 11/55] adding to the doc strings --- mbuild/lib/recipes/polymer.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index eda6e213f..10ac59de7 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -14,19 +14,25 @@ class Polymer(Compound): """Connect one or more components in a specified sequence. - Parameters + Attributes ---------- - monomers : mb.Compound or list of mb.Compound - The compound(s) to replicate. - 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 assigned by the built-in `sorted()`. - port_labels : 2-tuple of strs, optional, default=('up', 'down') - The names of the two ports to use to connect copies of proto. - + monomers : List of mb.Compounds + The compound(s) to replicate. Add to this list using the add_monomers method. + end_groups : List of mb.Compounds + The compound to cap the end of the polymer. Add to this list using the + add_end_groups method. + + Methods + ------- + add_monomer(monomer, bonding_indices, separation, port_labels, orientation, replace) + Use to add a monomer compound to Polymer.monomers + + add_end_groups(compound, bond_index, separation, orientation, replace) + Use to add an end group compound to Polymer.end_groups + + build(n, sequence) + Use to create a single polymer compound. This method uses the compounds created + by alling the add_monomer and add_end_group methods. """ def __init__(self): super(Polymer, self).__init__() @@ -197,3 +203,6 @@ def _add_port(compound, label, atom_idx, separation, orientation=None, replace=T ) compound.add(port, label=label) + + + From 7b6ccc0d9afdcdd2657ef3acba6a9cfee4be1b3d Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Wed, 24 Feb 2021 14:12:54 -0700 Subject: [PATCH 12/55] added update_separation function --- mbuild/port.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/mbuild/port.py b/mbuild/port.py index 20fb0a562..8ade2a1d8 100755 --- a/mbuild/port.py +++ b/mbuild/port.py @@ -1,4 +1,5 @@ import itertools +from warnings import warn import numpy as np @@ -83,6 +84,23 @@ def _clone(self, clone_of=None, root_container=None): newone.used = self.used return newone + def update_separation(self, separation): + if self.used: + warn("This port is already being used and changing its separation" + "will have no effect on distance between particles.") + return + if self.anchor: + self.translate_to(self.anchor.pos) + + self.translate(separation*self.direction) + + def update_orientation(self, orientation): + if self.used: + warn("This port is already being used and changing its separation" + "will have no effect on distance between particles.") + return + orientation = np.asarray(orientation) + @property def center(self): """The cartesian center of the Port""" @@ -93,6 +111,7 @@ def direction(self): """The unit vector pointing in the 'direction' of the Port """ return unit_vector(self.xyz_with_ports[1]-self.xyz_with_ports[0]) + @property def access_labels(self): From 650d8999ac522adb28677081891974d482d845ce Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Wed, 24 Feb 2021 15:33:33 -0700 Subject: [PATCH 13/55] update orientation func; separation property func --- mbuild/port.py | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/mbuild/port.py b/mbuild/port.py index 8ade2a1d8..98fe28d2d 100755 --- a/mbuild/port.py +++ b/mbuild/port.py @@ -35,7 +35,6 @@ class Port(Compound): used : bool Status of whether a port has been occupied following an equivalence transform. - """ def __init__(self, anchor=None, orientation=None, separation=0): super(Port, self).__init__(name='Port', port_particle=True) @@ -85,9 +84,17 @@ def _clone(self, clone_of=None, root_container=None): return newone def update_separation(self, separation): + """ + Change the distance between a port and its anchor particle + + separation : float, required + Distance to shift port along the orientation vector from the anchor + particle position. If no anchor is provided, the port will be shifted + from the origin. + """ if self.used: warn("This port is already being used and changing its separation" - "will have no effect on distance between particles.") + " will have no effect on distance between particles.") return if self.anchor: self.translate_to(self.anchor.pos) @@ -95,11 +102,31 @@ def update_separation(self, separation): self.translate(separation*self.direction) def update_orientation(self, orientation): + """ + Change the direction between a port and its anchor particle + + orientation : array-like, shape=(3,), optional, default=[0, 1, 0] + Vector along which to orient the port + """ if self.used: - warn("This port is already being used and changing its separation" - "will have no effect on distance between particles.") + warn("This port is already being used and changing its orientation" + " will have no effect on direction between particles.") return + orientation = np.asarray(orientation) + down = self.labels['down'] + + if np.allclose( + self.direction, unit_vector(-orientation)): + down.rotate(np.pi, [0, 0, 1]) + self.rotate(np.pi, [0, 0, 1]) + elif np.allclose( + self.direction, unit_vector(orientation)): + down.rotate(np.pi, [0, 0, 1]) + else: + normal = np.cross(self.direction, orientation) + self.rotate(angle(self.direction, orientation), normal) + down.rotate(np.pi, normal) @property def center(self): @@ -111,7 +138,11 @@ def direction(self): """The unit vector pointing in the 'direction' of the Port """ return unit_vector(self.xyz_with_ports[1]-self.xyz_with_ports[0]) - + + @property + def separation(self): + if self.anchor: + return np.linalg.norm(self.center - self.anchor.pos) @property def access_labels(self): From 9b4d93bff5869fa893fc5030f1fa304b3ab730ac Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Wed, 24 Feb 2021 19:39:33 -0700 Subject: [PATCH 14/55] added the up subports to the rotations --- mbuild/port.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mbuild/port.py b/mbuild/port.py index 98fe28d2d..8de371161 100755 --- a/mbuild/port.py +++ b/mbuild/port.py @@ -115,18 +115,22 @@ def update_orientation(self, orientation): orientation = np.asarray(orientation) down = self.labels['down'] + up = self.labels['up'] if np.allclose( self.direction, unit_vector(-orientation)): down.rotate(np.pi, [0, 0, 1]) + up.rotate(np.pi, [0, 0, 1]) self.rotate(np.pi, [0, 0, 1]) elif np.allclose( self.direction, unit_vector(orientation)): down.rotate(np.pi, [0, 0, 1]) + up.rotate(np.pi, [0, 0, 1]) else: normal = np.cross(self.direction, orientation) self.rotate(angle(self.direction, orientation), normal) down.rotate(np.pi, normal) + up.rotate(np.pi, normal) @property def center(self): From 67b6f28c43b54784f0a89ff7572565b6784d847c Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Thu, 25 Feb 2021 11:03:16 -0700 Subject: [PATCH 15/55] fixed the change_orientation function --- mbuild/port.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/mbuild/port.py b/mbuild/port.py index 8de371161..d2d1a257b 100755 --- a/mbuild/port.py +++ b/mbuild/port.py @@ -94,10 +94,12 @@ def update_separation(self, separation): """ if self.used: warn("This port is already being used and changing its separation" - " will have no effect on distance between particles.") + " will have no effect on the distance between particles.") return if self.anchor: self.translate_to(self.anchor.pos) + else: + self.translate_to((0,0,0)) self.translate(separation*self.direction) @@ -110,27 +112,17 @@ def update_orientation(self, orientation): """ if self.used: warn("This port is already being used and changing its orientation" - " will have no effect on direction between particles.") + " will have no effect on the direction between particles.") return orientation = np.asarray(orientation) down = self.labels['down'] up = self.labels['up'] - if np.allclose( - self.direction, unit_vector(-orientation)): - down.rotate(np.pi, [0, 0, 1]) - up.rotate(np.pi, [0, 0, 1]) - self.rotate(np.pi, [0, 0, 1]) - elif np.allclose( - self.direction, unit_vector(orientation)): - down.rotate(np.pi, [0, 0, 1]) - up.rotate(np.pi, [0, 0, 1]) - else: - normal = np.cross(self.direction, orientation) - self.rotate(angle(self.direction, orientation), normal) - down.rotate(np.pi, normal) - up.rotate(np.pi, normal) + normal = np.cross(self.direction, orientation) + self.rotate(angle(self.direction, orientation), normal) + down.rotate(np.pi, normal) + up.rotate(np.pi, normal) @property def center(self): From ccd312f9a332b9468d04b9b14eb44a0d64c2750c Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Thu, 25 Feb 2021 14:25:28 -0700 Subject: [PATCH 16/55] fix handling when their is no anchor particle --- mbuild/port.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mbuild/port.py b/mbuild/port.py index d2d1a257b..66a04211e 100755 --- a/mbuild/port.py +++ b/mbuild/port.py @@ -95,7 +95,7 @@ def update_separation(self, separation): if self.used: warn("This port is already being used and changing its separation" " will have no effect on the distance between particles.") - return + if self.anchor: self.translate_to(self.anchor.pos) else: @@ -113,7 +113,6 @@ def update_orientation(self, orientation): if self.used: warn("This port is already being used and changing its orientation" " will have no effect on the direction between particles.") - return orientation = np.asarray(orientation) down = self.labels['down'] @@ -137,8 +136,16 @@ def direction(self): @property def separation(self): + """ + The distance between a port and its anchor particle. + If the port has no anchor particle, returns a value of zero. + """ if self.anchor: return np.linalg.norm(self.center - self.anchor.pos) + else: + warn("This port is not anchored to another particle." + " Retruning a separation of None") + return None @property def access_labels(self): From 80a54b8a545f8a75ada62fbe39532d6c89c09d6a Mon Sep 17 00:00:00 2001 From: Jenny <39961845+jennyfothergill@users.noreply.github.com> Date: Thu, 4 Mar 2021 12:36:18 -0700 Subject: [PATCH 17/55] fix typo --- mbuild/port.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mbuild/port.py b/mbuild/port.py index 66a04211e..cc816f595 100755 --- a/mbuild/port.py +++ b/mbuild/port.py @@ -144,7 +144,7 @@ def separation(self): return np.linalg.norm(self.center - self.anchor.pos) else: warn("This port is not anchored to another particle." - " Retruning a separation of None") + " Returning a separation of None") return None @property From 41aec958f4c5d739604bf2d4649bf1387d43d47d Mon Sep 17 00:00:00 2001 From: Jenny <39961845+jennyfothergill@users.noreply.github.com> Date: Thu, 4 Mar 2021 12:38:30 -0700 Subject: [PATCH 18/55] docstring: separation is None when port has no anchor --- mbuild/port.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mbuild/port.py b/mbuild/port.py index cc816f595..8b3ffe0ae 100755 --- a/mbuild/port.py +++ b/mbuild/port.py @@ -138,7 +138,7 @@ def direction(self): def separation(self): """ The distance between a port and its anchor particle. - If the port has no anchor particle, returns a value of zero. + If the port has no anchor particle, returns None. """ if self.anchor: return np.linalg.norm(self.center - self.anchor.pos) From 33b7ecaf352a5c8742f3c774f35c2fc3ec73890b Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Mon, 15 Mar 2021 12:34:07 -0600 Subject: [PATCH 19/55] added 3 unit tests --- mbuild/tests/test_port.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mbuild/tests/test_port.py b/mbuild/tests/test_port.py index 574f4cd35..5700ba573 100755 --- a/mbuild/tests/test_port.py +++ b/mbuild/tests/test_port.py @@ -52,6 +52,20 @@ def test_port_direction(self): port.rotate(np.pi, [1, 0, 0]) assert(np.allclose([0, -1, 0], port.direction, atol=1e-15)) + def test_port_separation(self, ethane): + port = mb.Port(anchor=ethane, separation=0.7) + assert(np.allclose(0.7, port.separation, atol=1e-15)) + + def test_update_separation(self, ethane): + port = mb.Port(anchor=ethane, separation=0.7) + port.update_separation(separation=0.9) + assert(np.allclose(0.9, port.separation, atol=1e-15)) + + def test_update_orientaiton(self, ch2): + port = ch2['up'] + port.update_orientation(orientation=(1,0,0)) + assert(np.allclose([-1,0,0], port.direction, atol=1e-15)) + def test_access_labels(self): port = mb.Port() compound = mb.Compound() From a8fb3b9b5d0fcb8b8923b719b3d33242eb229bac Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Mon, 15 Mar 2021 14:49:57 -0600 Subject: [PATCH 20/55] remove the port updating stuff I was trying to use --- mbuild/lib/recipes/polymer.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 10ac59de7..0372a42fb 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -96,15 +96,6 @@ def build(self, n, sequence='A'): for compound in self.end_groups: self.add(compound) - # Update head_port and tail_port orientation and separation before using force_overlap - # Goal is for the orientation and separation in head and tail ports to match - # what was given in the ports created in the self.end_groups compounds - - #head_port.orientation = -self.end_groups[0].labels['up'].orientation - #head_port.separation = self.end_groups[0].labels['up'].separation - #tail_port.orientation = -self.end_groups[1].labels['up'].orientation - #tail_port.separation = self.end_groups[1].labels['up'].separation - force_overlap(self.end_groups[0], self.end_groups[0].labels['up'], head_port From c4c894fb222e6fef915aba3541df0ea1b9f762b7 Mon Sep 17 00:00:00 2001 From: chrisjonesbsu Date: Mon, 15 Mar 2021 15:46:55 -0600 Subject: [PATCH 21/55] updating port separations for end groups --- mbuild/lib/recipes/polymer.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 0372a42fb..244ab80f2 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -92,7 +92,16 @@ def build(self, n, sequence='A'): tail_port = tail[label] if not self.end_groups: - self.end_groups.extend([H(), H()]) + hydrogen = H() + hydrogen['up'].update_separation(0.0547) + hydrogen_2 = mb.clone(hydrogen) + self.end_groups.extend([hydrogen, hydrogen_2]) + head_port.update_separation(0.0547) + tail_port.update_separation(0.0547) + else: + head_port.update_separation(self.end_groups['up'].separation) + tail_port.update_separation(self.end_groups['up'].separation) + for compound in self.end_groups: self.add(compound) From a111ebf64111d25a4cb120579d55d743efdd70b2 Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Mon, 15 Mar 2021 16:21:48 -0600 Subject: [PATCH 22/55] fix the way clone is being called --- mbuild/lib/recipes/polymer.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 244ab80f2..d7133721d 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -1,5 +1,4 @@ import itertools as it - from mbuild.port import Port from mbuild.compound import Compound from mbuild.coordinate_transform import force_overlap @@ -94,13 +93,13 @@ def build(self, n, sequence='A'): if not self.end_groups: hydrogen = H() hydrogen['up'].update_separation(0.0547) - hydrogen_2 = mb.clone(hydrogen) + hydrogen_2 = clone(hydrogen) self.end_groups.extend([hydrogen, hydrogen_2]) head_port.update_separation(0.0547) tail_port.update_separation(0.0547) else: - head_port.update_separation(self.end_groups['up'].separation) - tail_port.update_separation(self.end_groups['up'].separation) + head_port.update_separation(self.end_groups[0]['up'].separation) + tail_port.update_separation(self.end_groups[1]['up'].separation) for compound in self.end_groups: self.add(compound) From df35343169e42ec2060769076bba12632429f56c Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Mon, 15 Mar 2021 22:12:29 -0600 Subject: [PATCH 23/55] added some doc strings and comments --- mbuild/lib/recipes/polymer.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index d7133721d..353edbd1b 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -31,7 +31,7 @@ class Polymer(Compound): build(n, sequence) Use to create a single polymer compound. This method uses the compounds created - by alling the add_monomer and add_end_group methods. + by calling the add_monomer and add_end_group methods. """ def __init__(self): super(Polymer, self).__init__() @@ -40,6 +40,19 @@ def __init__(self): self.end_groups = [] def build(self, n, sequence='A'): + """Connect one or more components in a specified sequence. + + Parameters + ---------- + monomers : mb.Compound or list of mb.Compound + The compound(s) to replicate. + 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 assigned by the built-in `sorted()`. + """ if n < 1: raise ValueError('n must be 1 or more') n_monomers = n*len(sequence) @@ -82,22 +95,22 @@ def build(self, n, sequence='A'): self.add(first_part.labels[self.port_labels[1]], self.port_labels[1], containment=False) # Add the end groups - head = self['monomer[0]'] # First monomer group - tail = self['monomer[{}]'.format(n_monomers - 1)] # Last monomer group + head = self['monomer[0]'] # First monomer + tail = self['monomer[{}]'.format(n_monomers - 1)] # Last monomer for label in self.port_labels: if not head[label].used: head_port = head[label] if not tail[label].used: tail_port = tail[label] - if not self.end_groups: + if not self.end_groups: # Cap each end with Hydrogens hydrogen = H() hydrogen['up'].update_separation(0.0547) hydrogen_2 = clone(hydrogen) self.end_groups.extend([hydrogen, hydrogen_2]) head_port.update_separation(0.0547) tail_port.update_separation(0.0547) - else: + else: # Use compounds in self.end_groups head_port.update_separation(self.end_groups[0]['up'].separation) tail_port.update_separation(self.end_groups[1]['up'].separation) @@ -112,7 +125,6 @@ def build(self, n, sequence='A'): self.end_groups[1].labels['up'], tail_port ) - def add_monomer(self, monomer, bonding_indices, separation, port_labels=['A', 'B'], orientation=None, From e71fc53e31445e03facc6e31cb5b6424d98e5771 Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Mon, 22 Mar 2021 10:30:42 -0600 Subject: [PATCH 24/55] updated doc strings --- mbuild/lib/recipes/polymer.py | 51 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 353edbd1b..706297eb8 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -40,29 +40,26 @@ def __init__(self): self.end_groups = [] def build(self, n, sequence='A'): - """Connect one or more components in a specified sequence. + """Connect one or more components in a specified sequence. - Parameters - ---------- - monomers : mb.Compound or list of mb.Compound - The compound(s) to replicate. - 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 assigned by the built-in `sorted()`. - """ + Parameters + ---------- + monomers : mb.Compound or list of mb.Compound + The compound(s) to replicate. + 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 assigned by the built-in `sorted()`.""" if n < 1: raise ValueError('n must be 1 or more') n_monomers = n*len(sequence) - if isinstance(self.monomers, Compound): - self.monomers = (self.monomers,) 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' @@ -105,7 +102,7 @@ def build(self, n, sequence='A'): if not self.end_groups: # Cap each end with Hydrogens hydrogen = H() - hydrogen['up'].update_separation(0.0547) + hydrogen['up'].update_separation(0.0547) # Defaut to H-C bond len hydrogen_2 = clone(hydrogen) self.end_groups.extend([hydrogen, hydrogen_2]) head_port.update_separation(0.0547) @@ -126,8 +123,8 @@ def build(self, n, sequence='A'): tail_port ) - def add_monomer(self, monomer, bonding_indices, separation, - port_labels=['A', 'B'], orientation=None, + def add_monomer(self, compound, bonding_indices, separation, + port_labels=['A', 'B'], orientation=[None, None], replace=True): """ Add an mBuild compound to self.monomers which will be used to build the polymer. @@ -135,8 +132,8 @@ def add_monomer(self, monomer, bonding_indices, separation, Parameters ---------- - monomer : mb.Compound - A compound of an individual monomer + compound : mb.Compound + A compound of the individual monomer bonding_indices : list of int of length 2 The particle indicies of monomer that represent the polymer bonding sites. You can specify the indices of particles that will @@ -174,15 +171,15 @@ def add_monomer(self, monomer, bonding_indices, separation, else: self.port_labels.extend(port_labels) - for idx, label in zip(bonding_indices, port_labels): - _add_port(monomer, label, idx, separation, orientation, replace) + for idx, label, orientation in zip(bonding_indices, port_labels, orientation): + _add_port(compound, label, idx, separation, orientation, replace) if replace: - remove_atom1 = monomer[bonding_indices[0]] - remove_atom2 = monomer[bonding_indices[1]] - monomer.remove(remove_atom1) - monomer.remove(remove_atom2) + remove_atom1 = compound[bonding_indices[0]] + remove_atom2 = compound[bonding_indices[1]] + compound.remove(remove_atom1) + compound.remove(remove_atom2) - self.monomers.append(monomer) + self.monomers.append(compound) def add_end_groups(self, compound, bond_index, separation, orientation=None, replace=True): """ From 90b5eed5cca96e940f95ae674e3c4920a6499b90 Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Mon, 22 Mar 2021 12:14:56 -0600 Subject: [PATCH 25/55] including jupyter notebook example and walk through --- mbuild/mbuild-polymer-example.ipynb | 963 ++++++++++++++++++++++++++++ 1 file changed, 963 insertions(+) create mode 100644 mbuild/mbuild-polymer-example.ipynb diff --git a/mbuild/mbuild-polymer-example.ipynb b/mbuild/mbuild-polymer-example.ipynb new file mode 100644 index 000000000..151673d9f --- /dev/null +++ b/mbuild/mbuild-polymer-example.ipynb @@ -0,0 +1,963 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + } + ], + "source": [ + "import mbuild as mb\n", + "Poly = mb.lib.recipes.polymer" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Quick example of the API and workflow\n", + "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", + "chain = Poly.Polymer()\n", + "\n", + "chain.add_monomer(compound=comp,\n", + " bonding_indices=[2, -1],\n", + " separation=.15,\n", + " replace=True)\n", + "\n", + "chain.add_end_groups(mb.load('C(=O)O',smiles=True), # Capping off this polymer with Carboxylic acid groups\n", + " bond_index=3,\n", + " separation=0.15)\n", + "\n", + "chain.build(n=10, sequence='A')\n", + "chain.visualize().show()" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Doing the same thing, but this time without adding an end group\n", + "# Now, the polymer is capped with hydrogens (default behavior)\n", + "\n", + "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", + "chain = Poly.Polymer()\n", + "\n", + "chain.add_monomer(compound=comp,\n", + " bonding_indices=[2, -1],\n", + " separation=.15,\n", + " replace=True)\n", + "\n", + "chain.build(n=10, sequence='A')\n", + "chain.visualize().show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Here's an example with a more complicated monomer and a little more detail into what's going on \n", + "\n", + "SMILES strings for Poly-ether-ether-ketone (PEEK) \n", + "\n", + "One has para linkages, the other has meta \n", + "\n", + "Goal is to build up a polymer with alternating PARA-META monomers \n", + "\n", + "Also, just for fun, adding carboxylic acid end groups (ca)" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "[]\n", + "[]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + } + ], + "source": [ + "peek_para = mb.load(\"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",smiles=True)\n", + "peek_meta = mb.load(\"Oc1cc(Oc2ccc(C(=O)c3ccccc3)cc2)ccc1\", smiles=True)\n", + "ca = mb.load('C(=O)O', smiles=True)\n", + "peek_polymer = Poly.Polymer() # Create polymer instance\n", + "\n", + "# For now, peek_polymer is an empty mBuild compound\n", + "print(peek_polymer)\n", + "\n", + "# monomers and end_groups attributes: empty lists at the moment\n", + "print(peek_polymer.monomers)\n", + "print(peek_polymer.end_groups)" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "[< 34 particles, non-periodic, 36 bonds, id: 140518426965264>, < 34 particles, non-periodic, 36 bonds, id: 140518789642064>]\n", + "\n", + "[< 4 particles, non-periodic, 3 bonds, id: 140518433410256>, < 4 particles, non-periodic, 3 bonds, id: 140518426652944>]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + } + ], + "source": [ + "# Use the add_monomer and add_end_group methods\n", + "# Pass in the compounds we want to use as monomers and end_groups\n", + "# The add_monomer function is called for each unique monomer type\n", + "# The add_end_groups funciton is called for the end group compound\n", + "\n", + "peek_polymer.add_monomer(compound=peek_para,\n", + " bonding_indices = [22, 29],\n", + " separation = 0.1376,\n", + " replace=True\n", + " )\n", + "\n", + "peek_polymer.add_monomer(compound=peek_meta,\n", + " bonding_indices = [22, 29],\n", + " separation = 0.1376,\n", + " replace=True\n", + " )\n", + "\n", + "peek_polymer.add_end_groups(ca,\n", + " bond_index=3,\n", + " separation=0.13,\n", + " replace=True)\n", + "\n", + "#At this point, peek_polymer is still an empty mBuild compound\n", + "#The monomers and end_groups attributes are no longer empty lists\n", + "print(peek_polymer)\n", + "print()\n", + "print(peek_polymer.monomers)\n", + "print()\n", + "print(peek_polymer.end_groups)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + } + ], + "source": [ + "# Now to actually make the polymer compound\n", + "# Essentially all of the currently exisitng Polymer() code was moved into a function called build\n", + "peek_polymer.build(n=3, sequence='AB')\n", + "\n", + "# peek_polymer is no longer an empty compound\n", + "print(peek_polymer)" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "peek_polymer.visualize()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A look at what is actually happening with each of the class methods \n", + "\n", + "The `add_monomer` and `add_end_group` functions are handling the creation of ports. \n", + "\n", + "The key is in the `bond_indices` and `replace` parameters.\n", + "`bond_indices` points to the hydrogen atoms that are occupying the polymer bonding site and \n", + "`replace` says to remove those atoms, and replace them with a port\n", + "\n", + "When the port is created, it defaults to using the orientation that already existed between the hydrogen atom and the atom it was bonded to. " + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before passing the compound into add_monomer()\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "After passing the compound into add_monomer()\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "peek_para = mb.load(\"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",smiles=True)\n", + "print('Before passing the compound into add_monomer()')\n", + "peek_para.visualize(show_ports=True).show()\n", + "\n", + "peek_polymer = Poly.Polymer()\n", + "\n", + "peek_polymer.add_monomer(compound=peek_para,\n", + " bonding_indices = [22, 29],\n", + " separation = 0.1376,\n", + " replace=True\n", + " )\n", + "print('After passing the compound into add_monomer()')\n", + "peek_polymer.monomers[0].visualize(show_ports=True).show()" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before passing the compound into add_end_groups()\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "After passing the compound into add_end_groups()\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Same thing with the end group\n", + "ca = mb.load('C(=O)O', smiles=True)\n", + "print('Before passing the compound into add_end_groups()')\n", + "ca.visualize(show_ports=True).show()\n", + "\n", + "peek_polymer.add_end_groups(ca,\n", + " bond_index=3,\n", + " separation=0.13,\n", + " replace=True)\n", + "\n", + "# ca[3] is the hydrogen bonded to the carbon atom\n", + "\n", + "print('After passing the compound into add_end_groups()')\n", + "peek_polymer.end_groups[0].visualize(show_ports=True).show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using replace=False instead\n", + "So far, all of the examples above used `replace=True` and the `bonding_indices` were the indices of hydrgogens that were being replaced by ports and removed to make room for the monomer-monomer bond.\n", + "\n", + "I imagine this would be the most common work-flow for going straight from a SMILES string or compound file to a polymer, but it's possible use `replace=False`. In this case, the atoms indicated in `bonding_indices` are the atoms forming the monomer-monomer bond.\n", + "\n", + "Below is an example using the `ch2.pdb` file in the `moieties` directory. In this case, we don't want to replace/remove any hydrogens, but add onto the carbon atom" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Reproducing the alkane chain\n", + "There currently exists a recipe that produces a simple alkane chain. Below I'll use the new `polymer.py` functionality to re-create the same alkane chain." + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ch2 = mb.load('lib/moieties/ch2.pdb')\n", + "chain = Poly.Polymer()\n", + "chain.add_monomer(ch2,\n", + " bonding_indices=[0, 0],\n", + " orientation=[[0, 1, 0], [0, -1, 0]],\n", + " separation=0.15,\n", + " replace=False)\n", + "chain.build(n=7, sequence='A')\n", + "\n", + "chain.visualize()" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# It's super easy to throw different end groups on there\n", + "\n", + "ch2 = mb.load('lib/moieties/ch2.pdb')\n", + "chain = Poly.Polymer()\n", + "chain.add_monomer(ch2,\n", + " bonding_indices=[0, 0],\n", + " orientation=[[0, 1, 0], [0, -1, 0]],\n", + " separation=0.15,\n", + " replace=False)\n", + "\n", + "chain.add_end_groups(mb.load('c1ccccc1', smiles=True),\n", + " bond_index=-1,\n", + " separation=0.15,\n", + " replace=True)\n", + "chain.build(n=8, sequence='A')\n", + "\n", + "chain.visualize().show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How we can use recipes\n", + "\n", + "With this approach, to build up a polymer you would just need the following information\n", + "\n", + "1. A SMILES string (or a path to a compound file)\n", + "2. The indices of the hydrogen atoms to be removed\n", + "3. The bond length\n", + "\n", + "So, having a library of this information to pull from could be a simple as a `.py` file with a dictionary, or a `.json` file\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "monomer_dict = {\n", + " \n", + " 'polyethylene': {'smiles': \"CC\",\n", + " 'bonding_indices': [2, -1],\n", + " 'bond_length': 0.1512},\n", + " \n", + " 'peek_para': {'smiles': \"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",\n", + " 'bonding_indices': [22, 29],\n", + " 'bond_length': 0.1376,\n", + " 'description': \"poly-ether-ether-ketone with a para configuration\"\n", + " \n", + " },\n", + " \n", + " 'peek_meta': {'smiles': \"Oc1cc(Oc2ccc(C(=O)c3ccccc3)cc2)ccc1\",\n", + " 'bonding_indices': [22, 29],\n", + " 'bond_length': 0.1376,\n", + " 'description': \"poly-ether-ether-ketone with a meta configuration\"\n", + " }\n", + " \n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Maybe there is a from_recipe() function, for example:\n", + "polymer = Poly.Polymer.from_recipe(monomer='peek_para',\n", + " n=10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 07c016c1741cb7f45d4681701dcffc067c003f07 Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Mon, 22 Mar 2021 12:28:36 -0600 Subject: [PATCH 26/55] example notebook ready --- mbuild/mbuild-polymer-example.ipynb | 652 ++-------------------------- 1 file changed, 25 insertions(+), 627 deletions(-) diff --git a/mbuild/mbuild-polymer-example.ipynb b/mbuild/mbuild-polymer-example.ipynb index 151673d9f..c8aa624a3 100644 --- a/mbuild/mbuild-polymer-example.ipynb +++ b/mbuild/mbuild-polymer-example.ipynb @@ -2,18 +2,9 @@ "cells": [ { "cell_type": "code", - "execution_count": 64, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - } - ], + "outputs": [], "source": [ "import mbuild as mb\n", "Poly = mb.lib.recipes.polymer" @@ -21,66 +12,11 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Quick example of the API and workflow\n", "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", @@ -101,67 +37,12 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Doing the same thing, but this time without adding an end group\n", - "# Now, the polymer is capped with hydrogens (default behavior)\n", + "# Now, the chain is capped with hydrogens (default behavior)\n", "\n", "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", "chain = Poly.Polymer()\n", @@ -192,29 +73,11 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "[]\n", - "[]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - } - ], + "outputs": [], "source": [ "peek_para = mb.load(\"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",smiles=True)\n", "peek_meta = mb.load(\"Oc1cc(Oc2ccc(C(=O)c3ccccc3)cc2)ccc1\", smiles=True)\n", @@ -231,29 +94,9 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "[< 34 particles, non-periodic, 36 bonds, id: 140518426965264>, < 34 particles, non-periodic, 36 bonds, id: 140518789642064>]\n", - "\n", - "[< 4 particles, non-periodic, 3 bonds, id: 140518433410256>, < 4 particles, non-periodic, 3 bonds, id: 140518426652944>]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - } - ], + "outputs": [], "source": [ "# Use the add_monomer and add_end_group methods\n", "# Pass in the compounds we want to use as monomers and end_groups\n", @@ -288,25 +131,9 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - } - ], + "outputs": [], "source": [ "# Now to actually make the polymer compound\n", "# Essentially all of the currently exisitng Polymer() code was moved into a function called build\n", @@ -318,76 +145,11 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 71, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "peek_polymer.visualize()" + "peek_polymer.visualize().show()" ] }, { @@ -407,124 +169,9 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Before passing the compound into add_monomer()\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "After passing the compound into add_monomer()\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "peek_para = mb.load(\"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",smiles=True)\n", "print('Before passing the compound into add_monomer()')\n", @@ -543,124 +190,9 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Before passing the compound into add_end_groups()\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "After passing the compound into add_end_groups()\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Same thing with the end group\n", "ca = mb.load('C(=O)O', smiles=True)\n", @@ -700,76 +232,11 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": null, "metadata": { "scrolled": false }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 90, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "ch2 = mb.load('lib/moieties/ch2.pdb')\n", "chain = Poly.Polymer()\n", @@ -785,64 +252,9 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# It's super easy to throw different end groups on there\n", "\n", @@ -863,27 +275,13 @@ "chain.visualize().show()" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": {}, "source": [ "## How we can use recipes\n", "\n", - "With this approach, to build up a polymer you would just need the following information\n", + "With this approach, to build up a polymer you would just need the following information:\n", "\n", "1. A SMILES string (or a path to a compound file)\n", "2. The indices of the hydrogen atoms to be removed\n", From 8588666f36657b4b730d577ac2551f733ed537f6 Mon Sep 17 00:00:00 2001 From: chrisjonesBSU Date: Mon, 22 Mar 2021 12:49:56 -0600 Subject: [PATCH 27/55] added to recipe examples --- mbuild/mbuild-polymer-example.ipynb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/mbuild/mbuild-polymer-example.ipynb b/mbuild/mbuild-polymer-example.ipynb index c8aa624a3..bdfc61b23 100644 --- a/mbuild/mbuild-polymer-example.ipynb +++ b/mbuild/mbuild-polymer-example.ipynb @@ -300,13 +300,19 @@ " \n", " 'polyethylene': {'smiles': \"CC\",\n", " 'bonding_indices': [2, -1],\n", - " 'bond_length': 0.1512},\n", + " 'bond_length': 0.1512\n", + " },\n", + " \n", + " 'alkane': {'file': 'lib/moieties/ch2.pdb',\n", + " 'bonding_indices': [0, 0],\n", + " 'bond_length': 0.1512,\n", + " 'replace': False\n", + " },\n", " \n", " 'peek_para': {'smiles': \"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",\n", " 'bonding_indices': [22, 29],\n", " 'bond_length': 0.1376,\n", - " 'description': \"poly-ether-ether-ketone with a para configuration\"\n", - " \n", + " 'description': \"poly-ether-ether-ketone with a para configuration\" \n", " },\n", " \n", " 'peek_meta': {'smiles': \"Oc1cc(Oc2ccc(C(=O)c3ccccc3)cc2)ccc1\",\n", @@ -328,13 +334,6 @@ "polymer = Poly.Polymer.from_recipe(monomer='peek_para',\n", " n=10)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From 1120d5f9622c44c7b30a604cc7f37ad212554d0d Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 29 Mar 2021 13:26:28 -0600 Subject: [PATCH 28/55] expanded end_group options --- mbuild/lib/recipes/polymer.py | 113 ++++++++++++++++++++++++++-------- 1 file changed, 88 insertions(+), 25 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 706297eb8..386fd5a0e 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -39,7 +39,7 @@ def __init__(self): self.port_labels = [] self.end_groups = [] - def build(self, n, sequence='A'): + def build(self, n, sequence='A', add_hydrogens=True): """Connect one or more components in a specified sequence. Parameters @@ -51,7 +51,16 @@ def build(self, n, sequence='A'): 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 assigned by the built-in `sorted()`.""" + monomers in the order assigned by the built-in `sorted()`. + add_hydrogens : bool, default=True + If True, and end group compounds were not added using the + add_end_groups() function, then the head and tail monomer + will be capped off with Hydrogens. If compounds were + added to end_groups, then they will be used to cap the + polymer. + If False, and end_groups is empty, then nothing will + be used to cap off the polymer. + """ if n < 1: raise ValueError('n must be 1 or more') n_monomers = n*len(sequence) @@ -100,28 +109,29 @@ def build(self, n, sequence='A'): if not tail[label].used: tail_port = tail[label] - if not self.end_groups: # Cap each end with Hydrogens + if not self.end_groups and add_hydrogens: # Cap each end with Hydrogens hydrogen = H() - hydrogen['up'].update_separation(0.0547) # Defaut to H-C bond len + hydrogen['up'].update_separation(0.0547) # Defaut to 1/2 H-C bond len hydrogen_2 = clone(hydrogen) self.end_groups.extend([hydrogen, hydrogen_2]) - head_port.update_separation(0.0547) + head_port.update_separation(0.0547) # 1/2 H-C bond len tail_port.update_separation(0.0547) - else: # Use compounds in self.end_groups + elif self.end_groups: # Use compounds in self.end_groups head_port.update_separation(self.end_groups[0]['up'].separation) tail_port.update_separation(self.end_groups[1]['up'].separation) - - for compound in self.end_groups: - self.add(compound) - force_overlap(self.end_groups[0], - self.end_groups[0].labels['up'], - head_port - ) - force_overlap(self.end_groups[1], - self.end_groups[1].labels['up'], - tail_port - ) + if self.end_groups: # if end_groups contains hydrogens or manually added compounds + for compound in self.end_groups: + self.add(compound) + + force_overlap(self.end_groups[0], + self.end_groups[0].labels['up'], + head_port + ) + force_overlap(self.end_groups[1], + self.end_groups[1].labels['up'], + tail_port + ) def add_monomer(self, compound, bonding_indices, separation, port_labels=['A', 'B'], orientation=[None, None], @@ -135,7 +145,7 @@ def add_monomer(self, compound, bonding_indices, separation, compound : mb.Compound A compound of the individual monomer bonding_indices : list of int of length 2 - The particle indicies of monomer that represent the polymer + The particle indicies of compound that represent the polymer bonding sites. You can specify the indices of particles that will be replaced by the polymer bond, or indices of particles that act as the bonding sites. See the 'replace' parameter notes. @@ -147,11 +157,13 @@ def add_monomer(self, compound, bonding_indices, separation, Ex.) ['head', 'tail'] or ['A', 'B'] The same port labels must be used for any subsequent monomer created using add_monomer() - orientation : array-like, shape=(3,), default=None + orientation : list of array-like, shape=(3,) of length 2, default=[None, None] Vector along which to orient the port If replace = True, and orientation = None, the orientation of the bond between the particle being removed and the anchor particle is used. + Recommended behavior is to leave orientation set to None + if you are using replace=True. replace : Bool, required, default=True If True, then the particles identified by bonding_indices will be removed and ports are added to the particles they @@ -181,17 +193,68 @@ def add_monomer(self, compound, bonding_indices, separation, self.monomers.append(compound) - def add_end_groups(self, compound, bond_index, separation, orientation=None, replace=True): + def add_end_groups(self, + compound, + bond_index, + separation, + orientation=None, + replace=True, + duplicate=True): """ + compound : mbuild.Compound + A compound of the end group structure + bond_index : int + The particle index of compound that represent the bonding + site between the end group and polymer. + You can specify the indes of a particle that will + be replaced by the polymer bond, or index of a particle that acts + as the bonding sites. See the 'replace' parameter notes. + separation : float, units nm + The bond length desired at the monomer-monomer bonding site. + (separation / 2) is used to set the length of each port + orientation : array-like, shape=(3,), default=None + Vector along which to orient the port + If replace = True, and orientation = None, + the orientation of the bond between the particle being + removed and the anchor particle is used. + Recommended behavior is to leave orientation set to None + if you are using replace=True. + replace : Bool, required, default=True + If True, then the particle identified by bond_index + will be removed and ports are added to the particle it + was initially bonded to. Only use replace=True in the case + that bond_index points to a hydrogen atom bonded to the + desired bonding site particles. + If False, then the particle identified by bond_index + will have a port added, and no particle is removed from + the end group compound. + duplicate : Bool, default = True + If True, then `compound` is duplicated and added to Polymer.end_groups + twice. Set to True, if you want the same end group compound at the + head and tail of the polymer. If that's the case, you only need to + call the add_end_groups() function one time. + If False, `compound` is not duplicated, and only instance of the + end group structure is added to Polymer.end_groups. You can call + the add_end_groups() funciton a second time to added another end group. """ - compound_2 = clone(compound) + if len(self.end_groups) == 2: + raise ValueError("self.end_groups already contains 2 compounds which" + "is the maximum amount of end group compounds allowed" + ) + if len(self.end_groups) == 1 and duplicate==True: + raise ValueError("Polymer.end_groups already contains 1 compound. " + "Calling add_end_group() a second time with duplicate=True" + "will result in more than two compounds in Polymer.end_groups. " + "To add a second end group, call add_end_group() and " + "set duplicate=False" + ) _add_port(compound, 'up', bond_index, separation, orientation, replace) - _add_port(compound_2, 'up', bond_index, separation, orientation, replace) if replace: compound.remove(compound[bond_index]) - compound_2.remove(compound_2[bond_index]) - - self.end_groups.extend([compound, compound_2]) + self.end_groups.append(compound) + if duplicate: + compound_2 = clone(compound) + self.end_groups.append(compound_2) def _add_port(compound, label, atom_idx, separation, orientation=None, replace=True): From 031d5eb2f43f62a49a3cc2440bdce6a414dd42a0 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Mon, 29 Mar 2021 14:51:05 -0600 Subject: [PATCH 29/55] update alkane recipe to make tests pass --- mbuild/lib/recipes/alkane.py | 105 ++++++++++++++++++++++++++--------- mbuild/tests/base_test.py | 14 ++--- 2 files changed, 86 insertions(+), 33 deletions(-) diff --git a/mbuild/lib/recipes/alkane.py b/mbuild/lib/recipes/alkane.py index 328880bd0..0f95fb03d 100644 --- a/mbuild/lib/recipes/alkane.py +++ b/mbuild/lib/recipes/alkane.py @@ -1,5 +1,8 @@ -import mbuild as mb +from os import path + +import numpy as np +import mbuild as mb from mbuild.lib.moieties import CH2, CH3 from mbuild.lib.molecules import Methane, Ethane from mbuild.lib.atoms import H @@ -11,9 +14,12 @@ def __init__(self, n=3, cap_front=True, cap_end=True): """Initialize an Alkane Compound. Args: - n: Number of carbon atoms. - cap_front: Add a methyl group to the beginning of chain ('down' port). - cap_end: Add a methyl group to the end of chain ('up' port). + n: int, optional, default 3 + Number of carbon atoms. + cap_front: bool, optional, default True + Add a methyl group to the beginning of chain ('down' port). + cap_end: bool, optional, default True + Add methyl groups to the ends of chain ('up' and 'down' port). """ if n < 1: raise ValueError('n must be 1 or more') @@ -63,27 +69,74 @@ def __init__(self, n=3, cap_front=True, cap_end=True): # Handle general case of n >= 3 else: # Adjust length of Polmyer for absence of methyl terminations. - if not cap_front: - n += 1 - if not cap_end: - n += 1 - chain = mb.recipes.Polymer(CH2(), n=n-2, - port_labels=('up','down')) - self.add(chain, 'chain') if cap_front: - self.add(CH3(), "methyl_front") - mb.force_overlap(move_this=self['chain'], - from_positions=self['chain']['up'], - to_positions=self['methyl_front']['up']) - else: - # Hoist port label to Alkane level. - self.add(chain['up'], 'up', containment=False) - + n -= 1 if cap_end: - self.add(CH3(), 'methyl_end') - mb.force_overlap(self['methyl_end'], - self['methyl_end']['up'], - self['chain']['down']) - else: - # Hoist port label to Alkane level. - self.add(chain['down'], 'down', containment=False) + n -= 1 + chain = mb.recipes.Polymer() + + cwd = path.abspath(path.dirname(__file__)) + ch2_path = path.abspath(path.join(cwd, "../moieties/ch2.pdb")) + ch2 = mb.load(ch2_path) + ch4 = Methane() + if cap_front and cap_end: + chain.add_end_groups(ch4, 1, 0.15) + + chain.add_monomer( + ch2, + bonding_indices=[0, 0], + orientation=[[0, 1, 0], [0, -1, 0]], + separation=0.15, + replace=False, + port_labels=['up','down'] + ) + chain.build(n) + self.add(chain, "chain") + if not cap_front or not cap_end: + last = [p for p in self.particles()][-1] + bond = [b for b in self.bonds() if last in b][0] + anchor = [p for p in bond if p != last][0] + orientation = last.pos - anchor.pos + separation = np.linalg.norm(orientation) + port = mb.Port( + anchor=anchor, + orientation=orientation, + separation=separation + ) + self.remove(last) + self.add(port, 'up') + + last = [p for p in self.particles()][-1] + bond = [b for b in self.bonds() if last in b][0] + anchor = [p for p in bond if p != last][0] + orientation = last.pos - anchor.pos + separation = np.linalg.norm(orientation) + port = mb.Port( + anchor=anchor, + orientation=orientation, + separation=separation + ) + self.remove(last) + self.add(port, 'down') + + ch3 = CH3() + if cap_front: + self.add(ch3, 'methyl1') + mb.force_overlap( + self['methyl1'], + self['methyl1']['up'], + self['up'] + ) + #else: + # # Hoist port label to Alkane level. + # self.add(self["chain"]['up'], 'up', containment=False) + if cap_end: + self.add(mb.clone(ch3), 'methyl2') + mb.force_overlap( + self['methyl2'], + self['methyl2']['up'], + self['down'] + ) + #else: + # # Hoist port label to Alkane level. + # self.add(self["chain"]['down'], 'down', containment=False) diff --git a/mbuild/tests/base_test.py b/mbuild/tests/base_test.py index b55088d0d..8b3a41816 100644 --- a/mbuild/tests/base_test.py +++ b/mbuild/tests/base_test.py @@ -118,7 +118,7 @@ def rigid_benzene(self): def benzene_from_parts(self): ch = mb.load(get_fn('ch.mol2')) ch.name = 'CH' - ch.translate(-ch[0].pos) + ch.translate(-ch[0].pos) ch.add(mb.Port(anchor=ch[0], separation=0.07), 'a') ch['a'].rotate(120.0 * (np.pi/180.0), around=np.asarray([0, 0, 1])) ch.add(mb.Port(anchor=ch[0], separation=0.07), 'b') @@ -148,7 +148,7 @@ def box_of_benzenes(self, benzene): benzene.name = 'Benzene' filled = mb.fill_box(benzene, n_compounds=n_benzenes, - box=[0, 0, 0, 4, 4, 4]) + box=[0, 0, 0, 4, 4, 4]) filled.label_rigid_bodies(discrete_bodies='Benzene', rigid_particles='C') return filled @@ -157,13 +157,13 @@ def rigid_ch(self): ch = mb.load(get_fn('ch.mol2')) ch.name = 'CH' ch.label_rigid_bodies() - ch.translate(-ch[0].pos) + ch.translate(-ch[0].pos) ch.add(mb.Port(anchor=ch[0]), 'a') - ch['a'].translate([0, 0.07, 0]) + ch['a'].translate([0, 0.07, 0]) ch['a'].rotate(120.0 * (np.pi/180.0), around=np.asarray([0, 0, 1])) ch.add(mb.Port(anchor=ch[0]), 'b') - ch['b'].translate([0, 0.07, 0]) + ch['b'].translate([0, 0.07, 0]) ch['b'].rotate(-120.0 * (np.pi/180.0), around=np.asarray([0, 0, 1])) return ch @@ -243,8 +243,8 @@ def cscl_crystal(self): spacing = [.4123, .4123, .4123] basis = {'Cs' : [[0.5, 0.5, 0.5]], 'Cl' : [[0, 0, 0]]} cscl_lattice = mb.Lattice(spacing, lattice_points=basis) - - + + cscl_dict = {'Cs' : cesium, 'Cl' : chlorine} cscl_compound = cscl_lattice.populate(x=3, y=3, z=1, compound_dict=cscl_dict) From 96c675bdbcc9bccbbe447e8ef70745a98c2f1869 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 30 Mar 2021 16:47:19 -0600 Subject: [PATCH 30/55] cleaned up if statements when updating head/tail ports --- mbuild/lib/recipes/polymer.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 386fd5a0e..61ae5b4c0 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -114,16 +114,13 @@ def build(self, n, sequence='A', add_hydrogens=True): hydrogen['up'].update_separation(0.0547) # Defaut to 1/2 H-C bond len hydrogen_2 = clone(hydrogen) self.end_groups.extend([hydrogen, hydrogen_2]) - head_port.update_separation(0.0547) # 1/2 H-C bond len - tail_port.update_separation(0.0547) - elif self.end_groups: # Use compounds in self.end_groups + + if self.end_groups: # Add self.end_group compounds to self head_port.update_separation(self.end_groups[0]['up'].separation) tail_port.update_separation(self.end_groups[1]['up'].separation) - - if self.end_groups: # if end_groups contains hydrogens or manually added compounds for compound in self.end_groups: self.add(compound) - + force_overlap(self.end_groups[0], self.end_groups[0].labels['up'], head_port From 11a082ccd91c2bf1512b6af6531b47a4cd984595 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Thu, 1 Apr 2021 15:01:35 -0600 Subject: [PATCH 31/55] remove trailing whitespace --- mbuild/lib/recipes/polymer.py | 44 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 61ae5b4c0..0b461a1ae 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -13,13 +13,13 @@ class Polymer(Compound): """Connect one or more components in a specified sequence. - Attributes + Attributes ---------- monomers : List of mb.Compounds The compound(s) to replicate. Add to this list using the add_monomers method. end_groups : List of mb.Compounds The compound to cap the end of the polymer. Add to this list using the - add_end_groups method. + add_end_groups method. Methods ------- @@ -30,7 +30,7 @@ class Polymer(Compound): Use to add an end group compound to Polymer.end_groups build(n, sequence) - Use to create a single polymer compound. This method uses the compounds created + Use to create a single polymer compound. This method uses the compounds created by calling the add_monomer and add_end_group methods. """ def __init__(self): @@ -53,11 +53,11 @@ def build(self, n, sequence='A', add_hydrogens=True): repetition of a monomer. Characters in `sequence` are assigned to monomers in the order assigned by the built-in `sorted()`. add_hydrogens : bool, default=True - If True, and end group compounds were not added using the + If True, and end group compounds were not added using the add_end_groups() function, then the head and tail monomer - will be capped off with Hydrogens. If compounds were + will be capped off with Hydrogens. If compounds were added to end_groups, then they will be used to cap the - polymer. + polymer. If False, and end_groups is empty, then nothing will be used to cap off the polymer. """ @@ -68,7 +68,7 @@ def build(self, n, sequence='A', add_hydrogens=True): 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' @@ -114,8 +114,8 @@ def build(self, n, sequence='A', add_hydrogens=True): hydrogen['up'].update_separation(0.0547) # Defaut to 1/2 H-C bond len hydrogen_2 = clone(hydrogen) self.end_groups.extend([hydrogen, hydrogen_2]) - - if self.end_groups: # Add self.end_group compounds to self + + if self.end_groups: # Add self.end_group compounds to self head_port.update_separation(self.end_groups[0]['up'].separation) tail_port.update_separation(self.end_groups[1]['up'].separation) for compound in self.end_groups: @@ -129,14 +129,14 @@ def build(self, n, sequence='A', add_hydrogens=True): self.end_groups[1].labels['up'], tail_port ) - + def add_monomer(self, compound, bonding_indices, separation, port_labels=['A', 'B'], orientation=[None, None], replace=True): """ Add an mBuild compound to self.monomers which will be used to build the polymer. Call this function for each unique monomer to be used in the polymer. - + Parameters ---------- compound : mb.Compound @@ -156,7 +156,7 @@ def add_monomer(self, compound, bonding_indices, separation, monomer created using add_monomer() orientation : list of array-like, shape=(3,) of length 2, default=[None, None] Vector along which to orient the port - If replace = True, and orientation = None, + If replace = True, and orientation = None, the orientation of the bond between the particle being removed and the anchor particle is used. Recommended behavior is to leave orientation set to None @@ -168,7 +168,7 @@ def add_monomer(self, compound, bonding_indices, separation, that bonding_indices point to hydrogen atoms bonded to the desired monomer-monomer bonding site particles. If False, then the particles identified by bonding_indices - will have ports added, and no particles are removed from + will have ports added, and no particles are removed from the monomer compound. """ if self.port_labels: @@ -191,10 +191,10 @@ def add_monomer(self, compound, bonding_indices, separation, self.monomers.append(compound) def add_end_groups(self, - compound, - bond_index, - separation, - orientation=None, + compound, + bond_index, + separation, + orientation=None, replace=True, duplicate=True): """ @@ -211,24 +211,24 @@ def add_end_groups(self, (separation / 2) is used to set the length of each port orientation : array-like, shape=(3,), default=None Vector along which to orient the port - If replace = True, and orientation = None, + If replace = True, and orientation = None, the orientation of the bond between the particle being removed and the anchor particle is used. Recommended behavior is to leave orientation set to None if you are using replace=True. replace : Bool, required, default=True If True, then the particle identified by bond_index - will be removed and ports are added to the particle it + will be removed and ports are added to the particle it was initially bonded to. Only use replace=True in the case that bond_index points to a hydrogen atom bonded to the desired bonding site particles. If False, then the particle identified by bond_index - will have a port added, and no particle is removed from + will have a port added, and no particle is removed from the end group compound. duplicate : Bool, default = True If True, then `compound` is duplicated and added to Polymer.end_groups twice. Set to True, if you want the same end group compound at the - head and tail of the polymer. If that's the case, you only need to + head and tail of the polymer. If that's the case, you only need to call the add_end_groups() function one time. If False, `compound` is not duplicated, and only instance of the end group structure is added to Polymer.end_groups. You can call @@ -264,7 +264,7 @@ def _add_port(compound, label, atom_idx, separation, orientation=None, replace=T orientation = compound[atom_idx].pos - anchor_particle.pos else: anchor_particle = compound[atom_idx] - + port = Port(anchor = anchor_particle, orientation=orientation, separation=separation/2 From a1b6654ec0bdf8d1e6e49673b1fe9621147d28ea Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Mon, 5 Apr 2021 17:22:57 -0600 Subject: [PATCH 32/55] allow use of compound with existing ports to be specified in init whitespace/formatting --- mbuild/lib/recipes/polymer.py | 222 +++++++++++++++++----------------- 1 file changed, 114 insertions(+), 108 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 0b461a1ae..8c4c15e1f 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -3,7 +3,6 @@ from mbuild.compound import Compound from mbuild.coordinate_transform import force_overlap from mbuild.utils.validation import assert_port_exists -from mbuild.lib.atoms import H from mbuild import clone @@ -16,30 +15,45 @@ class Polymer(Compound): Attributes ---------- monomers : List of mb.Compounds - The compound(s) to replicate. Add to this list using the add_monomers method. + The compound(s) to replicate. Add to this list using the add_monomers + method. end_groups : List of mb.Compounds The compound to cap the end of the polymer. Add to this list using the add_end_groups method. Methods ------- - add_monomer(monomer, bonding_indices, separation, port_labels, orientation, replace) + add_monomer(monomer, indices, separation, port_labels, orientation, replace) Use to add a monomer compound to Polymer.monomers add_end_groups(compound, bond_index, separation, orientation, replace) Use to add an end group compound to Polymer.end_groups build(n, sequence) - Use to create a single polymer compound. This method uses the compounds created - by calling the add_monomer and add_end_group methods. + Use to create a single polymer compound. This method uses the compounds + created by calling the add_monomer and add_end_group methods. """ - def __init__(self): + def __init__(self, monomers=None, end_groups=None): super(Polymer, self).__init__() - self.monomers = [] - self.port_labels = [] - self.end_groups = [] - - def build(self, n, sequence='A', add_hydrogens=True): + self._monomers = monomers or [] + self._end_groups = end_groups or [None, None] + if len(self._end_groups) != 2: + raise ValueError( + "Please provide two end groups;" + f"you provided {len(self._end_groups)}" + ) + self._port_labels = ["up", "down"] + self._headtail = [None, None] + + @property + def monomers(self): + return self._monomers + + @property + def end_groups(self): + return self._end_groups + + def build(self, n, sequence='A'): """Connect one or more components in a specified sequence. Parameters @@ -65,18 +79,20 @@ def build(self, n, sequence='A', add_hydrogens=True): raise ValueError('n must be 1 or more') n_monomers = n*len(sequence) - for monomer in self.monomers: - for label in self.port_labels: + 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): + unique_seq_ids = 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.') + #if len(self._end_groups) != 2: + # raise IndexError("Two end groups must be specified!") # 'A': monomer_1, 'B': monomer_2.... - seq_map = dict(zip(unique_seq_ids, self.monomers)) + seq_map = dict(zip(unique_seq_ids, self._monomers)) last_part = None for n_added, seq_item in enumerate(it.cycle(sequence)): @@ -88,60 +104,54 @@ def build(self, n, sequence='A', add_hydrogens=True): # 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[self.port_labels[1]], - last_part.labels[self.port_labels[0]]) + this_part.labels[self._port_labels[1]], + last_part.labels[self._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[self.port_labels[0]], self.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[self.port_labels[1]], self.port_labels[1], containment=False) - # Add the end groups head = self['monomer[0]'] # First monomer tail = self['monomer[{}]'.format(n_monomers - 1)] # Last monomer - for label in self.port_labels: + for label in self._port_labels: if not head[label].used: head_port = head[label] if not tail[label].used: tail_port = tail[label] - if not self.end_groups and add_hydrogens: # Cap each end with Hydrogens - hydrogen = H() - hydrogen['up'].update_separation(0.0547) # Defaut to 1/2 H-C bond len - hydrogen_2 = clone(hydrogen) - self.end_groups.extend([hydrogen, hydrogen_2]) + head_tail = [head_port, tail_port] - if self.end_groups: # Add self.end_group compounds to self - head_port.update_separation(self.end_groups[0]['up'].separation) - tail_port.update_separation(self.end_groups[1]['up'].separation) - for compound in self.end_groups: + 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 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) + + - force_overlap(self.end_groups[0], - self.end_groups[0].labels['up'], - head_port - ) - force_overlap(self.end_groups[1], - self.end_groups[1].labels['up'], - tail_port - ) - - def add_monomer(self, compound, bonding_indices, separation, - port_labels=['A', 'B'], orientation=[None, None], - replace=True): + def add_monomer(self, compound, indices, separation=None, + orientation=[None, None], replace=True): """ - Add an mBuild compound to self.monomers which will be used to build the polymer. - Call this function for each unique monomer to be used in the polymer. + Add an mBuild compound to self.monomers which will be used to build the + polymer. Call this function for each unique monomer to be used in the + polymer. Parameters ---------- compound : mb.Compound A compound of the individual monomer - bonding_indices : list of int of length 2 + indices : list of int of length 2 The particle indicies of compound that represent the polymer bonding sites. You can specify the indices of particles that will be replaced by the polymer bond, or indices of particles that act @@ -149,12 +159,8 @@ def add_monomer(self, compound, bonding_indices, separation, separation : float, units nm The bond length desired at the monomer-monomer bonding site. (separation / 2) is used to set the length of each port - port_labels : list of str of length 2, default=['A', 'B'] - Labels given to the two ports added to monomer. - Ex.) ['head', 'tail'] or ['A', 'B'] - The same port labels must be used for any subsequent - monomer created using add_monomer() - orientation : list of array-like, shape=(3,) of length 2, default=[None, None] + orientation : list of array-like, shape=(3,) of length 2, + default=[None, None] Vector along which to orient the port If replace = True, and orientation = None, the orientation of the bond between the particle being @@ -171,32 +177,28 @@ def add_monomer(self, compound, bonding_indices, separation, will have ports added, and no particles are removed from the monomer compound. """ - if self.port_labels: - if not sorted(set(port_labels)) == sorted(set(self.port_labels)): - raise ValueError("The port labels given for each" + - "monomer must match. The previous" + - "port labels used were {}".format(self.port_labels) - ) - else: - self.port_labels.extend(port_labels) + port_labels = ["up", "down"] + comp = mb.clone(compound) - for idx, label, orientation in zip(bonding_indices, port_labels, orientation): - _add_port(compound, label, idx, separation, orientation, replace) + for idx, label, orientation in zip(indices, port_labels, orientation): + _add_port(comp, label, idx, separation, orientation, replace) if replace: - remove_atom1 = compound[bonding_indices[0]] - remove_atom2 = compound[bonding_indices[1]] - compound.remove(remove_atom1) - compound.remove(remove_atom2) - - self.monomers.append(compound) - - def add_end_groups(self, - compound, - bond_index, - separation, - orientation=None, - replace=True, - duplicate=True): + remove_atom1 = comp[indices[0]] + remove_atom2 = comp[indices[1]] + comp.remove(remove_atom1) + comp.remove(remove_atom2) + self._monomers.append(comp) + + def add_end_groups( + self, + compound, + bond_index, + separation=None, + orientation=None, + replace=True, + label='head', + duplicate=True + ): """ compound : mbuild.Compound A compound of the end group structure @@ -226,51 +228,55 @@ def add_end_groups(self, will have a port added, and no particle is removed from the end group compound. duplicate : Bool, default = True - If True, then `compound` is duplicated and added to Polymer.end_groups - twice. Set to True, if you want the same end group compound at the - head and tail of the polymer. If that's the case, you only need to - call the add_end_groups() function one time. + If True, then `compound` is duplicated and added to + Polymer.end_groups twice. Set to True, if you want the same end + group compound at the head and tail of the polymer. If that's the + case, you only need to call the add_end_groups() function one time. If False, `compound` is not duplicated, and only instance of the end group structure is added to Polymer.end_groups. You can call - the add_end_groups() funciton a second time to added another end group. + the add_end_groups() function a second time to added another end + group. """ - if len(self.end_groups) == 2: - raise ValueError("self.end_groups already contains 2 compounds which" - "is the maximum amount of end group compounds allowed" - ) - if len(self.end_groups) == 1 and duplicate==True: - raise ValueError("Polymer.end_groups already contains 1 compound. " - "Calling add_end_group() a second time with duplicate=True" - "will result in more than two compounds in Polymer.end_groups. " - "To add a second end group, call add_end_group() and " - "set duplicate=False" - ) - _add_port(compound, 'up', bond_index, separation, orientation, replace) + comp = mb.clone(compound) + separation = _add_port( + comp, 'up', bond_index, separation, orientation, replace + ) if replace: - compound.remove(compound[bond_index]) - self.end_groups.append(compound) + comp.remove(comp[bond_index]) if duplicate: - compound_2 = clone(compound) - self.end_groups.append(compound_2) + self._end_groups = [comp, clone(comp)] + self._headtail = [separation/2, separation/2] + else: + if label.lower() == "head": + self._end_groups[0] = comp + self._headtail[0] = separation / 2 + elif label.lower() == "tail": + self._end_groups[1] = comp + self._headtail[1] = separation / 2 + else: + raise ValueError("Label must be 'head' or 'tail'") + -def _add_port(compound, label, atom_idx, separation, orientation=None, replace=True): +def _add_port(compound, label, idx, separation, orientation=None, replace=True): """ """ if replace: - atom_bonds = [bond for bond in compound.bonds() if compound[atom_idx] in bond][0] - anchor_particle = [p for p in atom_bonds if p != compound[atom_idx]][0] - if not orientation: - orientation = compound[atom_idx].pos - anchor_particle.pos + atom_bonds = [b for b in compound.bonds() if compound[idx] in b][0] + anchor_particle = [p for p in atom_bonds if p != compound[idx]][0] + if orientation is None: + orientation = compound[idx].pos - anchor_particle.pos + if separation is None: + separation = np.linalg.norm(compound[idx].pos - anchor_particle.pos) else: - anchor_particle = compound[atom_idx] + anchor_particle = compound[idx] - port = Port(anchor = anchor_particle, + port = Port(anchor=anchor_particle, orientation=orientation, separation=separation/2 ) compound.add(port, label=label) - + return separation From db0d723dfe26ff786cf19e2bdb32d44616a01613 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Tue, 6 Apr 2021 13:22:53 -0600 Subject: [PATCH 33/55] update alkane to new schema --- mbuild/lib/recipes/alkane.py | 74 +++++------------------------------- 1 file changed, 10 insertions(+), 64 deletions(-) diff --git a/mbuild/lib/recipes/alkane.py b/mbuild/lib/recipes/alkane.py index 0f95fb03d..1f248738f 100644 --- a/mbuild/lib/recipes/alkane.py +++ b/mbuild/lib/recipes/alkane.py @@ -68,75 +68,21 @@ def __init__(self, n=3, cap_front=True, cap_end=True): # Handle general case of n >= 3 else: + end_groups = [None, None] # Adjust length of Polmyer for absence of methyl terminations. if cap_front: n -= 1 + end_groups[0] = CH3() if cap_end: n -= 1 - chain = mb.recipes.Polymer() + end_groups[1] = CH3() - cwd = path.abspath(path.dirname(__file__)) - ch2_path = path.abspath(path.join(cwd, "../moieties/ch2.pdb")) - ch2 = mb.load(ch2_path) - ch4 = Methane() - if cap_front and cap_end: - chain.add_end_groups(ch4, 1, 0.15) - - chain.add_monomer( - ch2, - bonding_indices=[0, 0], - orientation=[[0, 1, 0], [0, -1, 0]], - separation=0.15, - replace=False, - port_labels=['up','down'] - ) + chain = mb.recipes.Polymer(monomers=[CH2()], end_groups=end_groups) chain.build(n) self.add(chain, "chain") - if not cap_front or not cap_end: - last = [p for p in self.particles()][-1] - bond = [b for b in self.bonds() if last in b][0] - anchor = [p for p in bond if p != last][0] - orientation = last.pos - anchor.pos - separation = np.linalg.norm(orientation) - port = mb.Port( - anchor=anchor, - orientation=orientation, - separation=separation - ) - self.remove(last) - self.add(port, 'up') - - last = [p for p in self.particles()][-1] - bond = [b for b in self.bonds() if last in b][0] - anchor = [p for p in bond if p != last][0] - orientation = last.pos - anchor.pos - separation = np.linalg.norm(orientation) - port = mb.Port( - anchor=anchor, - orientation=orientation, - separation=separation - ) - self.remove(last) - self.add(port, 'down') - - ch3 = CH3() - if cap_front: - self.add(ch3, 'methyl1') - mb.force_overlap( - self['methyl1'], - self['methyl1']['up'], - self['up'] - ) - #else: - # # Hoist port label to Alkane level. - # self.add(self["chain"]['up'], 'up', containment=False) - if cap_end: - self.add(mb.clone(ch3), 'methyl2') - mb.force_overlap( - self['methyl2'], - self['methyl2']['up'], - self['down'] - ) - #else: - # # Hoist port label to Alkane level. - # self.add(self["chain"]['down'], 'down', containment=False) + if not cap_front: + # Hoist port label to Alkane level. + self.add(self["chain"]['up'], 'up', containment=False) + if not cap_end: + # Hoist port label to Alkane level. + self.add(self["chain"]['down'], 'down', containment=False) From a169da9353e0779e0d2604cf5ea15a7eaab67b6e Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Tue, 6 Apr 2021 13:41:38 -0600 Subject: [PATCH 34/55] update tests --- mbuild/tests/test_monolayer.py | 12 ++++++++---- mbuild/tests/test_polymer.py | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/mbuild/tests/test_monolayer.py b/mbuild/tests/test_monolayer.py index ee4cfe53a..20b82fca3 100755 --- a/mbuild/tests/test_monolayer.py +++ b/mbuild/tests/test_monolayer.py @@ -12,7 +12,8 @@ def test_monolayer(self, ch2): m = 8 pattern = mb.Grid2DPattern(n, m) - chain = mb.recipes.Polymer(ch2, n=10) + chain = mb.recipes.Polymer(monomers=[ch2]) + chain.build(n=10) monolayer = mb.recipes.Monolayer( surface=Betacristobalite(), chains=chain, @@ -27,7 +28,8 @@ def test_pattern_kwargs(self, ch2): m = 8 pattern = mb.Grid2DPattern(n, m) - chain = mb.recipes.Polymer(ch2, n=10) + chain = mb.recipes.Polymer(monomers=[ch2]) + chain.build(n=10) monolayer = mb.recipes.Monolayer(surface=Betacristobalite(), chains=H(), guest_port_name='up', @@ -46,8 +48,10 @@ def test_mixed_monolayer(self, ch2): pattern = mb.Grid2DPattern(n, m) fractions = [0.75,0.25] - chain_a = mb.recipes.Polymer(ch2, n=5) - chain_b = mb.recipes.Polymer(ch2, n=15) + chain_a = mb.recipes.Polymer(monomers=[ch2]) + chain_a.build(n=5) + chain_b = mb.recipes.Polymer(monomers=[ch2]) + chain_b.build(n=15) monolayer = mb.recipes.Monolayer(surface=Betacristobalite(), chains=[chain_a, chain_b], fractions=fractions, diff --git a/mbuild/tests/test_polymer.py b/mbuild/tests/test_polymer.py index c89513fd7..cbe60b622 100755 --- a/mbuild/tests/test_polymer.py +++ b/mbuild/tests/test_polymer.py @@ -9,14 +9,16 @@ class TestPolymer(BaseTest): def test_polymer(self, ch2): n = 6 - c6 = mb.recipes.Polymer(ch2, n=n) + c6 = mb.recipes.Polymer(monomers=[ch2]) + c6.build(n=n) assert c6.n_particles == n * 3 assert c6.n_bonds == n * 2 + (n - 1) def test_block_copolymer(self, ch2, ester): n = 2 sequence = 'ABBA' - abba = mb.recipes.Polymer([ch2, ester], sequence=sequence, n=n) + abba = mb.recipes.Polymer(monomers=[ch2, ester]) + abba.build(n=n, sequence=sequence) assert abba.n_particles == n * 3 * len(sequence) assert len(abba.children) == len(sequence) * n From 5208f66310fac1a5372b7b50e7bc3531e271caa5 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Tue, 6 Apr 2021 13:41:59 -0600 Subject: [PATCH 35/55] blacked, changed how head/tail port work --- mbuild/lib/recipes/polymer.py | 202 +++++++++++++++++----------------- 1 file changed, 103 insertions(+), 99 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 8c4c15e1f..50a0b6693 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -1,4 +1,7 @@ import itertools as it + +import numpy as np + from mbuild.port import Port from mbuild.compound import Compound from mbuild.coordinate_transform import force_overlap @@ -6,7 +9,7 @@ from mbuild import clone -__all__ = ['Polymer'] +__all__ = ["Polymer"] class Polymer(Compound): @@ -14,10 +17,10 @@ class Polymer(Compound): Attributes ---------- - monomers : List of mb.Compounds + monomers : List of mbuild.Compounds The compound(s) to replicate. Add to this list using the add_monomers method. - end_groups : List of mb.Compounds + end_groups : List of mbuild.Compounds The compound to cap the end of the polymer. Add to this list using the add_end_groups method. @@ -26,22 +29,23 @@ class Polymer(Compound): add_monomer(monomer, indices, separation, port_labels, orientation, replace) Use to add a monomer compound to Polymer.monomers - add_end_groups(compound, bond_index, separation, orientation, replace) + add_end_groups(compound, index, separation, orientation, replace) Use to add an end group compound to Polymer.end_groups build(n, sequence) Use to create a single polymer compound. This method uses the compounds created by calling the add_monomer and add_end_group methods. """ + def __init__(self, monomers=None, end_groups=None): super(Polymer, self).__init__() self._monomers = monomers or [] self._end_groups = end_groups or [None, None] if len(self._end_groups) != 2: raise ValueError( - "Please provide two end groups;" - f"you provided {len(self._end_groups)}" - ) + "Please provide two end groups;" + f" you provided {len(self._end_groups)}" + ) self._port_labels = ["up", "down"] self._headtail = [None, None] @@ -53,71 +57,76 @@ def monomers(self): def end_groups(self): return self._end_groups - def build(self, n, sequence='A'): + def build(self, n, sequence="A"): """Connect one or more components in a specified sequence. Parameters ---------- - monomers : mb.Compound or list of mb.Compound + monomers : mbuild.Compound or list of mbuild.Compound The compound(s) to replicate. 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 assigned by the built-in `sorted()`. + monomers in the order they appear in `Polymer.monomers`. add_hydrogens : bool, default=True If True, and end group compounds were not added using the add_end_groups() function, then the head and tail monomer - will be capped off with Hydrogens. If compounds were + will be capped off with hydrogen atoms. If compounds were added to end_groups, then they will be used to cap the polymer. If False, and end_groups is empty, then nothing will be used to cap off the polymer. - """ + """ if n < 1: - raise ValueError('n must be 1 or more') - n_monomers = n*len(sequence) + 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 = set(sequence) + 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.') - #if len(self._end_groups) != 2: - # raise IndexError("Two end groups must be specified!") + 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)) - + print(seq_map) 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[$]') + 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[self._port_labels[1]], - last_part.labels[self._port_labels[0]]) + # 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 - for label in self._port_labels: - if not head[label].used: - head_port = head[label] - if not tail[label].used: - tail_port = tail[label] + 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] @@ -126,10 +135,7 @@ def build(self, n, sequence='A'): 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] - ) + force_overlap(compound, compound.labels["up"], head_tail[i]) else: # if None, hoist port to polymer level self.add(head_tail[i], self._port_labels[i], containment=False) @@ -138,18 +144,21 @@ def build(self, n, sequence='A'): if port not in self.available_ports(): self.remove(port) - - - def add_monomer(self, compound, indices, separation=None, - orientation=[None, None], replace=True): - """ - Add an mBuild compound to self.monomers which will be used to build the - polymer. Call this function for each unique monomer to be used in the - polymer. + def add_monomer( + self, + compound, + indices, + separation=None, + orientation=[None, None], + replace=True, + ): + """Add an mBuild compound to self.monomers which will be used to build + the polymer. Call this function for each unique monomer to be used in + the polymer. Parameters ---------- - compound : mb.Compound + compound : mbuild.Compound A compound of the individual monomer indices : list of int of length 2 The particle indicies of compound that represent the polymer @@ -178,7 +187,7 @@ def add_monomer(self, compound, indices, separation=None, the monomer compound. """ port_labels = ["up", "down"] - comp = mb.clone(compound) + comp = clone(compound) for idx, label, orientation in zip(indices, port_labels, orientation): _add_port(comp, label, idx, separation, orientation, replace) @@ -192,60 +201,59 @@ def add_monomer(self, compound, indices, separation=None, def add_end_groups( self, compound, - bond_index, + index, separation=None, orientation=None, replace=True, - label='head', - duplicate=True + label="head", + duplicate=True, ): """ compound : mbuild.Compound A compound of the end group structure - bond_index : int - The particle index of compound that represent the bonding + index : int + The particle index in compound that represents the bonding site between the end group and polymer. - You can specify the indes of a particle that will - be replaced by the polymer bond, or index of a particle that acts - as the bonding sites. See the 'replace' parameter notes. - separation : float, units nm - The bond length desired at the monomer-monomer bonding site. - (separation / 2) is used to set the length of each port - orientation : array-like, shape=(3,), default=None + You can specify the index of a particle that will + be replaced by the polymer bond or that acts as the bonding site. + See the `replace` parameter notes. + separation : float + The bond length (units nm) desired between monomer and end-group. + orientation : array-like, shape=(3,), default None Vector along which to orient the port - If replace = True, and orientation = None, - the orientation of the bond between the particle being - removed and the anchor particle is used. - Recommended behavior is to leave orientation set to None - if you are using replace=True. - replace : Bool, required, default=True - If True, then the particle identified by bond_index - will be removed and ports are added to the particle it - was initially bonded to. Only use replace=True in the case - that bond_index points to a hydrogen atom bonded to the - desired bonding site particles. - If False, then the particle identified by bond_index - will have a port added, and no particle is removed from - the end group compound. - duplicate : Bool, default = True + If `replace=True` and `orientation=None`, the orientation of the + bond between the particle being removed and the anchor particle is + used. + Recommended behavior is to leave orientation set to None if you + are using `replace=True`. + replace : Bool, default True + If True, then the particle identified by `index` will be removed + and ports are added to the particle it was initially bonded to. + Only use `replace=True` in the case that index points to a hydrogen + atom bonded to the desired bonding site particle. + If False, then the particle identified by `index` will have a port + added and no particle is removed from the end group compound. + label : str, default 'head' + Whether to add the end group to the 'head or 'tail' of the polymer. + If `duplicate=True`, `label` is ignored. + duplicate : Bool, default True If True, then `compound` is duplicated and added to - Polymer.end_groups twice. Set to True, if you want the same end + `Polymer.end_groups` twice. Set to True if you want the same end group compound at the head and tail of the polymer. If that's the - case, you only need to call the add_end_groups() function one time. - If False, `compound` is not duplicated, and only instance of the - end group structure is added to Polymer.end_groups. You can call - the add_end_groups() function a second time to added another end - group. + case, you only need to call `add_end_groups()` once. + If False, `compound` is not duplicated and only one instance of the + end group structure is added to `Polymer.end_groups`. You can call + `add_end_groups()` a second time to add another end group. """ - comp = mb.clone(compound) + comp = clone(compound) separation = _add_port( - comp, 'up', bond_index, separation, orientation, replace - ) + comp, "up", index, separation, orientation, replace + ) if replace: - comp.remove(comp[bond_index]) + comp.remove(comp[index]) if duplicate: self._end_groups = [comp, clone(comp)] - self._headtail = [separation/2, separation/2] + self._headtail = [separation / 2, separation / 2] else: if label.lower() == "head": self._end_groups[0] = comp @@ -257,26 +265,22 @@ def add_end_groups( raise ValueError("Label must be 'head' or 'tail'") - def _add_port(compound, label, idx, separation, orientation=None, replace=True): - """ + """Add the ports to the compound at the specified particle index, either + using that particle as the anchor or replacing it entirely. """ if replace: atom_bonds = [b for b in compound.bonds() if compound[idx] in b][0] - anchor_particle = [p for p in atom_bonds if p != compound[idx]][0] + anchor = [p for p in atom_bonds if p != compound[idx]][0] if orientation is None: - orientation = compound[idx].pos - anchor_particle.pos + orientation = compound[idx].pos - anchor.pos if separation is None: - separation = np.linalg.norm(compound[idx].pos - anchor_particle.pos) + separation = np.linalg.norm(compound[idx].pos - anchor.pos) else: - anchor_particle = compound[idx] + anchor = compound[idx] - port = Port(anchor=anchor_particle, - orientation=orientation, - separation=separation/2 - ) + port = Port( + anchor=anchor, orientation=orientation, separation=separation / 2, + ) compound.add(port, label=label) return separation - - - From cfdfbcf05e212c475776be82739ccd482374e7d7 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Tue, 6 Apr 2021 13:49:41 -0600 Subject: [PATCH 36/55] remove debug print --- mbuild/lib/recipes/polymer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 50a0b6693..1b6832185 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -97,7 +97,6 @@ def build(self, n, sequence="A"): # 'A': monomer_1, 'B': monomer_2.... seq_map = dict(zip(unique_seq_ids, self._monomers)) - print(seq_map) last_part = None for n_added, seq_item in enumerate(it.cycle(sequence)): this_part = clone(seq_map[seq_item]) From 31a9bd9874cbe07530c3df61672c90ee634dd170 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Tue, 6 Apr 2021 13:56:24 -0600 Subject: [PATCH 37/55] update notebook --- mbuild/mbuild-polymer-example.ipynb | 88 ++++++++++++++--------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/mbuild/mbuild-polymer-example.ipynb b/mbuild/mbuild-polymer-example.ipynb index bdfc61b23..31490f61a 100644 --- a/mbuild/mbuild-polymer-example.ipynb +++ b/mbuild/mbuild-polymer-example.ipynb @@ -7,32 +7,30 @@ "outputs": [], "source": [ "import mbuild as mb\n", - "Poly = mb.lib.recipes.polymer" + "from mbuild.lib.recipes.polymer import Polymer" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "# Quick example of the API and workflow\n", "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", - "chain = Poly.Polymer()\n", + "chain = Polymer()\n", "\n", "chain.add_monomer(compound=comp,\n", - " bonding_indices=[2, -1],\n", - " separation=.15,\n", - " replace=True)\n", + " indices=[2, -1],\n", + " separation=.15,\n", + " replace=True)\n", "\n", "chain.add_end_groups(mb.load('C(=O)O',smiles=True), # Capping off this polymer with Carboxylic acid groups\n", - " bond_index=3,\n", - " separation=0.15)\n", + " index=3,\n", + " separation=0.15)\n", "\n", "chain.build(n=10, sequence='A')\n", - "chain.visualize().show()" + "chain.visualize(show_ports=True).show()" ] }, { @@ -45,15 +43,15 @@ "# Now, the chain is capped with hydrogens (default behavior)\n", "\n", "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", - "chain = Poly.Polymer()\n", + "chain = Polymer()\n", "\n", "chain.add_monomer(compound=comp,\n", - " bonding_indices=[2, -1],\n", - " separation=.15,\n", - " replace=True)\n", + " indices=[2, -1],\n", + " separation=.15,\n", + " replace=True)\n", "\n", "chain.build(n=10, sequence='A')\n", - "chain.visualize().show()" + "chain.visualize(show_ports=True).show()" ] }, { @@ -74,15 +72,13 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "peek_para = mb.load(\"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",smiles=True)\n", "peek_meta = mb.load(\"Oc1cc(Oc2ccc(C(=O)c3ccccc3)cc2)ccc1\", smiles=True)\n", "ca = mb.load('C(=O)O', smiles=True)\n", - "peek_polymer = Poly.Polymer() # Create polymer instance\n", + "peek_polymer = Polymer() # Create polymer instance\n", "\n", "# For now, peek_polymer is an empty mBuild compound\n", "print(peek_polymer)\n", @@ -104,28 +100,28 @@ "# The add_end_groups funciton is called for the end group compound\n", "\n", "peek_polymer.add_monomer(compound=peek_para,\n", - " bonding_indices = [22, 29],\n", + " indices = [22, 29],\n", " separation = 0.1376,\n", " replace=True\n", " )\n", "\n", "peek_polymer.add_monomer(compound=peek_meta,\n", - " bonding_indices = [22, 29],\n", + " indices = [22, 29],\n", " separation = 0.1376,\n", " replace=True\n", " )\n", "\n", "peek_polymer.add_end_groups(ca,\n", - " bond_index=3,\n", + " index=3,\n", " separation=0.13,\n", " replace=True)\n", "\n", "#At this point, peek_polymer is still an empty mBuild compound\n", "#The monomers and end_groups attributes are no longer empty lists\n", "print(peek_polymer)\n", - "print()\n", + "print(\"Monomers:\")\n", "print(peek_polymer.monomers)\n", - "print()\n", + "print(\"End groups:\")\n", "print(peek_polymer.end_groups)" ] }, @@ -177,10 +173,10 @@ "print('Before passing the compound into add_monomer()')\n", "peek_para.visualize(show_ports=True).show()\n", "\n", - "peek_polymer = Poly.Polymer()\n", + "peek_polymer = Polymer()\n", "\n", "peek_polymer.add_monomer(compound=peek_para,\n", - " bonding_indices = [22, 29],\n", + " indices = [22, 29],\n", " separation = 0.1376,\n", " replace=True\n", " )\n", @@ -200,7 +196,7 @@ "ca.visualize(show_ports=True).show()\n", "\n", "peek_polymer.add_end_groups(ca,\n", - " bond_index=3,\n", + " index=3,\n", " separation=0.13,\n", " replace=True)\n", "\n", @@ -233,21 +229,19 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": false - }, + "metadata": {}, "outputs": [], "source": [ "ch2 = mb.load('lib/moieties/ch2.pdb')\n", - "chain = Poly.Polymer()\n", + "chain = Polymer()\n", "chain.add_monomer(ch2,\n", - " bonding_indices=[0, 0],\n", + " indices=[0, 0],\n", " orientation=[[0, 1, 0], [0, -1, 0]],\n", " separation=0.15,\n", " replace=False)\n", "chain.build(n=7, sequence='A')\n", "\n", - "chain.visualize()" + "chain.visualize(show_ports=True).show()" ] }, { @@ -259,15 +253,15 @@ "# It's super easy to throw different end groups on there\n", "\n", "ch2 = mb.load('lib/moieties/ch2.pdb')\n", - "chain = Poly.Polymer()\n", + "chain = Polymer()\n", "chain.add_monomer(ch2,\n", - " bonding_indices=[0, 0],\n", + " indices=[0, 0],\n", " orientation=[[0, 1, 0], [0, -1, 0]],\n", " separation=0.15,\n", " replace=False)\n", "\n", "chain.add_end_groups(mb.load('c1ccccc1', smiles=True),\n", - " bond_index=-1,\n", + " index=-1,\n", " separation=0.15,\n", " replace=True)\n", "chain.build(n=8, sequence='A')\n", @@ -299,24 +293,24 @@ "monomer_dict = {\n", " \n", " 'polyethylene': {'smiles': \"CC\",\n", - " 'bonding_indices': [2, -1],\n", + " 'indices': [2, -1],\n", " 'bond_length': 0.1512\n", " },\n", " \n", " 'alkane': {'file': 'lib/moieties/ch2.pdb',\n", - " 'bonding_indices': [0, 0],\n", + " 'indices': [0, 0],\n", " 'bond_length': 0.1512,\n", " 'replace': False\n", " },\n", " \n", " 'peek_para': {'smiles': \"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",\n", - " 'bonding_indices': [22, 29],\n", + " 'indices': [22, 29],\n", " 'bond_length': 0.1376,\n", " 'description': \"poly-ether-ether-ketone with a para configuration\" \n", " },\n", " \n", " 'peek_meta': {'smiles': \"Oc1cc(Oc2ccc(C(=O)c3ccccc3)cc2)ccc1\",\n", - " 'bonding_indices': [22, 29],\n", + " 'indices': [22, 29],\n", " 'bond_length': 0.1376,\n", " 'description': \"poly-ether-ether-ketone with a meta configuration\"\n", " }\n", @@ -331,9 +325,15 @@ "outputs": [], "source": [ "# Maybe there is a from_recipe() function, for example:\n", - "polymer = Poly.Polymer.from_recipe(monomer='peek_para',\n", - " n=10)" + "polymer = Polymer.from_recipe(monomer='peek_para', n=10)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -352,7 +352,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.9" + "version": "3.7.10" } }, "nbformat": 4, From 54064ec85f768ecbb0a2a8d81219126a5379d414 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Tue, 6 Apr 2021 14:46:25 -0600 Subject: [PATCH 38/55] update notebook --- mbuild/mbuild-polymer-example.ipynb | 104 ++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 5 deletions(-) diff --git a/mbuild/mbuild-polymer-example.ipynb b/mbuild/mbuild-polymer-example.ipynb index 31490f61a..396d0bcba 100644 --- a/mbuild/mbuild-polymer-example.ipynb +++ b/mbuild/mbuild-polymer-example.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -12,9 +12,56 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Quick example of the API and workflow\n", "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", @@ -35,9 +82,56 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Doing the same thing, but this time without adding an end group\n", "# Now, the chain is capped with hydrogens (default behavior)\n", From 2265a00bb25e48d29b879df0b5f37acdbd59e748 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Tue, 6 Apr 2021 14:47:06 -0600 Subject: [PATCH 39/55] clear nb --- mbuild/mbuild-polymer-example.ipynb | 104 ++-------------------------- 1 file changed, 5 insertions(+), 99 deletions(-) diff --git a/mbuild/mbuild-polymer-example.ipynb b/mbuild/mbuild-polymer-example.ipynb index 396d0bcba..31490f61a 100644 --- a/mbuild/mbuild-polymer-example.ipynb +++ b/mbuild/mbuild-polymer-example.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -12,56 +12,9 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Quick example of the API and workflow\n", "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", @@ -82,56 +35,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# Doing the same thing, but this time without adding an end group\n", "# Now, the chain is capped with hydrogens (default behavior)\n", From 95e10b41672177e8437cd77612eaca9982a4dbe7 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Tue, 6 Apr 2021 14:47:40 -0600 Subject: [PATCH 40/55] add back add_hydrogens --- mbuild/lib/recipes/polymer.py | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 1b6832185..12f6d91b9 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -5,6 +5,7 @@ from mbuild.port import Port from mbuild.compound import Compound from mbuild.coordinate_transform import force_overlap +from mbuild.lib.atoms import H from mbuild.utils.validation import assert_port_exists from mbuild import clone @@ -57,7 +58,7 @@ def monomers(self): def end_groups(self): return self._end_groups - def build(self, n, sequence="A"): + def build(self, n, sequence="A", add_hydrogens=True): """Connect one or more components in a specified sequence. Parameters @@ -66,18 +67,16 @@ def build(self, n, sequence="A"): The compound(s) to replicate. n : int The number of times to replicate the sequence. - sequence : str, optional, default='A' + 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`. - add_hydrogens : bool, default=True - If True, and end group compounds were not added using the - add_end_groups() function, then the head and tail monomer - will be capped off with hydrogen atoms. If compounds were - added to end_groups, then they will be used to cap the - polymer. - If False, and end_groups is empty, then nothing will - be used to cap off the polymer. + 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") @@ -136,8 +135,20 @@ def build(self, n, sequence="A"): self.add(compound) force_overlap(compound, compound.labels["up"], head_tail[i]) else: - # if None, hoist port to polymer level - self.add(head_tail[i], self._port_labels[i], containment=False) + 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(): From 9975ec27f1390306d1ef81e34308be57dcef2b89 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Tue, 6 Apr 2021 14:47:54 -0600 Subject: [PATCH 41/55] add add_hydrogens=False --- mbuild/lib/recipes/alkane.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mbuild/lib/recipes/alkane.py b/mbuild/lib/recipes/alkane.py index 1f248738f..abafb4ca3 100644 --- a/mbuild/lib/recipes/alkane.py +++ b/mbuild/lib/recipes/alkane.py @@ -78,7 +78,7 @@ def __init__(self, n=3, cap_front=True, cap_end=True): end_groups[1] = CH3() chain = mb.recipes.Polymer(monomers=[CH2()], end_groups=end_groups) - chain.build(n) + chain.build(n, add_hydrogens=False) self.add(chain, "chain") if not cap_front: # Hoist port label to Alkane level. From 3e937de679849ab9975d193f9af4ca180b98cfe5 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Tue, 6 Apr 2021 14:50:32 -0600 Subject: [PATCH 42/55] add_hydrogens=False, all tests pass --- mbuild/tests/test_monolayer.py | 11 +++++++---- mbuild/tests/test_polymer.py | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/mbuild/tests/test_monolayer.py b/mbuild/tests/test_monolayer.py index 20b82fca3..e82b8b2f4 100755 --- a/mbuild/tests/test_monolayer.py +++ b/mbuild/tests/test_monolayer.py @@ -13,7 +13,7 @@ def test_monolayer(self, ch2): pattern = mb.Grid2DPattern(n, m) chain = mb.recipes.Polymer(monomers=[ch2]) - chain.build(n=10) + chain.build(n=10, add_hydrogens=False) monolayer = mb.recipes.Monolayer( surface=Betacristobalite(), chains=chain, @@ -29,7 +29,8 @@ def test_pattern_kwargs(self, ch2): pattern = mb.Grid2DPattern(n, m) chain = mb.recipes.Polymer(monomers=[ch2]) - chain.build(n=10) + chain.build(n=10, add_hydrogens=False) + monolayer = mb.recipes.Monolayer(surface=Betacristobalite(), chains=H(), guest_port_name='up', @@ -49,9 +50,11 @@ def test_mixed_monolayer(self, ch2): fractions = [0.75,0.25] chain_a = mb.recipes.Polymer(monomers=[ch2]) - chain_a.build(n=5) + chain_a.build(n=5, add_hydrogens=False) + chain_b = mb.recipes.Polymer(monomers=[ch2]) - chain_b.build(n=15) + chain_b.build(n=15, add_hydrogens=False) + monolayer = mb.recipes.Monolayer(surface=Betacristobalite(), chains=[chain_a, chain_b], fractions=fractions, diff --git a/mbuild/tests/test_polymer.py b/mbuild/tests/test_polymer.py index cbe60b622..6fa7e1aea 100755 --- a/mbuild/tests/test_polymer.py +++ b/mbuild/tests/test_polymer.py @@ -10,7 +10,7 @@ class TestPolymer(BaseTest): def test_polymer(self, ch2): n = 6 c6 = mb.recipes.Polymer(monomers=[ch2]) - c6.build(n=n) + c6.build(n=n, add_hydrogens=False) assert c6.n_particles == n * 3 assert c6.n_bonds == n * 2 + (n - 1) @@ -18,7 +18,7 @@ def test_block_copolymer(self, ch2, ester): n = 2 sequence = 'ABBA' abba = mb.recipes.Polymer(monomers=[ch2, ester]) - abba.build(n=n, sequence=sequence) + abba.build(n=n, sequence=sequence, add_hydrogens=False) assert abba.n_particles == n * 3 * len(sequence) assert len(abba.children) == len(sequence) * n From aea0ddebfefc0630e4da3080967676713adeccec Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 6 Apr 2021 15:59:44 -0600 Subject: [PATCH 43/55] changed orientation of down port to face opposite direction of up port --- mbuild/lib/moieties/ch2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mbuild/lib/moieties/ch2.py b/mbuild/lib/moieties/ch2.py index a39f55668..95efc8baa 100755 --- a/mbuild/lib/moieties/ch2.py +++ b/mbuild/lib/moieties/ch2.py @@ -13,7 +13,7 @@ def __init__(self): self.add(mb.Port(anchor=self[0]), 'up') self['up'].translate([0, 0.07, 0]) - self.add(mb.Port(anchor=self[0]), 'down') + self.add(mb.Port(anchor=self[0], orientation=[0,-1,0]), 'down') self['down'].translate([0, -0.07, 0]) if __name__ == '__main__': From 01965ef096a0c9ff06fb69dd80d56f5db83927f6 Mon Sep 17 00:00:00 2001 From: Chris Date: Tue, 6 Apr 2021 16:00:04 -0600 Subject: [PATCH 44/55] Few more examples added to show new functionality --- mbuild/mbuild-polymer-example.ipynb | 1037 ++++++++++++++++++++++++++- 1 file changed, 1008 insertions(+), 29 deletions(-) diff --git a/mbuild/mbuild-polymer-example.ipynb b/mbuild/mbuild-polymer-example.ipynb index 31490f61a..0f0353abf 100644 --- a/mbuild/mbuild-polymer-example.ipynb +++ b/mbuild/mbuild-polymer-example.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -12,9 +12,64 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Quick example of the API and workflow\n", "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", @@ -35,9 +90,64 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Doing the same thing, but this time without adding an end group\n", "# Now, the chain is capped with hydrogens (default behavior)\n", @@ -54,6 +164,257 @@ "chain.visualize(show_ports=True).show()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Flexibility with end groups\n", + "- You can only add one end group, while the other is capped off with hydrogen.\n", + "- You can add 2 different end groups, labeled by 'head' and 'tail'\n", + "- Or you can leave the ends open with an available port" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Creating and adding 2 different end groups.\n", + "# The add_end_groups() function has a duplicate parameter (defaulted to True)\n", + "# When this is true, calling add_end_group will create 2 of the same compound\n", + "# and the polymer is capped off with each.\n", + "# If you want different end groups, change Duplicate to False and call the add_end_groups() function twice\n", + "\n", + "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", + "chain = Polymer()\n", + "\n", + "chain.add_monomer(compound=comp,\n", + " indices=[2, -1],\n", + " separation=.15,\n", + " replace=True)\n", + "\n", + "chain.add_end_groups(mb.load('C(=O)O',smiles=True), # Capping off this polymer with Carboxylic acid groups\n", + " index=3,\n", + " separation=0.15,\n", + " duplicate=False) # Change duplicate to false\n", + "\n", + "chain.add_end_groups(mb.load('N', smiles=True),\n", + " index=-1, separation=0.13,\n", + " duplicate=False, label=\"tail\") # label this one tail\n", + "\n", + "chain.build(n=10, sequence='A')\n", + "chain.visualize(show_ports=True).show()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# If you only add one end group, you can still have the other end capped off with hydrogen\n", + "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", + "chain = Polymer()\n", + "\n", + "chain.add_monomer(compound=comp,\n", + " indices=[2, -1],\n", + " separation=.15)\n", + "\n", + "chain.add_end_groups(mb.load('C(=O)O',smiles=True), # Capping off this polymer with Carboxylic acid groups\n", + " index=3,\n", + " separation=0.15,\n", + " duplicate=False) # Change duplicate to false\n", + "\n", + "chain.build(n=10, sequence='A')\n", + "chain.visualize(show_ports=True).show()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "#Or to leave the ends of the polymer open with ports, change the add_hydrogens parameter in build() to False\n", + "# This would still work when adding only one end group, then leaving the other end open.\n", + "\n", + "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", + "chain = Polymer()\n", + "chain.add_monomer(compound=comp,\n", + " indices=[2, -1],\n", + " separation=.15,\n", + " replace=True)\n", + "\n", + "chain.build(n=10, sequence='A', add_hydrogens=False)\n", + "chain.visualize(show_ports=True).show()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -71,9 +432,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "[]\n", + "[None, None]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + } + ], "source": [ "peek_para = mb.load(\"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",smiles=True)\n", "peek_meta = mb.load(\"Oc1cc(Oc2ccc(C(=O)c3ccccc3)cc2)ccc1\", smiles=True)\n", @@ -83,16 +462,36 @@ "# For now, peek_polymer is an empty mBuild compound\n", "print(peek_polymer)\n", "\n", - "# monomers and end_groups attributes: empty lists at the moment\n", + "# monomers and end_groups attributes:\n", "print(peek_polymer.monomers)\n", "print(peek_polymer.end_groups)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Monomers:\n", + "[< 34 particles, non-periodic, 36 bonds, id: 140467724729872>, < 34 particles, non-periodic, 36 bonds, id: 140467724730064>]\n", + "End groups:\n", + "[< 4 particles, non-periodic, 3 bonds, id: 140467732140944>, < 4 particles, non-periodic, 3 bonds, id: 140467733947920>]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + } + ], "source": [ "# Use the add_monomer and add_end_group methods\n", "# Pass in the compounds we want to use as monomers and end_groups\n", @@ -127,9 +526,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + } + ], "source": [ "# Now to actually make the polymer compound\n", "# Essentially all of the currently exisitng Polymer() code was moved into a function called build\n", @@ -141,9 +556,64 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "peek_polymer.visualize().show()" ] @@ -165,9 +635,124 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before passing the compound into add_monomer()\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "After passing the compound into add_monomer()\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "peek_para = mb.load(\"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",smiles=True)\n", "print('Before passing the compound into add_monomer()')\n", @@ -186,9 +771,124 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before passing the compound into add_end_groups()\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "After passing the compound into add_end_groups()\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# Same thing with the end group\n", "ca = mb.load('C(=O)O', smiles=True)\n", @@ -206,6 +906,178 @@ "peek_polymer.end_groups[0].visualize(show_ports=True).show()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Not using the add_monomer and add_end_group functions\n", + "- So far, the examples call these functions everytime to create a polymer, but they are optional\n", + "- These functions basically handle the creation of ports.\n", + "- If you have a compound where the ports already exist, or you want to add them yourself you can pass them into the polymer class instance, and go straight to the build() function.\n", + "- However, you can use both approaches for the same polymer. Passing in compounds when initializing a polymer, and then adding to self.monomers or self.end_groups with the add functions." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from mbuild.lib.moieties import ch2\n", + "\n", + "chain = Polymer(monomers=[ch2.CH2()])\n", + "chain.build(n=10, add_hydrogens=True)\n", + "chain.visualize()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# You can combine passing in compounds, and using the available \n", + "# add_end_group and add_monomer functions\n", + "chain = Polymer(monomers=[ch2.CH2()])\n", + "chain.add_monomer(mb.load('c1ccccc1', smiles=True), indices=[-1, -4])\n", + "chain.add_end_groups(mb.load('C(=O)O',smiles=True), # Capping off this polymer with Carboxylic acid groups\n", + " index=3,\n", + " separation=0.15,\n", + " duplicate=False)\n", + "chain.build(n=5,sequence='AB', add_hydrogens=True)\n", + "chain.visualize()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -223,14 +1095,73 @@ "metadata": {}, "source": [ "# Reproducing the alkane chain\n", + "\n", + "### NOTE: \n", + "With the latest changes, this method of building up an alkane isn't really needed, but it still shows the ability to use the add_monomer() and add_end_groups() functions with Replace=False\n", + "\n", "There currently exists a recipe that produces a simple alkane chain. Below I'll use the new `polymer.py` functionality to re-create the same alkane chain." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "ch2 = mb.load('lib/moieties/ch2.pdb')\n", "chain = Polymer()\n", @@ -246,9 +1177,64 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", + " and should_run_async(code)\n" + ] + }, + { + "data": { + "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", + "text/html": [ + "
\n", + "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", + " jupyter labextension install jupyterlab_3dmol

\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# It's super easy to throw different end groups on there\n", "\n", @@ -327,13 +1313,6 @@ "# Maybe there is a from_recipe() function, for example:\n", "polymer = Polymer.from_recipe(monomer='peek_para', n=10)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From deca5b2041b5a922a9b2798895cb823cb59e9319 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 14 Apr 2021 12:55:13 -0600 Subject: [PATCH 45/55] adding a few unit tests --- mbuild/tests/test_polymer.py | 42 +++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/mbuild/tests/test_polymer.py b/mbuild/tests/test_polymer.py index 6fa7e1aea..c2c2349cd 100755 --- a/mbuild/tests/test_polymer.py +++ b/mbuild/tests/test_polymer.py @@ -1,13 +1,53 @@ from collections import Counter +import os import mbuild as mb from mbuild.tests.base_test import BaseTest + #from mbuild.lib.recipes import Polymer class TestPolymer(BaseTest): + + def test_polymer_from_smiles(self): + chain = mb.recipes.Polymer() + ethane = mb.load('CC', smiles=True) + chain.add_monomer(ethane, indices=[2, -2], separation=0.15, replace=True) + chain.build(n=5, add_hydrogens=True) + assert len([p for p in chain.particles() if p.name == "C"]) == 10 + assert len([p for p in chain.particles() if p.name == "H"]) == 22 + assert len(chain.available_ports()) == 0 + + def test_add_end_groups(self, ch2): + n = 6 + c6 = mb.recipes.Polymer(monomers=[ch2]) + acid = mb.load("C(=O)O", smiles=True) + c6.add_end_groups(acid, index=3, separation=0.15) + c6.build(n=n, add_hydrogens=False) + assert len([p for p in c6.particles() if p.name=="O"]) == 4 + + def test_no_end_groups(self): + chain = mb.recipes.Polymer() + ethane = mb.load('CC', smiles=True) + chain.add_monomer(ethane, indices=[2, -2], separation=0.15, replace=True) + chain.build(n=5, add_hydrogens=False) + assert len([p for p in chain.particles() if p.name == "H"]) == 20 + assert len(chain.available_ports()) == 2 + + def test_replace_is_false(self): + n = 6 + ch2 = mb.load(os.path.join(mb.__path__[0], "lib/moieties/ch2.pdb")) + chain = mb.recipes.Polymer() + chain.add_monomer(ch2, + indices=[0, 0], + orientation=[[0, 1, 0], [0, -1, 0]], + separation=0.15, + replace=False) + chain.build(n=n, add_hydrogens=False) + assert chain.n_particles == n * 3 + assert chain.n_bonds == n * 2 + (n - 1) - def test_polymer(self, ch2): + def test_polymer_from_moieties(self, ch2): n = 6 c6 = mb.recipes.Polymer(monomers=[ch2]) c6.build(n=n, add_hydrogens=False) From 56575f0c64f648f6b081082f31bc9555631f00c4 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 14 Apr 2021 12:58:01 -0600 Subject: [PATCH 46/55] removing the example notebook from the PR --- mbuild/mbuild-polymer-example.ipynb | 1339 --------------------------- 1 file changed, 1339 deletions(-) delete mode 100644 mbuild/mbuild-polymer-example.ipynb diff --git a/mbuild/mbuild-polymer-example.ipynb b/mbuild/mbuild-polymer-example.ipynb deleted file mode 100644 index 0f0353abf..000000000 --- a/mbuild/mbuild-polymer-example.ipynb +++ /dev/null @@ -1,1339 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "import mbuild as mb\n", - "from mbuild.lib.recipes.polymer import Polymer" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Quick example of the API and workflow\n", - "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", - "chain = Polymer()\n", - "\n", - "chain.add_monomer(compound=comp,\n", - " indices=[2, -1],\n", - " separation=.15,\n", - " replace=True)\n", - "\n", - "chain.add_end_groups(mb.load('C(=O)O',smiles=True), # Capping off this polymer with Carboxylic acid groups\n", - " index=3,\n", - " separation=0.15)\n", - "\n", - "chain.build(n=10, sequence='A')\n", - "chain.visualize(show_ports=True).show()" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Doing the same thing, but this time without adding an end group\n", - "# Now, the chain is capped with hydrogens (default behavior)\n", - "\n", - "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", - "chain = Polymer()\n", - "\n", - "chain.add_monomer(compound=comp,\n", - " indices=[2, -1],\n", - " separation=.15,\n", - " replace=True)\n", - "\n", - "chain.build(n=10, sequence='A')\n", - "chain.visualize(show_ports=True).show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Flexibility with end groups\n", - "- You can only add one end group, while the other is capped off with hydrogen.\n", - "- You can add 2 different end groups, labeled by 'head' and 'tail'\n", - "- Or you can leave the ends open with an available port" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Creating and adding 2 different end groups.\n", - "# The add_end_groups() function has a duplicate parameter (defaulted to True)\n", - "# When this is true, calling add_end_group will create 2 of the same compound\n", - "# and the polymer is capped off with each.\n", - "# If you want different end groups, change Duplicate to False and call the add_end_groups() function twice\n", - "\n", - "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", - "chain = Polymer()\n", - "\n", - "chain.add_monomer(compound=comp,\n", - " indices=[2, -1],\n", - " separation=.15,\n", - " replace=True)\n", - "\n", - "chain.add_end_groups(mb.load('C(=O)O',smiles=True), # Capping off this polymer with Carboxylic acid groups\n", - " index=3,\n", - " separation=0.15,\n", - " duplicate=False) # Change duplicate to false\n", - "\n", - "chain.add_end_groups(mb.load('N', smiles=True),\n", - " index=-1, separation=0.13,\n", - " duplicate=False, label=\"tail\") # label this one tail\n", - "\n", - "chain.build(n=10, sequence='A')\n", - "chain.visualize(show_ports=True).show()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# If you only add one end group, you can still have the other end capped off with hydrogen\n", - "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", - "chain = Polymer()\n", - "\n", - "chain.add_monomer(compound=comp,\n", - " indices=[2, -1],\n", - " separation=.15)\n", - "\n", - "chain.add_end_groups(mb.load('C(=O)O',smiles=True), # Capping off this polymer with Carboxylic acid groups\n", - " index=3,\n", - " separation=0.15,\n", - " duplicate=False) # Change duplicate to false\n", - "\n", - "chain.build(n=10, sequence='A')\n", - "chain.visualize(show_ports=True).show()" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "#Or to leave the ends of the polymer open with ports, change the add_hydrogens parameter in build() to False\n", - "# This would still work when adding only one end group, then leaving the other end open.\n", - "\n", - "comp = mb.load('CC', smiles=True) # mBuild compound of the monomer unit\n", - "chain = Polymer()\n", - "chain.add_monomer(compound=comp,\n", - " indices=[2, -1],\n", - " separation=.15,\n", - " replace=True)\n", - "\n", - "chain.build(n=10, sequence='A', add_hydrogens=False)\n", - "chain.visualize(show_ports=True).show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Here's an example with a more complicated monomer and a little more detail into what's going on \n", - "\n", - "SMILES strings for Poly-ether-ether-ketone (PEEK) \n", - "\n", - "One has para linkages, the other has meta \n", - "\n", - "Goal is to build up a polymer with alternating PARA-META monomers \n", - "\n", - "Also, just for fun, adding carboxylic acid end groups (ca)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "[]\n", - "[None, None]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - } - ], - "source": [ - "peek_para = mb.load(\"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",smiles=True)\n", - "peek_meta = mb.load(\"Oc1cc(Oc2ccc(C(=O)c3ccccc3)cc2)ccc1\", smiles=True)\n", - "ca = mb.load('C(=O)O', smiles=True)\n", - "peek_polymer = Polymer() # Create polymer instance\n", - "\n", - "# For now, peek_polymer is an empty mBuild compound\n", - "print(peek_polymer)\n", - "\n", - "# monomers and end_groups attributes:\n", - "print(peek_polymer.monomers)\n", - "print(peek_polymer.end_groups)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "Monomers:\n", - "[< 34 particles, non-periodic, 36 bonds, id: 140467724729872>, < 34 particles, non-periodic, 36 bonds, id: 140467724730064>]\n", - "End groups:\n", - "[< 4 particles, non-periodic, 3 bonds, id: 140467732140944>, < 4 particles, non-periodic, 3 bonds, id: 140467733947920>]\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - } - ], - "source": [ - "# Use the add_monomer and add_end_group methods\n", - "# Pass in the compounds we want to use as monomers and end_groups\n", - "# The add_monomer function is called for each unique monomer type\n", - "# The add_end_groups funciton is called for the end group compound\n", - "\n", - "peek_polymer.add_monomer(compound=peek_para,\n", - " indices = [22, 29],\n", - " separation = 0.1376,\n", - " replace=True\n", - " )\n", - "\n", - "peek_polymer.add_monomer(compound=peek_meta,\n", - " indices = [22, 29],\n", - " separation = 0.1376,\n", - " replace=True\n", - " )\n", - "\n", - "peek_polymer.add_end_groups(ca,\n", - " index=3,\n", - " separation=0.13,\n", - " replace=True)\n", - "\n", - "#At this point, peek_polymer is still an empty mBuild compound\n", - "#The monomers and end_groups attributes are no longer empty lists\n", - "print(peek_polymer)\n", - "print(\"Monomers:\")\n", - "print(peek_polymer.monomers)\n", - "print(\"End groups:\")\n", - "print(peek_polymer.end_groups)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - } - ], - "source": [ - "# Now to actually make the polymer compound\n", - "# Essentially all of the currently exisitng Polymer() code was moved into a function called build\n", - "peek_polymer.build(n=3, sequence='AB')\n", - "\n", - "# peek_polymer is no longer an empty compound\n", - "print(peek_polymer)" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "peek_polymer.visualize().show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## A look at what is actually happening with each of the class methods \n", - "\n", - "The `add_monomer` and `add_end_group` functions are handling the creation of ports. \n", - "\n", - "The key is in the `bond_indices` and `replace` parameters.\n", - "`bond_indices` points to the hydrogen atoms that are occupying the polymer bonding site and \n", - "`replace` says to remove those atoms, and replace them with a port\n", - "\n", - "When the port is created, it defaults to using the orientation that already existed between the hydrogen atom and the atom it was bonded to. " - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Before passing the compound into add_monomer()\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "After passing the compound into add_monomer()\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "peek_para = mb.load(\"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",smiles=True)\n", - "print('Before passing the compound into add_monomer()')\n", - "peek_para.visualize(show_ports=True).show()\n", - "\n", - "peek_polymer = Polymer()\n", - "\n", - "peek_polymer.add_monomer(compound=peek_para,\n", - " indices = [22, 29],\n", - " separation = 0.1376,\n", - " replace=True\n", - " )\n", - "print('After passing the compound into add_monomer()')\n", - "peek_polymer.monomers[0].visualize(show_ports=True).show()" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Before passing the compound into add_end_groups()\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "After passing the compound into add_end_groups()\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# Same thing with the end group\n", - "ca = mb.load('C(=O)O', smiles=True)\n", - "print('Before passing the compound into add_end_groups()')\n", - "ca.visualize(show_ports=True).show()\n", - "\n", - "peek_polymer.add_end_groups(ca,\n", - " index=3,\n", - " separation=0.13,\n", - " replace=True)\n", - "\n", - "# ca[3] is the hydrogen bonded to the carbon atom\n", - "\n", - "print('After passing the compound into add_end_groups()')\n", - "peek_polymer.end_groups[0].visualize(show_ports=True).show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Not using the add_monomer and add_end_group functions\n", - "- So far, the examples call these functions everytime to create a polymer, but they are optional\n", - "- These functions basically handle the creation of ports.\n", - "- If you have a compound where the ports already exist, or you want to add them yourself you can pass them into the polymer class instance, and go straight to the build() function.\n", - "- However, you can use both approaches for the same polymer. Passing in compounds when initializing a polymer, and then adding to self.monomers or self.end_groups with the add functions." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from mbuild.lib.moieties import ch2\n", - "\n", - "chain = Polymer(monomers=[ch2.CH2()])\n", - "chain.build(n=10, add_hydrogens=True)\n", - "chain.visualize()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# You can combine passing in compounds, and using the available \n", - "# add_end_group and add_monomer functions\n", - "chain = Polymer(monomers=[ch2.CH2()])\n", - "chain.add_monomer(mb.load('c1ccccc1', smiles=True), indices=[-1, -4])\n", - "chain.add_end_groups(mb.load('C(=O)O',smiles=True), # Capping off this polymer with Carboxylic acid groups\n", - " index=3,\n", - " separation=0.15,\n", - " duplicate=False)\n", - "chain.build(n=5,sequence='AB', add_hydrogens=True)\n", - "chain.visualize()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Using replace=False instead\n", - "So far, all of the examples above used `replace=True` and the `bonding_indices` were the indices of hydrgogens that were being replaced by ports and removed to make room for the monomer-monomer bond.\n", - "\n", - "I imagine this would be the most common work-flow for going straight from a SMILES string or compound file to a polymer, but it's possible use `replace=False`. In this case, the atoms indicated in `bonding_indices` are the atoms forming the monomer-monomer bond.\n", - "\n", - "Below is an example using the `ch2.pdb` file in the `moieties` directory. In this case, we don't want to replace/remove any hydrogens, but add onto the carbon atom" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Reproducing the alkane chain\n", - "\n", - "### NOTE: \n", - "With the latest changes, this method of building up an alkane isn't really needed, but it still shows the ability to use the add_monomer() and add_end_groups() functions with Replace=False\n", - "\n", - "There currently exists a recipe that produces a simple alkane chain. Below I'll use the new `polymer.py` functionality to re-create the same alkane chain." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "ch2 = mb.load('lib/moieties/ch2.pdb')\n", - "chain = Polymer()\n", - "chain.add_monomer(ch2,\n", - " indices=[0, 0],\n", - " orientation=[[0, 1, 0], [0, -1, 0]],\n", - " separation=0.15,\n", - " replace=False)\n", - "chain.build(n=7, sequence='A')\n", - "\n", - "chain.visualize(show_ports=True).show()" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/chris/miniconda3/envs/mosdev/lib/python3.7/site-packages/ipykernel/ipkernel.py:283: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n", - " and should_run_async(code)\n" - ] - }, - { - "data": { - "application/3dmoljs_load.v0": "
\n

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n jupyter labextension install jupyterlab_3dmol

\n
\n", - "text/html": [ - "
\n", - "

You appear to be running in JupyterLab (or JavaScript failed to load for some other reason). You need to install the 3dmol extension:
\n", - " jupyter labextension install jupyterlab_3dmol

\n", - "
\n", - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# It's super easy to throw different end groups on there\n", - "\n", - "ch2 = mb.load('lib/moieties/ch2.pdb')\n", - "chain = Polymer()\n", - "chain.add_monomer(ch2,\n", - " indices=[0, 0],\n", - " orientation=[[0, 1, 0], [0, -1, 0]],\n", - " separation=0.15,\n", - " replace=False)\n", - "\n", - "chain.add_end_groups(mb.load('c1ccccc1', smiles=True),\n", - " index=-1,\n", - " separation=0.15,\n", - " replace=True)\n", - "chain.build(n=8, sequence='A')\n", - "\n", - "chain.visualize().show()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## How we can use recipes\n", - "\n", - "With this approach, to build up a polymer you would just need the following information:\n", - "\n", - "1. A SMILES string (or a path to a compound file)\n", - "2. The indices of the hydrogen atoms to be removed\n", - "3. The bond length\n", - "\n", - "So, having a library of this information to pull from could be a simple as a `.py` file with a dictionary, or a `.json` file\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "monomer_dict = {\n", - " \n", - " 'polyethylene': {'smiles': \"CC\",\n", - " 'indices': [2, -1],\n", - " 'bond_length': 0.1512\n", - " },\n", - " \n", - " 'alkane': {'file': 'lib/moieties/ch2.pdb',\n", - " 'indices': [0, 0],\n", - " 'bond_length': 0.1512,\n", - " 'replace': False\n", - " },\n", - " \n", - " 'peek_para': {'smiles': \"Oc1ccc(Oc2ccc(C(=O)c3ccccc3)cc2)cc1\",\n", - " 'indices': [22, 29],\n", - " 'bond_length': 0.1376,\n", - " 'description': \"poly-ether-ether-ketone with a para configuration\" \n", - " },\n", - " \n", - " 'peek_meta': {'smiles': \"Oc1cc(Oc2ccc(C(=O)c3ccccc3)cc2)ccc1\",\n", - " 'indices': [22, 29],\n", - " 'bond_length': 0.1376,\n", - " 'description': \"poly-ether-ether-ketone with a meta configuration\"\n", - " }\n", - " \n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Maybe there is a from_recipe() function, for example:\n", - "polymer = Polymer.from_recipe(monomer='peek_para', n=10)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.7.10" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} From 7b7e14afe6a40940696dc32e55e7e2ef1e56a856 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 14 Apr 2021 13:32:25 -0600 Subject: [PATCH 47/55] test for errors, and manually passing in end group compounds --- mbuild/tests/test_polymer.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/mbuild/tests/test_polymer.py b/mbuild/tests/test_polymer.py index c2c2349cd..cf1fab971 100755 --- a/mbuild/tests/test_polymer.py +++ b/mbuild/tests/test_polymer.py @@ -1,5 +1,6 @@ from collections import Counter import os +import pytest import mbuild as mb from mbuild.tests.base_test import BaseTest @@ -18,7 +19,7 @@ def test_polymer_from_smiles(self): assert len([p for p in chain.particles() if p.name == "H"]) == 22 assert len(chain.available_ports()) == 0 - def test_add_end_groups(self, ch2): + def test_add_end_groups(self, ch2, ester): n = 6 c6 = mb.recipes.Polymer(monomers=[ch2]) acid = mb.load("C(=O)O", smiles=True) @@ -26,6 +27,22 @@ def test_add_end_groups(self, ch2): c6.build(n=n, add_hydrogens=False) assert len([p for p in c6.particles() if p.name=="O"]) == 4 + def test_pass_end_groups(self, ch2, ester): + ester_2 = mb.clone(ester) + c6 = mb.recipes.Polymer(monomers=[ch2], end_groups=[ester, ester_2]) + c6.build(n=6) + assert c6.children[-1].name == 'Ester' + assert c6.children[-2].name == 'Ester' + + def test_errors(self, ch2, ester): + with pytest.raises(ValueError): + chain = mb.recipes.Polymer(monomers=[ch2], + end_groups=[ester]) + + with pytest.raises(ValueError): + chain = mb.recipes.Polymer(monomers=[ch2]) + chain.build(n=5, sequence="AB") + def test_no_end_groups(self): chain = mb.recipes.Polymer() ethane = mb.load('CC', smiles=True) @@ -34,6 +51,7 @@ def test_no_end_groups(self): assert len([p for p in chain.particles() if p.name == "H"]) == 20 assert len(chain.available_ports()) == 2 + def test_replace_is_false(self): n = 6 ch2 = mb.load(os.path.join(mb.__path__[0], "lib/moieties/ch2.pdb")) From 3eab8198dff655de260764cd8f8d2b911186c0c2 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 14 Apr 2021 13:59:46 -0600 Subject: [PATCH 48/55] expanded doc strings with more detailed explanations, and some examples --- mbuild/lib/recipes/polymer.py | 77 ++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 12f6d91b9..15f80d18d 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -36,6 +36,43 @@ class Polymer(Compound): build(n, sequence) Use to create a single polymer compound. This method uses the compounds created by calling the add_monomer and add_end_group methods. + + Notes + ----- + + There are two different approaches to using the Polymer class to create polymers + + 1) Pass in already created mb.Compound instances to the monomers and + end_groups parameters when creating a Polymer instance: + + You can then call the Polymer.build() method to create a polymer. + This approach can be used if the compounds being passed into the Polymer + instance already have the ports created, and correct atomic structure to + allow for the monomer-monomer and monomer-end group bonds. These compounds + are used as-is when creating the polymer chain. + + Example: + -------- + chain = Polymer(monomers=[mb.Compound], end_groups = [mb.Compound, mb.Compound]) + chain.build(n=5) + + 2) Use the add_monomer() and add_end_group() methods: + + These functions are there to help with the creation of mb.Ports, which are + required by the build() method, and they help with the removal of any atoms + (hydrogens) that are occupying what should be the monomer-monomer and + monomer-end group bonding sites. With this approach, create a Polymer() + instance, then call the add_monomer() and add_end_group() methodss before + calling the build() method. + + Example: + -------- + chain = Polymer() + chain.add_monomer(mb.Compound) + chain.add_end_groups(mb.Compound) + chain.build(n=5) + + Refer to the method specific doc strings to see the correct use. """ def __init__(self, monomers=None, end_groups=None): @@ -166,6 +203,32 @@ def add_monomer( the polymer. Call this function for each unique monomer to be used in the polymer. + Notes + ----- + + Using the 'replace' and 'indices' parameters: + + The atoms in an mbuild compound can be identified by their index numbers. + + For example, an ethane compound with the index number next to each atom. + + H(4) H(7) + \ / + H(3) - C(0) - C(1) - H(6) + / \ + H(2) H(5) + + If replace=True, then this fucntion removes the hydrogen atoms that are + occupying where the C-C bond should occur between monomers. + It is required that you specify which atoms should be removed which is + achieved by the `indices` parameter. + + In this example, you would remove H(2) and H(7) by indicating indices = [2, 7] + The resulting structure of the polymer can vary wildly depending on your choice + for `indices`, so you will have to test out different combinations to find + the two that result in the desired structure. + + Parameters ---------- compound : mbuild.Compound @@ -218,7 +281,19 @@ def add_end_groups( label="head", duplicate=True, ): - """ + """Add an mBuild compound to self.end_groups which will be used to cap + the polymer. Call this function for each unique end group compound to + be used in the polymer, or call it once with duplicate=True if the head + and tail end groups are the same. + + Note + ---- + Refer to the doc string notes of the add_monomer() function for an + explanation of the correct way to use the `replace` and `index` + parameters. + + Parameters: + ----------- compound : mbuild.Compound A compound of the end group structure index : int From 15d14b48ae4ca5fb5ea70dbb3954b491341f3ed3 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 14 Apr 2021 14:04:06 -0600 Subject: [PATCH 49/55] fix a conflict that was sticking around in the port doc strings --- mbuild/port.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/mbuild/port.py b/mbuild/port.py index 0ffd1e7fa..5d6e6d97b 100755 --- a/mbuild/port.py +++ b/mbuild/port.py @@ -106,12 +106,7 @@ def update_separation(self, separation): def update_orientation(self, orientation): """ Change the direction between a port and its anchor particle - -<<<<<<< HEAD - orientation : array-like, shape=(3,), optional, default=[0, 1, 0] -======= orientation : array-like, shape=(3,), required ->>>>>>> 16c39b01b3bd646ae67dac5c79ba1749f480c19a Vector along which to orient the port """ if self.used: From 19cb631498c8c01a89f56c8ab254975307ba2987 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 14 Apr 2021 14:16:53 -0600 Subject: [PATCH 50/55] fix formatting in the doc strings --- mbuild/lib/recipes/polymer.py | 43 ++++++++++++++++------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 15f80d18d..32bc5bfcf 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -39,7 +39,6 @@ class Polymer(Compound): Notes ----- - There are two different approaches to using the Polymer class to create polymers 1) Pass in already created mb.Compound instances to the monomers and @@ -205,29 +204,27 @@ def add_monomer( Notes ----- - Using the 'replace' and 'indices' parameters: - The atoms in an mbuild compound can be identified by their index numbers. - - For example, an ethane compound with the index number next to each atom. - - H(4) H(7) - \ / - H(3) - C(0) - C(1) - H(6) - / \ - H(2) H(5) - - If replace=True, then this fucntion removes the hydrogen atoms that are - occupying where the C-C bond should occur between monomers. - It is required that you specify which atoms should be removed which is - achieved by the `indices` parameter. - - In this example, you would remove H(2) and H(7) by indicating indices = [2, 7] - The resulting structure of the polymer can vary wildly depending on your choice - for `indices`, so you will have to test out different combinations to find - the two that result in the desired structure. - + The atoms in an mbuild compound can be identified by their index numbers. + + For example, an ethane compound with the index number next to each atom: + + H(4) H(7) + | | + H(3) - C(0) - C(1) - H(6) + | | + H(2) H(5) + + If replace=True, then this fucntion removes the hydrogen atoms that are + occupying where the C-C bond should occur between monomers. + It is required that you specify which atoms should be removed which is + achieved by the `indices` parameter. + + In this example, you would remove H(2) and H(7) by indicating indices = [2, 7] + The resulting structure of the polymer can vary wildly depending on your choice + for `indices`, so you will have to test out different combinations to find + the two that result in the desired structure. Parameters ---------- @@ -286,7 +283,7 @@ def add_end_groups( be used in the polymer, or call it once with duplicate=True if the head and tail end groups are the same. - Note + Notes ---- Refer to the doc string notes of the add_monomer() function for an explanation of the correct way to use the `replace` and `index` From 4bbeec19db899a98c69075184e7800a530307d9a Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 15 Apr 2021 10:34:31 -0600 Subject: [PATCH 51/55] Doc strings to explain implementation of sequence in the build function --- mbuild/lib/recipes/polymer.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 32bc5bfcf..0cfc0a642 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -95,18 +95,24 @@ def end_groups(self): return self._end_groups def build(self, n, sequence="A", add_hydrogens=True): - """Connect one or more components in a specified sequence. + """ + Connect one or more components in a specified sequence. + Uses the compounds that are stored in Polymer.monomers and + Polymer.end_groups. Parameters ---------- - monomers : mbuild.Compound or list of mbuild.Compound - The compound(s) to replicate. 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 From 4bf8660cfcbb23b5b39e867fad246f68a4bd68d4 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 28 Apr 2021 15:04:58 -0600 Subject: [PATCH 52/55] add unit test for value of n < 1 --- mbuild/tests/test_polymer.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mbuild/tests/test_polymer.py b/mbuild/tests/test_polymer.py index cf1fab971..99b37157c 100755 --- a/mbuild/tests/test_polymer.py +++ b/mbuild/tests/test_polymer.py @@ -43,6 +43,10 @@ def test_errors(self, ch2, ester): chain = mb.recipes.Polymer(monomers=[ch2]) chain.build(n=5, sequence="AB") + with pytest.raises(ValueError): + chain = mb.recipes.Polymer(monomers=[ch2]) + chain.build(n=0, sequence="A") + def test_no_end_groups(self): chain = mb.recipes.Polymer() ethane = mb.load('CC', smiles=True) From cdfd80d32e407f9a6854da2693751ea3fa618af3 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 28 Apr 2021 15:22:05 -0600 Subject: [PATCH 53/55] 2 new tests to test for errors --- mbuild/tests/test_polymer.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mbuild/tests/test_polymer.py b/mbuild/tests/test_polymer.py index 99b37157c..de0b70f1e 100755 --- a/mbuild/tests/test_polymer.py +++ b/mbuild/tests/test_polymer.py @@ -35,18 +35,24 @@ def test_pass_end_groups(self, ch2, ester): assert c6.children[-2].name == 'Ester' def test_errors(self, ch2, ester): - with pytest.raises(ValueError): + with pytest.raises(ValueError): # Not enough end groups chain = mb.recipes.Polymer(monomers=[ch2], end_groups=[ester]) - with pytest.raises(ValueError): + with pytest.raises(ValueError): # Bad sequence chain = mb.recipes.Polymer(monomers=[ch2]) chain.build(n=5, sequence="AB") - with pytest.raises(ValueError): + with pytest.raises(ValueError): # Bad n value chain = mb.recipes.Polymer(monomers=[ch2]) chain.build(n=0, sequence="A") + with pytest.raises(ValueError): # Bad end group label + chain = mb.recipes.Polymer(monomers=[ch2]) + acid = mb.load("C(=O)O", smiles=True) + chain.add_end_groups(acid, index=3, separation=0.15, + duplicate=False, label="front") + def test_no_end_groups(self): chain = mb.recipes.Polymer() ethane = mb.load('CC', smiles=True) From 883f25fedc982baf0288a398885639696048409e Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Thu, 13 May 2021 12:27:01 -0600 Subject: [PATCH 54/55] remove unused imports --- mbuild/lib/recipes/alkane.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mbuild/lib/recipes/alkane.py b/mbuild/lib/recipes/alkane.py index e269bfb55..cd796e845 100644 --- a/mbuild/lib/recipes/alkane.py +++ b/mbuild/lib/recipes/alkane.py @@ -1,8 +1,4 @@ """mBuild recipe for a generic alkane chain.""" -from os import path - -import numpy as np - import mbuild as mb from mbuild.lib.moieties import CH2, CH3 from mbuild.lib.molecules import Ethane, Methane From ed1042586ed5af6d9d34d60a7ff6b8d1c7cace07 Mon Sep 17 00:00:00 2001 From: Jenny Fothergill Date: Thu, 13 May 2021 13:08:14 -0600 Subject: [PATCH 55/55] oops, typo --- mbuild/lib/recipes/polymer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mbuild/lib/recipes/polymer.py b/mbuild/lib/recipes/polymer.py index 9543b14d8..b3eb8f94b 100755 --- a/mbuild/lib/recipes/polymer.py +++ b/mbuild/lib/recipes/polymer.py @@ -141,7 +141,7 @@ def build(self, n, sequence="A", add_hydrogens=True): unique_seq_ids = sorted(set(sequence)) - if len(monomers) != len(unique_seq_ids): + 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."