diff --git a/src/ansys/dpf/core/__init__.py b/src/ansys/dpf/core/__init__.py index 1c885d1d70..89711fcf5f 100644 --- a/src/ansys/dpf/core/__init__.py +++ b/src/ansys/dpf/core/__init__.py @@ -118,6 +118,7 @@ CustomTypeFieldsCollection:type = _CollectionFactory(CustomTypeField) GenericDataContainersCollection:type = _CollectionFactory(GenericDataContainer) StringFieldsCollection:type = _CollectionFactory(StringField) +OperatorsCollection: type = _CollectionFactory(Operator) AnyCollection:type = _Collection # for matplotlib diff --git a/src/ansys/dpf/core/custom_container_base.py b/src/ansys/dpf/core/custom_container_base.py new file mode 100644 index 0000000000..282bea71c7 --- /dev/null +++ b/src/ansys/dpf/core/custom_container_base.py @@ -0,0 +1,28 @@ +# Copyright (C) 2020 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ansys.dpf.core.generic_data_container import GenericDataContainer + + +class CustomContainerBase: + def __init__(self, container: GenericDataContainer) -> None: + self._container = container diff --git a/src/ansys/dpf/core/dpf_operator.py b/src/ansys/dpf/core/dpf_operator.py index ca83d61667..ca3e6a7a94 100644 --- a/src/ansys/dpf/core/dpf_operator.py +++ b/src/ansys/dpf/core/dpf_operator.py @@ -711,8 +711,10 @@ def default_config(name, server=None): def __del__(self): try: - if self._internal_obj is not None: - self._deleter_func[0](self._deleter_func[1](self)) + if hasattr(self, "_deleter_func"): + obj = self._deleter_func[1](self) + if obj is not None: + self._deleter_func[0](obj) except: warnings.warn(traceback.format_exc()) diff --git a/src/ansys/dpf/core/helpers/utils.py b/src/ansys/dpf/core/helpers/utils.py index 395f4b4ea5..9f9a7973e7 100644 --- a/src/ansys/dpf/core/helpers/utils.py +++ b/src/ansys/dpf/core/helpers/utils.py @@ -48,3 +48,18 @@ def _sort_supported_kwargs(bound_method, **kwargs): warnings.warn(txt) # Return the accepted arguments return kwargs_in + + +def indent(text, subsequent_indent="", initial_indent=None): + if initial_indent is None: + initial_indent = subsequent_indent + + if not isinstance(text, str): + text = str(text) + + lines = text.rstrip().splitlines() + indented_lines = [ + f"{initial_indent if index == 0 else subsequent_indent}{line}" for (index, line) in enumerate(lines) + ] + + return "\n".join(indented_lines) diff --git a/src/ansys/dpf/core/workflow.py b/src/ansys/dpf/core/workflow.py index d582f5c176..96a061b9bd 100644 --- a/src/ansys/dpf/core/workflow.py +++ b/src/ansys/dpf/core/workflow.py @@ -43,6 +43,7 @@ server_meet_version_and_raise, ) from ansys.dpf.core import server as server_module +from ansys.dpf.core.workflow_data.workflow_data import WorkflowData from ansys.dpf.gate import ( workflow_abstract_api, workflow_grpcapi, @@ -953,6 +954,13 @@ def to_graphviz(self, path: Union[os.PathLike, str]): """Saves the workflow to a GraphViz file.""" return self._api.work_flow_export_graphviz(self, str(path)) + def get_data(self): + workflow_to_workflow_data_op = dpf_operator.Operator("workflow_to_workflow_data") + workflow_to_workflow_data_op.inputs.workflow.connect(self) + workflow_data_container = workflow_to_workflow_data_op.outputs.workflow_data() + + return WorkflowData(workflow_data_container) + def __del__(self): try: if hasattr(self, "_internal_obj"): diff --git a/src/ansys/dpf/core/workflow_data/data_connection.py b/src/ansys/dpf/core/workflow_data/data_connection.py new file mode 100644 index 0000000000..fa89b7af27 --- /dev/null +++ b/src/ansys/dpf/core/workflow_data/data_connection.py @@ -0,0 +1,91 @@ +# Copyright (C) 2020 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ansys.dpf.core.custom_container_base import CustomContainerBase + + +class DataConnection(CustomContainerBase): + def __init__(self, container): + super().__init__(container) + self._source_data = None + self._target_operator = None + self._target_pin_id = None + + @property + def source_data(self): + if self._source_data is None: + self._source_data = self._container.get_property("source_data") + + return self._source_data + + @property + def target_operator(self): + from ansys.dpf.core.dpf_operator import Operator + + if self._target_operator is None: + self._target_operator = self._container.get_property( + "target_operator", Operator) + + return self._target_operator + + @property + def target_pin_id(self): + if self._target_pin_id is None: + self._target_pin_id = self._container.get_property( + "target_pin_id", int) + + return self._target_pin_id + + def __str__(self): + from ansys.dpf.core.helpers.utils import indent + + indents = " " + return ( + "DataConnection with properties:\n" + " - source_data:\n" + f"{indent(self.source_data, indents)}\n" + " - target_operator:\n" + f"{indent(self.target_operator.name, indents)}\n" + " - target_pin_id:\n" + f"{indent(self.target_pin_id, indents)}\n" + ) + + +class DataConnectionsCollection: + def __init__(self, collection): + self._collection = collection + + def __len__(self): + return len(self._collection) + + def __getitem__(self, index): + return DataConnection(self._collection[index]) + + def __iter__(self): + for i in range(len(self)): + yield self[i] + + def __str__(self): + from ansys.dpf.core.helpers.utils import indent + + indents = (" ", " - ") + return f"{"\n".join([indent(data_connection, *indents) for data_connection in self])}\n" diff --git a/src/ansys/dpf/core/workflow_data/exposed_pin.py b/src/ansys/dpf/core/workflow_data/exposed_pin.py new file mode 100644 index 0000000000..330ab02cf7 --- /dev/null +++ b/src/ansys/dpf/core/workflow_data/exposed_pin.py @@ -0,0 +1,92 @@ +# Copyright (C) 2020 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ansys.dpf.core.custom_container_base import CustomContainerBase + + +class ExposedPin(CustomContainerBase): + def __init__(self, container): + super().__init__(container) + self._name = None + self._operator = None + self._pin_id = None + + @property + def name(self): + if self._name is None: + self._name = self._container.get_property("name", str) + + return self._name + + @property + def operator(self): + from ansys.dpf.core.dpf_operator import Operator + + if self._operator is None: + self._operator = self._container.get_property("operator", Operator) + + return self._operator + + @property + def pin_id(self): + if self._pin_id is None: + self._pin_id = self._container.get_property("pin_id", int) + + return self._pin_id + + def __str__(self): + from ansys.dpf.core.helpers.utils import indent + + indents = " " + return ( + "ExposedPin with properties:\n" + " - name:\n" + f"{indent(self.name, indents)}\n" + " - operator:\n" + f"{indent(self.operator.name, indents)}\n" + " - pin_id:\n" + f"{indent(self.pin_id, indents)}\n" + ) + + def __repr__(self) -> str: + return self.__str__() + + +class ExposedPinsCollection: + def __init__(self, collection): + self._collection = collection + + def __len__(self): + return len(self._collection) + + def __getitem__(self, index): + return ExposedPin(self._collection[index]) + + def __iter__(self): + for i in range(len(self)): + yield self[i] + + def __str__(self): + from ansys.dpf.core.helpers.utils import indent + + indents = (" ", " - ") + return f"{"\n".join([indent(exposed_pin, *indents) for exposed_pin in self])}\n" diff --git a/src/ansys/dpf/core/workflow_data/operator_connection.py b/src/ansys/dpf/core/workflow_data/operator_connection.py new file mode 100644 index 0000000000..1e78b1c1ea --- /dev/null +++ b/src/ansys/dpf/core/workflow_data/operator_connection.py @@ -0,0 +1,105 @@ +# Copyright (C) 2020 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ansys.dpf.core.custom_container_base import CustomContainerBase + + +class OperatorConnection(CustomContainerBase): + def __init__(self, container): + super().__init__(container) + self._source_operator = None + self._source_pin_id = None + self._target_operator = None + self._target_pin_id = None + + @property + def source_operator(self): + from ansys.dpf.core.dpf_operator import Operator + + if self._source_operator is None: + self._source_operator = self._container.get_property( + "source_operator", Operator) + + return self._source_operator + + @property + def source_pin_id(self): + if self._source_pin_id is None: + self._source_pin_id = self._container.get_property( + "source_pin_id", int) + + return self._source_pin_id + + @property + def target_operator(self): + from ansys.dpf.core.dpf_operator import Operator + + if self._target_operator is None: + self._target_operator = self._container.get_property( + "target_operator", Operator) + + return self._target_operator + + @property + def target_pin_id(self): + if self._target_pin_id is None: + self._target_pin_id = self._container.get_property( + "target_pin_id", int) + + return self._target_pin_id + + def __str__(self): + from ansys.dpf.core.helpers.utils import indent + + indents = " " + return ( + "OperatorConnection with properties:\n" + " - source_operator:\n" + f"{indent(self.source_operator.name, indents)}\n" + " - source_pin_id:\n" + f"{indent(self.source_pin_id, indents)}\n" + " - target_operator:\n" + f"{indent(self.target_operator.name, indents)}\n" + " - target_pin_id:\n" + f"{indent(self.target_pin_id, indents)}\n" + ) + + +class OperatorConnectionsCollection: + def __init__(self, collection): + self._collection = collection + + def __len__(self): + return len(self._collection) + + def __getitem__(self, index): + return OperatorConnection(self._collection[index]) + + def __iter__(self): + for i in range(len(self)): + yield self[i] + + def __str__(self): + from ansys.dpf.core.helpers.utils import indent + + indents = (" ", " - ") + return f"{"\n".join([indent(operator_connection, *indents) for operator_connection in self])}\n" diff --git a/src/ansys/dpf/core/workflow_data/workflow_data.py b/src/ansys/dpf/core/workflow_data/workflow_data.py new file mode 100644 index 0000000000..6da6fb7fd1 --- /dev/null +++ b/src/ansys/dpf/core/workflow_data/workflow_data.py @@ -0,0 +1,106 @@ +# Copyright (C) 2020 - 2024 ANSYS, Inc. and/or its affiliates. +# SPDX-License-Identifier: MIT +# +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +from ansys.dpf.core.custom_container_base import CustomContainerBase + + +class WorkflowData(CustomContainerBase): + def __init__(self, *args) -> None: + super().__init__(*args) + + self._operators = None + self._operator_connections = None + self._data_connections = None + self._exposed_inputs = None + self._exposed_outputs = None + + @property + def operators(self): + from ansys.dpf.core import OperatorsCollection + + if self._operators is None: + self._operators = self._container.get_property( + "operators", OperatorsCollection) + + return self._operators + + @property + def operator_connections(self): + from ansys.dpf.core import GenericDataContainersCollection + from ansys.dpf.core.workflow_data.operator_connection import OperatorConnectionsCollection + + if self._operator_connections is None: + self._operator_connections = OperatorConnectionsCollection( + self._container.get_property("operator_connections", GenericDataContainersCollection)) + + return self._operator_connections + + @property + def data_connections(self): + from ansys.dpf.core import GenericDataContainersCollection + from ansys.dpf.core.workflow_data.data_connection import DataConnectionsCollection + + if self._data_connections is None: + self._data_connections = DataConnectionsCollection( + self._container.get_property("data_connections", GenericDataContainersCollection)) + + return self._data_connections + + @property + def exposed_inputs(self): + from ansys.dpf.core import GenericDataContainersCollection + from ansys.dpf.core.workflow_data.exposed_pin import ExposedPinsCollection + + if self._exposed_inputs is None: + self._exposed_inputs = ExposedPinsCollection(self._container.get_property( + "exposed_inputs", GenericDataContainersCollection)) + + return self._exposed_inputs + + @property + def exposed_outputs(self): + from ansys.dpf.core import GenericDataContainersCollection + from ansys.dpf.core.workflow_data.exposed_pin import ExposedPinsCollection + + if self._exposed_outputs is None: + self._exposed_outputs = ExposedPinsCollection(self._container.get_property( + "exposed_outputs", GenericDataContainersCollection)) + + return self._exposed_outputs + + def __str__(self): + from ansys.dpf.core.helpers.utils import indent + + indents = (" ", " - ") + return ( + "WorkflowData with properties:\n" + f" - operators (len: {len(self.operators)}):\n" + f"{"\n".join([indent(operator.name, *indents) for operator in self.operators])}\n" + f" - operator_connections (len: {len(self.operator_connections)}):\n" + f"{indent(self.operator_connections, " ")}\n" + f" - data_connections (len: {len(self.data_connections)}):\n" + f"{indent(self.data_connections, " ")}\n" + f" - exposed_inputs (len: {len(self.exposed_inputs)}):\n" + f"{indent(self.exposed_inputs, " ")}\n" + f" - exposed_outputs (len: {len(self.exposed_outputs)}):\n" + f"{indent(self.exposed_outputs, " ")}\n" + )