|
1 | 1 | # Energy Volume Curve |
2 | 2 | Based on [previous work](https://materialdigital.github.io/ADIS2023/README.html) from the [ADIS 2023 workshop](https://www.mpie.de/4902385/adis2023) |
3 | 3 | the calculation of an energy volume curve with the [quantum espresso](https://www.quantum-espresso.org) density |
4 | | -functional theory (DFT) simulation code is implemented in the Python Workflow Definition. |
| 4 | +functional theory (DFT) simulation code is implemented in the Python Workflow Definition. |
| 5 | + |
| 6 | +## Workflow |
| 7 | +An energy-volume curve is computed by relaxing a bulk crystal structure, straining it to a series of volumes and |
| 8 | +computing the total energy at each volume with a self-consistent-field (SCF) calculation. The pipeline is implemented |
| 9 | +as five Python functions in [workflow.py](example_workflows/quantum_espresso/workflow.py): |
| 10 | +```python |
| 11 | +def get_bulk_structure(element, a, cubic): |
| 12 | + # build an ASE bulk crystal structure, e.g. Al |
| 13 | + |
| 14 | +def calculate_qe(working_directory, input_dict): |
| 15 | + # write a pw.x input file, run "pw.x -in input.pwi > output.pwo" and parse the pwscf.xml output; |
| 16 | + # input_dict["calculation"] selects "vc-relax" (cell + geometry relaxation) or "scf" (single-point energy) |
| 17 | + |
| 18 | +def generate_structures(structure, strain_lst): |
| 19 | + # apply each volumetric strain in strain_lst to a structure, returning one structure per strain |
| 20 | + |
| 21 | +def plot_energy_volume_curve(volume_lst, energy_lst): |
| 22 | + # plot energy against volume and save it as evcurve.png |
| 23 | +``` |
| 24 | +`write_input`/`collect_output` handle the file-based `pw.x` input/output (`.pwi`/`.pwo`/`.xml` files), and |
| 25 | +`ase_to_json`/`json_to_ase` (de)serialize ASE `Atoms` objects to/from OPTIMADE-JSON strings, so structures can be |
| 26 | +passed between functions as plain JSON data instead of Python objects. |
| 27 | + |
| 28 | +The workflow combines these functions as: `get_bulk_structure` -> `calculate_qe` (`vc-relax`, once) -> |
| 29 | +`generate_structures` -> `calculate_qe` (`scf`, once per strained structure) -> `plot_energy_volume_curve`. With five |
| 30 | +strains this means one relaxation followed by five independent SCF calculations that fan out from |
| 31 | +`generate_structures` and feed back into a single plot. |
| 32 | + |
| 33 | +## workflow.json |
| 34 | +[workflow.json](example_workflows/quantum_espresso/workflow.json) encodes this fan-out as five separate |
| 35 | +`workflow.calculate_qe` function nodes, each connected to its own strained structure and its own `working_directory` |
| 36 | +input, all reading the same shared inputs (`pseudopotentials`, `kpts`, `calculation`, `smearing`) via |
| 37 | +`python_workflow_definition.shared.get_dict`. An excerpt showing the pattern for two of the five strained |
| 38 | +calculations: |
| 39 | +``` |
| 40 | +{ |
| 41 | + "nodes": [ |
| 42 | + {"id": 2, "type": "function", "value": "workflow.generate_structures"}, |
| 43 | + {"id": 3, "type": "function", "value": "workflow.calculate_qe"}, |
| 44 | + {"id": 4, "type": "function", "value": "workflow.calculate_qe"}, |
| 45 | + {"id": 19, "type": "input", "value": "strain_0", "name": "working_directory_1"}, |
| 46 | + {"id": 20, "type": "function", "value": "python_workflow_definition.shared.get_dict"}, |
| 47 | + {"id": 22, "type": "input", "value": "strain_1", "name": "working_directory_2"}, |
| 48 | + {"id": 23, "type": "function", "value": "python_workflow_definition.shared.get_dict"} |
| 49 | + ], |
| 50 | + "edges": [ |
| 51 | + {"target": 3, "targetPort": "working_directory", "source": 19, "sourcePort": null}, |
| 52 | + {"target": 20, "targetPort": "structure", "source": 2, "sourcePort": "s_0"}, |
| 53 | + {"target": 3, "targetPort": "input_dict", "source": 20, "sourcePort": null}, |
| 54 | + {"target": 4, "targetPort": "working_directory", "source": 22, "sourcePort": null}, |
| 55 | + {"target": 23, "targetPort": "structure", "source": 2, "sourcePort": "s_1"}, |
| 56 | + {"target": 4, "targetPort": "input_dict", "source": 23, "sourcePort": null} |
| 57 | + ] |
| 58 | +} |
| 59 | +``` |
| 60 | +Node 2 (`generate_structures`) exposes one output port per strain (`s_0`, `s_1`, ...); each port is wired into its |
| 61 | +own `get_dict`/`calculate_qe` pair. The energy and volume outputs of all five `calculate_qe` nodes are collected back |
| 62 | +into lists with `python_workflow_definition.shared.get_list` before being passed to `plot_energy_volume_curve`. |
| 63 | + |
| 64 | +## Requirements |
| 65 | +Running this workflow (rather than just loading/inspecting the JSON) requires the Quantum Espresso `pw.x` binary, |
| 66 | +matching pseudopotential files (see [espresso/pseudo](example_workflows/quantum_espresso/espresso/pseudo)) and the |
| 67 | +Python dependencies listed in [environment.yml](example_workflows/quantum_espresso/environment.yml). |
0 commit comments