class FancyDict(dict): """Dictionary with keys available as attributes also.""" def __getattr__(self, key): if key not in self: return dict.__getattribute__(self, key) value = self[key] if isinstance(value, dict): return FancyDict(value) return value formula = property(lambda self: hill(self.numbers)) def __dir__(self): return self.keys() # for tab-completion
def formula(self, d): return hill(d.numbers)
def __init__(self, references, filter='', verbose=True): """Phase-diagram. references: list of (name, energy) tuples List of references. The energy must be the total energy and not energy per atom. The names can also be dicts like ``{'Zn': 1, 'O': 2}`` which would be equivalent to ``'ZnO2'``. filter: str or list of str Use only those references that match the given filter. Example: ``filter='ZnO'`` will select those that contain zinc or oxygen. verbose: bool Write information. """ filter = parse_formula(filter)[0] self.verbose = verbose self.species = {} self.references = [] for name, energy in references: if isinstance(name, str): count = parse_formula(name)[0] else: count = name name = hill(count) if filter and any(symbol not in filter for symbol in count): continue natoms = 0 for symbol, n in count.items(): natoms += n if symbol not in self.species: self.species[symbol] = len(self.species) self.references.append((count, energy, name, natoms)) self.symbols = [None] * len(self.species) for symbol, id in self.species.items(): self.symbols[id] = symbol if verbose: print('Species:', ', '.join(self.symbols)) print('References:', len(self.references)) for i, (count, energy, name, natoms) in enumerate(self.references): print('{0:<5}{1:10}{2:10.3f}'.format(i, name, energy)) self.points = np.zeros((len(self.references), len(self.species) + 1)) for s, (count, energy, name, natoms) in enumerate(self.references): for symbol, n in count.items(): self.points[s, self.species[symbol]] = n / natoms self.points[s, -1] = energy / natoms hull = ConvexHull(self.points[:, 1:]) # Find relevant simplices: ok = hull.equations[:, -2] < 0 self.simplices = hull.simplices[ok] # Create a mask for those points that are on the convex hull: self.hull = np.zeros(len(self.points), bool) for simplex in self.simplices: self.hull[simplex] = True if verbose: print('Simplices:', len(self.simplices))
def __init__(self, references, filter='', verbose=True): """Phase-diagram. references: list of (name, energy) tuples List of references. The names can also be dicts like ``{'Zn': 1, 'O': 2}`` which would be equivalent to ``'ZnO2'``. filter: str or list of str Use only those references that match the given filter. Example: ``filter='ZnO'`` will select those that contain zinc or oxygen. verbose: bool Write information. """ filter = parse_formula(filter)[0] self.verbose = verbose self.species = {} self.references = [] for name, energy in references: if isinstance(name, str): count = parse_formula(name)[0] else: count = name name = hill(count) if filter and any(symbol not in filter for symbol in count): continue natoms = 0 for symbol, n in count.items(): natoms += n if symbol not in self.species: self.species[symbol] = len(self.species) self.references.append((count, energy, name, natoms)) if verbose: print('Species:', ', '.join(self.species)) print('References:', len(self.references)) for i, (count, energy, name, natoms) in enumerate(self.references): print('{0:<5}{1:10}{2:10.3f}'.format(i, name, energy)) self.points = np.zeros((len(self.references), len(self.species) + 1)) for s, (count, energy, name, natoms) in enumerate(self.references): for symbol, n in count.items(): self.points[s, self.species[symbol]] = n / natoms self.points[s, -1] = energy / natoms hull = ConvexHull(self.points[:, 1:]) # Find relevant vertices: ok = hull.equations[:, -2] < 0 vertices = set() for simplex in hull.simplices[ok]: vertices.update(simplex) self.vertices = np.array(list(vertices)) if verbose: print('Simplices:', ok.sum()) # Create triangulation: if len(self.species) == 2: D = Delaunay1D # scipy's Delaunay doesn't like 1-d! else: D = Delaunay self.tri = D(self.points[self.vertices, 1:-1])
def formula(self): """Chemical formula string.""" return hill(self.numbers)
def __init__(self, dct, subscript=None): self.dct = dct self.cell = [['{0:.3f}'.format(a) for a in axis] for axis in dct.cell] forces = dict2forces(dct) if forces is None: fmax = None self.forces = None else: fmax = (forces**2).sum(1).max()**0.5 N = len(forces) self.forces = [] for n, f in enumerate(forces): if n < 5 or n >= N - 5: f = tuple('{0:10.3f}'.format(x) for x in f) symbol = chemical_symbols[dct.numbers[n]] self.forces.append((n, symbol) + f) elif n == 5: self.forces.append( (' ...', '', ' ...', ' ...', ' ...')) self.stress = dct.get('stress') if self.stress is not None: self.stress = ', '.join('{0:.3f}'.format(s) for s in self.stress) if 'masses' in dct: mass = dct.masses.sum() else: mass = atomic_masses[dct.numbers].sum() formula = hill(dct.numbers) if subscript: formula = subscript.sub(r'<sub>\1</sub>', formula) table = [('id', dct.id), ('age', float_to_time_string(now() - dct.ctime, True)), ('formula', formula), ('user', dct.user), ('calculator', dct.get('calculator')), ('energy [eV]', dct.get('energy')), ('fmax [eV/Ang]', fmax), ('charge [|e|]', dct.get('charge')), ('mass [au]', mass), ('unique id', dct.unique_id), ('volume [Ang^3]', abs(np.linalg.det(dct.cell)))] self.table = [(name, value) for name, value in table if value is not None] if 'key_value_pairs' in dct: self.key_value_pairs = sorted(dct.key_value_pairs.items()) else: self.key_value_pairs = None if 'keywords' in dct: self.keywords = ', '.join(sorted(dct.keywords)) else: self.keywords = None self.dipole = dct.get('dipole') if self.dipole is not None: self.dipole = ', '.join('{0:.3f}'.format(d) for d in self.dipole) self.data = dct.get('data') if self.data: self.data = ', '.join(self.data.keys()) self.constraints = dct.get('constraints') if self.constraints: self.constraints = ', '.join(d['name'] for d in self.constraints)
def __init__(self, dct, subscript=None): self.dct = dct self.cell = [['{0:.3f}'.format(a) for a in axis] for axis in dct.cell] forces = dict2forces(dct) if forces is None: fmax = None self.forces = None else: fmax = (forces**2).sum(1).max()**0.5 N = len(forces) self.forces = [] for n, f in enumerate(forces): if n < 5 or n >= N - 5: f = tuple('{0:10.3f}'.format(x) for x in f) symbol = chemical_symbols[dct.numbers[n]] self.forces.append((n, symbol) + f) elif n == 5: self.forces.append((' ...', '', ' ...', ' ...', ' ...')) self.stress = dct.get('stress') if self.stress is not None: self.stress = ', '.join('{0:.3f}'.format(s) for s in self.stress) if 'masses' in dct: mass = dct.masses.sum() else: mass = atomic_masses[dct.numbers].sum() formula = hill(dct.numbers) if subscript: formula = subscript.sub(r'<sub>\1</sub>', formula) table = [ ('id', '', dct.id), ('age', '', float_to_time_string(now() - dct.ctime, True)), ('formula', '', formula), ('user', '', dct.user), ('calculator', '', dct.get('calculator')), ('energy', 'eV', dct.get('energy')), ('fmax', 'eV/Ang', fmax), ('charge', '|e|', dct.get('charge')), ('mass', 'au', mass), ('unique id', '', dct.unique_id), ('volume', 'Ang^3', abs(np.linalg.det(dct.cell)))] self.table = [(name, unit, value) for name, unit, value in table if value is not None] if 'key_value_pairs' in dct: self.key_value_pairs = sorted(dct.key_value_pairs.items()) else: self.key_value_pairs = None self.dipole = dct.get('dipole') if self.dipole is not None: self.dipole = ', '.join('{0:.3f}'.format(d) for d in self.dipole) self.data = dct.get('data') if self.data: self.data = ', '.join(self.data.keys()) self.constraints = dct.get('constraints') if self.constraints: self.constraints = ', '.join(d['name'] for d in self.constraints)
def __init__(self, row, subscript=None): self.row = row self.cell = [["{0:.3f}".format(a) for a in axis] for axis in row.cell] forces = row.get("constrained_forces") if forces is None: fmax = None self.forces = None else: fmax = (forces ** 2).sum(1).max() ** 0.5 N = len(forces) self.forces = [] for n, f in enumerate(forces): if n < 5 or n >= N - 5: f = tuple("{0:10.3f}".format(x) for x in f) symbol = chemical_symbols[row.numbers[n]] self.forces.append((n, symbol) + f) elif n == 5: self.forces.append((" ...", "", " ...", " ...", " ...")) self.stress = row.get("stress") if self.stress is not None: self.stress = ", ".join("{0:.3f}".format(s) for s in self.stress) if "masses" in row: mass = row.masses.sum() else: mass = atomic_masses[row.numbers].sum() formula = hill(row.numbers) if subscript: formula = subscript.sub(r"<sub>\1</sub>", formula) table = [ ("id", "", row.id), ("age", "", float_to_time_string(now() - row.ctime, True)), ("formula", "", formula), ("user", "", row.user), ("calculator", "", row.get("calculator")), ("energy", "eV", row.get("energy")), ("fmax", "eV/Ang", fmax), ("charge", "|e|", row.get("charge")), ("mass", "au", mass), ("magnetic moment", "au", row.get("magmom")), ("unique id", "", row.unique_id), ("volume", "Ang^3", row.get("volume")), ] self.table = [(name, unit, value) for name, unit, value in table if value is not None] self.key_value_pairs = sorted(row.key_value_pairs.items()) or None self.dipole = row.get("dipole") if self.dipole is not None: self.dipole = ", ".join("{0:.3f}".format(d) for d in self.dipole) self.plots = [] self.data = row.get("data") if self.data: plots = [] for name, value in self.data.items(): if isinstance(value, dict) and "xlabel" in value: plots.append((value.get("number"), name)) self.plots = [name for number, name in sorted(plots)] self.data = ", ".join(self.data.keys()) self.constraints = row.get("constraints") if self.constraints: self.constraints = ", ".join(d["name"] for d in self.constraints)