hofmann 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hofmann-0.1.0/LICENSE +21 -0
- hofmann-0.1.0/PKG-INFO +147 -0
- hofmann-0.1.0/README.md +108 -0
- hofmann-0.1.0/pyproject.toml +56 -0
- hofmann-0.1.0/setup.cfg +4 -0
- hofmann-0.1.0/src/hofmann/__init__.py +65 -0
- hofmann-0.1.0/src/hofmann/bonds.py +89 -0
- hofmann-0.1.0/src/hofmann/defaults.py +296 -0
- hofmann-0.1.0/src/hofmann/model.py +966 -0
- hofmann-0.1.0/src/hofmann/parser.py +146 -0
- hofmann-0.1.0/src/hofmann/polyhedra.py +273 -0
- hofmann-0.1.0/src/hofmann/py.typed +0 -0
- hofmann-0.1.0/src/hofmann/render_mpl.py +2421 -0
- hofmann-0.1.0/src/hofmann/scene.py +541 -0
- hofmann-0.1.0/src/hofmann.egg-info/PKG-INFO +147 -0
- hofmann-0.1.0/src/hofmann.egg-info/SOURCES.txt +25 -0
- hofmann-0.1.0/src/hofmann.egg-info/dependency_links.txt +1 -0
- hofmann-0.1.0/src/hofmann.egg-info/requires.txt +18 -0
- hofmann-0.1.0/src/hofmann.egg-info/top_level.txt +1 -0
- hofmann-0.1.0/tests/test_bonds.py +85 -0
- hofmann-0.1.0/tests/test_defaults.py +121 -0
- hofmann-0.1.0/tests/test_init.py +23 -0
- hofmann-0.1.0/tests/test_model.py +808 -0
- hofmann-0.1.0/tests/test_parser.py +151 -0
- hofmann-0.1.0/tests/test_polyhedra.py +245 -0
- hofmann-0.1.0/tests/test_render_mpl.py +1854 -0
- hofmann-0.1.0/tests/test_scene.py +487 -0
hofmann-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT Licence
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Benjamin J. Morgan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicence, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
hofmann-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hofmann
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A modern Python reimagining of the XBS ball-and-stick crystal structure viewer
|
|
5
|
+
Author-email: "Benjamin J. Morgan" <b.j.morgan@bath.ac.uk>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bjmorgan/hofmann
|
|
8
|
+
Project-URL: Documentation, https://hofmann.readthedocs.io
|
|
9
|
+
Project-URL: Repository, https://github.com/bjmorgan/hofmann
|
|
10
|
+
Project-URL: Issues, https://github.com/bjmorgan/hofmann/issues
|
|
11
|
+
Keywords: crystallography,molecular-visualisation,ball-and-stick,xbs,matplotlib
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: numpy>=1.24
|
|
25
|
+
Requires-Dist: matplotlib>=3.7
|
|
26
|
+
Requires-Dist: scipy>=1.10
|
|
27
|
+
Provides-Extra: pymatgen
|
|
28
|
+
Requires-Dist: pymatgen>=2024.1.1; extra == "pymatgen"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
32
|
+
Provides-Extra: docs
|
|
33
|
+
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
34
|
+
Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
|
|
35
|
+
Requires-Dist: sphinx-autodoc-typehints>=2.0; extra == "docs"
|
|
36
|
+
Provides-Extra: all
|
|
37
|
+
Requires-Dist: hofmann[dev,docs,pymatgen]; extra == "all"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# hofmann
|
|
41
|
+
|
|
42
|
+
[](https://github.com/bjmorgan/hofmann/actions/workflows/ci.yml)
|
|
43
|
+
[](https://hofmann.readthedocs.io/en/latest/)
|
|
44
|
+
|
|
45
|
+
A modern Python reimagining of Methfessel's [XBS](https://www.ccl.net/cca/software/X-WINDOW/xbs/) ball-and-stick viewer (1995), named after [August Wilhelm von Hofmann](https://en.wikipedia.org/wiki/August_Wilhelm_von_Hofmann) who built the first ball-and-stick molecular models in 1865.
|
|
46
|
+
|
|
47
|
+
hofmann renders crystal and molecular structures as depth-sorted ball-and-stick images with static, publication-quality vector output (SVG, PDF) via matplotlib.
|
|
48
|
+
|
|
49
|
+
<p align="center">
|
|
50
|
+
<img src="docs/_static/llzo.svg" width="480" alt="LLZO garnet with ZrO6 polyhedra rendered with hofmann">
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
## Features
|
|
54
|
+
|
|
55
|
+
- Static publication-quality output (SVG, PDF, PNG) via matplotlib
|
|
56
|
+
- XBS `.bs` and `.mv` (trajectory) file formats
|
|
57
|
+
- Optional pymatgen `Structure` interoperability
|
|
58
|
+
- Periodic boundary conditions with automatic image expansion
|
|
59
|
+
- Coordination polyhedra with configurable shading and slab clipping
|
|
60
|
+
- Unit cell wireframe rendering
|
|
61
|
+
- Interactive viewer with mouse rotation, zoom, and keyboard controls
|
|
62
|
+
- Orthographic and perspective projection
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install hofmann
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
For pymatgen interoperability:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install "hofmann[pymatgen]"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Requirements
|
|
77
|
+
|
|
78
|
+
- Python 3.11+
|
|
79
|
+
- numpy >= 1.24
|
|
80
|
+
- matplotlib >= 3.7
|
|
81
|
+
- scipy >= 1.10
|
|
82
|
+
- pymatgen >= 2024.1.1 (optional)
|
|
83
|
+
|
|
84
|
+
## Quick start
|
|
85
|
+
|
|
86
|
+
### From an XBS file
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from hofmann import StructureScene
|
|
90
|
+
|
|
91
|
+
scene = StructureScene.from_xbs("structure.bs")
|
|
92
|
+
scene.render_mpl("output.svg")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### From a pymatgen Structure
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from pymatgen.core import Lattice, Structure
|
|
99
|
+
from hofmann import StructureScene, BondSpec
|
|
100
|
+
|
|
101
|
+
lattice = Lattice.cubic(5.43)
|
|
102
|
+
structure = Structure(
|
|
103
|
+
lattice, ["Si"] * 8,
|
|
104
|
+
[[0.0, 0.0, 0.0], [0.5, 0.5, 0.0],
|
|
105
|
+
[0.5, 0.0, 0.5], [0.0, 0.5, 0.5],
|
|
106
|
+
[0.25, 0.25, 0.25], [0.75, 0.75, 0.25],
|
|
107
|
+
[0.75, 0.25, 0.75], [0.25, 0.75, 0.75]],
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
bonds = [BondSpec(species=("Si", "Si"), min_length=0.0,
|
|
111
|
+
max_length=2.8, radius=0.1, colour=0.5)]
|
|
112
|
+
scene = StructureScene.from_pymatgen(structure, bonds, pbc=True)
|
|
113
|
+
scene.render_mpl("si.pdf")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Controlling the view
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
scene.view.look_along([1, 1, 0]) # View along [110]
|
|
120
|
+
scene.view.zoom = 1.5 # Zoom in
|
|
121
|
+
scene.view.perspective = 0.3 # Mild perspective
|
|
122
|
+
scene.render_mpl("rotated.svg")
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Interactive viewer
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
view, style = scene.render_mpl_interactive()
|
|
129
|
+
|
|
130
|
+
# Reuse the adjusted view for static output:
|
|
131
|
+
scene.view = view
|
|
132
|
+
scene.render_mpl("final.svg", style=style)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Documentation
|
|
136
|
+
|
|
137
|
+
Full documentation is available at [hofmann.readthedocs.io](https://hofmann.readthedocs.io/), covering:
|
|
138
|
+
|
|
139
|
+
- [Getting started](https://hofmann.readthedocs.io/en/latest/getting-started.html) -- installation and first renders
|
|
140
|
+
- [User guide](https://hofmann.readthedocs.io/en/latest/user-guide.html) -- views, render styles, bonds, polyhedra, unit cells
|
|
141
|
+
- [Interactive viewer](https://hofmann.readthedocs.io/en/latest/interactive.html) -- mouse and keyboard controls
|
|
142
|
+
- [XBS file format](https://hofmann.readthedocs.io/en/latest/xbs-format.html) -- `.bs` and `.mv` format reference
|
|
143
|
+
- [API reference](https://hofmann.readthedocs.io/en/latest/api.html) -- full autodoc API
|
|
144
|
+
|
|
145
|
+
## Licence
|
|
146
|
+
|
|
147
|
+
MIT. See [LICENSE](LICENSE) for details.
|
hofmann-0.1.0/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# hofmann
|
|
2
|
+
|
|
3
|
+
[](https://github.com/bjmorgan/hofmann/actions/workflows/ci.yml)
|
|
4
|
+
[](https://hofmann.readthedocs.io/en/latest/)
|
|
5
|
+
|
|
6
|
+
A modern Python reimagining of Methfessel's [XBS](https://www.ccl.net/cca/software/X-WINDOW/xbs/) ball-and-stick viewer (1995), named after [August Wilhelm von Hofmann](https://en.wikipedia.org/wiki/August_Wilhelm_von_Hofmann) who built the first ball-and-stick molecular models in 1865.
|
|
7
|
+
|
|
8
|
+
hofmann renders crystal and molecular structures as depth-sorted ball-and-stick images with static, publication-quality vector output (SVG, PDF) via matplotlib.
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<img src="docs/_static/llzo.svg" width="480" alt="LLZO garnet with ZrO6 polyhedra rendered with hofmann">
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- Static publication-quality output (SVG, PDF, PNG) via matplotlib
|
|
17
|
+
- XBS `.bs` and `.mv` (trajectory) file formats
|
|
18
|
+
- Optional pymatgen `Structure` interoperability
|
|
19
|
+
- Periodic boundary conditions with automatic image expansion
|
|
20
|
+
- Coordination polyhedra with configurable shading and slab clipping
|
|
21
|
+
- Unit cell wireframe rendering
|
|
22
|
+
- Interactive viewer with mouse rotation, zoom, and keyboard controls
|
|
23
|
+
- Orthographic and perspective projection
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install hofmann
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For pymatgen interoperability:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install "hofmann[pymatgen]"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Requirements
|
|
38
|
+
|
|
39
|
+
- Python 3.11+
|
|
40
|
+
- numpy >= 1.24
|
|
41
|
+
- matplotlib >= 3.7
|
|
42
|
+
- scipy >= 1.10
|
|
43
|
+
- pymatgen >= 2024.1.1 (optional)
|
|
44
|
+
|
|
45
|
+
## Quick start
|
|
46
|
+
|
|
47
|
+
### From an XBS file
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from hofmann import StructureScene
|
|
51
|
+
|
|
52
|
+
scene = StructureScene.from_xbs("structure.bs")
|
|
53
|
+
scene.render_mpl("output.svg")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### From a pymatgen Structure
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from pymatgen.core import Lattice, Structure
|
|
60
|
+
from hofmann import StructureScene, BondSpec
|
|
61
|
+
|
|
62
|
+
lattice = Lattice.cubic(5.43)
|
|
63
|
+
structure = Structure(
|
|
64
|
+
lattice, ["Si"] * 8,
|
|
65
|
+
[[0.0, 0.0, 0.0], [0.5, 0.5, 0.0],
|
|
66
|
+
[0.5, 0.0, 0.5], [0.0, 0.5, 0.5],
|
|
67
|
+
[0.25, 0.25, 0.25], [0.75, 0.75, 0.25],
|
|
68
|
+
[0.75, 0.25, 0.75], [0.25, 0.75, 0.75]],
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
bonds = [BondSpec(species=("Si", "Si"), min_length=0.0,
|
|
72
|
+
max_length=2.8, radius=0.1, colour=0.5)]
|
|
73
|
+
scene = StructureScene.from_pymatgen(structure, bonds, pbc=True)
|
|
74
|
+
scene.render_mpl("si.pdf")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Controlling the view
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
scene.view.look_along([1, 1, 0]) # View along [110]
|
|
81
|
+
scene.view.zoom = 1.5 # Zoom in
|
|
82
|
+
scene.view.perspective = 0.3 # Mild perspective
|
|
83
|
+
scene.render_mpl("rotated.svg")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Interactive viewer
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
view, style = scene.render_mpl_interactive()
|
|
90
|
+
|
|
91
|
+
# Reuse the adjusted view for static output:
|
|
92
|
+
scene.view = view
|
|
93
|
+
scene.render_mpl("final.svg", style=style)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Documentation
|
|
97
|
+
|
|
98
|
+
Full documentation is available at [hofmann.readthedocs.io](https://hofmann.readthedocs.io/), covering:
|
|
99
|
+
|
|
100
|
+
- [Getting started](https://hofmann.readthedocs.io/en/latest/getting-started.html) -- installation and first renders
|
|
101
|
+
- [User guide](https://hofmann.readthedocs.io/en/latest/user-guide.html) -- views, render styles, bonds, polyhedra, unit cells
|
|
102
|
+
- [Interactive viewer](https://hofmann.readthedocs.io/en/latest/interactive.html) -- mouse and keyboard controls
|
|
103
|
+
- [XBS file format](https://hofmann.readthedocs.io/en/latest/xbs-format.html) -- `.bs` and `.mv` format reference
|
|
104
|
+
- [API reference](https://hofmann.readthedocs.io/en/latest/api.html) -- full autodoc API
|
|
105
|
+
|
|
106
|
+
## Licence
|
|
107
|
+
|
|
108
|
+
MIT. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hofmann"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A modern Python reimagining of the XBS ball-and-stick crystal structure viewer"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Benjamin J. Morgan", email = "b.j.morgan@bath.ac.uk"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["crystallography", "molecular-visualisation", "ball-and-stick", "xbs", "matplotlib"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Topic :: Scientific/Engineering :: Chemistry",
|
|
25
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"numpy>=1.24",
|
|
29
|
+
"matplotlib>=3.7",
|
|
30
|
+
"scipy>=1.10",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/bjmorgan/hofmann"
|
|
35
|
+
Documentation = "https://hofmann.readthedocs.io"
|
|
36
|
+
Repository = "https://github.com/bjmorgan/hofmann"
|
|
37
|
+
Issues = "https://github.com/bjmorgan/hofmann/issues"
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
pymatgen = ["pymatgen>=2024.1.1"]
|
|
41
|
+
dev = ["pytest>=7.0", "pytest-cov>=4.0"]
|
|
42
|
+
docs = [
|
|
43
|
+
"sphinx>=7.0",
|
|
44
|
+
"sphinx-rtd-theme>=2.0",
|
|
45
|
+
"sphinx-autodoc-typehints>=2.0",
|
|
46
|
+
]
|
|
47
|
+
all = ["hofmann[pymatgen,dev,docs]"]
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.package-data]
|
|
50
|
+
hofmann = ["py.typed"]
|
|
51
|
+
|
|
52
|
+
[tool.setuptools.packages.find]
|
|
53
|
+
where = ["src"]
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
testpaths = ["tests"]
|
hofmann-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Hofmann: a modern Python reimagining of the XBS ball-and-stick viewer.
|
|
2
|
+
|
|
3
|
+
Hofmann renders crystal and molecular structures as ball-and-stick images,
|
|
4
|
+
with static publication-quality output via matplotlib.
|
|
5
|
+
|
|
6
|
+
Example usage::
|
|
7
|
+
|
|
8
|
+
from hofmann import StructureScene
|
|
9
|
+
|
|
10
|
+
scene = StructureScene.from_xbs("structure.bs")
|
|
11
|
+
scene.render_mpl("output.svg")
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from hofmann.bonds import compute_bonds
|
|
15
|
+
from hofmann.polyhedra import compute_polyhedra
|
|
16
|
+
from hofmann.defaults import (
|
|
17
|
+
COVALENT_RADII,
|
|
18
|
+
ELEMENT_COLOURS,
|
|
19
|
+
default_atom_style,
|
|
20
|
+
default_bond_specs,
|
|
21
|
+
)
|
|
22
|
+
from hofmann.model import (
|
|
23
|
+
AtomStyle,
|
|
24
|
+
AxesStyle,
|
|
25
|
+
Bond,
|
|
26
|
+
BondSpec,
|
|
27
|
+
CellEdgeStyle,
|
|
28
|
+
Colour,
|
|
29
|
+
Frame,
|
|
30
|
+
Polyhedron,
|
|
31
|
+
PolyhedronSpec,
|
|
32
|
+
RenderStyle,
|
|
33
|
+
SlabClipMode,
|
|
34
|
+
StructureScene,
|
|
35
|
+
ViewState,
|
|
36
|
+
WidgetCorner,
|
|
37
|
+
normalise_colour,
|
|
38
|
+
)
|
|
39
|
+
from hofmann.scene import from_pymatgen, from_xbs
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"AtomStyle",
|
|
43
|
+
"AxesStyle",
|
|
44
|
+
"Bond",
|
|
45
|
+
"BondSpec",
|
|
46
|
+
"CellEdgeStyle",
|
|
47
|
+
"COVALENT_RADII",
|
|
48
|
+
"Colour",
|
|
49
|
+
"ELEMENT_COLOURS",
|
|
50
|
+
"Frame",
|
|
51
|
+
"Polyhedron",
|
|
52
|
+
"PolyhedronSpec",
|
|
53
|
+
"RenderStyle",
|
|
54
|
+
"SlabClipMode",
|
|
55
|
+
"StructureScene",
|
|
56
|
+
"ViewState",
|
|
57
|
+
"WidgetCorner",
|
|
58
|
+
"compute_bonds",
|
|
59
|
+
"compute_polyhedra",
|
|
60
|
+
"default_atom_style",
|
|
61
|
+
"default_bond_specs",
|
|
62
|
+
"from_pymatgen",
|
|
63
|
+
"from_xbs",
|
|
64
|
+
"normalise_colour",
|
|
65
|
+
]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Bond computation from declarative BondSpec rules."""
|
|
2
|
+
|
|
3
|
+
from fnmatch import fnmatch
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from hofmann.model import Bond, BondSpec
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def compute_bonds(
|
|
11
|
+
species: list[str],
|
|
12
|
+
coords: np.ndarray,
|
|
13
|
+
bond_specs: list[BondSpec],
|
|
14
|
+
) -> list[Bond]:
|
|
15
|
+
"""Compute bonds for a single frame based on bond specification rules.
|
|
16
|
+
|
|
17
|
+
For each pair of atoms (i < j), checks all bond specs in order to
|
|
18
|
+
find the first matching rule where the interatomic distance falls
|
|
19
|
+
within ``[min_length, max_length]``.
|
|
20
|
+
|
|
21
|
+
Species matching is pre-computed per spec so that the inner loop
|
|
22
|
+
over atom pairs is a vectorised numpy operation rather than
|
|
23
|
+
per-pair Python calls.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
species: List of species labels, length ``n_atoms``.
|
|
27
|
+
coords: Coordinates array of shape ``(n_atoms, 3)``.
|
|
28
|
+
bond_specs: List of BondSpec rules to apply.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
List of Bond objects for all detected bonds.
|
|
32
|
+
"""
|
|
33
|
+
if len(species) == 0 or len(bond_specs) == 0:
|
|
34
|
+
return []
|
|
35
|
+
|
|
36
|
+
coords = np.asarray(coords, dtype=float)
|
|
37
|
+
n_atoms = len(species)
|
|
38
|
+
|
|
39
|
+
# Vectorised pairwise distance matrix.
|
|
40
|
+
diff = coords[:, np.newaxis, :] - coords[np.newaxis, :, :]
|
|
41
|
+
dist_matrix = np.linalg.norm(diff, axis=2)
|
|
42
|
+
|
|
43
|
+
# Upper-triangle mask: only consider pairs (i < j).
|
|
44
|
+
upper = np.triu(np.ones((n_atoms, n_atoms), dtype=bool), k=1)
|
|
45
|
+
|
|
46
|
+
# Track which pairs have been claimed by an earlier spec.
|
|
47
|
+
claimed = np.zeros((n_atoms, n_atoms), dtype=bool)
|
|
48
|
+
|
|
49
|
+
# Pre-compute unique species for efficient matching.
|
|
50
|
+
unique_species = list(set(species))
|
|
51
|
+
|
|
52
|
+
bonds: list[Bond] = []
|
|
53
|
+
|
|
54
|
+
for spec in bond_specs:
|
|
55
|
+
# Determine which unique species match each side of the spec.
|
|
56
|
+
sp_a, sp_b = spec.species
|
|
57
|
+
match_a = {s for s in unique_species
|
|
58
|
+
if fnmatch(s, sp_a)}
|
|
59
|
+
match_b = {s for s in unique_species
|
|
60
|
+
if fnmatch(s, sp_b)}
|
|
61
|
+
|
|
62
|
+
# Boolean masks: which atoms match side a / side b.
|
|
63
|
+
mask_a = np.array([s in match_a for s in species])
|
|
64
|
+
mask_b = np.array([s in match_b for s in species])
|
|
65
|
+
|
|
66
|
+
# Species pair mask (symmetric matching): (a[i] & b[j]) | (b[i] & a[j]).
|
|
67
|
+
pair_mask = (
|
|
68
|
+
(mask_a[:, np.newaxis] & mask_b[np.newaxis, :])
|
|
69
|
+
| (mask_b[:, np.newaxis] & mask_a[np.newaxis, :])
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Distance filter.
|
|
73
|
+
dist_ok = (
|
|
74
|
+
(dist_matrix >= spec.min_length)
|
|
75
|
+
& (dist_matrix <= spec.max_length)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Combine: upper triangle, species match, distance match, not claimed.
|
|
79
|
+
hits = upper & pair_mask & dist_ok & ~claimed
|
|
80
|
+
|
|
81
|
+
# Extract matching pairs.
|
|
82
|
+
ii, jj = np.nonzero(hits)
|
|
83
|
+
if len(ii) > 0:
|
|
84
|
+
claimed[ii, jj] = True
|
|
85
|
+
for idx in range(len(ii)):
|
|
86
|
+
i, j = int(ii[idx]), int(jj[idx])
|
|
87
|
+
bonds.append(Bond(i, j, float(dist_matrix[i, j]), spec))
|
|
88
|
+
|
|
89
|
+
return bonds
|