EMSuite - Electrostatic Map Suite
A Python package for electrostatic tuning maps, APBS potential surfaces, and coupled potential→tuning workflows on molecular systems.
Figure 1: Tuning effects of a +1 e charge on the S1 excitation energy of water, calculated at the B3LYP/cc-pVTZ level in vacuum.
Overview
EMSuite qualifies and quantifies how external electrostatic environments shift electronic structure. Four channels work together:
| Channel | CLI | Role |
|---|---|---|
| Surface | emsuite -s surface.in |
Build a VDW surface (.surf) from SMILES or XYZ |
| Tuning | emsuite -t tuning.in |
QM/MM tuning maps for molecular properties |
| Potential | emsuite -p potential.in |
APBS φ or Gauss-law charges on a surface |
| Coupled | emsuite -c coupled.in |
Potential map → tuning in one run |
The tuning channel extends the electrostatic spectral tuning approach (Gozem et al., 2019).
Installation
pip install emsuite # CPU
pip install emsuite[gpu] # CPU + GPU4PySCF + CuPy (CUDA 12.x)
EMSuite detects GPUs automatically and falls back to CPU when none are available.
Command-line interface
The CLI is the primary interface. Every workflow is driven by a .in file
(a Python-syntax assignment list). Copy a template from examples/templates/,
edit paths and parameters, then run one command.
emsuite -s surface.in # generate VDW surface
emsuite -t tuning.in # electrostatic tuning maps
emsuite -p potential.in # APBS potential / Gauss-law charge map
emsuite -c coupled.in # potential → tuning pipeline
emsuite --help
| Flag | Channel | Input |
|---|---|---|
-s, --surface |
Surface | surface.in |
-t, --tuning |
Tuning | tuning.in |
-p, --potential |
Potential | potential.in |
-c, --coupled |
Coupled | coupled.in |
Typical workflows
A. Homogeneous probe charge (SMILES → surface → tuning)
SMILES/XYZ ──► emsuite -s surface.in ──► molecule.surf + molecule.xyz
│
▼
emsuite -t tuning.in
│
▼
results_{molecule}_{timestamp}/
B. Protein field (potential → coupled tuning)
ligand.xyz + protein.pdb ──► emsuite -c coupled.in
│
APBS Gauss-law charges on ligand VDW
│
▼
tuning maps on heterogeneous .surf
C. Potential only (inspect φ or charges before tuning)
molecule.xyz ──► emsuite -p potential.in ──► potential.surf (+ .csv)
Tutorial 1 — Water from SMILES (GPU tuning)
A complete worked example lives in examples/water-gpu/.
Step 1 — surface.in
input_type = 'SMILES'
input_data = 'O'
output_surf = 'Water.surf'
optimized_xyz = 'Water.xyz'
surface_density = 1.0
surface_type = 'homogenous'
surface_charge = 0.10 # uniform +0.10 e probe on every point
optimize = True
optimize_method = 'uff'
emsuite -s surface.in
Produces Water.xyz (UFF geometry) and Water.surf (~30 VDW points).
Step 2 — tuning.in
molecule = 'Water.xyz'
surface_file = 'Water.surf'
basis_set = '6-31G*'
method = 'dft'
functional = 'b3lyp'
charge = 0
spin = 0
solvent = None
calc_type = 'separate' # one QM/MM calc per surface point
properties = ['homo', 'lumo', 'gap']
state_of_interest = 1
triplet = False
parallel = True # Ray + GPU when available
num_procs = 1 # one GPU worker (set to match --gres=gpu:N)
emsuite -t tuning.in
On a SLURM cluster, run from examples/water-gpu/:
./run_gpu.sh # smoke (CLI)
./run_gpu.sh --full # full property set (see tuning.in in that folder)
Tutorial 2 — Ethanol excited-state tuning
surface.in
input_type = 'SMILES'
input_data = 'CCO'
output_surf = 'CCO.surf'
optimized_xyz = 'CCO.xyz'
surface_charge = 0.10
optimize = True
optimize_method = 'uff'
tuning.in
molecule = 'CCO.xyz'
surface_file = 'CCO.surf'
properties = ['exe', 'osc']
state_of_interest = 1
triplet = False
calc_type = 'separate'
parallel = True
emsuite -s surface.in
emsuite -t tuning.in
Sample inputs and outputs: examples/tuning/CCO2-exe/.
Tutorial 3 — Ligand in a protein field (coupled)
Use when the electrostatic environment comes from a macromolecule and you want Gauss-law surface charges feeding tuning.
coupled.in
molecule = 'ligand.xyz' # ligand geometry; VDW surface is built on this
protein = 'complex.pdb'
protein_format = 'pdb'
ligand_resname = 'LIG'
ligand_atoms = 'present' # 'present' | 'absent' | 'charged'
ligand_mol2 = 'ligand.mol2' # required for present/charged with PDB protein
output_surf = 'coupled.surf'
potential_method = 'apbs'
potential_quantity = 'charge' # Gauss-law q (e) at each surface point
properties = ['homo', 'lumo', 'gap']
basis_set = '6-31G*'
calc_type = 'separate'
parallel = True
num_procs = 1
emsuite -c coupled.in
To run potential and tuning separately instead:
emsuite -p potential.in # writes heterogeneous potential.surf
# edit tuning.in to point surface_file at that .surf
emsuite -t tuning.in
Reuse a precomputed potential surface in coupled mode (skip APBS on repeat runs):
potential_surf = 'precomputed.surf' # skips potential channel entirely
Tutorial 4 — Potential map only
potential.in
molecule = 'ligand.xyz'
surface_file = None # auto-generate ligand VDW if omitted
output_surf = 'potential.surf'
method = 'apbs'
quantity = 'potential' # 'potential' = interpolated φ; 'charge' = Gauss-law q
# Optional protein field (XYZ Gasteiger or PDB via pdb2pqr):
# protein = 'protein.xyz'
# ligand_atoms = 'absent'
pdie = 2.0
sdie = 78.54
emsuite -p potential.in
The .surf fourth column holds APBS potential (when quantity='potential') or
Gauss-law charge in e (when quantity='charge'). A companion .csv is written
alongside the .surf file.
Input file reference
Templates with comments: examples/templates/{surface,tuning,potential,coupled}.in.
surface.in — emsuite -s
| Key | Default | Description |
|---|---|---|
input_type |
(required) | 'SMILES' or 'XYZ' |
input_data |
(required) | SMILES string or path to XYZ |
output_surf |
'surface.surf' |
Output .surf path |
optimized_xyz |
auto | Path for optimized geometry |
surface_density |
1.0 |
Points per Ų |
surface_scale |
1.0 |
VDW radii scale factor |
surface_type |
'homogenous' |
'homogenous' or 'heterogenous' |
surface_charge |
0.10 |
Uniform charge (homogenous only) |
optimize |
auto | True for SMILES, False for XYZ |
optimize_method |
'mmff' |
'mmff', 'uff', or 'pyscf' |
method, basis_set, functional |
DFT defaults | Used when optimize_method='pyscf' |
charge, spin |
0, 0 |
Molecular charge and 2S spin |
solvent |
None |
PCM solvent name or None |
heterogenous writes zero charges as placeholders for hand-editing.
tuning.in — emsuite -t
| Key | Default | Description |
|---|---|---|
molecule |
(required) | Path to XYZ geometry |
surface_file |
(required) | Path to .surf file |
properties |
['all'] |
Property codes (see table below) |
basis_set |
'6-31G*' |
Basis set |
method |
'dft' |
'dft' or 'hf' |
functional |
'b3lyp' |
XC functional (DFT only) |
charge, spin |
0, 0 |
Molecular charge and 2S spin |
solvent |
None |
PCM solvent |
calc_type |
'separate' |
'separate' (per-point) or 'combined' |
state_of_interest |
2 |
TD states (for exe/osc) |
triplet |
False |
Triplet TD states |
parallel |
True |
Ray parallel workers |
num_procs |
None |
Worker count (None = auto-detect GPUs/CPUs) |
Interrupted runs resume from logs_*/.resume_metadata.json when parameters match.
potential.in — emsuite -p
| Key | Default | Description |
|---|---|---|
molecule |
(required) | Ligand / molecule XYZ (ligand alias) |
protein |
None |
Protein XYZ or PDB for the external field |
protein_format |
'xyz' |
'xyz' (Gasteiger) or 'pdb' (pdb2pqr) |
ligand_atoms |
'present' |
'present', 'absent', or 'charged' (PDB only) |
ligand_resname |
None |
HETATM residue name (PDB) |
ligand_mol2 |
None |
Ligand MOL2 for pdb2pqr occupancy |
forcefield |
'AMBER' |
pdb2pqr force field |
ph |
7.0 |
pdb2pqr protonation pH (None to disable) |
surface_file |
None |
Existing VDW surface; generated if omitted |
output_surf |
'potential.surf' |
Output heterogeneous .surf |
surface_density |
0.5 |
VDW density when generating surface |
method |
'apbs' |
APBS Poisson–Boltzmann |
quantity |
'potential' |
'potential' or 'charge' (Gauss-law) |
pdie, sdie |
2.0, 78.54 |
APBS dielectric constants |
When protein is set, the APBS box spans protein and ligand coordinates so a
pocket ligand is not clipped.
coupled.in — emsuite -c
Accepts all potential keys (prefixed potential_ where needed) plus all
tuning keys. Notable defaults:
| Key | Default | Description |
|---|---|---|
potential_method |
'apbs' |
Potential backend |
potential_quantity |
'charge' |
Gauss-law charges for tuning |
potential_surf |
None |
Skip potential step; reuse this .surf |
calc_type |
'separate' |
Tuning mode |
parallel |
False |
Parallel tuning (set True on GPU) |
Available tuning properties
| Property | Description | Units |
|---|---|---|
gse |
Ground state energy | kcal/mol |
homo |
HOMO energy | eV |
lumo |
LUMO energy | eV |
gap |
HOMO–LUMO gap | eV |
dm |
Dipole moment magnitude | Debye |
spin |
Spin magnitude | dimensionless |
ie |
Ionization energy | kcal/mol |
ea |
Electron affinity | kcal/mol |
cp |
Chemical potential | kcal/mol |
eng |
Electronegativity | eV |
hard |
Chemical hardness | eV |
efl |
Electrophilicity | eV |
nfl |
Nucleophilicity | eV |
fukui_plus |
Nucleophilic Fukui index | eV |
fukui_minus |
Electrophilic Fukui index | eV |
exe |
Excitation energies | eV |
osc |
Oscillator strengths | dimensionless |
Use properties = ['all'] for the full registry. Dependencies (e.g. gap pulls
in homo and lumo) are resolved automatically.
Output
Tuning (emsuite -t / coupled tuning step)
results_{molecule}_{timestamp}/
├── {molecule}_tuning_summary.csv
├── {molecule}_{property}.mol2
├── {molecule}_{property}_normalized.mol2
├── logs/
│ ├── calculation_summary.out
│ ├── .resume_metadata.json
│ └── point_*.log
└── README.txt
Potential (emsuite -p / coupled potential step)
potential.surf # heterogeneous x, y, z, value
potential.csv # same data, tabular
Surface (emsuite -s)
molecule.surf # x, y, z, q
molecule.xyz # optimized geometry (when optimize=True)
Python API
The CLI is a thin wrapper around frozen input dataclasses. Every .in file maps
to from_file(); every key maps to from_config(**kwargs).
from emsuite import SurfaceInput, TuningInput, PotentialInput, CoupledInput
# Equivalent to: emsuite -s surface.in
SurfaceInput.from_file("surface.in").run()
# Equivalent to: emsuite -t tuning.in
TuningInput.from_file("tuning.in").run()
# Build inline (no .in file):
surf = SurfaceInput.from_config(
input_type="SMILES", input_data="O",
output_surf="Water.surf", optimized_xyz="Water.xyz",
surface_charge=0.10,
).run()
TuningInput.from_config(
molecule="Water.xyz",
surface_file=surf.path,
properties=["homo", "lumo", "gap"],
parallel=True,
num_procs=1,
).run()
from_config accepts config= (path or dict) with keyword overrides:
TuningInput.from_config(config="tuning.in", parallel=False).run()
Lower-level runners (same calculations, no result objects):
import emsuite
emsuite.run_surface_calculation("surface.in")
emsuite.run_tuning_calculation("tuning.in")
emsuite.run_potential_calculation("potential.in")
emsuite.run_coupled_calculation("coupled.in")
See examples/water-gpu/run_api.py for a full SMILES→tuning script.
GPU notes
- Install with
pip install emsuite[gpu]on the compute node (or in your job env). - Set
parallel = Trueandnum_procsto the number of GPUs allocated. - Verify:
python -c "import gpu4pyscf.dft; from emsuite.core import check_gpu_info; print(check_gpu_info())" - Integration tests:
pytest -m gpu(CUDA required) - Water benchmark:
examples/water-gpu/run_gpu.sh
If gpu4pyscf import fails after install, reinstall cleanly:
pip uninstall -y gpu4pyscf-cuda12x gpu4pyscf-libxc-cuda12x gpu4pyscf-libxc-cuda11x
rm -rf ~/.local/lib/python*/site-packages/gpu4pyscf
pip install --no-cache-dir 'gpu4pyscf-cuda12x==1.4.3'
Examples
| Path | Contents |
|---|---|
examples/templates/ |
Annotated .in templates for all four channels |
examples/water-gpu/ |
SMILES water → GPU tuning (CLI + Python) |
examples/tuning/CCO2-exe/ |
Ethanol exe tuning sample outputs |
examples/families/ |
LOV / miniSOG / SOPP3 combined exe examples by family |
Citation
Gozem, S., et al. "Electrostatic Tuning of Molecular Properties" J. Phys. Chem. B 2019, DOI: 10.1021/acs.jpcb.9b00489
License
MIT License — see LICENSE.
Support
Open an issue on GitHub for bugs and feature requests.
Metadata
Release files for emsuite 1.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| emsuite-1.6.0.tar.gz | 5.1 MB | With provenance | Details for emsuite-1.6.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| emsuite-1.6.0-py3-none-any.whl | Python 3 | none | any | Details for emsuite-1.6.0-py3-none-any.whl |
Total release size: 5.2 MB
Release files / emsuite-1.6.0.tar.gz
| Download URL | emsuite-1.6.0.tar.gz |
|---|---|
| Size | 5.1 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e892ef4a16965c4d88e8c1313ecad0a092120d6341a25a9ed176800358402af4
|
|
BLAKE2b-256 checksum How to use checksums |
b8d216905d144051920dd96c5d110ddb8218e2d31ab376fd0bef66955aaa401a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logRelease files / emsuite-1.6.0-py3-none-any.whl
| Download URL | emsuite-1.6.0-py3-none-any.whl |
|---|---|
| Size | 76.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
512ed988ed8e61c23dd3cf31dc97c0be4f86f753da1c823b8ce5704fa1766327
|
|
BLAKE2b-256 checksum How to use checksums |
e4fd890c6f81c93bfa36d9ff0a218c6c7d1db1b571f3ef63b60bf124ab882d13
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logSecurity for emsuite 1.6.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for emsuite-1.6.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.