Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
nnn911 committed May 17, 2023
1 parent 15c4385 commit 3db74bb
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 70 deletions.
Empty file.
Empty file removed Examples/FileReaderNameExample.py
Empty file.
Binary file added Examples/example_01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions Examples/example_01.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from ovito.io import import_file


def main():
pipeline = import_file("lmp.h5")
data = pipeline.compute()

print("Particle properties:")
for prop in data.particles.keys():
print(prop)
print("\nAttributes:")
for attr in data.attributes.keys():
print(attr)


if __name__ == "__main__":
main()
9 changes: 9 additions & 0 deletions Examples/generate_example_data_01.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from pyiron_atomistics import Project

pr = Project("test")
structure = pr.create.structure.ase.bulk("Al", cubic=True)
structure.set_repeat([9, 9, 9])
job = pr.create.job.Lammps("lmp")
job.structure = structure
job.calc_md(n_ionic_steps=1000, n_print=10, temperature=500.0)
job.run()
Binary file added Examples/lmp.h5
Binary file not shown.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 ovito.org
Copyright (c) 2023 Daniel Utt

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
72 changes: 60 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,64 @@
# PythonFileReaderTemplate
# pyiron File Reader
*OVITO* Python file reader for the h5 data containers written by *pyiron*.

Template for a custom Python-based file reader that hooks into *OVITO* and can easily be shared with other users.
## Description
Python file reader for *OVITO* that reads structural data from the hdf5 containers written by [*pyiron*](https://pyiron.org/). After installation, *OVITO* will auto-detect *pyiron* files and open them for analysis and visualization.
Note, that the *"status"* of the pyiron job needs to be *"finished"* before its file can be read.
The following table gives an overview over all *particle properties* and *attributes* currently understood by this parser. Optional properties will be skipped if they are not included in the file **and** the parser is not in strict mode.

This repository contains a template for creating your own [Python file reader](https://docs.ovito.org/python/introduction/custom_modifiers.html) !!WRONG LINK!!, which can be installed into *OVITO Pro* or the [`ovito`](https://pypi.org/project/ovito/) Python module using *pip*.
**Particle properties**
| pyiron name | OVITO name | Components | Optional |
| --- | --- | :---: | :---: |
| `generic/indices` | `Particle Type` | `1` | |
| `generic/unwrapped_positions` | `Position` | `3` | `x*` |
| `generic/positions` | `Position` | `3` | `x*`|
| `generic/forces` | `Force` | `3` | `x` |
| `generic/velocities` | `Velocity` | `3` | `x` |

## Getting Started
`*` One of `generic/unwrapped_positions` or `generic/positions` is required.

1. Click the "Use this template" button to create your own repository based on this template.
2. Rename `src/FileReaderName` to reflect the name of your modifier.
3. Implement your [file reader](https://docs.ovito.org/python/introduction/custom_modifiers.html#advanced-interface) !!WRONG LINK!! in [`src/FileReaderName/__init__.py`](src/FileReaderName/__init__.py). Fill in the predefined functions as needed. More details on this method can be found in the [OVITO Python docs](https://www.ovito.org/docs/current/python/introduction/custom_modifiers.html#writing-custom-modifiers-advanced-interface) !!WRONG LINK!!.
4. Fill in the [`pyproject.toml`](pyproject.toml) file. Fields that need to be replaced with your information are enclosed in descriptive `[[field]]` tags. Please make sure to include ovito>=3.9 as a dependency. Depending on your needs, you can add additional fields to the `pyproject.toml` file. Information can be found [here](https://setuptools.pypa.io/en/latest/userguide/index.html).
5. Fill in the [`README_Template.md`](README_Template.md) file. Again, the `[[fields]]` placeholders should guide you. Feel free to add other sections like "Images", "Citation", or "References" as needed.
6. Add meaningful examples and data sample files to the `Examples` directory to help others understand the use of your modifier.
7. Pick a license for your project and replace the current (MIT) [`LICENSE`](LICENSE) file with your license. If you keep the MIT license, please update the name and year in the current file.
8. Once you're done, rename `README_Template.md` to `README.md`, replacing this file.
**Attributes**
| pyiron name | OVITO name | Components | Optional |
| --- | --- | :---: | :---: |
| `generic/steps` | `Timestep` | 1 | |
| `generic/natoms` | `Number of atoms` | 1 | |
| `generic/temperature` | `Temperature` | 1 | `x` |
| `generic/energy_tot` | `Total energy` | 1 | `x` |

The file reader can be installed either into *OVITO Pro* or the [*OVITO* Python module](https://pypi.org/project/ovito/) Python module using *pip*.

## Parameters
- `roundCell` / "Round cell to orthogonal": Round the off-diagonal components of the simulation cell to `0` if they are below a threshold value currently hard-coded to `1e-8` A.
- `strict` / "Strict mode": Activate strict mode which requires all optional keys to be present in the pyiron data container. In strict mode, any missing key will raise a `KeyError`. The default (non-strict) mode silently skips all missing optional keys.

## Example
1. [Example 01](Examples/example_01.py) loads the [`lmp.h5` structure file](Examples/lmp.h5) and prints all *particle properties* and *attributes* found therein.

The following image shows the same file in the *OVITO PRO* desktop application.
![Example 01](Examples/example_01.png)

### Example data generation
The example data was generated using the [`generate_example_data_01.py`](Examples/generate_example_data_01.py) script using `pyiron`. For more information visit their [website](https://pyiron.org/).

## Installation
- OVITO PRO built-in Python interpreter
```
ovitos -m pip install --user https://github.com/nnn911/pyironFileReader/main/archive.zip
```
- Standalone Python package or Conda environment
```
pip install --user https://github.com/nnn911/pyironFileReader/main/archive.zip
```
- Please note that the `--user` tag is recommended but optional and depends on your Python installation.

## Technical information / dependencies
- Tested with *OVITO* 3.9.0
- Depends on:
- `numpy`
- `h5py`

## Adding new properties or attributes
New optional *particle properties* and *attributes* can be included in the parser quite easily. To add new particle properties, include them in the `particle_props_dict` of the `PyironFileReader` class, which can be found [here](src/pyironFileReader/__init__.py). Similarly, new attributes need to be added to the `attributes_dict`. Both dictionaries map `pyiron names` to `OVITO names`. If you add new properties consider contacting the author or submitting a pull request to make these changes available to the whole community.

## Contact
- Daniel Utt ([email protected])
28 changes: 0 additions & 28 deletions README_Template.md

This file was deleted.

24 changes: 12 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@ requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "[[FileReaderName]]"
version = "[[Version number]]"
description = "[[Short description]]"
name = "pyironFileReader"
version = "2023.0"
description = "OVITO Python file reader for the h5 data containers written by pyiron."
keywords = ["ovito", "python-file-reader"]
authors = [{name = "[[Author 1 name]]", email = "[[Author 1 email]]"}, {name = "[[Author 2 name]]", email = "[[Author 2 email]]"}]
maintainers = [{name = "[[Maintainer 1 name]]", email = "[[Maintainer 1 email]]"}, {name = "[[Maintainer 2 name]]", email = "[[Maintainer 2 email]]"}]
license = {text = "[[License]]"}
authors = [{name = "Daniel Utt", email = "[email protected]"}]
maintainers = [{name = "Daniel Utt", email = "[email protected]"}]
license = {text = "MIT License"}
readme = "README.md"
requires-python = "[[>=3.7]]"
requires-python = ">=3.7"
dependencies = [
"ovito >= [[VersionNumber]]", # OVITO must be >=3.9!
# "[[dependency1]]",
# "[[dependency2 == Version]]",
"ovito >= 3.9.0",
"numpy",
"h5py"
]

[project.urls]
repository = "[[Repository Link]]"
repository = "https://github.com/nnn911/pyironFileReader"

[project.entry-points.'OVITO.FileReader']
"[[Human readable file reader name]]" = "[[FileReaderName]]:[[FileReaderName]]"
"pyiron File Reader" = "pyironFileReader:PyironFileReader"

[tool.setuptools.packages.find]
where = ["src"]
17 changes: 0 additions & 17 deletions src/FileReaderName/__init__.py

This file was deleted.

127 changes: 127 additions & 0 deletions src/pyironFileReader/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from ovito.data import DataCollection
from ovito.io import FileReaderInterface
import numpy as np
from typing import Callable, Any
from traits.api import Bool
import h5py


class PyironFileReader(FileReaderInterface):
roundCell = Bool(False, label="Round cell to orthogonal")
roundCellBy = 1e-8
strict = Bool(False, label="Strict mode")

particle_props_dict = {
"output/generic/forces": "Force",
"output/generic/velocities": "Velocity",
}

attributes_dict = {
"output/generic/steps": "Timestep",
"output/generic/natoms": "Number of atoms",
"output/generic/temperature": "Temperature",
"output/generic/energy_tot": "Total energy",
}

@staticmethod
def detect(filename: str):
try:
with h5py.File(filename, "r") as f:
for k in f.keys():
# Check if all jobs in the h5 container have status "finished" -> (encoded as ascii array)
if not np.all(
f[k]["status"][:] == [102, 105, 110, 105, 115, 104, 101, 100]
):
break
else:
return True
except Exception:
pass
return False

def scan(self, filename: str, register_frame: Callable[..., None]):
with h5py.File(filename, "r") as f:
for k in f.keys():
for i in range(len(f[k]["output/generic/natoms"])):
register_frame(
parser_data=i,
label=f"{k}: {f[k]['output/generic/steps'][i]}",
)

def parse(
self, data: DataCollection, filename: str, parser_data: tuple, **kwargs: Any
):
with h5py.File(filename, "r") as f:
# Map from global frame index (parser_data) to local run in the h5 file
keys = list(f.keys())
if len(keys) > 1:
total = [len(f[key]["output/generic/natoms"]) for key in keys]
total = np.cumsum(total)
keyIdx = np.argmin(total <= parser_data)
key = keys[keyIdx]
localIdx = parser_data - total[keyIdx - 1]
else:
key = keys[0]
localIdx = parser_data

# Create particles
particles = data.create_particles(
count=int(f[key]["output/generic/natoms"][localIdx])
)
# Particle type
typeProperty = particles.create_property("Particle Type")
typeList = eval(
"".join([chr(item) for item in f[key]["output/structure/species"][:]])
)
uTypesH5 = np.unique(f[key]["output/generic/indices"][localIdx])
uTypesOvito = np.zeros(uTypesH5[-1] + 1, dtype=int)
for u in uTypesH5:
uTypesOvito[u] = typeProperty.add_type_name(typeList[u], particles).id
types = np.array(f[key]["output/generic/indices"][localIdx])
for u in uTypesH5:
typeProperty[types == u] = uTypesOvito[u]

# Positions -> preference for unwrapped positions
try:
particles.create_property(
"Position",
data=f[key]["output/generic/unwrapped_positions"][localIdx],
)
except KeyError:
particles.create_property(
"Position",
data=f[key]["output/generic/positions"][localIdx],
)

# Optional particle properties
for h5Key, ovitoKey in self.particle_props_dict.items():
try:
particles.create_property(
ovitoKey,
data=f[key][h5Key][localIdx],
)
except KeyError:
if self.strict:
raise KeyError(
f"{key}/{h5Key} requested but not found in pyirion data container. Consider not running in strict mode."
)

# Cell
cellMatrix = np.zeros((3, 4))
cellMatrix[:3, :3] = f[key]["output/generic/cells"][localIdx]
if self.roundCell:
for idx in ((0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)):
cellMatrix[idx] = (
0 if cellMatrix[idx] <= self.roundCellBy else cellMatrix[idx]
)
data.create_cell(cellMatrix, pbc=f[key]["output/structure/cell/pbc"][:])

# Attributes
for h5Key, ovitoKey in self.attributes_dict.items():
try:
data.attributes[ovitoKey] = f[key][h5Key][localIdx]
except KeyError:
if self.strict:
raise KeyError(
f"{key}/{h5Key} requested but not found in pyirion data container. Consider not running in strict mode."
)

0 comments on commit 3db74bb

Please sign in to comment.