Ejemplo n.º 1
0
    def stage1(self, noise=0.):
        """
        Do nothing.

        Provided variables:
        repposition: replicated molecular positions (CoM, relative)
        repcell:     replicated simulation cell shape matrix
        """

        self.logger.info("Stage1: (...)")
        self.reppositions = self.waters

        # This must be done before the replication of the cell.
        self.logger.info("  Number of water molecules: {0}".format(
            len(self.reppositions)))

        # self.graph = self.prepare_random_graph(self.fixed)
        self.graph = self.prepare_random_graph(self.pairs)

        # scale the cell
        self.repcell = Cell(self.cell.mat)

        # self.repcell.scale2(self.rep)
        # add small perturbations to the molecular positions.
        if noise > 0.0:
            self.logger.info("  Add noise: {0}.".format(noise))
            perturb = np.random.normal(
                loc=0.0,
                scale=noise * 0.01 * 3.0 * 0.5,  # in percent, radius of water
                size=self.reppositions.shape)
            self.reppositions += self.repcell.abs2rel(perturb)

        self.logger.info("Stage1: end.")
Ejemplo n.º 2
0
    def stage1(self, noise=0.):
        """
        Replicate water molecules to make a repeated cell

        Provided variables:
        repposition: replicated molecular positions (CoM, relative)
        repcell:     replicated simulation cell shape matrix
        repcagetype: replicated cage types array
        repcagepos:  replicated cage positions (CoM, relative)
        cagetypes:   set of cage types
        """

        self.logger.info("Stage1: Replication.")
        self.reppositions = replicate_positions(self.waters, self.rep)

        # This must be done before the replication of the cell.
        self.logger.info("  Number of water molecules: {0}".format(
            len(self.reppositions)))
        self.graph = self.prepare_random_graph(self.fixed)

        # scale the cell
        self.repcell = Cell(self.cell.mat)
        self.repcell.scale2(self.rep)

        if noise > 0.0:
            self.logger.info("  Add noise: {0}.".format(noise))
            perturb = np.random.normal(
                loc=0.0,
                scale=noise * 0.01 * 3.0 * 0.5,  # in percent, radius of water
                size=self.reppositions.shape)
            self.reppositions += self.repcell.abs2rel(perturb)

        if self.cagepos is not None:
            self.logger.info("  Hints:")
            self.repcagepos = replicate_positions(self.cagepos, self.rep)
            nrepcages = self.repcagepos.shape[0]
            self.repcagetype = [
                self.cagetype[i % len(self.cagetype)] for i in range(nrepcages)
            ]
            self.cagetypes = defaultdict(set)

            for i, typ in enumerate(self.repcagetype):
                self.cagetypes[typ].add(i)

            # INFO for cage types
            self.logger.info("    Cage types: {0}".format(list(
                self.cagetypes)))

            for typ, cages in self.cagetypes.items():
                self.logger.info("    Cage type {0}: {1}".format(typ, cages))
            # Up here move to stage 1.

        self.logger.info("Stage1: end.")
Ejemplo n.º 3
0
    def __init__(
            self,
            lat,
            argv,
            density=0,
            rep=(1, 1, 1),
            cations=dict(),
            anions=dict(),
            spot_guests=dict(),
            spot_groups=dict(),
            asis=False,
    ):

        self.logger = getLogger()
        self.rep = rep
        self.asis = asis
        self.cations = cations
        self.anions = anions
        self.spot_guests = spot_guests
        self.spot_groups = spot_groups
        # Show the document of the module

        try:
            self.doc = lat.__doc__.splitlines()
        except BaseException:
            self.doc = []

        self.doc.append("")
        self.doc.append("Command line: {0}".format(" ".join(argv)))

        for line in self.doc:
            self.logger.info("  " + line)
        # ================================================================
        # rotmatrices (analice)
        #
        try:
            self.rotmatrices = lat.rotmat
        except BaseException:
            self.logger.info("No rotmatrices in lattice")
            self.rotmatrices = None
        # ================================================================
        # waters: positions of water molecules
        #
        self.waters = put_in_array(lat.waters)
        self.logger.debug("Waters: {0}".format(len(self.waters)))
        self.waters = self.waters.reshape((self.waters.size // 3, 3))

        # ================================================================
        # cell: cell dimension
        #   see parse_cell for syntax.
        #
        self.cell = Cell(lat.cell)

        # ================================================================
        # coord: "relative" or "absolute"
        #   Inside genice, molecular positions are always treated as "relative"
        #
        if lat.coord == "absolute":
            self.waters = self.cell.abs2rel(self.waters)

        self.waters = np.array([w - np.floor(w) for w in self.waters])

        # ================================================================
        # pairs: specify the pairs of molecules that are connected.
        #   Bond orientation will be shuffled later
        #   unless it is "fixed".
        #
        self.pairs = None

        try:
            self.pairs = parse_pairs(lat.pairs)
        except AttributeError:
            self.logger.info("HB connectivity is not defined.")

        # ================================================================
        # bondlen: specify the bond length threshold.
        #   This is used when "pairs" are not specified.
        #   It is applied to the original positions of molecules (before density setting).
        #
        nmol = self.waters.shape[0]  # nmol in a unit cell
        volume = self.cell.volume()  # volume of a unit cell in nm**3
        self.bondlen = None

        try:
            self.bondlen = lat.bondlen
            self.logger.info("Bond length (specified): {0}".format(
                self.bondlen))
        except AttributeError:
            self.logger.debug("  Estimating the bond threshold length...")
            # assume that the particles distribute homogeneously.
            rc = (volume / nmol)**(1 / 3) * 1.5
            grid = pl.determine_grid(self.cell.mat, rc)
            p = pl.pairs_fine(self.waters,
                              rc,
                              self.cell.mat,
                              grid,
                              distance=False)
            self.bondlen = 1.1 * shortest_distance(
                self.waters, self.cell, pairs=p)
            self.logger.info("Bond length (estim.): {0}".format(self.bondlen))

        # Set density
        mass = 18  # water
        NB = 6.022e23
        density0 = mass * nmol / (NB * volume * 1e-21)

        if density <= 0:
            try:
                self.density = lat.density
            except AttributeError:
                self.logger.info(
                    "Density is not specified. Assume the density from lattice."
                )
                dmin = shortest_distance(self.waters, self.cell)
                self.logger.info(
                    "Closest pair distance: {0} (should be around 0.276 nm)".
                    format(dmin))
                self.density = density0 / (0.276 / dmin)**3
                # self.density = density0
        else:
            self.density = density

        self.logger.info("Target Density: {0}".format(self.density))
        self.logger.info("Original Density: {0}".format(density0))

        # scale the cell according to the (specified) density
        ratio = (density0 / self.density)**(1.0 / 3.0)
        self.cell.scale(ratio)

        if self.bondlen is not None:
            self.bondlen *= ratio
        self.logger.info("Bond length (scaled, nm): {0}".format(self.bondlen))

        # ================================================================
        # double_network: True or False
        #   This is a special option for ices VI and VII that have
        #   interpenetrating double network.
        #   GenIce's fast depolarization algorithm fails in some case.
        #
        try:
            self.double_network = lat.double_network
        except AttributeError:
            self.double_network = False

        # ================================================================
        # cages: positions of the centers of cages
        #   In fractional coordinate.
        #
        self.cagepos = None
        self.cagetype = None

        if "cages" in lat.__dict__:
            self.cagepos, self.cagetype = parse_cages(lat.cages)
            self.logger.warn(
                "Use of 'cages' in a lattice-plugin is deprecated.")
        elif "cagepos" in lat.__dict__:
            # pre-parsed data
            self.cagepos, self.cagetype = np.array(lat.cagepos), lat.cagetype

        # ================================================================
        # fixed: specify the bonds whose directions are fixed.
        #   you can specify them in pairs at a time.
        #   You can also leave it undefined.
        #
        self.fixed = []
        try:
            self.fixed = parse_pairs(lat.fixed)
            self.logger.info("Orientations of some edges are fixed.")
        except AttributeError:
            pass

        if "dopeIonsToUnitCell" in lat.__dict__:
            self.dopeIonsToUnitCell = lat.dopeIonsToUnitCell
        else:
            self.dopeIonsToUnitCell = None
        self.dopants = set()

        # if asis, make pairs to be fixed.
        if self.asis and len(self.fixed) == 0:
            self.fixed = self.pairs

        # filled cages
        self.filled_cages = set()

        # groups info
        self.groups = defaultdict(dict)

        # groups for the semi-guest
        # experimental; there are many variation of semi-guest inclusion.
        self.groups_placer = {
            "Bu-": butyl,
            "Butyl-": butyl,
            "Pentyl-": pentyl,
            "Propyl-": propyl,
            "2,2-dimethylpropyl-": _2_2_dimethylpropyl,
            "2,3-dimethylbutyl-": _2_3_dimethylbutyl,
            "3,3-dimethylbutyl-": _3_3_dimethylbutyl,
            "3-methylbutyl-": _3_methylbutyl,
            "Ethyl-": ethyl
        }
Ejemplo n.º 4
0
class GenIce():
    def __init__(
            self,
            lat,
            argv,
            density=0,
            rep=(1, 1, 1),
            cations=dict(),
            anions=dict(),
            spot_guests=dict(),
            spot_groups=dict(),
            asis=False,
    ):

        self.logger = getLogger()
        self.rep = rep
        self.asis = asis
        self.cations = cations
        self.anions = anions
        self.spot_guests = spot_guests
        self.spot_groups = spot_groups
        # Show the document of the module

        try:
            self.doc = lat.__doc__.splitlines()
        except BaseException:
            self.doc = []

        self.doc.append("")
        self.doc.append("Command line: {0}".format(" ".join(argv)))

        for line in self.doc:
            self.logger.info("  " + line)
        # ================================================================
        # rotmatrices (analice)
        #
        try:
            self.rotmatrices = lat.rotmat
        except BaseException:
            self.logger.info("No rotmatrices in lattice")
            self.rotmatrices = None
        # ================================================================
        # waters: positions of water molecules
        #
        self.waters = put_in_array(lat.waters)
        self.logger.debug("Waters: {0}".format(len(self.waters)))
        self.waters = self.waters.reshape((self.waters.size // 3, 3))

        # ================================================================
        # cell: cell dimension
        #   see parse_cell for syntax.
        #
        self.cell = Cell(lat.cell)

        # ================================================================
        # coord: "relative" or "absolute"
        #   Inside genice, molecular positions are always treated as "relative"
        #
        if lat.coord == "absolute":
            self.waters = self.cell.abs2rel(self.waters)

        self.waters = np.array([w - np.floor(w) for w in self.waters])

        # ================================================================
        # pairs: specify the pairs of molecules that are connected.
        #   Bond orientation will be shuffled later
        #   unless it is "fixed".
        #
        self.pairs = None

        try:
            self.pairs = parse_pairs(lat.pairs)
        except AttributeError:
            self.logger.info("HB connectivity is not defined.")

        # ================================================================
        # bondlen: specify the bond length threshold.
        #   This is used when "pairs" are not specified.
        #   It is applied to the original positions of molecules (before density setting).
        #
        nmol = self.waters.shape[0]  # nmol in a unit cell
        volume = self.cell.volume()  # volume of a unit cell in nm**3
        self.bondlen = None

        try:
            self.bondlen = lat.bondlen
            self.logger.info("Bond length (specified): {0}".format(
                self.bondlen))
        except AttributeError:
            self.logger.debug("  Estimating the bond threshold length...")
            # assume that the particles distribute homogeneously.
            rc = (volume / nmol)**(1 / 3) * 1.5
            grid = pl.determine_grid(self.cell.mat, rc)
            p = pl.pairs_fine(self.waters,
                              rc,
                              self.cell.mat,
                              grid,
                              distance=False)
            self.bondlen = 1.1 * shortest_distance(
                self.waters, self.cell, pairs=p)
            self.logger.info("Bond length (estim.): {0}".format(self.bondlen))

        # Set density
        mass = 18  # water
        NB = 6.022e23
        density0 = mass * nmol / (NB * volume * 1e-21)

        if density <= 0:
            try:
                self.density = lat.density
            except AttributeError:
                self.logger.info(
                    "Density is not specified. Assume the density from lattice."
                )
                dmin = shortest_distance(self.waters, self.cell)
                self.logger.info(
                    "Closest pair distance: {0} (should be around 0.276 nm)".
                    format(dmin))
                self.density = density0 / (0.276 / dmin)**3
                # self.density = density0
        else:
            self.density = density

        self.logger.info("Target Density: {0}".format(self.density))
        self.logger.info("Original Density: {0}".format(density0))

        # scale the cell according to the (specified) density
        ratio = (density0 / self.density)**(1.0 / 3.0)
        self.cell.scale(ratio)

        if self.bondlen is not None:
            self.bondlen *= ratio
        self.logger.info("Bond length (scaled, nm): {0}".format(self.bondlen))

        # ================================================================
        # double_network: True or False
        #   This is a special option for ices VI and VII that have
        #   interpenetrating double network.
        #   GenIce's fast depolarization algorithm fails in some case.
        #
        try:
            self.double_network = lat.double_network
        except AttributeError:
            self.double_network = False

        # ================================================================
        # cages: positions of the centers of cages
        #   In fractional coordinate.
        #
        self.cagepos = None
        self.cagetype = None

        if "cages" in lat.__dict__:
            self.cagepos, self.cagetype = parse_cages(lat.cages)
            self.logger.warn(
                "Use of 'cages' in a lattice-plugin is deprecated.")
        elif "cagepos" in lat.__dict__:
            # pre-parsed data
            self.cagepos, self.cagetype = np.array(lat.cagepos), lat.cagetype

        # ================================================================
        # fixed: specify the bonds whose directions are fixed.
        #   you can specify them in pairs at a time.
        #   You can also leave it undefined.
        #
        self.fixed = []
        try:
            self.fixed = parse_pairs(lat.fixed)
            self.logger.info("Orientations of some edges are fixed.")
        except AttributeError:
            pass

        if "dopeIonsToUnitCell" in lat.__dict__:
            self.dopeIonsToUnitCell = lat.dopeIonsToUnitCell
        else:
            self.dopeIonsToUnitCell = None
        self.dopants = set()

        # if asis, make pairs to be fixed.
        if self.asis and len(self.fixed) == 0:
            self.fixed = self.pairs

        # filled cages
        self.filled_cages = set()

        # groups info
        self.groups = defaultdict(dict)

        # groups for the semi-guest
        # experimental; there are many variation of semi-guest inclusion.
        self.groups_placer = {
            "Bu-": butyl,
            "Butyl-": butyl,
            "Pentyl-": pentyl,
            "Propyl-": propyl,
            "2,2-dimethylpropyl-": _2_2_dimethylpropyl,
            "2,3-dimethylbutyl-": _2_3_dimethylbutyl,
            "3,3-dimethylbutyl-": _3_3_dimethylbutyl,
            "3-methylbutyl-": _3_methylbutyl,
            "Ethyl-": ethyl
        }

    def generate_ice(self,
                     water_type,
                     guests,
                     formatter,
                     record_depolarization_path=None,
                     depolarize=True,
                     noise=0.):

        hooks = formatter.hooks
        arg = formatter.arg
        maxstage = max(0, *hooks.keys())
        logger = getLogger()

        if 0 in hooks:
            hooks[0](self, arg)
        elif arg != "":
            logger.info(
                "Arguments are given but the module does not accept them.")
            if "usage" in formatter.__dict__:
                formatter.usage()
            else:
                for line in formatter.__doc__.splitlines():
                    logger.info("  " + line)

        self.stage1(noise)

        if 1 in hooks:
            abort = hooks[1](self)
            if maxstage < 2 or abort:
                return

        res = self.stage2()

        if 2 in hooks:
            abort = hooks[2](self)
            if maxstage < 3 or abort:
                return

        self.stage3()

        if 3 in hooks:
            abort = hooks[3](self)
            if maxstage < 4 or abort:
                return

        self.stage4(depolarize=depolarize,
                    record_depolarization_path=record_depolarization_path)

        if 4 in hooks:
            abort = hooks[4](self)
            if maxstage < 5 or abort:
                return

        self.stage5()

        if 5 in hooks:
            abort = hooks[5](self)
            if maxstage < 6 or abort:
                return

        self.stage6(water_type)

        if 6 in hooks:
            abort = hooks[6](self)
            if maxstage < 7 or abort:
                return

        self.stage7(guests)

        if 7 in hooks:
            hooks[7](self)

    def stage1(self, noise=0.):
        """
        Replicate water molecules to make a repeated cell

        Provided variables:
        repposition: replicated molecular positions (CoM, relative)
        repcell:     replicated simulation cell shape matrix
        repcagetype: replicated cage types array
        repcagepos:  replicated cage positions (CoM, relative)
        cagetypes:   set of cage types
        """

        self.logger.info("Stage1: Replication.")
        self.reppositions = replicate_positions(self.waters, self.rep)

        # This must be done before the replication of the cell.
        self.logger.info("  Number of water molecules: {0}".format(
            len(self.reppositions)))
        self.graph = self.prepare_random_graph(self.fixed)

        # scale the cell
        self.repcell = Cell(self.cell.mat)
        self.repcell.scale2(self.rep)

        if noise > 0.0:
            self.logger.info("  Add noise: {0}.".format(noise))
            perturb = np.random.normal(
                loc=0.0,
                scale=noise * 0.01 * 3.0 * 0.5,  # in percent, radius of water
                size=self.reppositions.shape)
            self.reppositions += self.repcell.abs2rel(perturb)

        if self.cagepos is not None:
            self.logger.info("  Hints:")
            self.repcagepos = replicate_positions(self.cagepos, self.rep)
            nrepcages = self.repcagepos.shape[0]
            self.repcagetype = [
                self.cagetype[i % len(self.cagetype)] for i in range(nrepcages)
            ]
            self.cagetypes = defaultdict(set)

            for i, typ in enumerate(self.repcagetype):
                self.cagetypes[typ].add(i)

            # INFO for cage types
            self.logger.info("    Cage types: {0}".format(list(
                self.cagetypes)))

            for typ, cages in self.cagetypes.items():
                self.logger.info("    Cage type {0}: {1}".format(typ, cages))
            # Up here move to stage 1.

        self.logger.info("Stage1: end.")

    def stage2(self):
        """
        Make a random graph and replicate.

        Provided variables:
        dopants:
        groups:  replicated positions of the chemical groups (CoM, relative)
        filled_cages:
        graph:   replicated network topology (bond orientation may be random)
        """

        self.logger.info("Stage2: Graph preparation.")

        # Some edges are directed when ions are doped.
        if self.dopeIonsToUnitCell is not None:
            self.dopeIonsToUnitCell(self)  # may be defined in the plugin

        # Replicate the dopants in the unit cell
        self.dopants = replicate_labeldict(self.dopants, len(self.waters),
                                           self.rep)
        self.groups = replicate_groups(self.groups, self.waters, self.cagepos,
                                       self.rep)

        # self.groups_info(self.groups)
        for root, cages in self.groups.items():
            self.filled_cages |= set(cages)

        # self.logger.info(("filled",self.filled_cages))
        # Replicate the graph
        self.graph = replicate_graph(self.graph, self.waters, self.rep)

        # Dope ions by options.
        if len(self.anions) > 0:
            self.logger.info("  Anionize: {0}.".format(self.anions))

            for site, name in self.anions.items():
                self.graph.anionize(site)
                self.dopants[site] = name

        if len(self.cations) > 0:
            self.logger.info("  Cationize: {0}.".format(self.cations))

            for site, name in self.cations.items():
                self.graph.cationize(site)
                self.dopants[site] = name

        # Count bonds
        nrandom = 0
        nfixed = 0
        for i, j, data in self.graph.edges(data=True):
            if self.graph[i][j]['fixed']:  # fixed pair
                nfixed += 1
            else:
                nrandom += 1
        self.logger.info(
            "  Number of pre-oriented hydrogen bonds: {0}".format(nfixed))
        self.logger.info(
            "  Number of unoriented hydrogen bonds: {0}".format(nrandom))
        self.logger.info(
            "  Number of hydrogen bonds: {0} (regular num: {1})".format(
                nfixed + nrandom,
                len(self.reppositions) * 2))

        # test2==True means it is a z=4 graph.
        self.test2 = self.test_undirected_graph(self.graph)
        if not self.test2:
            self.logger.warn("Test2 failed.")

        self.logger.info("Stage2: end.")
        return self.test2

    def stage3(self):
        """
        Make a true ice graph.

        Provided variables:
        graph: network obeying B-F rule.
        """

        self.logger.info("Stage3: Bernal-Fowler rule.")

        if self.asis:
            self.logger.info("  Skip applying the ice rule by request.")
        else:
            self.graph.purge_ice_defects()

        self.logger.info("Stage3: end.")

    def stage4(self, depolarize=True, record_depolarization_path=None):
        """
        Depolarize.

        Provided variables:
        spacegraph: depolarized network with node positions.
        yapresult:  Animation of the depolarization process in YaPlot format.
        """

        self.logger.info("Stage4: Depolarization.")

        if not depolarize or self.asis:
            self.logger.info(
                "  Skip depolarization by request. {0} {1}".format(
                    depolarize, self.asis))
            self.yapresult = ""
            self.spacegraph = dg.SpaceIceGraph(self.graph,
                                               coord=self.reppositions,
                                               ignores=self.graph.ignores)
        else:
            if self.double_network:
                if (self.rep[0] % 2 == 0) and (self.rep[1] % 2
                                               == 0) and (self.rep[2] % 2
                                                          == 0):
                    pass
                else:
                    self.logger.error(
                        "In making the ice structure having the double network (e.g. ices 6 and 7), all the repetition numbers (--rep) must be even."
                    )
                    sys.exit(1)
            self.spacegraph = dg.SpaceIceGraph(self.graph,
                                               coord=self.reppositions,
                                               ignores=self.graph.ignores)
            if record_depolarization_path is not None:
                draw = dg.YaplotDraw(self.reppositions,
                                     self.repcell.mat,
                                     data=self.spacegraph)
                yapresult = dg.depolarize(self.spacegraph,
                                          self.repcell.mat,
                                          draw=draw)
                record_depolarization_path.write(yapresult)
            else:
                dg.depolarize(self.spacegraph, self.repcell.mat, draw=None)
        self.logger.info("Stage4: end.")

    def stage5(self):
        """
        Prepare orientations for rigid molecules.

        Provided variables:
        reppositions: molecular positions.
        rotmatrices:  rotation matrices for water molecules
        """

        self.logger.info("Stage5: Orientation.")

        # determine the orientations of the water molecules based on edge
        # directions.
        self.rotmatrices = orientations(self.reppositions, self.spacegraph,
                                        self.repcell)

        # Activate it.
        # logger.info("The network is not specified.  Water molecules will be orinented randomly.")
        # rotmatrices = [rigid.rand_rotation_matrix() for pos in positions]
        self.logger.info("Stage5: end.")

    def stage6(self, water_type):
        """
        Arrange water atoms and replacements

        Provided variables:
        atoms: atomic positions of water molecules. (absolute)
        """

        self.logger.info("Stage6: Atomic positions of water.")

        # assert audit_name(water_type), "Dubious water name: {0}".format(water_type)
        # water = importlib.import_module("genice.molecules."+water_type)
        water = safe_import("molecule", water_type)

        try:
            mdoc = water.__doc__.splitlines()
        except BaseException:
            mdoc = []

        for line in mdoc:
            self.logger.info("  " + line)

        self.atoms = arrange_atoms(self.reppositions,
                                   self.repcell,
                                   self.rotmatrices,
                                   water.sites,
                                   water.labels,
                                   water.name,
                                   ignores=set(self.dopants))

        self.logger.info("Stage6: end.")

    def stage7(self, guests):
        """
        Arrange guest atoms

        Provided variables:
        atoms: atomic positions of all molecules.
        """

        self.logger.info("Stage7: Atomic positions of the guest.")

        if self.cagepos is not None:

            # the cages around the dopants.
            dopants_neighbors = self.dopants_info(self.dopants,
                                                  self.reppositions,
                                                  self.repcagepos,
                                                  self.repcell)

            # put the (one-off) groups
            if len(self.spot_groups) > 0:
                # process the -H option
                for cage, group_to in self.spot_groups.items():
                    group, root = group_to.split(":")
                    self.add_group(cage, group, int(root))

            molecules = defaultdict(list)

            if len(self.spot_guests) > 0:

                # process the -G option
                for cage, molec in self.spot_guests.items():
                    molecules[molec].append(cage)
                    self.filled_cages.add(cage)

            if guests is not None:

                # process the -g option
                for arg in guests:
                    self.logger.debug(arg[0])
                    cagetype, spec = arg[0].split("=")
                    assert cagetype in self.cagetypes, "Nonexistent cage type: {0}".format(
                        cagetype)
                    resident = dict()
                    rooms = list(self.cagetypes[cagetype] - self.filled_cages)

                    for room in rooms:
                        resident[room] = None

                    # spec contains a formula consisting of "+" and "*"
                    contents = spec.split("+")
                    vacant = len(rooms)

                    for content in contents:

                        if "*" in content:
                            molec, frac = content.split("*")
                            frac = float(frac)
                        else:
                            molec = content
                            frac = 1.0

                        nmolec = int(frac * len(rooms) + 0.5)
                        vacant -= nmolec
                        assert vacant >= 0, "Too many guests."
                        remain = nmolec
                        movedin = []

                        while remain > 0:
                            r = random.randint(0, len(rooms) - 1)
                            room = rooms[r]

                            if resident[room] is None:
                                resident[room] = molec
                                molecules[molec].append(room)
                                movedin.append(room)
                                remain -= 1

            # Now ge got the address book of the molecules.
            if len(molecules):
                self.logger.info("  Summary of guest placements:")
                self.guests_info(self.cagetypes, molecules)

            if len(self.spot_groups) > 0:
                self.logger.info("  Summary of groups:")
                self.groups_info(self.groups)

            # semi-guests
            for root, cages in self.groups.items():
                assert root in self.dopants
                name = self.dopants[root]
                molname = "G{0}".format(root)
                pos = self.reppositions[root]
                rot = self.rotmatrices[root]
                self.atoms.append(
                    [0, molname, name,
                     self.repcell.rel2abs(pos), 0])
                del self.dopants[root]  # processed.
                self.logger.debug((root, cages, name, molname, pos, rot))

                for cage, group in cages.items():
                    assert group in self.groups_placer
                    assert cage in dopants_neighbors[root]
                    cpos = self.repcagepos[cage]
                    self.atoms += self.groups_placer[group](cpos, pos,
                                                            self.repcell,
                                                            molname)

            # molecular guests
            for molec, cages in molecules.items():
                gmol = safe_import("molecule", molec)

                try:
                    mdoc = gmol.__doc__.splitlines()
                except BaseException:
                    mdoc = []
                for line in mdoc:
                    logger.info("  " + line)
                cpos = [self.repcagepos[i] for i in cages]
                cmat = [np.identity(3) for i in cages]
                self.atoms += arrange_atoms(cpos, self.repcell, cmat,
                                            gmol.sites, gmol.labels, gmol.name)

        # Assume the dopant is monatomic and replaces one water molecule
        atomset = defaultdict(set)
        for label, name in self.dopants.items():
            atomset[name].add(label)

        for name, labels in atomset.items():
            pos = [self.reppositions[i] for i in sorted(labels)]
            rot = [self.rotmatrices[i] for i in sorted(labels)]
            self.atoms += arrange_atoms(pos, self.repcell, rot, [
                [0., 0., 0.],
            ], [name], name)

        self.logger.info("Stage7: end.")

    def prepare_random_graph(self, fixed):

        if self.pairs is None:
            self.logger.info("  Pairs are not given explicitly.")
            self.logger.info(
                "  Estimating the bonds according to the pair distances.")

            self.logger.debug("Bondlen: {0}".format(self.bondlen))
            # make bonded pairs according to the pair distance.
            # make before replicating them.
            grid = pl.determine_grid(self.cell.mat, self.bondlen)
            assert np.product(
                grid
            ) > 0, "Too thin unit cell. Consider use of --rep option if the cell was made by cif2ice."
            self.pairs = pl.pairs_fine(self.waters,
                                       self.bondlen,
                                       self.cell.mat,
                                       grid,
                                       distance=False)

            # self.pairs = [v for v in zip(j0,j1)]
            # Check using a simpler algorithm.
            # Do not use it for normal debug because it is too slow
            if False:  # self.logger.level <= logging.DEBUG:
                pairs1 = self.pairs
                pairs2 = [
                    v for v in pl.pairs_crude(self.waters,
                                              self.bondlen,
                                              self.cell.mat,
                                              distance=False)
                ]
                self.logger.debug("pairs1: {0}".format(len(pairs1)))
                self.logger.debug("pairs2: {0}".format(len(pairs2)))
                for pair in pairs1:
                    i, j = pair
                    assert (i, j) in pairs2 or (j, i) in pairs2
                for pair in pairs2:
                    i, j = pair
                    assert (i, j) in pairs1 or (j, i) in pairs1

        graph = dg.IceGraph()
        if fixed is not None:
            for i, j in fixed:
                graph.add_edge(i, j, fixed=True)

        # Fixed pairs are default.
        for pair in self.pairs:
            i, j = pair

            if graph.has_edge(i, j) or graph.has_edge(j, i):
                pass
            else:
                if random.randint(0, 1) == 0:
                    graph.add_edge(i, j, fixed=False)
                else:
                    graph.add_edge(j, i, fixed=False)

        self.logger.info("  Number of water nodes: {0}".format(
            graph.number_of_nodes()))

        return graph

    def test_undirected_graph(self, graph):
        # Test

        undir = graph.to_undirected()
        for node in range(undir.number_of_nodes()):
            if node not in undir:
                self.logger.debug("z=0 at {0}".format(node))
            else:
                z = len(list(undir.neighbors(node)))
                if z != 4:
                    self.logger.debug("z={0} at {1}".format(z, node))

        if graph.number_of_edges() != len(self.reppositions) * 2:
            self.logger.info(
                "Inconsistent number of HBs {0} for number of molecules {1}.".
                format(graph.number_of_edges(), len(self.reppositions)))
            return False

        return True

    def dopants_info(self, dopants=None, waters=None, cagepos=None, cell=None):
        if dopants is None:
            dopants = self.dopants

        if waters is None:
            waters = self.waters

        if cagepos is None:
            cagepos = self.cagepos

        if cell is None:
            cell = self.cell

        dopants_neighbors = neighbor_cages_of_dopants(dopants, waters, cagepos,
                                                      cell)

        for dopant, cages in dopants_neighbors.items():
            self.logger.info("    Cages adjacent to dopant {0}: {1}".format(
                dopant, cages))

        return dopants_neighbors

    def groups_info(self, groups):
        for root, cages in groups.items():
            for cage, group in cages.items():
                self.logger.info(
                    "    Group {0} of dopant {1} in cage {2}".format(
                        group, root, cage))

    def guests_info(self, cagetypes, molecules):
        for cagetype, cageid in cagetypes.items():
            self.logger.info("    Guests in cage type {0}:".format(cagetype))

            for molec, cages in molecules.items():
                cages = set(cages)
                cages &= cageid

                if len(cages):
                    self.logger.info("      {0} * {1} @ {2}".format(
                        molec, len(cages), cages))

    def add_group(self, cage, group, root):
        self.groups[root][cage] = group
        self.filled_cages.add(cage)

    def __del__(self):
        self.logger.info("Completed.")
Ejemplo n.º 5
0
import numpy as np
from math import pi, acos

from logging import getLogger, StreamHandler, DEBUG, INFO
logger = getLogger(__name__)
handler = StreamHandler()
handler.setLevel(INFO)
logger.setLevel(INFO)
logger.addHandler(handler)
logger.propagate = False
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")

name = sys.argv[1]
module = importlib.import_module(name)

cell = Cell(module.cell.mat)
La = np.linalg.norm(cell.mat[0])
Lb = np.linalg.norm(cell.mat[1])
Lc = np.linalg.norm(cell.mat[2])
alpha = acos((cell.mat[1] @ cell.mat[2]) / (Lb * Lc)) * 180 / pi
beta = acos((cell.mat[2] @ cell.mat[0]) / (Lc * La)) * 180 / pi
gamma = acos((cell.mat[0] @ cell.mat[1]) / (La * Lb)) * 180 / pi

s = []
s.append("a={0}".format(La))
s.append("b={0}".format(Lb))
s.append("c={0}".format(Lc))

if alpha != 90.0:
    s.append("A={0}".format(alpha))
Ejemplo n.º 6
0
class AnalIce(GenIce):
    def __init__(self, lat, argv):

        self.logger = logging.getLogger()
        self.rep = (1, 1, 1)
        density = 0.0
        #         cations=dict(),
        #         anions=dict(),
        #         spot_guests=dict(),
        #         spot_groups=dict(),
        self.asis = False
        # Show the document of the module

        try:
            self.doc = lat.__doc__.splitlines()
        except BaseException:
            self.doc = []

        self.doc.append("")
        self.doc.append("Command line: {0}".format(" ".join(argv)))

        for line in self.doc:
            self.logger.info("  " + line)
        # ================================================================
        # rotmatrices (analice)
        #
        try:
            self.rotmatrices = lat.rotmat
        except BaseException:
            self.logger.info("No rotmatrices in lattice")
            self.rotmatrices = None
        # ================================================================
        # waters: positions of water molecules
        #
        self.waters = put_in_array(lat.waters)
        self.logger.debug("Waters: {0}".format(len(self.waters)))
        self.waters = self.waters.reshape((self.waters.size // 3, 3))

        # ================================================================
        # cell: cell dimension
        #   see parse_cell for syntax.
        #
        self.cell = Cell(lat.cell)

        # ================================================================
        # coord: "relative" or "absolute"
        #   Inside genice, molecular positions are always treated as "relative"
        #
        if lat.coord == "absolute":
            self.waters = self.cell.abs2rel(self.waters)

        self.waters = np.array([w - np.floor(w) for w in self.waters])

        # ================================================================
        # pairs: specify the pairs of molecules that are connected.
        #   Bond orientation will be shuffled later
        #   unless it is "fixed".
        #
        self.pairs = None

        try:
            self.pairs = parse_pairs(lat.pairs)
        except AttributeError:
            self.logger.info("HB connectivity is not defined.")

        # ================================================================
        # bondlen: specify the bond length threshold.
        #   This is used when "pairs" are not specified.
        #   It is applied to the original positions of molecules (before density setting).
        #
        nmol = self.waters.shape[0]  # nmol in a unit cell
        volume = self.cell.volume()  # volume of a unit cell in nm**3
        self.bondlen = None

        try:
            self.bondlen = lat.bondlen
            self.logger.info("Bond length (specified): {0}".format(
                self.bondlen))
        except AttributeError:
            self.logger.debug("  Estimating the bond threshold length...")
            # assume that the particles distribute homogeneously.
            rc = (volume / nmol)**(1 / 3) * 1.5
            grid = pl.determine_grid(self.cell.mat, rc)
            p = pl.pairs_fine(self.waters,
                              rc,
                              self.cell.mat,
                              grid,
                              distance=False)
            self.bondlen = 1.1 * shortest_distance(
                self.waters, self.cell, pairs=p)
            self.logger.info("Bond length (estim.): {0}".format(self.bondlen))

        # Set density
        mass = 18  # water
        NB = 6.022e23
        density0 = mass * nmol / (NB * volume * 1e-21)

        if density <= 0:
            try:
                self.density = lat.density
            except AttributeError:
                self.logger.info(
                    "Density is not specified. Assume the density from lattice."
                )
                dmin = shortest_distance(self.waters, self.cell)
                self.logger.info(
                    "Closest pair distance: {0} (should be around 0.276 nm)".
                    format(dmin))
                self.density = density0 / (0.276 / dmin)**3
                # self.density = density0
        else:
            self.density = density

        self.logger.info("Target Density: {0}".format(self.density))
        self.logger.info("Original Density: {0}".format(density0))

        # scale the cell according to the (specified) density
        ratio = (density0 / self.density)**(1.0 / 3.0)
        self.cell.scale(ratio)

        if self.bondlen is not None:
            self.bondlen *= ratio
        self.logger.info("Bond length (scaled, nm): {0}".format(self.bondlen))

        # ================================================================
        # double_network: True or False
        #   This is a special option for ices VI and VII that have
        #   interpenetrating double network.
        #   GenIce's fast depolarization algorithm fails in some case.
        #
        try:
            self.double_network = lat.double_network
        except AttributeError:
            self.double_network = False

        # ================================================================
        # cages: positions of the centers of cages
        #   In fractional coordinate.
        #
        self.cagepos = None
        self.cagetype = None

        # ================================================================
        # fixed: specify the bonds whose directions are fixed.
        #   you can specify them in pairs at a time.
        #   You can also leave it undefined.
        #
        self.fixed = []
        try:
            self.fixed = parse_pairs(lat.fixed)
            self.logger.info("Orientations of some edges are fixed.")
        except AttributeError:
            pass

        self.dopeIonsToUnitCell = None
        self.dopants = set()

        # if asis, make pairs to be fixed.
        if self.asis and len(self.fixed) == 0:
            self.fixed = self.pairs

        # filled cages
        self.filled_cages = set()

        # groups info
        self.groups = defaultdict(dict)

    def analyze_ice(self, water_type, formatter, noise=0.):
        """
        Protocol for analice
        """
        hooks = formatter.hooks
        arg = formatter.arg

        maxstage = max(0, *hooks.keys())

        if 0 in hooks:
            hooks[0](self, arg)
        elif arg != "":
            logger.info(
                "Arguments are given but the module does not accept them.")
            if "usage" in formatter.__dict__:
                formatter.usage()
            else:
                for line in formatter.__doc__.splitlines():
                    logger.info("  " + line)

        self.stage1(noise)

        if 1 in hooks:
            abort = hooks[1](self)
            if maxstage < 2 or abort:
                return

        if self.rotmatrices is None:
            res = self.stage2()

        if 2 in hooks:
            abort = hooks[2](self)
            if maxstage < 3 or abort:
                return

        if self.rotmatrices is None:
            self.stage3()

        if 3 in hooks:
            abort = hooks[3](self)
            if maxstage < 4 or abort:
                return

        self.stage4()

        if 4 in hooks:
            abort = hooks[4](self)
            if maxstage < 5 or abort:
                return

        # molecular orientation should be given in the loader.
        if self.rotmatrices is None:
            self.stage5()

        if 5 in hooks:
            abort = hooks[5](self)
            if maxstage < 6 or abort:
                return

        self.stage6(water_type)

        if 6 in hooks:
            abort = hooks[6](self)
            if maxstage < 7 or abort:
                return

        # self.stage7_analice(guests)
        if 7 in hooks:
            hooks[7](self)

    def stage1(self, noise=0.):
        """
        Do nothing.

        Provided variables:
        repposition: replicated molecular positions (CoM, relative)
        repcell:     replicated simulation cell shape matrix
        """

        self.logger.info("Stage1: (...)")
        self.reppositions = self.waters

        # This must be done before the replication of the cell.
        self.logger.info("  Number of water molecules: {0}".format(
            len(self.reppositions)))

        # self.graph = self.prepare_random_graph(self.fixed)
        self.graph = self.prepare_random_graph(self.pairs)

        # scale the cell
        self.repcell = Cell(self.cell.mat)

        # self.repcell.scale2(self.rep)
        # add small perturbations to the molecular positions.
        if noise > 0.0:
            self.logger.info("  Add noise: {0}.".format(noise))
            perturb = np.random.normal(
                loc=0.0,
                scale=noise * 0.01 * 3.0 * 0.5,  # in percent, radius of water
                size=self.reppositions.shape)
            self.reppositions += self.repcell.abs2rel(perturb)

        self.logger.info("Stage1: end.")

    def stage4(self):
        """
        Depolarize.

        Provided variables:
        spacegraph: depolarized network with node positions.
        yapresult:  Animation of the depolarization process in YaPlot format.
        """

        self.logger.info("Stage4: (...)")
        self.yapresult = ""
        self.spacegraph = dg.SpaceIceGraph(self.graph,
                                           coord=self.reppositions,
                                           ignores=self.graph.ignores)
        self.logger.info("Stage4: end.")