def stage1(self): """ replicate water molecules to make a repeated cell """ 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) self.repcell.scale2(self.rep) 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 stage1_analice(self): """ 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) # self.repcell.scale2(self.rep) self.logger.info("Stage1: end.")
def stage1(self): """ 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) self.repcell.scale2(self.rep) 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.")
""" Test for generating water positions from cage positions """ cages = """ 12 0.5 0.5 0.5 14 0.5 0.0 -0.25 14 0.0 0.25 0.5 14 0.75 0.5 0.0 14 0.5 0.0 0.25 12 0.0 0.0 0.0 14 0.25 0.5 0.0 14 0.0 -0.25 0.5 """ celltype = 'rect' cell = """ 12.747893943706936 12.747893943706936 12.747893943706936 """ from genice import FrankKasper from genice.lattice import parse_cages from genice.cells import Cell cagepos, cagetype = parse_cages(cages) cell9 = Cell(cell, celltype) waters = [w for w in FrankKasper.toWater(cagepos, cell9.mat)] coord = "relative" density = FrankKasper.estimate_density(waters, cell9.mat, 2.76) bondlen = 2.76 * 1.2
def __init__(self, lattice_type=None, density=0, rep=(1, 1, 1), depolarize=True, asis=False, cations=dict(), anions=dict(), spot_guests=dict(), spot_groups=dict(), ): self.logger = logging.getLogger() self.lattice_type = lattice_type self.rep = rep self.depolarize = depolarize self.asis = asis self.cations = cations self.anions = anions self.spot_guests = spot_guests self.spot_groups = spot_groups if lattice_type is None: return lat = safe_import("lattice", lattice_type) # Show the document of the module try: self.doc = lat.__doc__.splitlines() except: self.doc = [] self.doc.append("") self.doc.append("Command line: {0}".format(" ".join(sys.argv))) for line in self.doc: self.logger.info("!!! {0}".format(line)) # ================================================================ # rotmatrices (analice) # try: self.rotmatrices = lat.rotmat except: self.logger.info("No rotmatrices in lattice") pass # ================================================================ # waters: positions of water molecules # self.waters = load_numbers(lat.waters) self.logger.debug("Waters: {0}".format(len(self.waters))) self.waters = self.waters.reshape((self.waters.size // 3, 3)) # ================================================================ # cell: cell dimension # celltype: symmetry of the cell # see parse_cell for syntax. # self.cell = Cell(lat.cell, lat.celltype) #self.cell = parse_cell(lat.cell, lat.celltype) # ================================================================ # 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: if type(lat.pairs) is str: lines = lat.pairs.split("\n") self.pairs = [] for line in lines: columns = line.split() if len(columns) == 2: i, j = [int(x) for x in columns] self.pairs.append((i, j)) elif type(lat.pairs) is list: self.pairs = lat.pairs # for pair in lat.pairs: # self.pairs.append(pair) except AttributeError: self.logger.info("Graph 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). # self.bondlen = None try: self.bondlen = lat.bondlen self.logger.info("Bond length (specified): {0}".format(self.bondlen)) except AttributeError: self.bondlen = 1.1 * shortest_distance(self.waters, self.cell) self.logger.info("Bond length (assumed): {0}".format(self.bondlen)) # Set density mass = 18 # water NB = 6.022e23 nmol = self.waters.shape[0] # nmol in a unit cell volume = self.cell.volume() # volume of a unit cell in nm**3 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) # ================================================================ # fixed: specify the bonds whose directions are fixed. # you can specify them in pairs at a time. # You can also leave it undefined. # try: if type(lat.fixed) is str: lines = lat.fixed.split("\n") self.fixed = [] for line in lines: columns = line.split() if len(columns) == 2: i, j = [int(x) for x in columns] self.fixed.append((i, j)) # Is a tuple elif type(lat.fixed) is list: self.fixed = [] for pair in lat.fixed: self.fixed.append(tuple(pair[:2])) # Must be a tuple except AttributeError: self.fixed = [] 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}
class Lattice(): def __init__(self, lattice_type=None, density=0, rep=(1, 1, 1), depolarize=True, asis=False, cations=dict(), anions=dict(), spot_guests=dict(), spot_groups=dict(), ): self.logger = logging.getLogger() self.lattice_type = lattice_type self.rep = rep self.depolarize = depolarize self.asis = asis self.cations = cations self.anions = anions self.spot_guests = spot_guests self.spot_groups = spot_groups if lattice_type is None: return lat = safe_import("lattice", lattice_type) # Show the document of the module try: self.doc = lat.__doc__.splitlines() except: self.doc = [] self.doc.append("") self.doc.append("Command line: {0}".format(" ".join(sys.argv))) for line in self.doc: self.logger.info("!!! {0}".format(line)) # ================================================================ # rotmatrices (analice) # try: self.rotmatrices = lat.rotmat except: self.logger.info("No rotmatrices in lattice") pass # ================================================================ # waters: positions of water molecules # self.waters = load_numbers(lat.waters) self.logger.debug("Waters: {0}".format(len(self.waters))) self.waters = self.waters.reshape((self.waters.size // 3, 3)) # ================================================================ # cell: cell dimension # celltype: symmetry of the cell # see parse_cell for syntax. # self.cell = Cell(lat.cell, lat.celltype) #self.cell = parse_cell(lat.cell, lat.celltype) # ================================================================ # 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: if type(lat.pairs) is str: lines = lat.pairs.split("\n") self.pairs = [] for line in lines: columns = line.split() if len(columns) == 2: i, j = [int(x) for x in columns] self.pairs.append((i, j)) elif type(lat.pairs) is list: self.pairs = lat.pairs # for pair in lat.pairs: # self.pairs.append(pair) except AttributeError: self.logger.info("Graph 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). # self.bondlen = None try: self.bondlen = lat.bondlen self.logger.info("Bond length (specified): {0}".format(self.bondlen)) except AttributeError: self.bondlen = 1.1 * shortest_distance(self.waters, self.cell) self.logger.info("Bond length (assumed): {0}".format(self.bondlen)) # Set density mass = 18 # water NB = 6.022e23 nmol = self.waters.shape[0] # nmol in a unit cell volume = self.cell.volume() # volume of a unit cell in nm**3 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) # ================================================================ # fixed: specify the bonds whose directions are fixed. # you can specify them in pairs at a time. # You can also leave it undefined. # try: if type(lat.fixed) is str: lines = lat.fixed.split("\n") self.fixed = [] for line in lines: columns = line.split() if len(columns) == 2: i, j = [int(x) for x in columns] self.fixed.append((i, j)) # Is a tuple elif type(lat.fixed) is list: self.fixed = [] for pair in lat.fixed: self.fixed.append(tuple(pair[:2])) # Must be a tuple except AttributeError: self.fixed = [] 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): if 0 in formatter.hooks: formatter.hooks[0](self) if max(0,*formatter.hooks.keys()) < 1: return self.stage1() if 1 in formatter.hooks: formatter.hooks[1](self) if max(0,*formatter.hooks.keys()) < 2: return res = self.stage2() if 2 in formatter.hooks: formatter.hooks[2](self) if max(0,*formatter.hooks.keys()) < 3: return self.stage3() if 3 in formatter.hooks: formatter.hooks[3](self) if max(0,*formatter.hooks.keys()) < 4: return self.stage4() if 4 in formatter.hooks: formatter.hooks[4](self) if max(0,*formatter.hooks.keys()) < 5: return self.stage5() if 5 in formatter.hooks: formatter.hooks[5](self) if max(0,*formatter.hooks.keys()) < 6: return self.stage6(water_type) if 6 in formatter.hooks: formatter.hooks[6](self) if max(0,*formatter.hooks.keys()) < 7: return self.stage7(guests) if 7 in formatter.hooks: formatter.hooks[7](self) def analize_ice(self, water_type, formatter): """ Protocol for analice """ if 0 in formatter.hooks: formatter.hooks[0](self) if max(0,*formatter.hooks.keys()) < 1: return self.stage1_analice() if 1 in formatter.hooks: formatter.hooks[1](self) if max(0,*formatter.hooks.keys()) < 2: return # res = self.stage2_analice() if 2 in formatter.hooks: formatter.hooks[2](self) if max(0,*formatter.hooks.keys()) < 3: return # self.stage3_analice() if 3 in formatter.hooks: formatter.hooks[3](self) if max(0,*formatter.hooks.keys()) < 4: return self.stage4_analice() if 4 in formatter.hooks: formatter.hooks[4](self) if max(0,*formatter.hooks.keys()) < 5: return # self.stage5_analice() if 5 in formatter.hooks: formatter.hooks[5](self) if max(0,*formatter.hooks.keys()) < 6: return self.stage6(water_type) if 6 in formatter.hooks: formatter.hooks[6](self) if max(0,*formatter.hooks.keys()) < 7: return # self.stage7_analice(guests) if 7 in formatter.hooks: formatter.hooks[7](self) def stage1(self): """ 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) self.repcell.scale2(self.rep) 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.info("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. Provided variables: spacegraph: depolarized network with node positions. yapresult: Animation of the depolarization process in YaPlot format. """ self.logger.info("Stage4: Depolarization.") if not self.depolarize or self.asis: self.logger.info(" Skip depolarization by request.") 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) draw = dg.YaplotDraw( self.reppositions, self.repcell.mat, data=self.spacegraph) self.yapresult = dg.depolarize( self.spacegraph, self.repcell.mat, draw=draw) self.logger.info("Stage4: end.") def stage5(self): """ Prepare orientations for rigid molecules. Provided variables: reppositions: molecular positions with random noise. rotmatrices: rotation matrices for water molecules """ self.logger.info("Stage5: Orientation.") # Add small noises to the molecular positions # Will be removed in v0.24. # if not self.asis: # self.reppositions += self.repcell.abs2rel(np.random.random(self.reppositions.shape)* 0.01 - 0.005) # 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) 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: 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 #self.logger.info( # " {0} * {1} @ {2}".format(molec, nmolec, movedin)) # 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) 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( " Start estimating the bonds according to the pair distances.") # 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 = [v for v in pl.pairs_fine( self.waters, self.bondlen, self.cell.mat, grid, distance=False)] # Check using a simpler algorithm. if self.logger.level <= logging.DEBUG: pairs2 = [v for v in pl.pairs_crude( self.waters, self.bondlen, self.cell.mat, distance=False)] self.logger.debug("pairs: {0}".format(len(self.pairs))) self.logger.debug("pairs2: {0}".format(len(pairs2))) for pair in self.pairs: i, j = pair assert (i, j) in pairs2 or (j, i) in pairs2 for pair in pairs2: i, j = pair assert (i, j) in self.pairs or (j, i) in self.pairs graph = dg.IceGraph() 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) 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.") def stage1_analice(self): """ 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) # self.repcell.scale2(self.rep) self.logger.info("Stage1: end.") def stage4_analice(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.")
class Lattice(): def __init__(self, lattice_type=None, density=0, rep=(1, 1, 1), depolarize=True, asis=False, cations=dict(), anions=dict(), spot_guests=dict(), spot_groups=dict(), ): self.logger = logging.getLogger() self.lattice_type = lattice_type self.rep = rep self.depolarize = depolarize self.asis = asis self.cations = cations self.anions = anions self.spot_guests = spot_guests self.spot_groups = spot_groups if lattice_type is None: return lat = safe_import("lattice", lattice_type) # Show the document of the module try: self.doc = lat.__doc__.splitlines() except: self.doc = [] self.doc.append("") self.doc.append("Command line: {0}".format(" ".join(sys.argv))) for line in self.doc: self.logger.info("!!! {0}".format(line)) # ================================================================ # rotmatrices (analice) # try: self.rotmatrices = lat.rotmat except: self.logger.info("No rotmatrices in lattice") pass # ================================================================ # waters: positions of water molecules # self.waters = load_numbers(lat.waters) self.logger.debug("Waters: {0}".format(len(self.waters))) self.waters = self.waters.reshape((self.waters.size // 3, 3)) # ================================================================ # cell: cell dimension # celltype: symmetry of the cell # see parse_cell for syntax. # self.cell = Cell(lat.cell, lat.celltype) #self.cell = parse_cell(lat.cell, lat.celltype) # ================================================================ # 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: if type(lat.pairs) is str: lines = lat.pairs.split("\n") self.pairs = [] for line in lines: columns = line.split() if len(columns) == 2: i, j = [int(x) for x in columns] self.pairs.append((i, j)) elif type(lat.pairs) is list: self.pairs = lat.pairs # for pair in lat.pairs: # self.pairs.append(pair) except AttributeError: self.logger.info("Graph 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). # self.bondlen = None try: self.bondlen = lat.bondlen self.logger.info("Bond length (specified): {0}".format(self.bondlen)) except AttributeError: self.bondlen = 1.1 * shortest_distance(self.waters, self.cell) self.logger.info("Bond length (assumed): {0}".format(self.bondlen)) # Set density mass = 18 # water NB = 6.022e23 nmol = self.waters.shape[0] # nmol in a unit cell volume = self.cell.volume() # volume of a unit cell in nm**3 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) # ================================================================ # fixed: specify the bonds whose directions are fixed. # you can specify them in pairs at a time. # You can also leave it undefined. # try: if type(lat.fixed) is str: lines = lat.fixed.split("\n") self.fixed = [] for line in lines: columns = line.split() if len(columns) == 2: i, j = [int(x) for x in columns] self.fixed.append((i, j)) # Is a tuple elif type(lat.fixed) is list: self.fixed = [] for pair in lat.fixed: self.fixed.append(tuple(pair[:2])) # Must be a tuple except AttributeError: self.fixed = [] 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): if 0 in formatter.hooks: formatter.hooks[0](self) if max(0,*formatter.hooks.keys()) < 1: return self.stage1() if 1 in formatter.hooks: formatter.hooks[1](self) if max(0,*formatter.hooks.keys()) < 2: return res = self.stage2() if 2 in formatter.hooks: formatter.hooks[2](self) if max(0,*formatter.hooks.keys()) < 3: return self.stage3() if 3 in formatter.hooks: formatter.hooks[3](self) if max(0,*formatter.hooks.keys()) < 4: return self.stage4() if 4 in formatter.hooks: formatter.hooks[4](self) if max(0,*formatter.hooks.keys()) < 5: return self.stage5() if 5 in formatter.hooks: formatter.hooks[5](self) if max(0,*formatter.hooks.keys()) < 6: return self.stage6(water_type) if 6 in formatter.hooks: formatter.hooks[6](self) if max(0,*formatter.hooks.keys()) < 7: return self.stage7(guests) if 7 in formatter.hooks: formatter.hooks[7](self) def analize_ice(self, water_type, formatter): """ Protocol for analice """ if 0 in formatter.hooks: formatter.hooks[0](self) if max(0,*formatter.hooks.keys()) < 1: return self.stage1_analice() if 1 in formatter.hooks: formatter.hooks[1](self) if max(0,*formatter.hooks.keys()) < 2: return # res = self.stage2_analice() if 2 in formatter.hooks: formatter.hooks[2](self) if max(0,*formatter.hooks.keys()) < 3: return # self.stage3_analice() if 3 in formatter.hooks: formatter.hooks[3](self) if max(0,*formatter.hooks.keys()) < 4: return self.stage4_analice() if 4 in formatter.hooks: formatter.hooks[4](self) if max(0,*formatter.hooks.keys()) < 5: return # self.stage5_analice() if 5 in formatter.hooks: formatter.hooks[5](self) if max(0,*formatter.hooks.keys()) < 6: return self.stage6(water_type) if 6 in formatter.hooks: formatter.hooks[6](self) if max(0,*formatter.hooks.keys()) < 7: return # self.stage7_analice(guests) if 7 in formatter.hooks: formatter.hooks[7](self) def stage1(self): """ 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) self.repcell.scale2(self.rep) 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.info("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. Provided variables: spacegraph: depolarized network with node positions. yapresult: Animation of the depolarization process in YaPlot format. """ self.logger.info("Stage4: Depolarization.") if not self.depolarize or self.asis: self.logger.info(" Skip depolarization by request.") 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) draw = dg.YaplotDraw( self.reppositions, self.repcell.mat, data=self.spacegraph) self.yapresult = dg.depolarize( self.spacegraph, self.repcell.mat, draw=draw) self.logger.info("Stage4: end.") def stage5(self): """ Prepare orientations for rigid molecules. Provided variables: reppositions: molecular positions with random noise. rotmatrices: rotation matrices for water molecules """ self.logger.info("Stage5: Orientation.") # Add small noises to the molecular positions if not self.asis: self.reppositions += self.repcell.abs2rel(np.random.random(self.reppositions.shape)* 0.01 - 0.005) # 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) 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: 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 #self.logger.info( # " {0} * {1} @ {2}".format(molec, nmolec, movedin)) # 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) 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( " Start estimating the bonds according to the pair distances.") # 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 = [v for v in pl.pairs_fine( self.waters, self.bondlen, self.cell.mat, grid, distance=False)] # Check using a simpler algorithm. if self.logger.level <= logging.DEBUG: pairs2 = [v for v in pl.pairs_crude( self.waters, self.bondlen, self.cell.mat, distance=False)] self.logger.debug("pairs: {0}".format(len(self.pairs))) self.logger.debug("pairs2: {0}".format(len(pairs2))) for pair in self.pairs: i, j = pair assert (i, j) in pairs2 or (j, i) in pairs2 for pair in pairs2: i, j = pair assert (i, j) in self.pairs or (j, i) in self.pairs graph = dg.IceGraph() 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) 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.") def stage1_analice(self): """ 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) # self.repcell.scale2(self.rep) self.logger.info("Stage1: end.") def stage4_analice(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.")