aiida-pythonjob 0.1.7__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/.gitignore +2 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/PKG-INFO +3 -2
- aiida_pythonjob-0.2.0/docs/gallery/autogen/pyfunction.py +191 -0
- aiida_pythonjob-0.1.7/docs/gallery/autogen/how_to.py → aiida_pythonjob-0.2.0/docs/gallery/autogen/pythonjob.py +33 -8
- aiida_pythonjob-0.2.0/docs/source/index.rst +52 -0
- aiida_pythonjob-0.2.0/docs/source/tutorial/dft.ipynb +1291 -0
- aiida_pythonjob-0.2.0/docs/source/tutorial/html/atomization_energy.html +290 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/docs/source/tutorial/html/pythonjob_eos_emt.html +10 -10
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/pyproject.toml +3 -1
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/__init__.py +3 -1
- aiida_pythonjob-0.2.0/src/aiida_pythonjob/calculations/pyfunction.py +268 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/data/serializer.py +13 -10
- aiida_pythonjob-0.2.0/src/aiida_pythonjob/decorator.py +134 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/launch.py +70 -1
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/utils.py +28 -19
- aiida_pythonjob-0.2.0/tests/test_pyfunction.py +87 -0
- aiida_pythonjob-0.2.0/tests/test_utils.py +46 -0
- aiida_pythonjob-0.1.7/docs/source/index.rst +0 -26
- aiida_pythonjob-0.1.7/docs/source/tutorial/dft.ipynb +0 -1128
- aiida_pythonjob-0.1.7/docs/source/tutorial/html/atomization_energy.html +0 -290
- aiida_pythonjob-0.1.7/tests/test_utils.py +0 -17
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/.github/workflows/ci.yml +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/.github/workflows/python-publish.yml +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/.pre-commit-config.yaml +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/.readthedocs.yml +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/LICENSE +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/README.md +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/docs/Makefile +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/docs/environment.yml +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/docs/gallery/autogen/GALLERY_HEADER.rst +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/docs/make.bat +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/docs/requirements.txt +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/docs/source/conf.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/docs/source/installation.rst +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/docs/source/tutorial/index.rst +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/examples/test_add.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/calculations/__init__.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/calculations/pythonjob.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/calculations/utils.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/config.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/data/__init__.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/data/atoms.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/data/deserializer.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/data/pickled_data.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/parsers/__init__.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/src/aiida_pythonjob/parsers/pythonjob.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/tests/conftest.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/tests/inputs_folder/another_input.txt +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/tests/test_create_env.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/tests/test_data.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/tests/test_entry_points.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/tests/test_parser.py +0 -0
- {aiida_pythonjob-0.1.7 → aiida_pythonjob-0.2.0}/tests/test_pythonjob.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiida-pythonjob
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Run Python functions on a remote computer.
|
|
5
5
|
Project-URL: Source, https://github.com/aiidateam/aiida-pythonjob
|
|
6
6
|
Author-email: Xing Wang <xingwang1991@gmail.com>
|
|
@@ -37,7 +37,8 @@ Requires-Python: >=3.9
|
|
|
37
37
|
Requires-Dist: aiida-core<3,>=2.3
|
|
38
38
|
Requires-Dist: ase
|
|
39
39
|
Requires-Dist: cloudpickle
|
|
40
|
-
|
|
40
|
+
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: hatch; extra == 'dev'
|
|
41
42
|
Provides-Extra: docs
|
|
42
43
|
Requires-Dist: furo; extra == 'docs'
|
|
43
44
|
Requires-Dist: markupsafe<2.1; extra == 'docs'
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyFunction
|
|
3
|
+
===============
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
######################################################################
|
|
8
|
+
# Default outputs
|
|
9
|
+
# --------------
|
|
10
|
+
#
|
|
11
|
+
# The default output of the function is `result`. The `pyfunction` task
|
|
12
|
+
# will store the result as one node in the database with the key `result`.
|
|
13
|
+
#
|
|
14
|
+
from aiida import load_profile
|
|
15
|
+
from aiida.engine import run_get_node
|
|
16
|
+
from aiida_pythonjob import pyfunction
|
|
17
|
+
|
|
18
|
+
load_profile()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pyfunction()
|
|
22
|
+
def add(x, y):
|
|
23
|
+
return x + y
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
result, node = run_get_node(add, x=1, y=2)
|
|
27
|
+
print("result: ", result)
|
|
28
|
+
|
|
29
|
+
######################################################################
|
|
30
|
+
# Custom outputs
|
|
31
|
+
# --------------
|
|
32
|
+
# If the function return a dictionary with fixed number of keys, and you
|
|
33
|
+
# want to store the values as separate outputs, you can specify the `function_outputs` parameter.
|
|
34
|
+
# For a dynamic number of outputs, you can use the namespace output, which is explained later.
|
|
35
|
+
#
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pyfunction(outputs=[{"name": "sum"}, {"name": "diff"}])
|
|
39
|
+
def add(x, y):
|
|
40
|
+
return {"sum": x + y, "diff": x - y}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
result, node = run_get_node(add, x=1, y=2)
|
|
44
|
+
|
|
45
|
+
print("result: ")
|
|
46
|
+
print("sum: ", result["sum"])
|
|
47
|
+
print("diff: ", result["diff"])
|
|
48
|
+
|
|
49
|
+
######################################################################
|
|
50
|
+
# Namespace Output
|
|
51
|
+
# --------------
|
|
52
|
+
#
|
|
53
|
+
# The `pyfunction` allows users to define namespace outputs. A namespace output
|
|
54
|
+
# is a dictionary with keys and values returned by a function. Each value in
|
|
55
|
+
# this dictionary will be serialized to AiiDA data, and the key-value pair
|
|
56
|
+
# will be stored in the database.
|
|
57
|
+
#
|
|
58
|
+
# Why Use Namespace Outputs?
|
|
59
|
+
#
|
|
60
|
+
# - **Dynamic and Flexible**: The keys and values in the namespace output are not fixed and can change based on the task's execution. # noqa
|
|
61
|
+
# - **Querying**: The data in the namespace output is stored as an AiiDA data node, allowing for easy querying and retrieval. # noqa
|
|
62
|
+
# - **Data Provenance**: When the data is used as input for subsequent tasks, the origin of data is tracked.
|
|
63
|
+
#
|
|
64
|
+
# For example: Consider a molecule adsorption calculation where the namespace
|
|
65
|
+
# output stores the surface slabs of the molecule adsorbed on different surface
|
|
66
|
+
# sites. The number of surface slabs can vary depending on the surface. These
|
|
67
|
+
# output surface slabs can be utilized as input to the next task to calculate the energy.
|
|
68
|
+
|
|
69
|
+
from ase import Atoms # noqa: E402
|
|
70
|
+
from ase.build import bulk # noqa: E402
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@pyfunction(outputs=[{"name": "scaled_structures", "identifier": "namespace"}])
|
|
74
|
+
def generate_structures(structure: Atoms, factor_lst: list) -> dict:
|
|
75
|
+
"""Scale the structure by the given factor_lst."""
|
|
76
|
+
scaled_structures = {}
|
|
77
|
+
for i in range(len(factor_lst)):
|
|
78
|
+
atoms = structure.copy()
|
|
79
|
+
atoms.set_cell(atoms.cell * factor_lst[i], scale_atoms=True)
|
|
80
|
+
scaled_structures[f"s_{i}"] = atoms
|
|
81
|
+
return {"scaled_structures": scaled_structures}
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
result, node = run_get_node(generate_structures, structure=bulk("Al"), factor_lst=[0.95, 1.0, 1.05])
|
|
85
|
+
print("scaled_structures: ")
|
|
86
|
+
for key, value in result["scaled_structures"].items():
|
|
87
|
+
print(key, value)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
######################################################################
|
|
91
|
+
# Exit Code
|
|
92
|
+
# --------------
|
|
93
|
+
# Users can define custom exit codes to indicate the status of the task.
|
|
94
|
+
#
|
|
95
|
+
# When the function returns a dictionary with an `exit_code` key, the system
|
|
96
|
+
# automatically parses and uses this code to indicate the task's status. In
|
|
97
|
+
# the case of an error, the non-zero `exit_code` value helps identify the specific problem.
|
|
98
|
+
#
|
|
99
|
+
#
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@pyfunction()
|
|
103
|
+
def add(x, y):
|
|
104
|
+
sum = x + y
|
|
105
|
+
if sum < 0:
|
|
106
|
+
exit_code = {"status": 410, "message": "Some elements are negative"}
|
|
107
|
+
return {"sum": sum, "exit_code": exit_code}
|
|
108
|
+
return {"sum": sum}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
result, node = run_get_node(add, x=1, y=-2)
|
|
112
|
+
print("exit_status:", node.exit_status)
|
|
113
|
+
print("exit_message:", node.exit_message)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
######################################################################
|
|
117
|
+
# Define your data serializer and deserializer
|
|
118
|
+
# --------------
|
|
119
|
+
#
|
|
120
|
+
# PythonJob search data serializer from the `aiida.data` entry point by the
|
|
121
|
+
# module name and class name (e.g., `ase.atoms.Atoms`).
|
|
122
|
+
#
|
|
123
|
+
# In order to let the PythonJob find the serializer, you must register the
|
|
124
|
+
# AiiDA data with the following format:
|
|
125
|
+
#
|
|
126
|
+
# .. code-block:: ini
|
|
127
|
+
#
|
|
128
|
+
# [project.entry-points."aiida.data"]
|
|
129
|
+
# abc.ase.atoms.Atoms = "abc.xyz:MyAtomsData"
|
|
130
|
+
#
|
|
131
|
+
# This will register a data serializer for `ase.atoms.Atoms` data. `abc` is
|
|
132
|
+
# the plugin name, the module name is `xyz`, and the AiiDA data class name is
|
|
133
|
+
# `AtomsData`. Learn how to create an AiiDA data class `here <https://aiida.readthedocs.io/projects/aiida-core/en/stable/topics/data_types.html#adding-support-for-custom-data-types>`_.
|
|
134
|
+
#
|
|
135
|
+
# *Avoid duplicate data serializer*: If you have multiple plugins that
|
|
136
|
+
# register the same data serializer, the PythonJob will raise an error.
|
|
137
|
+
# You can avoid this by selecting the plugin that you want to use in the configuration file.
|
|
138
|
+
#
|
|
139
|
+
#
|
|
140
|
+
# .. code-block:: json
|
|
141
|
+
#
|
|
142
|
+
# {
|
|
143
|
+
# "serializers": {
|
|
144
|
+
# "ase.atoms.Atoms": "abc.ase.atoms.AtomsData" # use the full path to the serializer
|
|
145
|
+
# }
|
|
146
|
+
# }
|
|
147
|
+
#
|
|
148
|
+
# Save the configuration file as `pythonjob.json` in the aiida configuration
|
|
149
|
+
# directory (by default, `~/.aiida` directory).
|
|
150
|
+
#
|
|
151
|
+
# If you want to pass AiiDA Data node as input, and the node does not have a `value` attribute,
|
|
152
|
+
# then one must provide a deserializer for it.
|
|
153
|
+
#
|
|
154
|
+
|
|
155
|
+
from aiida import orm # noqa: E402
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@pyfunction()
|
|
159
|
+
def make_supercell(structure, n=2):
|
|
160
|
+
return structure * [n, n, n]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
structure = orm.StructureData(cell=[[1, 0, 0], [0, 1, 0], [0, 0, 1]])
|
|
164
|
+
structure.append_atom(position=(0.0, 0.0, 0.0), symbols="Li")
|
|
165
|
+
|
|
166
|
+
result, node = run_get_node(
|
|
167
|
+
make_supercell,
|
|
168
|
+
structure=structure,
|
|
169
|
+
deserializers={
|
|
170
|
+
"aiida.orm.nodes.data.structure.StructureData": "aiida_pythonjob.data.deserializer.structure_data_to_atoms"
|
|
171
|
+
},
|
|
172
|
+
)
|
|
173
|
+
print("result: ", result)
|
|
174
|
+
|
|
175
|
+
######################################################################
|
|
176
|
+
# One can also set the deserializer in the configuration file.
|
|
177
|
+
#
|
|
178
|
+
#
|
|
179
|
+
# .. code-block:: json
|
|
180
|
+
#
|
|
181
|
+
# {
|
|
182
|
+
# "serializers": {
|
|
183
|
+
# "ase.atoms.Atoms": "abc.ase.atoms.Atoms"
|
|
184
|
+
# },
|
|
185
|
+
# "deserializers": {
|
|
186
|
+
# "aiida.orm.nodes.data.structure.StructureData": "aiida_pythonjob.data.deserializer.structure_data_to_pymatgen" # noqa
|
|
187
|
+
# }
|
|
188
|
+
# }
|
|
189
|
+
#
|
|
190
|
+
# The `orm.List`, `orm.Dict`and `orm.StructureData` data types already have built-in deserializers.
|
|
191
|
+
#
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
PythonJob
|
|
3
3
|
===============
|
|
4
4
|
|
|
5
5
|
"""
|
|
@@ -241,14 +241,12 @@ print("retrieved files: ", result["retrieved"].list_object_names())
|
|
|
241
241
|
# is a dictionary with keys and values returned by a function. Each value in
|
|
242
242
|
# this dictionary will be serialized to AiiDA data, and the key-value pair
|
|
243
243
|
# will be stored in the database.
|
|
244
|
+
#
|
|
244
245
|
# Why Use Namespace Outputs?
|
|
245
246
|
#
|
|
246
|
-
# - **Dynamic and Flexible**: The keys and values in the namespace output are
|
|
247
|
-
#
|
|
248
|
-
# - **
|
|
249
|
-
# node, allowing for easy querying and retrieval.
|
|
250
|
-
# - **Data Provenance**: When the data is used as input for subsequent tasks,
|
|
251
|
-
# the origin of data is tracked.
|
|
247
|
+
# - **Dynamic and Flexible**: The keys and values in the namespace output are not fixed and can change based on the task's execution. # noqa
|
|
248
|
+
# - **Querying**: The data in the namespace output is stored as an AiiDA data node, allowing for easy querying and retrieval. # noqa
|
|
249
|
+
# - **Data Provenance**: When the data is used as input for subsequent tasks, the origin of data is tracked.
|
|
252
250
|
#
|
|
253
251
|
# For example: Consider a molecule adsorption calculation where the namespace
|
|
254
252
|
# output stores the surface slabs of the molecule adsorbed on different surface
|
|
@@ -332,7 +330,7 @@ for key, value in result["scaled_structures"].items():
|
|
|
332
330
|
|
|
333
331
|
def add(x, y):
|
|
334
332
|
sum = x + y
|
|
335
|
-
if
|
|
333
|
+
if sum < 0:
|
|
336
334
|
exit_code = {"status": 410, "message": "Some elements are negative"}
|
|
337
335
|
return {"sum": sum, "exit_code": exit_code}
|
|
338
336
|
return {"sum": sum}
|
|
@@ -347,6 +345,33 @@ result, node = run_get_node(PythonJob, inputs=inputs)
|
|
|
347
345
|
print("exit_status:", node.exit_status)
|
|
348
346
|
print("exit_message:", node.exit_message)
|
|
349
347
|
|
|
348
|
+
######################################################################
|
|
349
|
+
# Using `register_pickle_by_value`
|
|
350
|
+
# --------------------------------
|
|
351
|
+
#
|
|
352
|
+
# If the function is defined inside an external module that is **not installed** on
|
|
353
|
+
# the remote computer, this can cause import errors during execution.
|
|
354
|
+
#
|
|
355
|
+
# **Solution:**
|
|
356
|
+
# By enabling `register_pickle_by_value=True`, the function is serialized **by value**
|
|
357
|
+
# instead of being referenced by its module path. This embeds the function unpickled
|
|
358
|
+
# even if the original module is unavailable on the remote computer.
|
|
359
|
+
#
|
|
360
|
+
# **Example:**
|
|
361
|
+
#
|
|
362
|
+
# .. code-block:: python
|
|
363
|
+
#
|
|
364
|
+
# inputs = prepare_pythonjob_inputs(
|
|
365
|
+
# my_function,
|
|
366
|
+
# function_inputs={"x": 1, "y": 2},
|
|
367
|
+
# computer="localhost",
|
|
368
|
+
# register_pickle_by_value=True, # Ensures function is embedded
|
|
369
|
+
# )
|
|
370
|
+
#
|
|
371
|
+
# **Important Considerations:**: If the function **contains import statements**,
|
|
372
|
+
# the imported modules **must still be installed** on the remote computer.
|
|
373
|
+
#
|
|
374
|
+
|
|
350
375
|
|
|
351
376
|
######################################################################
|
|
352
377
|
# Define your data serializer and deserializer
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
AiiDA PythonJob & PyFunction
|
|
2
|
+
============================
|
|
3
|
+
|
|
4
|
+
`PythonJob` and `pyfunction` enable users to run Python functions—either locally or remotely—with automatic serialization, provenance tracking, and workflow integration.
|
|
5
|
+
|
|
6
|
+
These tools are designed to simplify the experience for users not familiar with AiiDA's internal types, allowing them to write pure Python functions while still benefiting from AiiDA's powerful infrastructure.
|
|
7
|
+
|
|
8
|
+
**Key Features**
|
|
9
|
+
|
|
10
|
+
1. **Remote Execution with PythonJob**
|
|
11
|
+
Seamlessly run Python functions on a remote computer via `PythonJob`. A working directory is automatically created for the job, allowing full support for:
|
|
12
|
+
|
|
13
|
+
- File upload and retrieval
|
|
14
|
+
- Parent/remote folders
|
|
15
|
+
- Integration with external codes (e.g. ASE + DFT engines)
|
|
16
|
+
|
|
17
|
+
2. **Local Execution with PyFunction**
|
|
18
|
+
Use `@pyfunction` to run pure Python functions locally. All inputs and outputs are automatically serialized/deserialized, enabling users to work with native Python types.
|
|
19
|
+
This decorator is ideal for functions that are:
|
|
20
|
+
|
|
21
|
+
.. note::
|
|
22
|
+
`pyfunction` does **not** create a dedicated working directory. It executes in the current local Python environment.
|
|
23
|
+
File I/O or relative path access should be avoided unless explicitly handled.
|
|
24
|
+
|
|
25
|
+
3. **User-Friendly Interface**
|
|
26
|
+
Designed for users unfamiliar with AiiDA’s internal `Data` classes. The decorators handle all conversion and provenance tracking behind the scenes.
|
|
27
|
+
|
|
28
|
+
4. **Workflow Management with Checkpoints**
|
|
29
|
+
Combine `pyfunction` and `pythonjob` in `WorkGraph` workflows to build robust, restartable workflows with full checkpoint support.
|
|
30
|
+
|
|
31
|
+
5. **Full Data Provenance**
|
|
32
|
+
All inputs, outputs, and intermediate results are stored in the AiiDA provenance graph, ensuring reproducibility and traceability.
|
|
33
|
+
|
|
34
|
+
**When to Use What**
|
|
35
|
+
|
|
36
|
+
+----------------+--------------------+---------------------+------------------+---------------------------------------------+
|
|
37
|
+
| Decorator | Execution | Input/Output Types | AiiDA Provenance | Recommended Use Case |
|
|
38
|
+
+================+====================+=====================+==================+=============================================+
|
|
39
|
+
| ``@pyfunction``| Local | Python native | ✅ Yes | Pure functions |
|
|
40
|
+
+----------------+--------------------+---------------------+------------------+---------------------------------------------+
|
|
41
|
+
| ``@pythonjob`` | Remote | Python native | ✅ Yes | Remote jobs with file handling |
|
|
42
|
+
+----------------+--------------------+---------------------+------------------+---------------------------------------------+
|
|
43
|
+
|
|
44
|
+
.. toctree::
|
|
45
|
+
:maxdepth: 1
|
|
46
|
+
:caption: Contents:
|
|
47
|
+
:hidden:
|
|
48
|
+
|
|
49
|
+
installation
|
|
50
|
+
autogen/pythonjob
|
|
51
|
+
autogen/pyfunction
|
|
52
|
+
tutorial/index
|