Skip to content

Commit

Permalink
Merge branch 'main' into trigger-atomate2-ci-on-pmg-master-push
Browse files Browse the repository at this point in the history
  • Loading branch information
janosh committed May 11, 2024
2 parents 0fa6356 + df7c769 commit 06c4444
Show file tree
Hide file tree
Showing 168 changed files with 2,108 additions and 843 deletions.
7 changes: 0 additions & 7 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,6 @@ jobs:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
# ERROR: Cannot install atomate2 and atomate2[strict,tests]==0.0.1 because these package versions have conflicting dependencies.
# The conflict is caused by:
# atomate2[strict,tests] 0.0.1 depends on pymatgen>=2023.10.4
# atomate2[strict,tests] 0.0.1 depends on pymatgen==2023.10.4; extra == "strict"
# ERROR: ResolutionImpossible: for help visit https://pip.pypa.io/en/latest/topics/dependency-resolution/#dealing-with-dependency-conflicts
# chgnet 0.2.1 depends on pymatgen>=2023.5.31
# emmet-core 0.70.0 depends on pymatgen>=2023.10.11
run: |
python -m pip install --upgrade pip
mkdir -p ~/.abinit/pseudos
Expand Down
4 changes: 2 additions & 2 deletions docs/about/contributors.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ Lawrence Berkeley National Laboratory
[0000-0003-3439-4856]: https://orcid.org/0000-0003-3439-4856

**Matthew McDermott** [![gh]][mattmcdermott] [![orc]][0000-0002-4071-3000] \
PhD student \
University of California, Berkeley
Postdoctoral Researcher \
Lawrence Berkeley National Laboratory

[mattmcdermott]: https://github.com/mattmcdermott
[0000-0002-4071-3000]: https://orcid.org/0000-0002-4071-3000
Expand Down
21 changes: 11 additions & 10 deletions docs/dev/workflow_tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

## Anatomy of an `atomate2` computational workflow (i.e., what do I need to write?)

Every `atomate2` workflow is an instance of jobflow's `Flow ` class, which is a collection of Job and/or other `Flow` objects. So your end goal is to produce a `Flow `.
Every `atomate2` workflow is an instance of jobflow's `Flow` class, which is a collection of Job and/or other `Flow` objects. So your end goal is to produce a `Flow`.

In the context of computational materials science, `Flow ` objects are most easily created by a `Maker`, which contains a factory method make() that produces a `Flow `, given certain inputs. Typically, the input to `Maker`.make() includes atomic coordinate information in the form of a `pymatgen` `Structure` or `Molecule` object. So the basic signature looks like this:
In the context of computational materials science, `Flow` objects are most easily created by a `Maker`, which contains a factory method make() that produces a `Flow`, given certain inputs. Typically, the input to `Maker`.make() includes atomic coordinate information in the form of a `pymatgen` `Structure` or `Molecule` object. So the basic signature looks like this:

```py
class ExampleMaker(Maker):
def make(self, coordinates: Structure) -> Flow:
# take the input coordinates and return a `Flow `
# take the input coordinates and return a `Flow`
return Flow(...)
```

Expand Down Expand Up @@ -49,15 +49,16 @@ Finally, most `atomate2` workflows return structured output in the form of "Task
**TODO - extend code block above to illustrate TaskDoc usage**

In summary, a new `atomate2` workflow consists of the following components:
- A `Maker` that actually generates the workflow
- One or more `Job` and/or `Flow ` classes that define the discrete steps in the workflow
- (optionally) an `InputGenerator` that produces a `pymatgen` `InputSet` for writing calculation input files
- (optionally) a `TaskDocument` that defines a schema for storing the output data

- A `Maker` that actually generates the workflow
- One or more `Job` and/or `Flow` classes that define the discrete steps in the workflow
- (optionally) an `InputGenerator` that produces a `pymatgen` `InputSet` for writing calculation input files
- (optionally) a `TaskDocument` that defines a schema for storing the output data

## Where do I put my code?

Because of the distributed design of the MP Software Ecosystem, writing a complete new workflow may involve making contributions to more than one GitHub repository. The following guidelines should help you understand where to put your contribution.

- All workflow code (`Job`, `Flow `, `Maker`) belongs in `atomate2`
- `InputSet` and `InputGenerator` code belongs in `pymatgen`. However, if you need to create these classes from scratch (i.e., you are working with a code that is not already supported in`pymatgen`), then it is recommended to include them in `atomate2` at first to facilitate rapid iteration. Once mature, they can be moved to `pymatgen` or to a `pymatgen` [addon package](https://pymatgen.org/addons).
- `TaskDocument` schemas should generally be developed in `atomate2` alongside the workflow code. We recommend that you first check emmet to see if there is an existing schema that matches what you need. If so, you can import it. If not, check [`cclib`](https://cclib.github.io/). `cclib` output can be imported via [`atomate2.common.schemas.TaskDocument`](https://github.com/materialsproject/atomate2/blob/main/src/atomate2/common/schemas/cclib.py). If neither code has what you need, then new schemas should be developed within `atomate2` (or `cclib`).
- All workflow code (`Job`, `Flow`, `Maker`) belongs in `atomate2`
- `InputSet` and `InputGenerator` code belongs in `pymatgen`. However, if you need to create these classes from scratch (i.e., you are working with a code that is not already supported in`pymatgen`), then it is recommended to include them in `atomate2` at first to facilitate rapid iteration. Once mature, they can be moved to `pymatgen` or to a `pymatgen` [addon package](https://pymatgen.org/addons).
- `TaskDocument` schemas should generally be developed in `atomate2` alongside the workflow code. We recommend that you first check emmet to see if there is an existing schema that matches what you need. If so, you can import it. If not, check [`cclib`](https://cclib.github.io/). `cclib` output can be imported via [`atomate2.common.schemas.TaskDocument`](https://github.com/materialsproject/atomate2/blob/main/src/atomate2/common/schemas/cclib.py). If neither code has what you need, then new schemas should be developed within `atomate2` (or `cclib`).
39 changes: 38 additions & 1 deletion docs/user/codes/vasp.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ adjust them if necessary. The default might not be strict enough
for your specific case.
```

### Lobster
### LOBSTER

Perform bonding analysis with [LOBSTER](http://cohp.de/) and [LobsterPy](https://github.com/jageo/lobsterpy)

Expand Down Expand Up @@ -367,6 +367,43 @@ for number, (key, cohp) in enumerate(
plotter.add_cohp(key, cohp)
plotter.save_plot(f"plots_cation_anion_bonds{number}.pdf")
```
# Running the LOBSTER workflow without database and with one job script only

It is also possible to run the VASP-LOBSTER workflow with a minimal setup.
In this case, you will run the VASP calculations on the same node as the LOBSTER calculations.
In between, the different computations you will switch from MPI to OpenMP parallelization.

For example, for a node with 48 cores, you could use an adapted version of the following SLURM script:

```bash
#!/bin/bash
#SBATCH -J vasplobsterjob
#SBATCH -o ./%x.%j.out
#SBATCH -e ./%x.%j.err
#SBATCH -D ./
#SBATCH --mail-type=END
#SBATCH [email protected]
#SBATCH --time=24:00:00
#SBATCH --nodes=1
#This needs to be adapted if you run with different cores
#SBATCH --ntasks=48

# ensure you load the modules to run VASP, e.g., module load vasp
module load my_vasp_module
# please activate the required conda environment
conda activate my_environment
cd my_folder
# the following script needs to contain the workflow
python xyz.py
```

The `LOBSTER_CMD` now needs an additional export of the number of threads.

```yaml
VASP_CMD: <<VASP_CMD>>
LOBSTER_CMD: OMP_NUM_THREADS=48 <<LOBSTER_CMD>>
```
(modifying_input_sets)=
Modifying input sets
Expand Down
50 changes: 30 additions & 20 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ requires-python = ">=3.9"
dependencies = [
"PyYAML",
"click",
"custodian>=2024.2.15",
"emmet-core>=0.78.0rc4",
"custodian>=2024.4.18",
"emmet-core==0.82.2",
"jobflow>=0.1.11",
"monty>=2024.2.2",
"numpy",
"pydantic-settings>=2.0.3",
"pydantic>=2.0.1",
"pymatgen>=2024.3.1",
"pymatgen>=2024.4.13",
]

[project.optional-dependencies]
Expand All @@ -43,19 +43,19 @@ amset = ["amset>=0.4.15", "pydash"]
cclib = ["cclib"]
mp = ["mp-api>=0.37.5"]
phonons = ["phonopy>=1.10.8", "seekpath"]
lobster = ["ijson>=3.2.2", "lobsterpy>=0.3.7"]
lobster = ["ijson>=3.2.2", "lobsterpy>=0.3.8"]
defects = [
"dscribe>=1.2.0",
"pymatgen-analysis-defects>=2022.11.30",
"pymatgen-analysis-defects>=2024.5.11",
"python-ulid",
]
forcefields = [
"ase>=3.22.1",
"torch<=2.2.1", # incompatibility with dgl if newer versions are used
"chgnet>=0.2.2",
"mace-torch>=0.3.3",
"matgl>=1.0.0",
"matgl>=1.1.1",
"quippy-ase>=0.9.14",
"torch<=2.2.1", # incompatibility with dgl if newer versions are used
]
docs = [
"FireWorks==2.0.3",
Expand All @@ -70,35 +70,40 @@ docs = [
"sphinx_design==0.5.0",
]
dev = ["pre-commit>=2.12.1"]
tests = ["FireWorks==2.0.3", "pytest-cov==5.0.0", "pytest==8.0.2", "pytest-mock==3.14.0"]
tests = [
"FireWorks==2.0.3",
"pytest-cov==5.0.0",
"pytest-mock==3.14.0",
"pytest==8.0.2",
]
strict = [
"PyYAML==6.0.1",
# must use >= for ase to not uninstall main branch install in CI
# last known working commit: https://gitlab.com/ase/ase@2bab58f4e
"ase>=3.22.1",
"torch==2.2.1",
"cclib==1.8.1",
"chgnet==0.3.5",
"click==8.1.7",
"custodian==2024.3.12",
"custodian==2024.4.18",
"dscribe==2.1.0",
"emmet-core==0.82.2",
"ijson==3.2.3",
"jobflow==0.1.17",
"lobsterpy==0.3.8",
"lobsterpy==0.4.0",
"mace-torch>=0.3.3",
"matgl==1.0.0",
"matgl==1.1.1",
"monty==2024.3.31",
"mp-api==0.41.2",
"numpy",
"phonopy==2.22.1",
"pydantic-settings==2.2.1",
"pydantic==2.7.1",
"pymatgen-analysis-defects==2024.4.23",
"pymatgen==2024.3.1",
"pymatgen-analysis-defects==2024.5.11",
"pymatgen==2024.5.1",
"python-ulid==2.5.0",
"quippy-ase==0.9.14",
"seekpath==2.1.0",
"torch==2.2.1",
"typing-extensions==4.11.0",
]

Expand Down Expand Up @@ -126,7 +131,7 @@ ignore_missing_imports = true
no_strict_optional = true

[tool.pytest.ini_options]
addopts = "-p no:warnings --import-mode=importlib"
addopts = "-p no:warnings --import-mode=importlib --cov-config=pyproject.toml"
filterwarnings = [
"ignore:.*POTCAR.*:UserWarning",
"ignore:.*input structure.*:UserWarning",
Expand Down Expand Up @@ -154,8 +159,10 @@ exclude_lines = [

[tool.ruff]
target-version = "py39"
lint.select = ["ALL"]
lint.ignore = [

[tool.ruff.lint]
select = ["ALL"]
ignore = [
"ANN002",
"ANN003",
"ANN101", # missing self type annotation
Expand Down Expand Up @@ -188,9 +195,12 @@ lint.ignore = [
"TD", # TODOs
"TRY003", # long message outside exception class
]
lint.pydocstyle.convention = "numpy"
lint.isort.known-first-party = ["atomate2"]
lint.isort.split-on-trailing-comma = false
pydocstyle.convention = "numpy"
isort.known-first-party = ["atomate2"]
isort.split-on-trailing-comma = false

[tool.ruff.format]
docstring-code-format = true

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
Expand Down
6 changes: 2 additions & 4 deletions src/atomate2/abinit/flows/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ def make(
structure: Structure,
restart_from: str | Path | None = None,
) -> Flow:
"""
Create a band structure flow.
"""Create a band structure flow.
Parameters
----------
Expand Down Expand Up @@ -109,8 +108,7 @@ def make(
structure: Structure | None = None,
restart_from: str | Path | None = None,
) -> Flow:
"""
Create a relaxation flow.
"""Create a relaxation flow.
Parameters
----------
Expand Down
3 changes: 1 addition & 2 deletions src/atomate2/abinit/jobs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ def make(
restart_from: str | Path | list[str] | None = None,
history: JobHistory | None = None,
) -> jobflow.Job:
"""
Return an ABINIT jobflow.Job.
"""Get an ABINIT jobflow.Job.
Parameters
----------
Expand Down
3 changes: 1 addition & 2 deletions src/atomate2/abinit/jobs/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ def make(
history: JobHistory | None = None,
mode: str = "uniform",
) -> Job:
"""
Run a non-scf ABINIT job.
"""Run a non-scf ABINIT job.
Parameters
----------
Expand Down
37 changes: 16 additions & 21 deletions src/atomate2/abinit/schemas/calculation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
import os
from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING, Optional, Union

if TYPE_CHECKING:
pass
from typing import Optional, Union

from abipy.electrons.gsr import GsrFile
from abipy.flowtk import events
Expand All @@ -18,6 +15,7 @@
from jobflow.utils import ValueEnum
from pydantic import BaseModel, Field
from pymatgen.core import Structure
from typing_extensions import Self

from atomate2.abinit.utils.common import LOG_FILE_NAME, MPIABORTFILE, get_event_report

Expand Down Expand Up @@ -110,9 +108,8 @@ class CalculationOutput(BaseModel):
def from_abinit_gsr(
cls,
output: GsrFile, # Must use auto_load kwarg when passed
) -> CalculationOutput:
"""
Create an Abinit output document from Abinit outputs.
) -> Self:
"""Create an Abinit output document from Abinit outputs.
Parameters
----------
Expand Down Expand Up @@ -211,9 +208,8 @@ def from_abinit_files(
abinit_gsr_file: Path | str = "out_GSR.nc",
abinit_log_file: Path | str = LOG_FILE_NAME,
abinit_abort_file: Path | str = MPIABORTFILE,
) -> tuple[Calculation, dict[AbinitObject, dict]]:
"""
Create an Abinit calculation document from a directory and file paths.
) -> tuple[Self, dict[AbinitObject, dict]]:
"""Create an Abinit calculation document from a directory and file paths.
Parameters
----------
Expand Down Expand Up @@ -263,15 +259,14 @@ def from_abinit_files(
msg = f"{cls} exception while parsing event_report:\n{exc}"
logger.critical(msg)

return (
cls(
dir_name=str(dir_name),
task_name=task_name,
abinit_version=abinit_gsr.abinit_version,
has_abinit_completed=has_abinit_completed,
completed_at=completed_at,
output=output_doc,
event_report=report,
),
None, # abinit_objects,
instance = cls(
dir_name=str(dir_name),
task_name=task_name,
abinit_version=abinit_gsr.abinit_version,
has_abinit_completed=has_abinit_completed,
completed_at=completed_at,
output=output_doc,
event_report=report,
)

return instance, None # abinit_objects,
Loading

0 comments on commit 06c4444

Please sign in to comment.