{"id":46996106,"url":"https://github.com/pyiron/pyiron_workflow_atomistics","last_synced_at":"2026-05-15T00:18:42.482Z","repository":{"id":297308681,"uuid":"990558753","full_name":"pyiron/pyiron_workflow_atomistics","owner":"pyiron","description":"Atomistic workflows for use in pyiron_workflow","archived":false,"fork":false,"pushed_at":"2025-11-12T22:48:57.000Z","size":10642,"stargazers_count":0,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-13T00:21:18.590Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pyiron.png","metadata":{"files":{"readme":"docs/README.md","changelog":null,"contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-26T09:48:10.000Z","updated_at":"2025-11-02T23:32:09.000Z","dependencies_parsed_at":"2025-07-06T01:21:16.422Z","dependency_job_id":"59a966fc-3a60-454c-a483-22d761a85512","html_url":"https://github.com/pyiron/pyiron_workflow_atomistics","commit_stats":null,"previous_names":["pyiron/pyiron_workflow_atomistics"],"tags_count":8,"template":false,"template_full_name":"pyiron/pyiron_module_template","purl":"pkg:github/pyiron/pyiron_workflow_atomistics","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyiron%2Fpyiron_workflow_atomistics","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyiron%2Fpyiron_workflow_atomistics/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyiron%2Fpyiron_workflow_atomistics/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyiron%2Fpyiron_workflow_atomistics/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pyiron","download_url":"https://codeload.github.com/pyiron/pyiron_workflow_atomistics/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pyiron%2Fpyiron_workflow_atomistics/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30385499,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-11T14:10:17.325Z","status":"ssl_error","status_checked_at":"2026-03-11T14:09:37.934Z","response_time":84,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-11T15:17:28.121Z","updated_at":"2026-05-15T00:18:42.471Z","avatar_url":"https://github.com/pyiron.png","language":"Jupyter Notebook","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pyiron_workflow_atomistics\n\n[![Tests](https://github.com/pyiron/pyiron_workflow_atomistics/actions/workflows/push-pull.yml/badge.svg)](https://github.com/pyiron/pyiron_workflow_atomistics/actions/workflows/push-pull.yml)\n[![Coverage Status](https://coveralls.io/repos/github/pyiron/pyiron_workflow_atomistics/badge.svg?branch=main)](https://coveralls.io/github/pyiron/pyiron_workflow_atomistics?branch=main)\n[![PyPI version](https://img.shields.io/pypi/v/pyiron_workflow_atomistics.svg)](https://pypi.org/project/pyiron_workflow_atomistics/)\n[![Python versions](https://img.shields.io/pypi/pyversions/pyiron_workflow_atomistics.svg)](https://pypi.org/project/pyiron_workflow_atomistics/)\n[![Documentation Status](https://readthedocs.org/projects/pyiron_workflow_atomistics/badge/?version=latest)](https://pyiron_workflow_atomistics.readthedocs.io/en/latest/?badge=latest)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![License: BSD-3-Clause](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)\n\n## Overview\n\n`pyiron_workflow_atomistics` provides atomistic-simulation workflows for the [pyiron](https://pyiron.org) ecosystem. It exposes a single generic **`Engine`** interface that workflows talk to uniformly, plus topical physics macros (bulk, surface, point defects, grain boundaries) that compose into pyiron workflows.\n\n## Layout\n\n```\npyiron_workflow_atomistics/\n├── engine/      # Engine Protocol, EngineOutput, run(), ASEEngine\n├── structure/   # Generic builders / transforms / defect-structure generation\n├── physics/     # Topical workflow macros — import per-topic\n│   ├── bulk.py\n│   ├── surface.py\n│   ├── point_defect.py\n│   └── grain_boundary.py\n├── analysis/    # Featurisers, GB-plane finder, derived quantities\n└── _internal/   # Private plumbing (not part of the public API)\n```\n\nUsers import from the four public subpackages (`engine`, `structure`, `physics.*`, `analysis`); `_internal` is intentionally private.\n\n## Installation\n\n```bash\npip install pyiron_workflow_atomistics\n# or\nconda install -c conda-forge pyiron_workflow_atomistics\n```\n\n## Quick start\n\nEvery workflow follows the same pattern: build an `Engine`, build a structure, and either call `run(structure, engine)` directly or drop into a topical macro.\n\n```python\nfrom ase.build import bulk\nfrom ase.calculators.emt import EMT\n\nfrom pyiron_workflow_atomistics.engine import ASEEngine, CalcInputMinimize, run\n\nengine = ASEEngine(\n    EngineInput=CalcInputMinimize(force_convergence_tolerance=0.05),\n    calculator=EMT(),\n    working_directory=\"./_runs\",\n)\n\nstructure = bulk(\"Cu\", \"fcc\", a=3.6, cubic=True)\nnode = run(structure, engine=engine)\nnode.run()\n\nout = node.outputs.engine_output.value           # EngineOutput dataclass\nprint(out.final_energy, out.converged)\n```\n\n`Engine.with_working_directory(\"subdir\")` returns a pickleable copy with the path composed — use it to fan out per-calculation directories.\n\n## The Engine layer\n\n```python\nfrom pyiron_workflow_atomistics.engine import (\n    Engine,              # the Protocol every backend implements (runtime-checkable)\n    EngineOutput,        # @dataclass returned by every engine call\n    run,                 # the single workflow node: run(structure, engine)\n    subengine,           # @as_function_node wrapper around engine.with_working_directory\n    subdir_path,         # @as_function_node returning os.path.join(engine.wd, subdir)\n    CalcInputStatic,\n    CalcInputMinimize,\n    CalcInputMD,\n    ASEEngine,\n)\n```\n\nThe three `CalcInput*` dataclasses are jargon-free physics-level inputs (`force_convergence_tolerance`, `temperature`, `thermostat_time_constant`, ...) — engines map them to their backend's native parameters.\n\n## Topical physics workflows\n\n`physics/__init__.py` deliberately re-exports nothing — import per-topic so the path tells you what you're using.\n\n### Bulk\n\n```python\nfrom pyiron_workflow_atomistics.physics.bulk import eos_volume_scan, optimise_cubic_lattice_parameter\nfrom pyiron_workflow_atomistics.structure import get_bulk\n\nstructure = get_bulk.node_function(\"Cu\", crystalstructure=\"fcc\", a=3.6, cubic=True)\nwf = eos_volume_scan(base_structure=structure, engine=engine,\n                     axes=[\"a\", \"b\", \"c\"], strain_range=(-0.05, 0.05), num_points=7)\nwf.run()\nprint(f\"v0 = {wf.outputs.v0.value:.3f} Å^3   B = {wf.outputs.B.value:.1f} GPa\")\n```\n\n### Surface\n\n```python\nfrom ase.build import bulk\nfrom pyiron_workflow_atomistics.physics.surface import calculate_surface_energy\n\ncu_bulk = bulk(\"Cu\", \"fcc\", a=3.6, cubic=True)\nwf = calculate_surface_energy(\n    bulk_structure=cu_bulk, engine=engine,\n    miller_indices=(1, 1, 1), layers=3, vacuum=10.0,\n)\nwf.run()\nprint(wf.outputs.surface_energy.value, \"J/m²\")\n```\n\n### Point defects (vacancy + substitutional)\n\n```python\nfrom pyiron_workflow_atomistics.physics.point_defect import (\n    get_vacancy_formation_energy,\n    get_substitutional_formation_energy,\n)\n\nwf = get_vacancy_formation_energy(\n    structure=bulk(\"Cu\", \"fcc\", a=3.6, cubic=True),\n    engine=engine,\n    min_dimensions=[12, 12, 12],\n)\nwf.run()\nprint(wf.outputs.vacancy_formation_energy.value, \"eV\")\n\nwf_sub = get_substitutional_formation_energy(\n    structure=bulk(\"Cu\", \"fcc\", a=3.6, cubic=True),\n    engine=engine,\n    new_symbol=\"Ni\",\n    min_dimensions=[12, 12, 12],\n)\nwf_sub.run()\n```\n\n### Grain boundaries\n\n```python\nfrom pyiron_workflow_atomistics.physics.grain_boundary import (\n    cleave_gb_structure,\n    find_viable_cleavage_planes_around_plane,\n    get_GB_energy,\n    pure_gb_study,\n)\n```\n\n`pure_gb_study` composes length optimisation, segregation, and cleavage in one macro; the individual functions are available for finer control. See `notebooks/pure_grain_boundary_study.ipynb` and `notebooks/gb_cleavage.ipynb`.\n\n## Structure manipulation\n\n```python\nfrom pyiron_workflow_atomistics.structure import (\n    get_bulk, create_surface_slab,\n    add_vacuum, create_supercell, create_supercell_with_min_dimensions, rattle,\n    create_vacancy, substitutional_swap,\n)\n```\n\n## Analysis\n\n```python\nfrom pyiron_workflow_atomistics.analysis import (\n    voronoi_site_featuriser,\n    distance_matrix_site_featuriser,\n    soap_site_featuriser,\n    find_gb_plane, plot_gb_plane,\n    get_per_atom_quantity,\n)\n```\n\n## Implementing a custom engine\n\n`Engine` is a `typing.Protocol` — any class that satisfies the contract works. There is no base class to inherit from.\n\n```python\nimport os\nfrom dataclasses import dataclass, field, replace\nfrom typing import Any, Callable\nfrom ase import Atoms\n\nfrom pyiron_workflow_atomistics.engine import (\n    CalcInputStatic, CalcInputMinimize, CalcInputMD, EngineOutput,\n)\n\n\n@dataclass\nclass MyCustomEngine:\n    \"\"\"Drop-in replacement for ASEEngine targeting your backend.\"\"\"\n\n    EngineInput: CalcInputStatic | CalcInputMinimize | CalcInputMD\n    backend_config: dict[str, Any] = field(default_factory=dict)\n    working_directory: str = field(default_factory=os.getcwd)\n\n    def get_calculate_fn(self, structure: Atoms) -\u003e tuple[Callable[..., EngineOutput], dict[str, Any]]:\n        \"\"\"Return ``(callable, kwargs)``. The callable is invoked as\n        ``callable(structure=structure, **kwargs)`` and must return an\n        :class:`EngineOutput`. ``structure`` must NOT be in kwargs.\"\"\"\n\n        from my_backend import run_calculation\n\n        kwargs = {\n            \"working_directory\": self.working_directory,\n            \"config\": self.backend_config,\n            # map self.EngineInput.* into your backend's native parameters\n        }\n        return run_calculation, kwargs\n\n    def with_working_directory(self, subdir: str) -\u003e \"MyCustomEngine\":\n        \"\"\"Pure copy with the working directory composed — never mutate self.\"\"\"\n        return replace(\n            self, working_directory=os.path.join(self.working_directory, subdir)\n        )\n```\n\nThe contract:\n- **Pickleable** — workflows checkpoint to disk and may resubmit to SLURM.\n- **`with_working_directory` is pure** — return a copy, do not mutate `self`. The recommended idiom is `dataclasses.replace`.\n- **`get_calculate_fn(structure)`** returns `(callable, kwargs)`; the callable returns an `EngineOutput`.\n\nUse `subengine(engine=engine, subdir=\"foo\")` inside `@pwf.as_macro_node` bodies — calling `engine.with_working_directory(...)` directly on a channel input crashes pyiron_workflow's readiness checks.\n\n## EngineOutput\n\n```python\n@dataclass\nclass EngineOutput:\n    final_structure: Atoms\n    final_energy: float\n    converged: bool\n\n    final_forces:        np.ndarray | None = None\n    final_stress:        np.ndarray | None = None   # (3, 3)\n    final_stress_voigt:  np.ndarray | None = None   # (6,)\n    final_volume:        float       | None = None\n    final_magmoms:       np.ndarray  | None = None\n\n    energies:      list[float]        | None = None\n    forces:        list[np.ndarray]   | None = None\n    stresses:      list[np.ndarray]   | None = None\n    structures:    list[Atoms]        | None = None\n    n_ionic_steps: int                | None = None\n```\n\n`EngineOutput.to_dict()` returns a `dataclasses.asdict` view, with ASE objects preserved by reference.\n\n## Notebooks\n\nWorked examples covering every public workflow live in [`notebooks/`](../notebooks/). Each notebook supplies its own calculator (EMT for the Cu / Ni / Pd / Ag / Pt / Au / Al demos; EAM with the bundled `Al-Fe.eam.fs` for the Fe ones) so it executes self-contained.\n\n## Documentation\n\nFor full API documentation: [ReadTheDocs](https://pyiron_workflow_atomistics.readthedocs.io).\n\n## Contributing\n\nWe welcome contributions — see [CONTRIBUTING.rst](../CONTRIBUTING.rst) for the development workflow.\n\n## License\n\nBSD 3-Clause — see [LICENSE](../LICENSE).\n\n## Citation\n\n```bibtex\n@software{pyiron_workflow_atomistics,\n  author = {pyiron team},\n  title  = {pyiron_workflow_atomistics},\n  year   = {2024},\n  url    = {https://github.com/pyiron/pyiron_workflow_atomistics}\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyiron%2Fpyiron_workflow_atomistics","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpyiron%2Fpyiron_workflow_atomistics","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpyiron%2Fpyiron_workflow_atomistics/lists"}