physikmdb
Photoemission momentum maps and molecular orbitals from PhysikMDB, in Python.
The same C physics kernels the website runs, plus a client for the database behind it. Look up a calculation, ask for an orbital by name, get a numpy array.
pip install physikmdb
numpy is the only dependency, and no compiler is needed — a prebuilt kernel library ships for Linux, macOS and Windows, and is built from source only on a platform without one.
Five lines
import physikmdb
db = physikmdb.Database()
calc = db.calculation(21)
image = calc.momentum_map("HOMO") # (200, 200); every argument but the
# orbital has a default (hnu=30 eV, k_max=3 Å⁻¹, ...)
basis.bin and the HOMO's coefficients are downloaded on first use and cached,
so the obvious loop costs one small request per orbital:
for name in ("HOMO", "HOMO-1", "HOMO-2"):
image = calc.momentum_map(name)
Finding a calculation
db.systems() # every molecule
db.systems(short_name="2A")
db.systems(xc_functional="B3LYP", basis_set="cc-pVTZ", charge=0)
db.calculations() # every calculation
db.calculations(system_id=7) # one molecule's
db.calculations(code="Orca", xc_functional="B3LYP")
db.filters() # what those filters can be set to
calc = db.calculation(21) # by database id — the number in
# an entry page's URL
Both listings return plain records and do no downloading.
One calculation
calc.formula, calc.code, calc.xc_functional, calc.basis_set
calc.charge, calc.spin, calc.spin_restricted
calc.total_energy, calc.homo_energy, calc.lumo_energy, calc.gap # eV
calc.orbitals # every orbital
calc.orbital("HOMO-2") # by name, case-insensitive
calc.orbital(21) # by row index
calc.homo, calc.lumo
calc.basis # the basis set
calc.coefficients # the full (Nmo, Nbasis) matrix
An orbital is a record — index, name, energy (eV) and energy_hartree,
occupation, symmetry, spin. For an unrestricted calculation a bare "HOMO" is
the up channel, the one the website shows first; pass
calc.orbital("HOMO", spin="down") for the other.
calc.basis/calc.coefficients cover basis.bin/coefficients.bin. Any
other stored file — the archival HDF5, the .xyz geometry, or the code's own
input/output — downloads (and caches) with calc.download():
calc.download(".hdf5") # geometry + full basis + MO coefficients
calc.download(".xyz") # geometry
calc.download(".out") # ORCA's output; NWChem uses ".nwo", ".molden"
calc.download(".inp") # ORCA's input; NWChem uses ".nwi"
Computing
Every compute method takes an orbital name, an index, an Orbital, or a list
of them. Every other argument has a default:
calc.momentum_map("HOMO", hnu=30.0, # photon energy [eV]
k_max=3.0, points=200,
angles=(0, 30, 0), # orientation (phi, theta, psi) [deg]
substrate="fcc110", # average over its domains
polarisation=(45, 0), # (polar, azimuth) of A [deg]
polarisation_type="linear", # or "circular", "toroid"
s_share=0.0, # s-polarised share [%]
handedness="left", # or "right", "cd"
gamma=None, # IMFP damping [Å⁻¹]; None computes
# it from the kinetic energy, as the
# website itself does
normalise=False)
calc.momentum_map(["HOMO", "HOMO-1"], weights=[1.0, 0.5]) # incoherent sum
calc.momentum_map("HOMO", kinetic_energy=15.5) # E_kin directly, instead of hν
calc.wavefunction("HOMO", extent=8.0, points=64) # signed ψ(r)
calc.density(["HOMO", "HOMO-1"], extent=8.0) # Σ|ψ(r)|²
calc.momentum_density("HOMO", k_max=3.0) # |ψ̃(k)|²
kinetic_energy, if given, is used directly instead of deriving it from hnu.
E_kin = hnu + orbital.energy, with the binding energy negative, as on the
entry page. Several orbitals are summed incoherently, each at its own kinetic
energy; one the photon cannot emit contributes nothing.
gamma (the inelastic-mean-free-path damping) is computed automatically from
each orbital's kinetic energy unless you override it — the same "universal
curve" the website's own JS uses. handedness="cd" depends on it, so it is
zero everywhere only if you explicitly pass gamma=0.
Energy spectrum
A broadened density of states, like the entry page's energy plot:
energies, intensity = calc.energy_spectrum(fwhm=0.15, shape="gaussian") # both eV
shape is "gaussian" or "lorentzian"; energy_range=(low, high) restricts
the window, otherwise it is sized around the orbital energies automatically.
Excited states
A TD-DFT (casida) calculation carries its excited states, and each is a
coherent sum of one-electron transitions. calc.calculation_type is
"groundstate" or "casida"; find casida calculations directly rather than
checking .excitations for a non-empty list as a side effect:
casida_calcs = db.calculations(calculation_type="casida")
state = calc.excitation(5) # the 5th root, as the code numbered it
state.energy, state.oscillator_strength, state.tda
state.holes() # every occupied orbital it empties
Photoemission from an exciton is one map per photohole, each at its own
kinetic energy hν + ε_j + Ω — an entangled state genuinely looks different at
each of them, which is the point of measuring it:
for hole in state.holes():
image = calc.exciton_momentum_map(state, hole, hnu=35.0)
Underneath, the coherent sum over conduction orbitals is a linear combination of coefficient rows, so it is a coefficient row — one field evaluation per photohole, not one per transition:
row = calc.dyson_row(state, hole=32) # same shape as any coefficients.bin row
coverage= is the target fraction of the state's weight its kept pairs must
cover (0.95 by default, matching the website; the stored file itself covers
0.99, so that is the ceiling). Omit hole= to sum every photohole of the
state, which is what an analyser with no energy resolution would see.
Working offline
calc.save("naphthalene/") # basis.bin + coefficients.bin + calculation.json
calc = physikmdb.load("naphthalene/") # same object, no network at all
load() also opens a folder holding just basis.bin and coefficients.bin —
the website's download button, or your own writer. Without calculation.json
there are no orbital names or energies, so address orbitals by index and pass
kinetic_energy=.
Units
eV and Ångström, the same units the website's own controls are labelled in.
Energies in eV, k_max and gamma in Å⁻¹, extent in Å.
Orbitals carry both: orbital.energy is eV, orbital.energy_hartree is
Hartree. physikmdb.units holds the two constants and the four conversions,
and is the only place in the package where a number changes meaning.
Plotting
Optional, and deliberately small — enough to see whether a map looks right:
pip install physikmdb[plot]
from physikmdb import plot
ax = plot.momentum_map(image, k_max=3.0, title="HOMO")
ax.figure.savefig("homo.png") # it's a plain matplotlib Axes - use it as usual
Each function takes and returns an ordinary Axes (ax= to draw into an
existing one), so once matplotlib is installed you drive it directly -
import matplotlib.pyplot as plt for multi-panel figures, ax.figure for
anything else. Nothing outside physikmdb.plot imports matplotlib.
The kernels, unconverted
For your own basis and coefficients, or when you want nothing at all between you and the C:
from physikmdb import kernels # Hartree, Bohr, Bohr⁻¹ throughout
basis = kernels.read_basis("basis.bin")
rows = kernels.read_coefficients("coefficients.bin", basis)
image = kernels.momentum_map(basis, rows[21], E_kin=0.779, k_max=1.59)
kernels.Basis is nine plain numpy arrays, so a basis you built yourself works
the same way. physikmdb.binary reads and writes the basis.bin /
coefficients.bin format the website serves.
Examples
examples/ in the source distribution, simplest first:
01_first_map.py |
one orbital, one map |
02_browse.py |
systems, calculations, filters, orbitals |
03_orbital_series.py |
a map per orbital, and plotting |
04_experiment.py |
tilt, substrate, polarisation, dichroism, damping |
05_offline_and_fields.py |
save/load, and the 3D fields |
06_energy_spectrum.py |
the broadened density of states |
07_kernels_directly.py |
the atomic-units layer, and your own basis |
Licence
EUPL-1.2. See LICENSE.
The scientific data served by a PhysikMDB instance is licensed separately — see physikmdb.uni-graz.at/license.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file physikmdb-1.5.0.tar.gz.
File metadata
- Download URL: physikmdb-1.5.0.tar.gz
- Upload date:
- Size: 81.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4cdadb85d5e29915fa7d4b2196f644b01941cdb8991f3aaac5884e58c46e053c
|
|
| MD5 |
fe12830de2a99d54fe5f1cb4ccc2b2d3
|
|
| BLAKE2b-256 |
eaa666663ea010c737b50336786dee2336a426c6a0a600a763b8bb1a634270b0
|
File details
Details for the file physikmdb-1.5.0-py3-none-win_amd64.whl.
File metadata
- Download URL: physikmdb-1.5.0-py3-none-win_amd64.whl
- Upload date:
- Size: 113.7 kB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f7f0ef51e41723e2c3fb5ecc74fc3ab6023d2b861b32f9b797922af6d54c0e64
|
|
| MD5 |
fb57743326ecf7aded4f818f7b57bee2
|
|
| BLAKE2b-256 |
118587fb2fb80c90c47f178f4c3b1f682e1a0da8607b1bc853f20f7b9b2b3401
|
File details
Details for the file physikmdb-1.5.0-py3-none-manylinux_2_28_x86_64.whl.
File metadata
- Download URL: physikmdb-1.5.0-py3-none-manylinux_2_28_x86_64.whl
- Upload date:
- Size: 81.9 kB
- Tags: Python 3, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
768313a033f3d1134ee390153d823b416fdac6e2cffd5f89ddbd7c1a253d7671
|
|
| MD5 |
53312d407ff6c1243895bb7d16b1c325
|
|
| BLAKE2b-256 |
962c4b71dc5e68aed2a0b889fed13e84fcec8864a4ecf6e11b70dc8daca10f4c
|
File details
Details for the file physikmdb-1.5.0-py3-none-macosx_11_0_universal2.whl.
File metadata
- Download URL: physikmdb-1.5.0-py3-none-macosx_11_0_universal2.whl
- Upload date:
- Size: 94.1 kB
- Tags: Python 3, macOS 11.0+ universal2 (ARM64, x86-64)
- Uploaded using Trusted Publishing? No
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e33a0b1cf1eb9b56b2b851e2e1a94d4ef9f7a48508f1dcb8bd64234d9caeadf6
|
|
| MD5 |
5e097866142bf2c305e1076e3f17e14f
|
|
| BLAKE2b-256 |
59c733caabf9a651d00c2563c2a0ad5b7083bfcda19c27057390ddb7340a7ff6
|