├── .github
└── workflows
│ ├── test.yml
│ └── wheels.yml
├── .gitignore
├── LICENSE
├── MANIFEST.in
├── README.md
├── docs
├── .gitignore
├── README.md
├── assets
│ ├── archiving.py
│ ├── clustering.xcf
│ ├── conventional_cell_comparison.py
│ ├── sbc.svg
│ └── stacked.py
├── babel.config.js
├── docs
│ ├── about.md
│ ├── get-started
│ │ ├── _category_.json
│ │ ├── installation.md
│ │ └── quick-start.md
│ ├── learn
│ │ ├── _category_.json
│ │ ├── dimensionality.mdx
│ │ ├── symmetry-analysis.mdx
│ │ └── symmetry-based-clustering.mdx
│ └── reference
│ │ ├── _category_.json
│ │ ├── cluster.md
│ │ ├── geometry.md
│ │ ├── sbc.md
│ │ └── symmetryanalyzer.md
├── docusaurus.config.js
├── package-lock.json
├── package.json
├── pydoc-markdown.yml
├── sidebars.js
├── src
│ ├── components
│ │ ├── Figure.js
│ │ └── HomepageFeatures
│ │ │ ├── index.js
│ │ │ └── styles.module.css
│ ├── css
│ │ └── custom.css
│ └── pages
│ │ ├── index.js
│ │ └── index.module.css
├── static
│ ├── .nojekyll
│ ├── img
│ │ ├── all_cropped.png
│ │ ├── conventional.svg
│ │ ├── cu_cropped.png
│ │ ├── docusaurus-social-card.jpg
│ │ ├── docusaurus.png
│ │ ├── favicon.ico
│ │ ├── graphene_cropped.png
│ │ ├── logo.png
│ │ ├── logo.svg
│ │ ├── mos2_cropped.png
│ │ ├── sbc.png
│ │ ├── tsa.jpg
│ │ ├── undraw_docusaurus_mountain.svg
│ │ ├── undraw_docusaurus_react.svg
│ │ └── undraw_docusaurus_tree.svg
│ └── snippets
│ │ ├── clathrate.xyz
│ │ ├── conventional_cell.py
│ │ ├── dimensionality.py
│ │ ├── material_id.py
│ │ ├── readme.py
│ │ ├── sbc.py
│ │ ├── system.xyz
│ │ └── wyckoff_sets.py
└── yarn.lock
├── examples
├── classification1.py
├── classification2.py
├── data
│ ├── C26H24N4O2.xyz
│ ├── C32Mo32+CO2.xyz
│ ├── C49+N.xyz
│ ├── H2O.xyz
│ ├── Mg61O62+CH4Ni.xyz
│ ├── Ru.xyz
│ └── system.xyz
├── summary.py
├── surface.py
└── symmetry.py
├── matid
├── __init__.py
├── classification
│ ├── __init__.py
│ ├── classifications.py
│ └── classifier.py
├── clustering
│ ├── __init__.py
│ ├── cluster.py
│ └── sbc.py
├── core
│ ├── __init__.py
│ ├── distances.py
│ ├── lattice.py
│ ├── linkedunits.py
│ ├── periodicfinder.py
│ └── system.py
├── data
│ ├── __init__.py
│ ├── alphabet_data.py
│ ├── constants.py
│ ├── element_data.py
│ └── symmetry_data.py
├── ext
│ ├── celllist.cpp
│ ├── celllist.h
│ ├── ext.cpp
│ ├── geometry.cpp
│ └── geometry.h
├── geometry
│ ├── __init__.py
│ └── geometry.py
├── symmetry
│ ├── __init__.py
│ ├── symmetryanalyzer.py
│ └── wyckoffset.py
└── utils
│ ├── __init__.py
│ ├── exceptions.py
│ ├── segfault_protect.py
│ ├── surfacegenerator.py
│ └── symmetry_info_generation
│ ├── generate_symmetry_info.py
│ ├── query_continuous_translations.py
│ ├── query_conventional_transform_info.py
│ ├── query_normalizers.py
│ ├── query_space_group_info.py
│ └── query_wyckoff_sets.py
├── pyproject.toml
├── reports
└── coverage
│ └── coverage-badge.svg
├── requirements.txt
├── scripts
├── doc_deploy.sh
├── doc_reference.sh
├── format.sh
├── lint.sh
├── release.sh
├── setup.sh
└── test.sh
├── setup.py
└── tests
├── classificationtests.py
├── clustering
└── test_sbc.py
├── conftest.py
├── data
├── R6JuJXj20goPQ0vv6aAVYpNyuwGgN+P_PaYo5EiiPChgUe9B6JnTX6BcOwt.xyz
├── RDtJ5cTyLBPt4PA182VbCzoCxf5Js+P8Wnwz4dfyea6UAD0WEBadXv83wyf.xyz
├── RDtJ5cTyLBPt4PA182VbCzoCxf5Js+PEzXqLISX8Pam-HlJMxeLc86lcKgf.xyz
├── RDtJ5cTyLBPt4PA182VbCzoCxf5Js+PFw_-OtcPJ5og8XMItaAAFYhQUaY6.xyz
├── RJv-r5Vwf6ypWElBSq_hTCOaxEU89+PDLFIM7Xvy9JaEqwS72kDtDr_Szhp.xyz
├── RJv-r5Vwf6ypWElBSq_hTCOaxEU89+PKPif9Fqbl30oVX-710UwCHGMd83y.xyz
├── RJv-r5Vwf6ypWElBSq_hTCOaxEU89+PgZTqAjcn_4hHS3fozZkAI0Jxtdas.xyz
├── ROHGEranIWm-gnS6jhQaLZRORWDKx+Pbco91p05ftuJQ38__Y0_TDg9tNIy.xyz
├── ROHGEranIWm-gnS6jhQaLZRORWDKx+Pbsl6Hlb_C1aXadFiJ58UCUek5a8x.xyz
├── ROJiORHNwL4q0WTvNUy0mW5s2Buuq+PSX9X4dQR2r1cjQ9kBtuC-wI6MO8B.xyz
├── Rhn-EWQQN8Z-lbmZwoWPyrGiM9Isx+PbYDgCBSwbq3nxONqWaq03HYUn8_V.xyz
├── RloVGNkMhI83gtwzF5DmftT6fM31d+P9ZCykgTQkZ7aIFmr-vje9gq8p6fc.xyz
├── RloVGNkMhI83gtwzF5DmftT6fM31d+PKxGoPkNrvdpZrlLS-V14MszJ-57L.xyz
├── RmlNIfj-YIQ14UBYjtAHtXcAEXZif+PIkKcrxeOf997qnQ_hWRXLdMsmpAf.xyz
├── RmlNIfj-YIQ14UBYjtAHtXcAEXZif+PYu3zrqdlNhhs9tII2lnvJ3Gj7tai.xyz
├── RmlNIfj-YIQ14UBYjtAHtXcAEXZif+Pkl2CiGU9KP0uluTY8M3PeGEb4OS_.xyz
├── RmlNIfj-YIQ14UBYjtAHtXcAEXZif+PmZsb-Uf3AIGQyTBZDg4ZgxXaq5UB.xyz
├── Rq0LUBXa6rZ-mddbQUZJXOIVAIg-J+Pm73-Kx5CWtuIHzLTr5R-Nir2el0i.xyz
├── RscdVKibS4pD0O_Yo1CSwkznfiL1c+PCvflj-qTkfRcUaCISfn8fm-2oaVW.xyz
├── RzQh5XijWuXsNZiRSxeOlPFUY_9Gl+PY5NRLMRYyQXsYmBN9hMcT-FftquP.xyz
├── cu55.xyz
├── system-AlOPbSeSiC_PbSe.xyz
├── system-BNPbSeBN.xyz
└── system-CVC.extxyz
├── performance
├── benchmark_cpu.sh
├── benchmark_memory.sh
├── performance.py
└── plot.sh
├── symmetry
└── test_symmetry.py
├── symmetrytests.py
├── test_geometry.py
└── testrunner.py
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a
2 | # variety of Python versions For more information see:
3 | # https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
4 |
5 | name: test
6 |
7 | on:
8 | push:
9 | branches: [ "main"]
10 | pull_request:
11 | branches: [ "main"]
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 | strategy:
17 | fail-fast: false
18 | matrix:
19 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
20 |
21 | steps:
22 | - uses: actions/checkout@v4
23 | - name: Set up Python ${{ matrix.python-version }}
24 | uses: actions/setup-python@v5
25 | with:
26 | python-version: ${{ matrix.python-version }}
27 | - name: Install dependencies
28 | run: |
29 | cd ${GITHUB_WORKSPACE}/scripts && ./setup.sh
30 | - name: format
31 | if: always()
32 | run: |
33 | cd ${GITHUB_WORKSPACE}/scripts && ./format.sh
34 | - name: lint
35 | if: always()
36 | run: |
37 | cd ${GITHUB_WORKSPACE}/scripts && ./lint.sh
38 | - name: Run unit tests
39 | run: |
40 | cd ${GITHUB_WORKSPACE}/scripts && ./test.sh
--------------------------------------------------------------------------------
/.github/workflows/wheels.yml:
--------------------------------------------------------------------------------
1 | name: wheels
2 |
3 | on: [workflow_dispatch]
4 |
5 | jobs:
6 | build_wheels:
7 | name: Build wheels on ${{ matrix.os }}
8 | runs-on: ${{ matrix.os }}
9 | strategy:
10 | matrix:
11 | os: [ubuntu-latest, windows-latest, macos-13, macos-14]
12 |
13 | steps:
14 | - uses: actions/checkout@v4
15 |
16 | - name: Build wheels
17 | uses: pypa/cibuildwheel@v2.20.0
18 | env:
19 | # All PyPy builds are currently failing, CPython 3.6 and 3.13 are also failing
20 | CIBW_SKIP: cp36-* cp313* pp*
21 |
22 | - uses: actions/upload-artifact@v4
23 | with:
24 | name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
25 | path: ./wheelhouse/*.whl
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # https://github.com/github/gitignore/blob/main/Global/macOS.gitignore
2 | # General
3 | .DS_Store
4 | .AppleDouble
5 | .LSOverride
6 |
7 | # Icon must end with two \r
8 | Icon
9 |
10 |
11 | # Thumbnails
12 | ._*
13 |
14 | # Files that might appear in the root of a volume
15 | .DocumentRevisions-V100
16 | .fseventsd
17 | .Spotlight-V100
18 | .TemporaryItems
19 | .Trashes
20 | .VolumeIcon.icns
21 | .com.apple.timemachine.donotpresent
22 |
23 | # Directories potentially created on remote AFP share
24 | .AppleDB
25 | .AppleDesktop
26 | Network Trash Folder
27 | Temporary Items
28 | .apdisk
29 |
30 | # https://github.com/github/gitignore/blob/main/Python.gitignore
31 |
32 | # Byte-compiled / optimized / DLL files
33 | __pycache__/
34 | *.py[cod]
35 | *$py.class
36 |
37 | # C extensions
38 | *.so
39 |
40 | # Distribution / packaging
41 | .Python
42 | build/
43 | develop-eggs/
44 | dist/
45 | downloads/
46 | eggs/
47 | .eggs/
48 | lib/
49 | lib64/
50 | parts/
51 | sdist/
52 | var/
53 | wheels/
54 | share/python-wheels/
55 | *.egg-info/
56 | .installed.cfg
57 | *.egg
58 | MANIFEST
59 |
60 | # PyInstaller
61 | # Usually these files are written by a python script from a template
62 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
63 | *.manifest
64 | *.spec
65 |
66 | # Installer logs
67 | pip-log.txt
68 | pip-delete-this-directory.txt
69 |
70 | # Unit test / coverage reports
71 | htmlcov/
72 | .tox/
73 | .nox/
74 | .coverage
75 | .coverage.*
76 | .cache
77 | nosetests.xml
78 | coverage.xml
79 | *.cover
80 | *.py,cover
81 | .hypothesis/
82 | .pytest_cache/
83 | cover/
84 |
85 | # Translations
86 | *.mo
87 | *.pot
88 |
89 | # Django stuff:
90 | *.log
91 | local_settings.py
92 | db.sqlite3
93 | db.sqlite3-journal
94 |
95 | # Flask stuff:
96 | instance/
97 | .webassets-cache
98 |
99 | # Scrapy stuff:
100 | .scrapy
101 |
102 | # Sphinx documentation
103 | docs/_build/
104 |
105 | # PyBuilder
106 | .pybuilder/
107 | target/
108 |
109 | # Jupyter Notebook
110 | .ipynb_checkpoints
111 |
112 | # IPython
113 | profile_default/
114 | ipython_config.py
115 |
116 | # pyenv
117 | # For a library or package, you might want to ignore these files since the code is
118 | # intended to run in multiple environments; otherwise, check them in:
119 | # .python-version
120 |
121 | # pipenv
122 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
123 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
124 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
125 | # install all needed dependencies.
126 | #Pipfile.lock
127 |
128 | # poetry
129 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
130 | # This is especially recommended for binary packages to ensure reproducibility, and is more
131 | # commonly ignored for libraries.
132 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
133 | #poetry.lock
134 |
135 | # pdm
136 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
137 | #pdm.lock
138 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
139 | # in version control.
140 | # https://pdm.fming.dev/#use-with-ide
141 | .pdm.toml
142 |
143 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
144 | __pypackages__/
145 |
146 | # Celery stuff
147 | celerybeat-schedule
148 | celerybeat.pid
149 |
150 | # SageMath parsed files
151 | *.sage.py
152 |
153 | # Environments
154 | .env
155 | .venv
156 | env/
157 | venv/
158 | ENV/
159 | env.bak/
160 | venv.bak/
161 |
162 | # Spyder project settings
163 | .spyderproject
164 | .spyproject
165 |
166 | # Rope project settings
167 | .ropeproject
168 |
169 | # mkdocs documentation
170 | /site
171 |
172 | # mypy
173 | .mypy_cache/
174 | .dmypy.json
175 | dmypy.json
176 |
177 | # Pyre type checker
178 | .pyre/
179 |
180 | # pytype static type analyzer
181 | .pytype/
182 |
183 | # Cython debug symbols
184 | cython_debug/
185 |
186 | # PyCharm
187 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
188 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
189 | # and can be added to the global gitignore or merged into this file. For a more nuclear
190 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
191 | #.idea/
192 |
193 | # VSCode
194 | .vscode/*
195 |
196 | # Coverage report
197 | coverage
198 | tests/.coverage
199 |
200 | # performance results
201 | tests/performance/results
202 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 | include requirements.txt
3 | recursive-include matid/ext *.h
4 | recursive-exclude tests *
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 | 
5 | [](https://github.com/astral-sh/ruff)
6 |
7 | MatID is a Python package for identifying and analyzing atomistic systems based
8 | on their structure.
9 |
10 | # Documentation
11 | For more details and tutorials, visit the documentation at:
12 | [https://nomad-coe.github.io/matid/](https://nomad-coe.github.io/matid/)
13 |
14 | You can find even more details in the following open-access articles:
15 |
16 | - [Automated identification of bulk structures, two-dimensional materials, and interfaces using symmetry-based clustering]()
17 | - [Materials structure genealogy and high-throughput topological classification of surfaces and 2D materials]()
18 |
19 | ## Example: Surface detection and analysis
20 |
21 | ```python
22 | import ase.io
23 | from ase.visualize import view
24 |
25 | from matid.clustering import SBC
26 | from matid.symmetry import SymmetryAnalyzer
27 |
28 | # Load structure from a file
29 | system = ase.io.read('data/system.xyz')
30 |
31 | # Find interesting substructures using Symmetry-based Clustering (SBC)
32 | sbc = SBC()
33 | clusters = sbc.get_clusters(system)
34 |
35 | # Analyze each found cluster printing out the indices of the atoms belonging to
36 | # this cluster and visualizing the conventional cell from which the cluster was
37 | # built from.
38 | for cluster in clusters:
39 |
40 | # Get the indices of the atoms belonging to this cluster
41 | indices = cluster.indices
42 | print(indices)
43 |
44 | # Get the dimensionality of the cluster
45 | dimensionality = cluster.get_dimensionality()
46 | print(dimensionality)
47 |
48 | # Get the cell from which the cluster is constructed from. The periodicity
49 | # of this cell indicates in which directions the unit cell has been found to
50 | # be repeated in (at least once, possibly infinitely).
51 | cell = cluster.get_cell()
52 | n_repeated_directions = sum(cell.get_pbc())
53 | print(n_repeated_directions)
54 |
55 | # Analyze some symmetry properties of the underlying cell to better identify
56 | # the material from which the cluster has been constructed from.
57 | analyzer = SymmetryAnalyzer(cell, symmetry_tol=0.5)
58 | conv_sys = analyzer.get_conventional_system()
59 | view(conv_sys)
60 | ```
61 |
62 | # Installation
63 |
64 | ## pip
65 | ```sh
66 | pip install matid
67 | ```
68 |
69 | ## From source
70 | ```sh
71 | git clone https://github.com/nomad-coe/matid.git
72 | cd matid
73 | pip install .
74 | ```
75 |
76 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
4 |
5 | ### Installation
6 |
7 | ```
8 | $ yarn
9 | ```
10 |
11 | ### Local Development
12 |
13 | ```
14 | $ yarn start
15 | ```
16 |
17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18 |
19 | ### Build
20 |
21 | ```
22 | $ yarn build
23 | ```
24 |
25 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
26 |
27 | ### Deployment
28 |
29 | Using SSH:
30 |
31 | ```
32 | $ USE_SSH=true yarn deploy
33 | ```
34 |
35 | Not using SSH:
36 |
37 | ```
38 | $ GIT_USER= yarn deploy
39 | ```
40 |
41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
42 |
--------------------------------------------------------------------------------
/docs/assets/archiving.py:
--------------------------------------------------------------------------------
1 | from ase.io import read
2 | from nomad.datamodel import EntryArchive
3 | from nomad.datamodel.metainfo.simulation.run import Run, Program
4 | from nomad.datamodel.metainfo.simulation.system import System, Atoms as NOMADAtoms
5 | from nomad.datamodel.metainfo.simulation.method import (
6 | Method,
7 | BasisSetContainer,
8 | BasisSet,
9 | Electronic,
10 | DFT,
11 | XCFunctional,
12 | Functional,
13 | Electronic,
14 | )
15 |
16 | from nomad.datamodel.metainfo.simulation.calculation import (
17 | Calculation,
18 | Energy,
19 | EnergyEntry,
20 | BandEnergies,
21 | )
22 | from nomad.datamodel.metainfo.simulation.workflow import GeometryOptimization
23 | from nomad.normalizing import normalizers
24 |
25 |
26 | atoms = read("system.xyz")
27 | template = EntryArchive()
28 | run = template.m_create(Run)
29 | run.program = Program(name="VASP", version="4.6.35")
30 | method = run.m_create(Method)
31 | method.electrons_representation = [
32 | BasisSetContainer(
33 | type="plane waves",
34 | scope=["wavefunction"],
35 | basis_set=[
36 | BasisSet(
37 | type="plane waves",
38 | scope=["valence"],
39 | )
40 | ],
41 | )
42 | ]
43 | method.electronic = Electronic(method="DFT")
44 | xc_functional = XCFunctional(exchange=[Functional(name="GGA_X_PBE")])
45 | method.dft = DFT(xc_functional=xc_functional)
46 | system = run.m_create(System)
47 | system.atoms = NOMADAtoms(
48 | lattice_vectors=[
49 | [5.76372622e-10, 0.0, 0.0],
50 | [0.0, 5.76372622e-10, 0.0],
51 | [0.0, 0.0, 4.0755698899999997e-10],
52 | ],
53 | positions=[
54 | [2.88186311e-10, 0.0, 2.0377849449999999e-10],
55 | [0.0, 2.88186311e-10, 2.0377849449999999e-10],
56 | [0.0, 0.0, 0.0],
57 | [2.88186311e-10, 2.88186311e-10, 0.0],
58 | ],
59 | labels=["Br", "K", "Si", "Si"],
60 | periodic=[True, True, True],
61 | )
62 | scc = run.m_create(Calculation)
63 | scc.system_ref = system
64 | scc.method_ref = method
65 | scc.energy = Energy(
66 | free=EnergyEntry(value=-1.5936767191492225e-18),
67 | total=EnergyEntry(value=-1.5935696296699573e-18),
68 | total_t0=EnergyEntry(value=-3.2126683561907e-22),
69 | )
70 | template.workflow2 = GeometryOptimization()
71 |
72 | template.run[0].calculation[0].system_ref = None
73 | template.run[0].calculation[0].eigenvalues.append(BandEnergies())
74 | template.run[0].calculation[0].eigenvalues[0].kpoints = [[0, 0, 0]]
75 | template.run[0].system = None
76 |
77 | system = System()
78 | system.atoms = NOMADAtoms(
79 | positions=atoms.get_positions() * 1e-10,
80 | labels=atoms.get_chemical_symbols(),
81 | atomic_numbers=atoms.get_atomic_numbers(),
82 | lattice_vectors=atoms.get_cell() * 1e-10,
83 | periodic=atoms.get_pbc(),
84 | )
85 |
86 | template.run[0].m_add_sub_section(Run.system, system)
87 | for normalizer_class in normalizers:
88 | normalizer = normalizer_class(template)
89 | normalizer.normalize()
90 |
91 | archive = template.m_to_json()
92 | with open("system.archive.json", "w") as fout:
93 | fout.write(archive)
94 |
--------------------------------------------------------------------------------
/docs/assets/clustering.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nomad-coe/matid/642d3caa8bf6c048ee223454ab6b6b076946a069/docs/assets/clustering.xcf
--------------------------------------------------------------------------------
/docs/assets/conventional_cell_comparison.py:
--------------------------------------------------------------------------------
1 | from matid import SymmetryAnalyzer
2 | from ase.spacegroup import crystal
3 | from ase.io import write
4 | from ase import Atoms
5 | from ase.visualize import view
6 | import spglib
7 |
8 | def get_spglib_conventional(system):
9 | cell = (
10 | system.get_cell(),
11 | system.get_scaled_positions(),
12 | system.get_atomic_numbers(),
13 | )
14 | dataset = spglib.get_symmetry_dataset(cell)
15 |
16 | return Atoms(
17 | symbols=dataset.std_types,
18 | scaled_positions=dataset.std_positions,
19 | cell=dataset.std_lattice,
20 | )
21 |
22 | # Lets define two variants of NaCl in rocksalt structure
23 | a = 5.64
24 | nacl1 = crystal(
25 | ['Na', 'Cl'],
26 | [(0, 0, 0), (0.5, 0.5, 0.5)],
27 | spacegroup=225,
28 | cellpar=[a, a, a, 90, 90, 90]
29 | )
30 | write(f"original1.eps", nacl1, rotation="60y,20x,0y", show_unit_cell=2, scale=50, maxwidth=5000)
31 | a = 5.64
32 | nacl2 = crystal(
33 | ['Cl', 'Na'],
34 | [(0, 0, 0), (0.5, 0.5, 0.5)],
35 | spacegroup=225,
36 | cellpar=[a, a, a, 90, 90, 90]
37 | )
38 | write(f"original2.eps", nacl2, rotation="60y,20x,0y", show_unit_cell=2, scale=50, maxwidth=5000)
39 |
40 | # Lets retrieve the conventional cell using spglib
41 | conventional1_spglib = get_spglib_conventional(nacl1)
42 | conventional2_spglib = get_spglib_conventional(nacl2)
43 | write(f"spglib1.eps", conventional1_spglib, rotation="60y,20x,0y", show_unit_cell=2, scale=50, maxwidth=5000)
44 | write(f"spglib2.eps", conventional2_spglib, rotation="60y,20x,0y", show_unit_cell=2, scale=50, maxwidth=5000)
45 |
46 | # Let's retrieve the conventional cell as ASE Atoms using MatID
47 | analyzer = SymmetryAnalyzer(nacl1)
48 | conventional1_matid = analyzer.get_conventional_system()
49 | print(analyzer.get_wyckoff_sets_conventional())
50 | analyzer = SymmetryAnalyzer(nacl2)
51 | print(analyzer.get_wyckoff_sets_conventional())
52 | conventional2_matid = analyzer.get_conventional_system()
53 | write(f"matid1.eps", conventional1_matid, rotation="60y,20x,0y", show_unit_cell=2, scale=50, maxwidth=5000)
54 | write(f"matid2.eps", conventional2_matid, rotation="60y,20x,0y", show_unit_cell=2, scale=50, maxwidth=5000)
55 |
56 |
--------------------------------------------------------------------------------
/docs/assets/stacked.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from ase import Atoms
3 | from ase.io import write
4 | from ase.build import fcc111, mx2
5 | from ase.visualize import view
6 |
7 | from matid import SBC, SymmetryAnalyzer
8 |
9 | # Copper surface
10 | surface = fcc111("Cu", size=(6, 6, 3), vacuum=0)
11 | surface.rotate(v=[0, 0, 1], a=-60, rotate_cell=True)
12 | surface.wrap()
13 |
14 | # MoS2
15 | mos2 = mx2(formula="MoS2", kind="2H", a=3.18, thickness=3.19, size=(5, 5, 1))
16 |
17 | # Graphene sheet
18 | graphene = Atoms(
19 | symbols=["C", "C"],
20 | cell=np.array(
21 | (
22 | [2.4595121467478055, 0.0, 0.0],
23 | [-1.2297560733739028, 2.13, 0.0],
24 | [0.0, 0.0, 20.0],
25 | )
26 | ),
27 | scaled_positions=np.array(([1 / 3, 2 / 3, 0.5], [2 / 3, 1 / 3, 0.5])),
28 | pbc=[True, True, False],
29 | ) * [6, 6, 1]
30 |
31 | # Center structures in the horizontal plane
32 | surface_com = surface.get_center_of_mass()
33 | cell_center = 1 / 2 * surface.get_cell().sum(axis=0)
34 | mos2_com = mos2.get_center_of_mass()
35 | mos2.translate(cell_center - mos2_com)
36 | graphene_com = graphene.get_center_of_mass()
37 | graphene.translate(cell_center - graphene_com)
38 |
39 | # Stack structures vertically
40 | surface_top = np.max(surface.get_positions()[:, 2], axis=0)
41 | mos2_bottom = np.min(mos2.get_positions()[:, 2], axis=0)
42 | distance_surface_mos2 = 2
43 | mos2.translate([0, 0, surface_top - mos2_bottom + distance_surface_mos2])
44 | mos2_top = np.max(mos2.get_positions()[:, 2], axis=0)
45 | graphene_bottom = np.max(graphene.get_positions()[:, 2], axis=0)
46 | distance_mos2_graphene = 2
47 | graphene.translate([0, 0, mos2_top - graphene_bottom + distance_mos2_graphene])
48 | system = surface + mos2 + graphene
49 |
50 | # Fit in cell and set periodicity
51 | scaled_positions = system.get_scaled_positions()
52 | system.set_cell(3 * system.get_cell())
53 | system.center()
54 | system.set_pbc(False)
55 | write("original.eps", system, rotation="30z,-60x,0y", show_unit_cell=0, maxwidth=5000)
56 |
57 | # Peform clustering and view results
58 | sbc = SBC()
59 | clusters = sbc.get_clusters(system)
60 | rotations = [
61 | "240z,-70x,0y",
62 | "-30z,-70x,0y",
63 | "240z,-70x,0y",
64 | ]
65 |
66 | for i, cluster in enumerate(clusters):
67 | write(f"cluster{i}.eps", system[cluster.indices], rotation="30z,-60x,0y", show_unit_cell=0, maxwidth=3000)
68 | unit_cell = cluster.get_cell()
69 | analyzer = SymmetryAnalyzer(unit_cell)
70 | conv_system = analyzer.get_conventional_system()
71 | cell = conv_system.get_cell()
72 | cell[~conv_system.get_pbc(), :] = 0
73 | conv_system.set_cell(cell)
74 | write(f"cluster{i}_unit_cell.eps", conv_system, rotation=rotations[i], show_unit_cell=2, maxwidth=3000)
75 |
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/docs/docs/about.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | # About
6 |
7 | ## Citing
8 |
9 | When using versions < 2.0.0, please consider citing the article:
10 |
11 | ```bibtex
12 | @article{matid,
13 | author = {Himanen, Lauri and Rinke, Patrick and Foster, Adam Stuart},
14 | title = {{Materials structure genealogy and high-throughput topological classification of surfaces and 2D materials}},
15 | journal = {npj Computational Materials},
16 | volume = {4},
17 | number = {52},
18 | year = {2018}
19 | publisher = {Springer US},
20 | doi = {10.1038/s41524-018-0107-6},
21 | url = {https://www.nature.com/articles/s41524-018-0107-6},
22 | }
23 | ```
24 |
25 | When using versions >= 2.0.0, please additionally cite:
26 |
27 | ```bibtex
28 | @article{matidv2,
29 | author = {Denell, Thea and Himanen, Lauri and Scheidgen, Markus and Draxl, Claudia},
30 | title = {{Automated identification of bulk structures, two-dimensional materials, and interfaces using symmetry-based clustering}},
31 | journal = {npj Computational Materials},
32 | volume = {11},
33 | number = {25},
34 | year = {2025}
35 | publisher = {Springer US},
36 | doi = {10.1038/s41524-024-01498-x},
37 | url = {https://www.nature.com/articles/s41524-024-01498-x},
38 | }
39 | ```
40 |
41 | ## License
42 | The matid source code is released under the Apache-2.0 license.
43 |
44 | ## Funding
45 | This project has received funding from the European Union’s Horizon 2020 research and innovation programme under grant agreement No 676580 with The Novel Materials Discovery (NOMAD) Laboratory, a European Center of Excellence and from the Jenny and Antti Wihuri Foundation. This work was furthermore supported by the Academy of Finland through its Centres of Excellence Programme 2015-2017 under project number 284621, as well as its Key Project Funding scheme under project number 305632. Additionally this work has been funded by the German Research Foundation (DFG) through the project FAIRmat (DFG project no. 460197019, https://www.fairmat-nfdi.eu/) within the German National Research Data Infrastructure (NFDI, https://www.nfdi.de/)
46 |
47 | ## Contact
48 | If you encounter issues with the software please submit them directly as GitHub issues. For general discussion and possible improvements/additions use the GitHub discussion forum. This ensures that all issues/discussion are publicly available and archived.
--------------------------------------------------------------------------------
/docs/docs/get-started/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Get started",
3 | "position": 1
4 | }
5 |
--------------------------------------------------------------------------------
/docs/docs/get-started/installation.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | ---
4 |
5 | # Installation
6 |
7 | ## pip
8 |
9 | The package is available through pip:
10 |
11 | ```sh
12 | pip install matid
13 | ```
14 |
15 | ## From source
16 |
17 | To install the latest development version from source, clone the source code
18 | from github and install with pip from local file:
19 |
20 | ```sh
21 | git clone https://github.com/nomad-coe/matid.git
22 | cd matid
23 | pip install .
24 | ```
--------------------------------------------------------------------------------
/docs/docs/get-started/quick-start.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | import CodeBlock from '@theme/CodeBlock'
6 | import ReadmeSource from '!!raw-loader!@site/static/snippets/readme.py';
7 |
8 | # Quick Start
9 |
10 | MatID is a Python package for identifying and analyzing atomistic systems based
11 | on their structure. MatID is designed to help researchers in the automated
12 | analysis and labeling of atomistic data.
13 |
14 | The package comes with different tools that can be connected together to form a
15 | workflow for analyzing structures. The **Learn** section contains documentation
16 | that helps you get to know the basic functionality of the package, most
17 | importantly:
18 |
19 | - [Symmetry-based Clustering](/docs/learn/symmetry-based-clustering): Learn how to identify and report repeating structures in
20 | atomistic system. Works both with finite and periodic systems.
21 | - [Symmetry Analysis](/docs/learn/symmetry-analysis): Routines for analyzing the symmetry of structures. Can be used to return a highly unique conventional cell, detailed Wyckoff position information etc.
22 | - [Dimensionality Analysis](/docs/learn/dimensionality): Used to determine the intended dimensionality of a system taking periodic boundary conditions into account.
23 |
24 | As a user, you are free to choose one or many of these tools for your analysis
25 | purposes. A small example that combines several of the provided tools for
26 | analysing an existing structure file is shown below
27 |
28 | {ReadmeSource}
29 |
30 | To learn more about the exact call signature of classes and functions, visit the
31 | documents in the **Reference** section. It contains autogenerated documentation
32 | for the key classes and modules.
33 |
--------------------------------------------------------------------------------
/docs/docs/learn/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Learn",
3 | "position": 2
4 | }
5 |
--------------------------------------------------------------------------------
/docs/docs/learn/dimensionality.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 5
3 | ---
4 |
5 | import Image from '@theme/IdealImage'
6 | import tsa from '@site/static/img/tsa.jpg'
7 | import CodeBlock from '@theme/CodeBlock'
8 | import DimensionalitySource from '!!raw-loader!@site/static/snippets/dimensionality.py';
9 |
10 | # Dimensionality Analysis
11 | MatID can be used to find out the dimensionality of an atomistic geometry. It
12 | should be noted that MatID does not consider the shape or size of a structure
13 | when assigning dimensionality, but instead looks at __the number of dimensions
14 | in which the structure is connected to itself through periodic boundary
15 | conditions__. This definition is often relevant for atomistic simulations with
16 | periodic boundary conditions, but may not be useful in other contexts where e.g.
17 | the shape is the deciding factor.
18 |
19 |
20 |
21 | To determine the dimensionality of a system, MatID uses a modified version of the
22 | topological scaling algorithm (TSA)[^1]. The algorithm is based on analyzing
23 | the size scaling of atomic clusters when going from the original system to a
24 | bigger supercell of the same system. With TSA, the dimensionality $D$ is
25 | given by
26 |
27 | $$
28 | D=\begin{cases}
29 | n_\text{pbc}-\log_n (N_{n}) \text{, when}~n_\text{pbc} \neq 0 \\
30 | 0\text{, when}~n_\text{pbc} = 0
31 | \end{cases}
32 | $$
33 |
34 | where $N_n$ is the number of clusters in a supercell that is repeated
35 | $n$ times in each periodic direction and $n_\mathrm{pbc}$ is the
36 | number of periodic dimensions. For the clustering we use the Density-Based
37 | Spatial Clustering of Applications with Noise (DBSCAN)[^2] data clustering algorithm.
38 | The advantage of this algorithm is that it does not require an initial guess
39 | for the number of clusters and it can find arbitrarily shaped clusters. The
40 | clustering requires that we define a metric for the distance between atoms. We
41 | use the following metric:
42 |
43 | $$
44 | d_{ij} = \lvert \vec{R}_i - \vec{R}_j \rvert^{\text{MIC}} - r_i - r_j
45 | $$
46 |
47 | where $\vec{R}_i$ and $\vec{R}_j$ are the cartesian positions of atom $i$ and
48 | $j$, respectively, and $r_i$ and $r_j$ are their radii. The radii definition can
49 | be changed and defaults to covalent radii[^3]. It is important to notice that in
50 | this metric the distances always follow the minimum image convention (MIC), i.e.
51 | the distance is calculated between two closest periodic neighbours. By using the
52 | distance to the closest periodic neighbour we obtain the correct clusters
53 | regardless of what shape of cell is used in the original simulation.
54 |
55 | The clustering uses two parameters: the minimum cluster size
56 | $n_\mathrm{min}$ and the neighbourhood radius $\epsilon$. We set
57 | $n_\mathrm{min}$ to 1 to allow clusters consisting of even single atoms
58 | and $\epsilon$ defaults to 3.5 Å. At present, a system, in which there is
59 | more than one cluster in the original non-repeated system ($N_1 \gt 1$),
60 | is classified as unknown. Such a case corresponds to systems with multiple
61 | components that are spatially separated, such as a molecule far above a
62 | surface, low density gases, widely spaced clusters in vacuum, etc.
63 |
64 | The following code illustrates how dimensionality detection can be performed for
65 | any atomistic structure with MatID.
66 |
67 | {DimensionalitySource}
68 |
69 | If you wish to get the dimensionality of a [`Cluster`](/docs/reference/cluster) object, then you can
70 | directly use the [`Cluster.get_dimensionality()`](/docs/reference/cluster#get_dimensionality) method, which is essentially a
71 | shortcut to the [`matid.geometry.get_dimensionality()`](/docs/reference/geometry#get_dimensionality) function.
72 |
73 | [^1]: Ashton, M., Paul, J., Sinnott, S. B. & Hennig, R. G. Topology-scaling identification of layered solids and stable exfoliated 2d materials. Phys. Rev. Lett. 118, 106101 (2017)
74 | [^2]: Ester, M., Kriegel, H.-P., Sander, J. & Xu, X. A density-based algorithm for discovering clusters in large spatial databases with noise. KDD’96 Proceedings of the Second International Conference on Knowledge Discovery and Data Mining 226–231 (1996).
75 | [^3]: Cordero, B. et al. Covalent radii revisited. Dalton Trans. 2832–2838 (2008)
--------------------------------------------------------------------------------
/docs/docs/learn/symmetry-analysis.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 4
3 | ---
4 |
5 | import Figure from '@site/src/components/Figure'
6 | import CodeBlock from '@theme/CodeBlock'
7 | import ConventionalCellSource from '!!raw-loader!@site/static/snippets/conventional_cell.py';
8 | import MaterialIDSource from '!!raw-loader!@site/static/snippets/material_id.py';
9 | import WyckoffSetsSource from '!!raw-loader!@site/static/snippets/wyckoff_sets.py';
10 |
11 | # Symmetry Analysis
12 |
13 | The [`SymmetryAnalyzer`](/docs/reference/symmetryanalyzer) class in MatID contains several symmetry routines for
14 | analyzing 3D structures. The basic features are based on the excellent `spglib`
15 | library, but with an extended feature set that makes MatID unique. The following
16 | subsections showcase some of its most interesting features and you can learn
17 | more in the API reference.
18 |
19 | ## Conventional Cell
20 |
21 | For each space group there is a conventional cell that has been standardized by
22 | in [Volume A of International Tables for Crystallography
23 | (ITA)](https://it.iucr.org/A/). This conventional cell can be retrieved by using
24 | the
25 | [`get_conventional_system()`](/docs/reference/symmetryanalyzer#get_conventional_system)
26 | method, for example:
27 |
28 | {ConventionalCellSource}
29 |
30 | You could also use `spglib` to retrieve the conventional cell, but MatID adds an
31 | additional step of normalization to make the structure even more standardized.
32 | Although the shape and symmetries of the cell are already well-defined for a
33 | conventional cell reported by `spglib`, the exact location of atoms in the cell
34 | can vary. The same structure can have multiple representations, which all look
35 | different. If you are only interested in the symmetry properties of the system,
36 | this may not be relevant. But if instead you wish to generate a more unique
37 | structure for the conventional cell for visualization or identification
38 | purposes, then additional processing is required.
39 |
40 | Below is an example of the same rock salt structure (space group 225) in two
41 | different configurations, and the result of spglib conventional cell and the
42 | MatID conventional cell.
43 |
44 |
45 |
46 | The only difference in the two original structures is the way the Wyckoff
47 | positions have been filled: structure A has has Na in Wyckoff position "a" and
48 | Cl in Wyckoff position "b" while in the second one these positions are reversed.
49 | The conventional cell reported by MatID makes an additional step of searching
50 | for the most unique representation based on chirality-preserving Euclidean
51 | Normalizers. These are reported e.g. by the [Bilbao Crystal
52 | database](https://www.cryst.ehu.es/). Each normalizer is essentially a transform
53 | that can be applied to the structure to gain a new form of the conventional cell
54 | without modifying the underlying material. MatID iterates over these different
55 | representations and selects one based on the population of different Wyckoff
56 | sites which differs among these representations.
57 |
58 | It should be noted that certain Wyckoff sites in certain space groups are
59 | parametrized. MatID does not perform any selection between these parameters, so
60 | in these cases the exact visual representation will still differ. By calling
61 | [`get_has_free_wyckoff_parameters()`](/docs/reference/symmetryanalyzer#get_has_free_wyckoff_parameters)
62 | you can query whether the structure contains parametrized wyckoff positions.
63 |
64 | ## Wyckoff sets
65 |
66 | You can use
67 | [`get_wyckoff_sets_conventional()`](/docs/reference/symmetryanalyzer#get_wyckoff_sets_conventional)
68 | to query a detailed breakdown of the occupied Wyckoff sets in a material. The
69 | following example shows an example of how you could query the Wyckoff sets in a
70 | fairly complicated clathrate material:
71 |
72 | {WyckoffSetsSource}
73 |
74 | The output of this script is:
75 |
76 | ```
77 | Set 0
78 | Letter: c
79 | Element: Si
80 | Indices: [40, 41, 42, 43, 44, 45]
81 | Multiplicity: 6
82 | Repr.: ['1/4', '0', '1/2']
83 | x: None
84 | y: None
85 | z: None
86 | Set 1
87 | Letter: i
88 | Element: Si
89 | Indices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
90 | Multiplicity: 16
91 | Repr.: ['x', 'x', 'x']
92 | x: 0.6837
93 | y: None
94 | z: None
95 | Set 2
96 | Letter: k
97 | Element: Si
98 | Indices: [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]
99 | Multiplicity: 24
100 | Repr.: ['0', 'y', 'z']
101 | x: None
102 | y: 0.6922999999999999
103 | z: 0.8827999999999999
104 |
105 | ```
106 |
107 | ## Material ID
108 |
109 | You can use
110 | [`get_material_id()`](/docs/reference/symmetryanalyzer#get_material_id) to
111 | report a 28-digit character sequence for a given structure. This is essentially
112 | a hash-digest which is seeded using information about the space group,
113 | dimensionality and the Wyckoff sites occupied in the conventional cell. Due to
114 | the way MatID generates the conventional cell, this identifier is fairly unique
115 | and can be used in many cases to identify structures 3D and 2D materials with
116 | "enough" symmetry.
117 |
118 | The material id can act as a convenient handle for finding materials with very
119 | specific structure. As a simple example one could imagine trying to find
120 | [skutterudite](https://en.wikipedia.org/wiki/Skutterudite). The filters required for this would include:
121 |
122 | - Space group number
123 | - Wyckoff position occupation (assuming a normalized one is available)
124 |
125 | Often this information is not directly reported and if it is, it can be a
126 | daunting task for the user to formulate the queries precisely. This is where the
127 | material id can offer a shortcut: you can use MatID to retrieve it for a
128 | structure and use it in queries, provided that a database has it stored. For
129 | example, if you have an atomic structure, you could use the following script to
130 | retrieve the material id for it:
131 |
132 | {MaterialIDSource}
133 |
134 | This material id can then be used e.g. in the [NOMAD](https://nomad-lab.eu/) platform to query for structures
135 | containing skutterudite (`material id = zI9HC39lOUK_w2Clv8XUHb43tYVV`).
136 |
137 | The material id is not guaranteed to be unique, but can be highly useful in
138 | practice. In some sense it has a similar value proposal to SMILES for molecules.
139 | There are some of the cases in which it will fail:
140 |
141 | - Parametrized Wyckoff sites
142 | - If isotropic scaling of cell should affect the material identification
143 | - If variable angles for the conventional cell are a problem
144 |
--------------------------------------------------------------------------------
/docs/docs/learn/symmetry-based-clustering.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 3
3 | ---
4 |
5 | import Image from '@theme/IdealImage'
6 | import sbc from '@site/static/img/sbc.png'
7 |
8 | # Symmetry-based Clustering (SBC)
9 |
10 | There are several tools for building atomistic systems from components, but the
11 | reverse process of identifying these components from an existing system is much
12 | harder. MatID utilizes a custom clustering algorithm, called Symmetry-based
13 | Clustering (SBC), which can cluster atoms based on local translational symmetry.
14 | Essentially this means that atoms which are built from the same underlying unit
15 | cell, will get clustered together.
16 |
17 |
18 |
19 | Unlike clustering methods that work solely on the basis of atomic distances or
20 | chemical identities, SBC can produce meaningful clustering results even for
21 | structures like grain boundaries and defected structures, where other tools
22 | would struggle. SBC works for both finite and periodic structures, and can deal
23 | with noise and curvature in the structures. SBC currently returns clusters where
24 | the underlying unit cell is repeated either in two or three distinct directions
25 | (one-dimensional clusters, such as polymers are currently not handled).
26 |
27 | Clustering is performed using the [`SBC`](/docs/reference/sbc) class. The basic syntax is relatively
28 | simple: you have to initialize the SBC class with parameters that are suitable
29 | for your use case (sensible defaults are provided), and then call the
30 | [`get_clusters`](/docs/reference/sbc#get_clusters)-method. The following demonstrates this on a existing structure
31 | file:
32 |
33 | import CodeBlock from '@theme/CodeBlock'
34 | import SBCSource from '!!raw-loader!@site/static/snippets/sbc.py';
35 |
36 | {`${SBCSource.split('\n').slice(0, 8).join('\n')}`}
37 |
38 | The return value is a list of the found [`Cluster`](/docs/reference/cluster) instances. You can perform
39 | further analysis on these found clusters by using methods and attributes of this
40 | class. E.g. to print out the indices of atoms belonging to the cluster and to
41 | visualize the units cells of the found clusters, we could do the following:
42 |
43 | {`${SBCSource.split('\n').slice(9, 12).join('\n')}`}
--------------------------------------------------------------------------------
/docs/docs/reference/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 | "label": "Reference",
3 | "position": 3
4 | }
5 |
--------------------------------------------------------------------------------
/docs/docs/reference/cluster.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 2
3 | sidebar_label: Cluster Class
4 | ---
5 | # Cluster Class
6 |
7 | Contains information about a cluster, i.e. a part of some bigger original
8 | atomistic system.
9 |
10 | This class is a simple data container where every attribute should be set
11 | only once, but does not have to be set in one go and can be insted built
12 | gradually.
13 |
14 | ## \_\_init\_\_
15 |
16 | ```python
17 | def __init__(indices=None,
18 | species=None,
19 | region=None,
20 | dimensionality=None,
21 | cell=None,
22 | system=None,
23 | distances=None,
24 | radii=None,
25 | bond_threshold=None)
26 | ```
27 |
28 | **Arguments**:
29 |
30 | - `indices(Iterable)` - Contains the indices of atoms belonging to this
31 | cluster.
32 | - `species(set)` - Contains the species of atoms belonging to this
33 | cluster. Each unique species should be include only once.
34 | - `region(Region)` - The Region instance from which this cluster was
35 | exracted from.
36 | - `dimensionality(int)` - The dimensionality of the cluster. Can be set
37 | initially here or calculated through the get_dimensionality-function.
38 | - `cell(ase.Atoms)` - The unit cell from which this cluster is
39 | constructed from.
40 | - `system(ase.Atoms)` - Reference to the original system which this
41 | cluster is a part of.
42 | - `distances(Distances)` - Contains cached distance information about
43 | this cluster.
44 | - `radii(ndarray)` - Contains the radii for each atom in the cluster as
45 | floating point numbers.
46 |
47 | ## get\_cell
48 |
49 | ```python
50 | def get_cell() -> Atoms
51 | ```
52 |
53 | Used to fetch the prototypical cell for this cluster if one exists.
54 |
55 | ## get\_atoms
56 |
57 | ```python
58 | def get_atoms() -> Atoms
59 | ```
60 |
61 | Returns the ase.Atoms object for this cluster.
62 |
63 | ## get\_dimensionality
64 |
65 | ```python
66 | def get_dimensionality() -> int
67 | ```
68 |
69 | Shortcut for fetching the dimensionality of the cluster using
70 | matid.geometry.get_dimensionality and the radii + bond thresholds that
71 | were used during the clustering.
72 |
--------------------------------------------------------------------------------
/docs/docs/reference/sbc.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | sidebar_label: SBC Class
4 | ---
5 | # SBC Class
6 |
7 | Class for performing Symmetry-based clustering (SBC).
8 |
9 | You can apply this class for partitioning a larger material into grains, a
10 | heterostructure into it's component etc. The clustering is based on finding
11 | periodically repeating motifs, and as such it is not suitable for e.g.
12 | finding molecules. Any atoms that do not have enough periodic repetitions
13 | will be returned as isolated clusters.
14 |
15 | ## get\_clusters
16 |
17 | ```python
18 | def get_clusters(system,
19 | angle_tol=20,
20 | max_cell_size=6,
21 | pos_tol=0.7,
22 | merge_threshold=0.5,
23 | merge_radius=1,
24 | bond_threshold=0.65,
25 | overlap_threshold=-0.1,
26 | radii="covalent",
27 | seed=7)
28 | ```
29 |
30 | Used to detect and return structurally separate clusters within the
31 | given system.
32 |
33 | **Arguments**:
34 |
35 | - `system` _ase.Atoms_ - The structure to partition.
36 | - `angle_tol` _float_ - angle_tol parameter for PeriodicFinder
37 | - `max_cell_size` _float_ - max_cell_size parameter for PeriodicFinder.get_region
38 | - `pos_tol` _float_ - pos_tol parameter for PeriodicFinder.get_region
39 | - `merge_threshold` _float_ - A threshold for merging two clusters
40 | together. Give as a fraction of shared atoms. Value of 1 would
41 | mean that clusters are never merged, value of 0 means that they
42 | are merged always when at least one atom is shared.
43 | - `merge_radius` _float_ - Radius for finding nearby atoms when deciding
44 | which cluster is closest. The atomic radii are subtracted from
45 | distances. Given in angstroms.
46 | - `bond_threshold(float)` - Used to control the connectivity threshold
47 | for defining a chemical connection between atoms. Controls e.g.
48 | what types of unit cells are accepted and how outliers are
49 | removed from clusters.
50 | - `overlap_threshold(float)` - Used to exclude non-physical cells by
51 | checking overlap of atoms. Overlap between two atoms is
52 | calculated by subtracting atomic radii from the distance between
53 | the atoms.
54 | - `radii(str|np.ndarray)` - The radii to use for atoms. Use either a preset
55 | or a custom list of atomic radii for each atom. The available presets are:
56 |
57 | - covalent: Covalent radii from DOI:10.1039/B801115J
58 | - vdw: van Der Waals radii from DOI:10.1039/C3DT50599E
59 | - vdw_covalent: preferably van Der Waals radii, covalent if vdw
60 | not defined.
61 | - `seed(int)` - The seed that is used for random number generation.
62 |
63 |
64 | **Returns**:
65 |
66 | A list of Clusters.
67 |
--------------------------------------------------------------------------------
/docs/docusaurus.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // Note: type annotations allow type checking and IDEs autocompletion
3 | import remarkMath from 'remark-math';
4 | import rehypeKatex from 'rehype-katex';
5 |
6 | const lightCodeTheme = require('prism-react-renderer').themes.github;
7 | const darkCodeTheme = require('prism-react-renderer').themes.dracula;
8 |
9 | /** @type {import('@docusaurus/types').Config} */
10 | const config = {
11 | title: 'MatID',
12 | tagline: 'MatID is a Python package for identifying and analyzing atomistic systems based on their structure.',
13 | favicon: 'img/favicon.ico',
14 |
15 | stylesheets: [
16 | {
17 | href: 'https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css',
18 | type: 'text/css',
19 | integrity:
20 | 'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM',
21 | crossorigin: 'anonymous',
22 | },
23 | ],
24 |
25 | plugins: [
26 | [
27 | '@docusaurus/plugin-ideal-image',
28 | {
29 | quality: 70,
30 | max: 1030, // max resized image's size.
31 | min: 640, // min resized image's size. if original is lower, use that size.
32 | steps: 2, // the max number of images generated between min and max (inclusive)
33 | disableInDev: false,
34 | },
35 | ],
36 | ],
37 |
38 | // Set the production url of your site here
39 | url: 'https://nomad-coe.github.io',
40 | // Set the // pathname under which your site is served
41 | // For GitHub pages deployment, it is often '//'
42 | baseUrl: '/matid',
43 | deploymentBranch: 'gh-pages',
44 | // GitHub pages deployment config.
45 | // If you aren't using GitHub pages, you don't need these.
46 | organizationName: 'nomad-coe', // Usually your GitHub org/user name.
47 | projectName: 'matid', // Usually your repo name.
48 |
49 | onBrokenLinks: 'throw',
50 | onBrokenMarkdownLinks: 'warn',
51 |
52 | // Even if you don't use internalization, you can use this field to set useful
53 | // metadata like html lang. For example, if your site is Chinese, you may want
54 | // to replace "en" with "zh-Hans".
55 | i18n: {
56 | defaultLocale: 'en',
57 | locales: ['en'],
58 | },
59 |
60 | presets: [
61 | [
62 | 'classic',
63 | /** @type {import('@docusaurus/preset-classic').Options} */
64 | ({
65 | docs: {
66 | //routeBasePath: '/', // Serve the docs at the site's root
67 | remarkPlugins: [remarkMath],
68 | rehypePlugins: [rehypeKatex],
69 | sidebarPath: require.resolve('./sidebars.js'),
70 | sidebarCollapsible: false,
71 | },
72 | blog: false,
73 | theme: {
74 | customCss: require.resolve('./src/css/custom.css'),
75 | },
76 | }),
77 | ],
78 | ],
79 | themeConfig:
80 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */
81 | ({
82 | // Replace with your project's social card
83 | image: 'img/docusaurus-social-card.jpg',
84 | navbar: {
85 | title: '',
86 | logo: {
87 | alt: 'MatID logo',
88 | src: 'img/logo.png',
89 | },
90 | items: [
91 | {
92 | type: 'docSidebar',
93 | sidebarId: 'docsSidebar',
94 | position: 'left',
95 | label: 'Docs',
96 | },
97 | {
98 | href: 'https://github.com/nomad-coe/matid',
99 | label: 'GitHub',
100 | position: 'right',
101 | },
102 | ],
103 | },
104 | colorMode: {
105 | defaultMode: 'light',
106 | disableSwitch: true,
107 | respectPrefersColorScheme: false,
108 | },
109 | footer: {
110 | links: [
111 | {
112 | title: 'Docs',
113 | items: [
114 | {
115 | label: 'Quick Start',
116 | to: '/docs/get-started/quick-start',
117 | },
118 | {
119 | label: 'Symmetry-based Clustering',
120 | to: '/docs/learn/symmetry-based-clustering',
121 | },
122 | {
123 | label: 'Symmetry Analysis',
124 | to: '/docs/learn/symmetry-analysis',
125 | },
126 | ],
127 | },
128 | {
129 | title: 'Links',
130 | items: [
131 | {
132 | label: 'GitHub',
133 | href: 'https://github.com/nomad-coe/matid',
134 | },
135 | {
136 | label: 'FAIRmat',
137 | href: 'https://www.fairmat-nfdi.eu/fairmat',
138 | },
139 | {
140 | label: 'NOMAD',
141 | href: 'https://nomad-lab.eu/nomad-lab/',
142 | },
143 | ],
144 | },
145 | ],
146 | },
147 | prism: {
148 | theme: lightCodeTheme,
149 | darkTheme: darkCodeTheme,
150 | },
151 | }),
152 | };
153 |
154 | module.exports = config;
155 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "swizzle": "docusaurus swizzle",
10 | "deploy": "docusaurus deploy",
11 | "clear": "docusaurus clear",
12 | "serve": "docusaurus serve",
13 | "write-translations": "docusaurus write-translations",
14 | "write-heading-ids": "docusaurus write-heading-ids"
15 | },
16 | "dependencies": {
17 | "@docusaurus/core": "^3.4.0",
18 | "@docusaurus/plugin-ideal-image": "^3.4.0",
19 | "@docusaurus/preset-classic": "^3.4.0",
20 | "@mdx-js/react": "^3.0.0",
21 | "clsx": "^1.2.1",
22 | "hast-util-is-element": "^1.1.0",
23 | "prism-react-renderer": "^2.1.0",
24 | "raw-loader": "^4.0.2",
25 | "react": "^18.2.0",
26 | "react-dom": "^18.2.0",
27 | "rehype-katex": "^7.0.0",
28 | "remark-math": "^6.0.0"
29 | },
30 | "devDependencies": {
31 | "@docusaurus/module-type-aliases": "^3.4.0",
32 | "@docusaurus/types": "^3.4.0"
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.5%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 1 chrome version",
42 | "last 1 firefox version",
43 | "last 1 safari version"
44 | ]
45 | },
46 | "engines": {
47 | "node": ">=18.0"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/docs/pydoc-markdown.yml:
--------------------------------------------------------------------------------
1 | loaders:
2 | - type: python
3 | search_path: [../]
4 | processors:
5 | - type: filter
6 | skip_empty_modules: true
7 | - type: google
8 | - type: crossref
9 | renderer:
10 | type: markdown
11 | render_page_title: false
12 | render_module_header: false
13 | use_fixed_header_levels: true
14 | insert_header_anchors: false
15 | header_level_by_type:
16 | Module: 1
17 | Class: 1
18 | Method: 2
19 | Function: 2
20 | Data: 4
21 | descriptive_class_title: '$ Class'
22 | classdef_code_block: false
23 |
--------------------------------------------------------------------------------
/docs/sidebars.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Creating a sidebar enables you to:
3 | - create an ordered group of docs
4 | - render a sidebar for each doc of that group
5 | - provide next/previous navigation
6 |
7 | The sidebars can be generated from the filesystem, or explicitly defined here.
8 |
9 | Create as many sidebars as you want.
10 | */
11 |
12 | // @ts-check
13 |
14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
15 | const sidebars = {
16 | // By default, Docusaurus generates a sidebar from the docs folder structure
17 | docsSidebar: [{type: 'autogenerated', dirName: '.'}],
18 | };
19 |
20 | module.exports = sidebars;
21 |
--------------------------------------------------------------------------------
/docs/src/components/Figure.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import useBaseUrl from '@docusaurus/useBaseUrl'
3 |
4 | export default function Figure({ src, caption }) {
5 | return (
6 |
7 |
8 | {`Figure: ${caption}`}
9 |
10 | )
11 | }
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Link from '@docusaurus/Link'
3 | import clsx from 'clsx';
4 | import styles from './styles.module.css';
5 |
6 | const FeatureList = [
7 | {
8 | title: 'Symmetry-based Clustering',
9 | link: '/docs/learn/symmetry-based-clustering',
10 | description: (
11 | <>
12 | MatID contains a novel clustering approach called Symmetry-based
13 | Clustering (SBC) that can be used to identify meaningful components from
14 | atomistic systems.
15 | >
16 | ),
17 | },
18 | {
19 | title: 'Symmetry Analysis',
20 | link: '/docs/learn/symmetry-analysis',
21 | description: (
22 | <>
23 | MatID contains several symmetry routines for analyzing structures. The
24 | basic features are based on the excellent {spglib library}, but with an
26 | extended feature set that makes MatID unique.
27 | >
28 | ),
29 | },
30 | {
31 | title: 'Powering identification in NOMAD',
32 | link: 'https://nomad-lab.eu/nomad-lab/',
33 | linktext: 'Visit NOMAD',
34 | description: (
35 | <>
36 | MatID is being developed at FAIRmat and is powering the automated
37 | analysis of structures at scale in the NOMAD platform.
38 | >
39 | ),
40 | },
41 | ];
42 |
43 | function Feature({title, description, link, linktext}) {
44 | return (
45 |