From 1d660749c3dff91322718ec6663c964ec8ae6abf Mon Sep 17 00:00:00 2001 From: Co Quach <43968221+daico007@users.noreply.github.com> Date: Wed, 27 Sep 2023 13:58:38 -0500 Subject: [PATCH] Consolidate pNBDAC (#35) * consolidate different pNBDAC to a single class * add more surfaces unit tests * better handling of buffer_length=0 * udpate ci yml * fix test for au lattice --- .github/workflows/CI.yml | 8 +- surface_coatings/chains/nbdac_polymer.py | 165 +++--------------- surface_coatings/surfaces/au_interface.py | 3 +- .../surfaces/graphene_interface.py | 4 +- surface_coatings/tests/test_surfaces.py | 11 ++ 5 files changed, 48 insertions(+), 143 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2715aaf..864ad91 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,13 +1,17 @@ -name: Python Package using Conda +name: CI on: + push: + branches: + - "main" pull_request: branches: - - master + - main workflow_dispatch: jobs: test: + if: github.event.pull_request.draft == false name: Unit Test on ${{ matrix.os }}, Python ${{ matrix.python-version }} runs-on: ${{ matrix.os }} strategy: diff --git a/surface_coatings/chains/nbdac_polymer.py b/surface_coatings/chains/nbdac_polymer.py index 0b8fd33..8b1789e 100644 --- a/surface_coatings/chains/nbdac_polymer.py +++ b/surface_coatings/chains/nbdac_polymer.py @@ -81,7 +81,7 @@ def __init__( self.labels["down"] = backbone["down"] -class SilaneNBDAC(mb.Compound): +class pNBDAC(mb.Compound): """A general method to create a NBDAC polymer with varying side chains/terminal groups. Parameters @@ -94,8 +94,11 @@ class SilaneNBDAC(mb.Compound): Cap the front of the polymer (NBDAC end) cap_end : bool, optional, default=False Cap the end of the polymer (Silane end) - silane_buffer : int, optional, default=1 - Silane monomer used to buffer at one end of the NBDAC polymer + buffer : str, optional, default=None + Type of buffer to be attached to the end of the pNBDAC. + Available options: None, "alkyl", "ether", "silane" + buffer_length : int, optional, default=1 + Length of CH2 monomer to be added to the buffer at one end of the NBDAC polymer n : int, optional, default=1 Number of repeat for the monomer port_labels: tuple, optional, default=('up', 'down') @@ -111,14 +114,15 @@ def __init__( monomer, side_chains=AminoPropyl(), terminal_groups=Acetaldehyde(), - silane_buffer=1, + buffer=None, + buffer_length=1, cap_front=True, cap_end=False, n=1, port_labels=("up", "down"), align=True, ): - super(SilaneNBDAC, self).__init__() + super(pNBDAC, self).__init__() if monomer: assert isinstance(monomer, fmNBDAC) if side_chains and terminal_groups: @@ -134,138 +138,27 @@ def __init__( polymer.build(n=n, add_hydrogens=False) self.add(polymer, "Polymer") - if silane_buffer: + if buffer: tail = mb.Compound(name="tail") - tail.add(Silane(), "Silane") + buffer_options = {"alkyl": CH2, + "ether": O, + "silane": Silane} + tail.add(buffer_options.get(buffer.lower())(), "Buffer") tail.add(CH2(), "CH2_0") - for i in range(silane_buffer): - tail.add(CH2(), f"CH2_{i+1}") - mb.force_overlap( - tail[f"CH2_{i+1}"], - tail[f"CH2_{i+1}"]["down"], - tail[f"CH2_{i}"]["up"], - ) - - mb.force_overlap( - tail["Silane"], tail["Silane"]["up"], tail[f"CH2_0"]["down"] - ) - - self.add(tail, "tail") - - if align: - polymer_vector = ( - polymer[port_labels[1]].anchor.pos - - polymer[port_labels[0]].anchor.pos - ) - norm_vector = polymer_vector / np.linalg.norm(polymer_vector) - - new_port = mb.Port( - anchor=polymer[port_labels[0]].anchor, - orientation=norm_vector, - separation=0.07, - ) - - polymer[port_labels[0]].update_orientation( - orientation=norm_vector - ) - - mb.force_overlap( - tail, tail[f"CH2_{i+1}"]["up"], polymer[port_labels[0]] - ) - - self.labels["up"] = self["Polymer"][port_labels[1]] - self.labels["down"] = self["tail"]["Silane"]["down"] - else: - self.labels["up"] = self["Polymer"]["up"] - self.labels["down"] = self["Polymer"]["down"] - - if cap_front: - front_cap = H() - self.add(front_cap, "front_cap") - mb.force_overlap( - move_this=front_cap, - from_positions=front_cap["up"], - to_positions=self["up"], - ) - if cap_end: - end_cap = H() - self.add(end_cap, "end_cap") - mb.force_overlap( - move_this=end_cap, - from_positions=end_cap["up"], - to_positions=self["down"], - ) - -class EtherNBDAC(mb.Compound): - """A general method to create a NBDAC polymer with varying side chains/terminal groups. - Option to add an ether cap (to connect to a surface) - - Parameters - ---------- - side_chains : mb.Compound or list of Compounds (len 2) - Side chains attached to NBDAC monomer - terminal_groups: mb.Compound or list of Compounds (len 2) - Terminal groups which will be matched with side chains - cap_front : bool, optional, default=True - Cap the front of the polymer (NBDAC end) - cap_end : bool, optional, default=False - Cap the end of the polymer (Silane end) - ether_buffer : int, optional, default=1 - Ether used to buffer at one end of the NBDAC polymer - n : int, optional, default=1 - Number of repeat for the monomer - port_labels: tuple, optional, default=('up', 'down') - The list of ports of the monomers. The Silane will connect with the first port - and the last port will be capped by a Hydrogen - algin : bool, optional, default=True - If True, align the port connected to the silane buffer with the rest of the polymer. - The goal is to create a straight polymer when grafted on a surface. - """ - - def __init__( - self, - monomer, - side_chains=AminoPropyl(), - terminal_groups=Acetaldehyde(), - ether_buffer=1, - cap_front=True, - cap_end=False, - n=1, - port_labels=("up", "down"), - align=True, - ): - super(EtherNBDAC, self).__init__() - if monomer: - assert isinstance(monomer, fmNBDAC) - if side_chains and terminal_groups: - warnings.warn( - "Both monomer and (side_chains, terminal_groups) are provided," - "only monomer will be used to construct the polymer." - "Please refer to docstring for more information." - ) - else: - monomer = fmNBDAC(side_chains, terminal_groups) - - polymer = Polymer(monomers=[monomer]) - polymer.build(n=n, add_hydrogens=False) - self.add(polymer, "Polymer") - - if ether_buffer: - tail = mb.Compound(name="tail") - tail.add(O(), "O_init") - tail.add(CH2(), "CH2_0") - - for i in range(ether_buffer): - tail.add(CH2(), f"CH2_{i+1}") - mb.force_overlap( - tail[f"CH2_{i+1}"], - tail[f"CH2_{i+1}"]["down"], - tail[f"CH2_{i}"]["up"], - ) + if buffer_length >= 1: + for i in range(buffer_length): + tail.add(CH2(), f"CH2_{i+1}") + mb.force_overlap( + tail[f"CH2_{i+1}"], + tail[f"CH2_{i+1}"]["down"], + tail[f"CH2_{i}"]["up"], + ) + else: + i = -1 mb.force_overlap( - tail["O_init"], tail["O_init"]["up"], tail[f"CH2_0"]["down"] + tail["Buffer"], tail["Buffer"]["up"], tail[f"CH2_0"]["down"] ) self.add(tail, "tail") @@ -277,12 +170,6 @@ def __init__( ) norm_vector = polymer_vector / np.linalg.norm(polymer_vector) - new_port = mb.Port( - anchor=polymer[port_labels[0]].anchor, - orientation=norm_vector, - separation=0.07, - ) - polymer[port_labels[0]].update_orientation( orientation=norm_vector ) @@ -292,7 +179,7 @@ def __init__( ) self.labels["up"] = self["Polymer"][port_labels[1]] - self.labels["down"] = self["tail"]["O_init"]["down"] + self.labels["down"] = self["tail"]["Buffer"]["down"] else: self.labels["up"] = self["Polymer"]["up"] self.labels["down"] = self["Polymer"]["down"] diff --git a/surface_coatings/surfaces/au_interface.py b/surface_coatings/surfaces/au_interface.py index f49da53..c1478bb 100644 --- a/surface_coatings/surfaces/au_interface.py +++ b/surface_coatings/surfaces/au_interface.py @@ -55,6 +55,7 @@ def __init__(self, x=5, y=5, n_layers=3): self.add(au_sheet) + self.xyz -= np.min(self.xyz, axis=0) self.xyz -= np.min(self.xyz, axis=0) - self.xyz -= np.min(self.xyz, axis=0) \ No newline at end of file + self.periodicity = (True, True, True) \ No newline at end of file diff --git a/surface_coatings/surfaces/graphene_interface.py b/surface_coatings/surfaces/graphene_interface.py index 78edac3..2aec5e4 100644 --- a/surface_coatings/surfaces/graphene_interface.py +++ b/surface_coatings/surfaces/graphene_interface.py @@ -52,4 +52,6 @@ def __init__(self, x=5, y=5, n_layers=3): self.add(graphene) self.xyz -= np.min(self.xyz, axis=0) - self.xyz -= np.min(self.xyz, axis=0) \ No newline at end of file + self.xyz -= np.min(self.xyz, axis=0) + + self.periodicity = (True, True, False) \ No newline at end of file diff --git a/surface_coatings/tests/test_surfaces.py b/surface_coatings/tests/test_surfaces.py index 8d2aad7..fd6b6c8 100644 --- a/surface_coatings/tests/test_surfaces.py +++ b/surface_coatings/tests/test_surfaces.py @@ -4,6 +4,9 @@ SilicaInterface, SilicaInterfaceCarve, SiliconInterface, + GrapheneSheet, + AuLattice, + ) @@ -19,3 +22,11 @@ def test_silica_interface_carve(self): def test_silica_interface(self): silica_surface = SilicaInterface() assert silica_surface.periodicity == (True, True, False) + + def test_graphene_interface(self): + graphene_sheet = GrapheneSheet() + assert graphene_sheet.periodicity == (True, True, False) + + def test_au_interface(self): + au_interface = AuLattice() + assert au_interface.periodicity == (True, True, True)