Coverage for /builds/kinetik161/ase/ase/gui/quickinfo.py: 72.15%
79 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
1"Module for displaying information about the system."
4import warnings
6import numpy as np
8from ase.gui.i18n import _
10ucellformat = """\
11 {:8.3f} {:8.3f} {:8.3f}
12 {:8.3f} {:8.3f} {:8.3f}
13 {:8.3f} {:8.3f} {:8.3f}
14"""
17def info(gui):
18 images = gui.images
19 nimg = len(images)
20 atoms = gui.atoms
22 tokens = []
24 def add(token=''):
25 tokens.append(token)
27 if len(atoms) < 1:
28 add(_('This frame has no atoms.'))
29 else:
30 img = gui.frame
32 if nimg == 1:
33 add(_('Single image loaded.'))
34 else:
35 add(_('Image {} loaded (0–{}).').format(img, nimg - 1))
36 add()
37 add(_('Number of atoms: {}').format(len(atoms)))
39 # We need to write ų further down, so we have no choice but to
40 # use proper subscripts in the chemical formula:
41 formula = atoms.get_chemical_formula()
42 subscripts = dict(zip('0123456789', '₀₁₂₃₄₅₆₇₈₉'))
43 pretty_formula = ''.join(subscripts.get(c, c) for c in formula)
44 add(pretty_formula)
46 add()
47 add(_('Unit cell [Å]:'))
48 add(ucellformat.format(*atoms.cell.ravel()))
49 periodic = [[_('no'), _('yes')][int(periodic)]
50 for periodic in atoms.pbc]
51 # TRANSLATORS: This has the form Periodic: no, no, yes
52 add(_('Periodic: {}, {}, {}').format(*periodic))
53 add()
55 cellpar = atoms.cell.cellpar()
56 add()
57 add(_('Lengths [Å]: {:.3f}, {:.3f}, {:.3f}').format(*cellpar[:3]))
58 add(_('Angles: {:.1f}°, {:.1f}°, {:.1f}°').format(*cellpar[3:]))
60 if atoms.cell.rank == 3:
61 add(_('Volume: {:.3f} ų').format(atoms.cell.volume))
63 add()
65 if nimg > 1:
66 if all((atoms.cell == img.cell).all() for img in images):
67 add(_('Unit cell is fixed.'))
68 else:
69 add(_('Unit cell varies.'))
71 if atoms.pbc[:2].all() and atoms.cell.rank >= 1:
72 try:
73 lat = atoms.cell.get_bravais_lattice()
74 except RuntimeError:
75 add(_('Could not recognize the lattice type'))
76 except Exception:
77 add(_('Unexpected error determining lattice type'))
78 else:
79 add(_('Reduced Bravais lattice:\n{}').format(lat))
81 # Print electronic structure information if we have a calculator
82 if atoms.calc:
83 calc = atoms.calc
85 def getresult(name, get_quantity):
86 # ase/io/trajectory.py line 170 does this by using
87 # the get_property(prop, atoms, allow_calculation=False)
88 # so that is an alternative option.
89 try:
90 if calc.calculation_required(atoms, [name]):
91 quantity = None
92 else:
93 quantity = get_quantity()
94 except Exception as err:
95 quantity = None
96 errmsg = ('An error occurred while retrieving {} '
97 'from the calculator: {}'.format(name, err))
98 warnings.warn(errmsg)
99 return quantity
101 # SinglePointCalculators are named after the code which
102 # produced the result, so this will typically list the
103 # name of a code even if they are just cached results.
104 add()
105 from ase.calculators.singlepoint import SinglePointCalculator
106 if isinstance(calc, SinglePointCalculator):
107 add(_('Calculator: {} (cached)').format(calc.name))
108 else:
109 add(_('Calculator: {} (attached)').format(calc.name))
111 energy = getresult('energy', atoms.get_potential_energy)
112 forces = getresult('forces', atoms.get_forces)
113 magmom = getresult('magmom', atoms.get_magnetic_moment)
115 if energy is not None:
116 energy_str = _('Energy: {:.3f} eV').format(energy)
117 add(energy_str)
119 if forces is not None:
120 maxf = np.linalg.norm(forces, axis=1).max()
121 forces_str = _('Max force: {:.3f} eV/Å').format(maxf)
122 add(forces_str)
124 if magmom is not None:
125 mag_str = _('Magmom: {:.3f} µ').format(magmom)
126 add(mag_str)
128 return '\n'.join(tokens)