Handling complex workflows using the workflow builders¶
The WorkflowBuilder and MultipleWorkflowBuilder classes are designed to manage extended workflows consisting of a conjunction of different AiiDA processes. Input-parameters and logical dependencies can be defined in a protocol which is given as a specifically formated python-dictionary or written in a yaml-file.
The objects can determine the current state (accomplished tasks) of a workflow for a given AiiDA parent-node and generate the input-parameters for the next task of the workflow.
While the WorkflowBuilder class controls the workflow for a single parent-node the MultipleWorkflowBuilder class consists of several WorkflowBuilder’s instances and can handle multiple parent-nodes using the same workflow protocol for each.
In the following we use the WorkflowBuilder class to examplify the user interface, however, setting the workflow protocol and input parameters works the same for both objects. First, the appropriate AiiDA profile is loaded and an instance of the class is created:
[1]:
from aim2dat.aiida_workflows.workflow_builder import WorkflowBuilder
import aiida
aiida.load_profile("tests")
wf_builder = WorkflowBuilder()
---------------------------------------------------------------------------
ProfileConfigurationError Traceback (most recent call last)
Cell In[1], line 4
1 from aim2dat.aiida_workflows.workflow_builder import WorkflowBuilder
2 import aiida
----> 4 aiida.load_profile("tests")
5 wf_builder = WorkflowBuilder()
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/manage/configuration/__init__.py:165, in load_profile(profile, allow_switch)
152 """Load a global profile, unloading any previously loaded profile.
153
154 .. note:: if a profile is already loaded and no explicit profile is specified, nothing will be done
(...)
161 if another profile has already been loaded and allow_switch is False
162 """
163 from aiida.manage import get_manager
--> 165 return get_manager().load_profile(profile, allow_switch)
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/manage/manager.py:117, in Manager.load_profile(self, profile, allow_switch)
114 return self._profile
116 if profile is None or isinstance(profile, str):
--> 117 profile = self.get_config().get_profile(profile)
118 elif not isinstance(profile, Profile):
119 raise TypeError(f'profile must be None, a string, or a Profile instance, got: {type(profile)}')
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/manage/configuration/config.py:453, in Config.get_profile(self, name)
450 if not name:
451 name = self.default_profile_name
--> 453 self.validate_profile(name)
455 return self._profiles[name]
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/manage/configuration/config.py:435, in Config.validate_profile(self, name)
432 from aiida.common import exceptions
434 if name not in self.profile_names:
--> 435 raise exceptions.ProfileConfigurationError(f'profile `{name}` does not exist')
ProfileConfigurationError: profile `tests` does not exist
The workflow protocol¶
The workflow protocols consists of three different sections:
tasks: Is a dictionary containing the details and dependencies for the tasks that can be run with the current workflow.
general_input: defines the preset parameters shared by all work chains.
user_input: defines input parameters that are set by the user.
All predefined protocols are found in the folder: “aim2dat/aim2dat/aiida_workflows/protocols/”. The workflow protocols support versions, which the suffix "_v*.*" (* denotes an integer number) a specific protocol version can be chosen. If the suffix is omitted the latest protocol version is chosen. At the moment the following protocols are implemented:
Protocol |
Latest version |
Description |
|---|---|---|
arithmetic-testing |
v1.1 |
Protocol for testing purposes. |
seekpath-standard |
v1.0 |
Protocol for a seek-path analysis. |
cp2k-crystal-mofs |
v2.0 |
Protocol to run DFT calculations on MOFs using CP2K. |
cp2k-crystal-preopt |
v3.1 |
Protocol to pre-optimize inorganic crystals with loose parameters using CP2K. |
cp2k-crystal-standard |
v3.2 |
Standard protocol to run DFT calculations on inorganic crystals using CP2K (doi:10.1063/5.0082710). |
cp2k-crystal-standard-keep-angles |
v1.1 |
Standard protocol for inorganic crystals but constraining lattice parameters. |
cp2k-surface-standard |
v1.0 |
Protocol to run the surface workflow using CP2K. |
cp2k-crystal-testing |
v2.0 |
Protocol to test the workflow for inorganic crystals with loose parameters using CP2K. |
cp2k-surface-testing |
v1.0 |
Protocol to test the surface workflow with loose parameters using CP2K. |
The protocol can be loaded by using the property protocol <aim2dat.aiida_workflows.workflow_builder.WorkflowBuilder.protocol>` (same property for both classes), in this case we use a test protocol that is merely based on the arithmetic add_multiply calcfunction. In general workflows can combine any kind of AiiDA processes defining input-parameters and dependencies.
[2]:
wf_builder.protocol = "arithmetic-testing"
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[2], line 1
----> 1 wf_builder.protocol = "arithmetic-testing"
NameError: name 'wf_builder' is not defined
All tasks of the workflow can be printed with the property tasks <aim2dat.aiida_workflows.workflow_builder.WorkflowBuilder.tasks>`:
[3]:
wf_builder.tasks
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[3], line 1
----> 1 wf_builder.tasks
NameError: name 'wf_builder' is not defined
Setting up the input parameters and parent node¶
The provenance of the workflow is defined via the parent node, it is input for all initial tasks of the workflow. Here, we create a new aiida node without history and pass it to the builder-object:
[4]:
from aiida.plugins import DataFactory
Float = DataFactory("core.float")
parent_node = Float(4)
wf_builder.parent_node = parent_node
---------------------------------------------------------------------------
ConfigurationError Traceback (most recent call last)
Cell In[4], line 4
1 from aiida.plugins import DataFactory
3 Float = DataFactory("core.float")
----> 4 parent_node = Float(4)
6 wf_builder.parent_node = parent_node
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/data/base.py:32, in BaseType.__init__(self, value, **kwargs)
29 except AttributeError:
30 raise RuntimeError('Derived class must define the `_type` class member')
---> 32 super().__init__(**kwargs)
34 self.value = value or self._type()
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/data/data.py:49, in Data.__init__(self, source, *args, **kwargs)
47 def __init__(self, *args, source=None, **kwargs):
48 """Construct a new instance, setting the ``source`` attribute if provided as a keyword argument."""
---> 49 super().__init__(*args, **kwargs)
50 if source is not None:
51 self.source = source
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/node.py:194, in Node.__init__(self, backend, user, computer, **kwargs)
187 def __init__(
188 self,
189 backend: Optional['StorageBackend'] = None,
(...)
192 **kwargs: Any,
193 ) -> None:
--> 194 backend = backend or get_manager().get_profile_storage()
196 if computer and not computer.is_stored:
197 raise ValueError('the computer is not stored')
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/manage/manager.py:254, in Manager.get_profile_storage(self)
252 profile = self.get_profile()
253 if profile is None:
--> 254 raise ConfigurationError(
255 'Could not determine the current profile. Consider loading a profile using `aiida.load_profile()`.'
256 )
258 # request access to the profile (for example, if it is being used by a maintenance operation)
259 ProfileAccessManager(profile).request_access()
ConfigurationError: Could not determine the current profile. Consider loading a profile using `aiida.load_profile()`.
And we can set additional input-parameters (parameters can be given as python types or AiiDA nodes). A dash and subsequent greater than sign (->) highlight an individual input parameter defined for just one task of the workflow.
[5]:
wf_builder.set_user_input("y", 5)
wf_builder.set_user_input("y->task_4.1", 11.0)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[5], line 1
----> 1 wf_builder.set_user_input("y", 5)
2 wf_builder.set_user_input("y->task_4.1", 11.0)
NameError: name 'wf_builder' is not defined
Checking the workflow state¶
At any time we can check the status of the workflow via the method determine_workflow_state:
[6]:
wf_builder.determine_workflow_state()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[6], line 1
----> 1 wf_builder.determine_workflow_state()
NameError: name 'wf_builder' is not defined
The builder checks whether any work chains with matching input parameters have been performed on the structure. In this case there are no processes run that conform the workflow protocol.
Executing workflow tasks¶
The input for the initial task can be created using the ‘builder’-method of the AiiDA work chain or calculation or a dictionary of input-parameters for AiiDA calcfunctions:
[7]:
from aiida.engine import run
wc_builder = wf_builder.generate_inputs("task_1.1")
result = run(**wc_builder)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[7], line 3
1 from aiida.engine import run
----> 3 wc_builder = wf_builder.generate_inputs("task_1.1")
4 result = run(**wc_builder)
NameError: name 'wf_builder' is not defined
If we check the workflow again, we see that the task ‘task_1.1’ is accomplished and we can continue with the next task:
[8]:
wf_builder.determine_workflow_state()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[8], line 1
----> 1 wf_builder.determine_workflow_state()
NameError: name 'wf_builder' is not defined
Alternatively, we can run or submit the task straightaway using the methods :meth:run_task <aim2dat.aiida_workflows.workflow_builder.WorkflowBuilder.run_task> or :meth:submit_task <aim2dat.aiida_workflows.workflow_builder.WorkflowBuilder.submit_task>. The difference between the two methods is that the first uses AiiDA’s run method which starts the process in the foreground and blocks the interface while the latter uses AiiDA’s submit method which passes the process to the daemon
that is running in the background.
[9]:
wf_builder.run_task("task_1.2")
wf_builder.run_task("task_1.3")
wf_builder.run_task("task_2.1")
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[9], line 1
----> 1 wf_builder.run_task("task_1.2")
2 wf_builder.run_task("task_1.3")
3 wf_builder.run_task("task_2.1")
NameError: name 'wf_builder' is not defined
Visualizing the provenance graph of the workflow¶
Using the AiiDA built-in features the provenance graph of the workflow can be plotted:
[10]:
wf_builder.graph_attributes = {"graph_attr": {"size": "6!,6"}}
graph = wf_builder.generate_provenance_graph()
graph.graphviz
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
Cell In[10], line 1
----> 1 wf_builder.graph_attributes = {"graph_attr": {"size": "6!,6"}}
2 graph = wf_builder.generate_provenance_graph()
3 graph.graphviz
NameError: name 'wf_builder' is not defined
The MultipleWorkflowBuilder class¶
The main difference between the WorkflowBuilder and the MultipleWorkflowBuilder class is that the latter hosts a list of parent-nodes and WorkflowBuilder instances:
[11]:
from aim2dat.aiida_workflows.workflow_builder import MultipleWorkflowBuilder
mwf_builder = MultipleWorkflowBuilder()
mwf_builder.protocol = "arithmetic-testing"
for n in range(0, 5):
mwf_builder.add_parent_node(Float(n))
---------------------------------------------------------------------------
ConfigurationError Traceback (most recent call last)
Cell In[11], line 4
1 from aim2dat.aiida_workflows.workflow_builder import MultipleWorkflowBuilder
3 mwf_builder = MultipleWorkflowBuilder()
----> 4 mwf_builder.protocol = "arithmetic-testing"
6 for n in range(0, 5):
7 mwf_builder.add_parent_node(Float(n))
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aim2dat/aiida_workflows/workflow_builder.py:157, in _BaseWorkflowBuilder.protocol(self, value)
155 if input_cat in protocol:
156 for input_p, input_details in protocol[input_cat].items():
--> 157 _validate_input_details(input_details)
158 input_p_sp = input_p.split("->")
159 if len(input_p_sp) > 1:
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aim2dat/aiida_workflows/workflow_builder.py:62, in _validate_input_details(input_details)
60 input_details["aiida_node"] = False
61 if input_details["aiida_node"] and "value" in input_details:
---> 62 input_details["value"] = create_aiida_node(input_details["value"])
63 else:
64 input_details["value"] = None
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aim2dat/aiida_workflows/utils.py:272, in create_aiida_node(value, node_type)
270 aiida_node = aiida_orm.Int(value)
271 elif node_type == "float" or (check_node_type and isinstance(value, float)):
--> 272 aiida_node = aiida_orm.Float(value)
273 elif node_type == "str" or (check_node_type and isinstance(value, str)):
274 aiida_node = aiida_orm.Str(value)
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/data/base.py:32, in BaseType.__init__(self, value, **kwargs)
29 except AttributeError:
30 raise RuntimeError('Derived class must define the `_type` class member')
---> 32 super().__init__(**kwargs)
34 self.value = value or self._type()
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/data/data.py:49, in Data.__init__(self, source, *args, **kwargs)
47 def __init__(self, *args, source=None, **kwargs):
48 """Construct a new instance, setting the ``source`` attribute if provided as a keyword argument."""
---> 49 super().__init__(*args, **kwargs)
50 if source is not None:
51 self.source = source
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/node.py:194, in Node.__init__(self, backend, user, computer, **kwargs)
187 def __init__(
188 self,
189 backend: Optional['StorageBackend'] = None,
(...)
192 **kwargs: Any,
193 ) -> None:
--> 194 backend = backend or get_manager().get_profile_storage()
196 if computer and not computer.is_stored:
197 raise ValueError('the computer is not stored')
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/manage/manager.py:254, in Manager.get_profile_storage(self)
252 profile = self.get_profile()
253 if profile is None:
--> 254 raise ConfigurationError(
255 'Could not determine the current profile. Consider loading a profile using `aiida.load_profile()`.'
256 )
258 # request access to the profile (for example, if it is being used by a maintenance operation)
259 ProfileAccessManager(profile).request_access()
ConfigurationError: Could not determine the current profile. Consider loading a profile using `aiida.load_profile()`.
The user input parameters can be set likewise to the WorkflowBuilder class:
[12]:
mwf_builder.set_user_input("y", 2.0)
mwf_builder.set_user_input("y->task_4.1", 3.0)
---------------------------------------------------------------------------
ConfigurationError Traceback (most recent call last)
Cell In[12], line 1
----> 1 mwf_builder.set_user_input("y", 2.0)
2 mwf_builder.set_user_input("y->task_4.1", 3.0)
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aim2dat/aiida_workflows/workflow_builder.py:1165, in MultipleWorkflowBuilder.set_user_input(self, input_port, value)
1154 def set_user_input(self, input_port, value):
1155 """
1156 Set a user input parameter of the workflow for all parent nodes.
1157
(...)
1163 Value of the input parameter.
1164 """
-> 1165 super().set_user_input(input_port, value)
1166 for wf_builder in self._wf_builders:
1167 wf_builder._user_input = self._user_input.copy()
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aim2dat/aiida_workflows/workflow_builder.py:324, in _BaseWorkflowBuilder.set_user_input(self, input_port, value)
322 input_details = self._user_input[input_port]
323 if input_details["aiida_node"] and not hasattr(value, "uuid"):
--> 324 value = create_aiida_node(value)
325 if input_details["validation"] is not None:
326 self._validate_work_chain_input(input_port, value, input_details["validation"])
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aim2dat/aiida_workflows/utils.py:272, in create_aiida_node(value, node_type)
270 aiida_node = aiida_orm.Int(value)
271 elif node_type == "float" or (check_node_type and isinstance(value, float)):
--> 272 aiida_node = aiida_orm.Float(value)
273 elif node_type == "str" or (check_node_type and isinstance(value, str)):
274 aiida_node = aiida_orm.Str(value)
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/data/base.py:32, in BaseType.__init__(self, value, **kwargs)
29 except AttributeError:
30 raise RuntimeError('Derived class must define the `_type` class member')
---> 32 super().__init__(**kwargs)
34 self.value = value or self._type()
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/data/data.py:49, in Data.__init__(self, source, *args, **kwargs)
47 def __init__(self, *args, source=None, **kwargs):
48 """Construct a new instance, setting the ``source`` attribute if provided as a keyword argument."""
---> 49 super().__init__(*args, **kwargs)
50 if source is not None:
51 self.source = source
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/node.py:194, in Node.__init__(self, backend, user, computer, **kwargs)
187 def __init__(
188 self,
189 backend: Optional['StorageBackend'] = None,
(...)
192 **kwargs: Any,
193 ) -> None:
--> 194 backend = backend or get_manager().get_profile_storage()
196 if computer and not computer.is_stored:
197 raise ValueError('the computer is not stored')
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/manage/manager.py:254, in Manager.get_profile_storage(self)
252 profile = self.get_profile()
253 if profile is None:
--> 254 raise ConfigurationError(
255 'Could not determine the current profile. Consider loading a profile using `aiida.load_profile()`.'
256 )
258 # request access to the profile (for example, if it is being used by a maintenance operation)
259 ProfileAccessManager(profile).request_access()
ConfigurationError: Could not determine the current profile. Consider loading a profile using `aiida.load_profile()`.
The status information as well as process nodes and workflow results is therefore given as pandas dataframes:
[13]:
mwf_builder.return_workflow_states()
[13]:
Different tasks can be started for all parent-nodes within one function call via the :meth:run_task <aim2dat.aiida_workflows.workflow_builder.MultipleWorkflowBuilder.run_task> or :meth:submit_task <aim2dat.aiida_workflows.workflow_builder.MultipleWorkflowBuilder.submit_task> functions:
[14]:
mwf_builder.run_task("task_1.1")
mwf_builder.return_workflow_states()
[14]:
The tasks can be started for a subset of the parent-nodes by using the interval parameter:
[15]:
mwf_builder.run_task("task_1.2", interval=[0, 3])
mwf_builder.return_workflow_states()
[15]:
Several tasks can be started consecutively by setting a task queue:
[16]:
mwf_builder.add_to_task_queue("task_1.2", run_type="run")
mwf_builder.add_to_task_queue("task_1.3", run_type="run")
mwf_builder.add_to_task_queue("task_2.1", run_type="run")
mwf_builder.add_to_task_queue("task_2.2", run_type="run")
mwf_builder.add_to_task_queue("task_3.1", run_type="run")
mwf_builder.add_to_task_queue("task_4.1", run_type="run")
mwf_builder.execute_task_queue()
Additional information can be returned via the functions `return_process_nodes <aim2dat.aiida_workflows.workflow_builder.MultipleWorkflowBuilder.return_process_nodes>`__ and `return_results <aim2dat.aiida_workflows.workflow_builder.MultipleWorkflowBuilder.return_results>`__:
[17]:
mwf_builder.return_process_nodes()
[17]:
[18]:
mwf_builder.return_results()
[18]:
Storing and loading workflows¶
Both, the WorkflowBuilder and the MultipleWorkflowBuilder have the methods to_file and from_file implemented which allows to store the workflow protocol and process nodes in a yaml-file.
This feature can be also used to share the workflow information by exporting/importing the process nodes as well (see the AiiDA documentation for more details).
[19]:
mwf_builder.to_file("test_workflow.yaml")
mwf_builder2 = MultipleWorkflowBuilder.from_file("test_workflow.yaml")
mwf_builder2.return_workflow_states()
---------------------------------------------------------------------------
ConfigurationError Traceback (most recent call last)
Cell In[19], line 3
1 mwf_builder.to_file("test_workflow.yaml")
----> 3 mwf_builder2 = MultipleWorkflowBuilder.from_file("test_workflow.yaml")
4 mwf_builder2.return_workflow_states()
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aim2dat/aiida_workflows/workflow_builder.py:273, in _BaseWorkflowBuilder.from_file(cls, file_name)
271 content = load_yaml_file(file_name)
272 wf_builder = cls()
--> 273 wf_builder.protocol = content["protocol"]
274 if "parent_node" in content:
275 if hasattr(wf_builder, "_wf_builders"):
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aim2dat/aiida_workflows/workflow_builder.py:157, in _BaseWorkflowBuilder.protocol(self, value)
155 if input_cat in protocol:
156 for input_p, input_details in protocol[input_cat].items():
--> 157 _validate_input_details(input_details)
158 input_p_sp = input_p.split("->")
159 if len(input_p_sp) > 1:
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aim2dat/aiida_workflows/workflow_builder.py:62, in _validate_input_details(input_details)
60 input_details["aiida_node"] = False
61 if input_details["aiida_node"] and "value" in input_details:
---> 62 input_details["value"] = create_aiida_node(input_details["value"])
63 else:
64 input_details["value"] = None
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aim2dat/aiida_workflows/utils.py:272, in create_aiida_node(value, node_type)
270 aiida_node = aiida_orm.Int(value)
271 elif node_type == "float" or (check_node_type and isinstance(value, float)):
--> 272 aiida_node = aiida_orm.Float(value)
273 elif node_type == "str" or (check_node_type and isinstance(value, str)):
274 aiida_node = aiida_orm.Str(value)
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/data/base.py:32, in BaseType.__init__(self, value, **kwargs)
29 except AttributeError:
30 raise RuntimeError('Derived class must define the `_type` class member')
---> 32 super().__init__(**kwargs)
34 self.value = value or self._type()
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/data/data.py:49, in Data.__init__(self, source, *args, **kwargs)
47 def __init__(self, *args, source=None, **kwargs):
48 """Construct a new instance, setting the ``source`` attribute if provided as a keyword argument."""
---> 49 super().__init__(*args, **kwargs)
50 if source is not None:
51 self.source = source
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/orm/nodes/node.py:194, in Node.__init__(self, backend, user, computer, **kwargs)
187 def __init__(
188 self,
189 backend: Optional['StorageBackend'] = None,
(...)
192 **kwargs: Any,
193 ) -> None:
--> 194 backend = backend or get_manager().get_profile_storage()
196 if computer and not computer.is_stored:
197 raise ValueError('the computer is not stored')
File ~/checkouts/readthedocs.org/user_builds/aim2dat/envs/latest/lib/python3.10/site-packages/aiida/manage/manager.py:254, in Manager.get_profile_storage(self)
252 profile = self.get_profile()
253 if profile is None:
--> 254 raise ConfigurationError(
255 'Could not determine the current profile. Consider loading a profile using `aiida.load_profile()`.'
256 )
258 # request access to the profile (for example, if it is being used by a maintenance operation)
259 ProfileAccessManager(profile).request_access()
ConfigurationError: Could not determine the current profile. Consider loading a profile using `aiida.load_profile()`.