|
4 | 4 | Custom operators |
5 | 5 | ================ |
6 | 6 |
|
7 | | -In Ansys 2023 R1 and later, you can create custom operators in CPython. Creating custom operators |
8 | | -consists of wrapping Python routines in a DPF-compliant way so that you can access them in the same way |
9 | | -as you access the native operators in the :class:`Operator <ansys.dpf.core.dpf_operator.Operator>` class in |
10 | | -PyDPF-Core or in any supported client API. |
| 7 | +This tutorial shows the basics of creating a custom operator in Python and loading it ont a server for use. |
11 | 8 |
|
12 | | -With support for custom operators, PyDPF-Core becomes a development tool offering: |
| 9 | +.. note: |
| 10 | + You can create custom operators in CPython using PyDPF-Core for use with DPF in Ansys 2023 R1 and later. |
13 | 11 |
|
14 | | -- **Accessibility:** A simple script can define a basic operator plugin. |
| 12 | +It first presents how to :ref:`create a custom DPF operator<tutorials_custom_operators_and_plugins_custom_operator_create_custom_operator>` |
| 13 | +in Python using PyDPF-Core. |
15 | 14 |
|
16 | | -- **Componentization:** Operators with similar applications can be grouped in Python plug-in packages. |
| 15 | +It then shows how to :ref:`make a plugin<tutorials_custom_operators_and_plugins_custom_operator_create_custom_plugin>` |
| 16 | +out of this single operator. |
17 | 17 |
|
18 | | -- **Easy distribution:** Standard Python tools can be used to package, upload, and download custom operators. |
| 18 | +The next step is to :ref:`load the plugin on the server<tutorials_custom_operators_and_plugins_custom_operator_load_the_plugin>` to record its operators. |
19 | 19 |
|
20 | | -- **Dependency management:** Third-party Python modules can be added to the Python package. |
| 20 | +The final step is to instantiate the custom operator from the client API and :ref:`use it<tutorials_custom_operators_and_plugins_custom_operator_use_the_custom_operator>`. |
21 | 21 |
|
22 | | -- **Reusability:** A documented and packaged operator can be reused in an infinite number of workflows. |
| 22 | +.. note: |
| 23 | + In this tutorial the DPF client API used is PyDPF-Core but, once recorded on the server, |
| 24 | + you can call the operators of the plugin using any of the DPF client APIs |
| 25 | + (C++, CPython, IronPython), as you would any other operator. |
23 | 26 |
|
24 | | -- **Remotable and parallel computing:** Native DPF capabilities are inherited by custom operators. |
25 | 27 |
|
26 | | -The only prerequisite for creating custom operators is to be familiar with native operators. |
27 | | -For more information, see :ref:`ref_user_guide_operators`. |
| 28 | +:jupyter-download-script:`Download tutorial as Python script<custom_operators>` |
| 29 | +:jupyter-download-notebook:`Download tutorial as Jupyter notebook<custom_operators>` |
28 | 30 |
|
29 | | -Install module |
30 | | --------------- |
31 | 31 |
|
32 | | -Once an Ansys-unified installation is complete, you must install the ``ansys-dpf-core`` module in the Ansys |
33 | | -installer's Python interpreter. |
| 32 | +.. _tutorials_custom_operators_and_plugins_custom_operator_create_custom_operator: |
34 | 33 |
|
35 | | -#. Download the script for you operating system: |
| 34 | +Create a custom operator |
| 35 | +------------------------ |
36 | 36 |
|
37 | | - - For Windows, download this :download:`PowerShell script </user_guide/tutorials/enriching_dpf_capabilities/install_ansys_dpf_core_in_ansys.ps1>`. |
38 | | - - For Linux, download this :download:`Shell script </user_guide/tutorials/enriching_dpf_capabilities/install_ansys_dpf_core_in_ansys.sh>` |
| 37 | +To create a custom DPF operator using PyDPF-Core, define a custom operator class inheriting from |
| 38 | +the :class:`CustomOperatorBase <ansys.dpf.core.custom_operator.CustomOperatorBase>` class in a dedicated Python file. |
39 | 39 |
|
40 | | -#. Run the downloaded script for installing with optional arguments: |
| 40 | +The following are sections of a file named `custom_operator_example.py`. |
41 | 41 |
|
42 | | - - ``-awp_root``: Path to the Ansys root installation folder. For example, the 2023 R1 installation folder ends |
43 | | - with ``Ansys Inc/v231``, and the default environment variable is ``AWP_ROOT231``. |
44 | | - - ``-pip_args``: Optional arguments to add to the ``pip`` command. For example, ``--extra-index-url`` or |
45 | | - ``--trusted-host``. |
| 42 | +First, create the custom operator class, with necessary imports and a first property to define the operator scripting name: |
46 | 43 |
|
47 | | -If you ever want to uninstall the ``ansys-dpf-core`` module from the Ansys installation, you can do so. |
| 44 | +.. literalinclude:: custom_operator_example.py |
| 45 | + :end-at: return "my_custom_operator" |
| 46 | + |
| 47 | +Next, set the `specification` property of your operator with: |
| 48 | +- a description of what the operator does |
| 49 | +- a dictionary for each input and output pin. This dictionary includes the name, a list of supported types, a description, |
| 50 | + and whether it is optional and/or ellipsis (meaning that the specification is valid for pins going from pin |
| 51 | + number *x* to infinity) |
| 52 | +- a list for operator properties, including name to use in the documentation and code generation and the |
| 53 | + operator category. The optional ``license`` property lets you define a required license to check out |
| 54 | + when running the operator. Set it equal to ``any_dpf_supported_increments`` to allow any license |
| 55 | + currently accepted by DPF (see :ref:`here<target_to_ansys_license_increments_list>`) |
| 56 | + |
| 57 | +.. literalinclude:: custom_operator_example.py |
| 58 | + :start-after: return "my_custom_operator" |
| 59 | + :end-at: return spec |
48 | 60 |
|
49 | | -#. Download the script for your operating system: |
| 61 | +Next, implement the operator behavior in its `run` method: |
| 62 | + |
| 63 | +.. literalinclude:: custom_operator_example.py |
| 64 | + :start-after: return spec |
| 65 | + :end-at: self.set_succeeded() |
50 | 66 |
|
51 | | - - For Windows, download this :download:`PowerShell script </user_guide/tutorials/enriching_dpf_capabilities/uninstall_ansys_dpf_core_in_ansys.ps1>`. |
52 | | - - For Linux, download this :download:`Shell script </user_guide/tutorials/enriching_dpf_capabilities/uninstall_ansys_dpf_core_in_ansys.sh>`. |
53 | | - |
54 | | -#. Run the downloaded script for uninstalling with the optional argument: |
| 67 | +The `CustomOperator` class is now ready for packaging into any DPF Python plugin. |
55 | 68 |
|
56 | | - - ``-awp_root``: Path to the Ansys root installation folder. For example, the 2023 R1 installation folder ends |
57 | | - with ``Ansys Inc/v231``, and the default environment variable is ``AWP_ROOT231``. |
| 69 | +.. _tutorials_custom_operators_and_plugins_custom_operator_create_custom_plugin: |
58 | 70 |
|
| 71 | +Package as a plugin |
| 72 | +------------------- |
59 | 73 |
|
60 | | -Create operators |
61 | | ----------------- |
| 74 | +You must package your custom operator as a `plugin`, |
| 75 | +which is what you can later load onto a running DPF server, |
| 76 | +or configure your installation to automatically load when starting a DPF server. |
62 | 77 |
|
63 | | -Creating a basic operator plugin consists of writing a single Python script. An operator implementation |
64 | | -derives from the :class:`CustomOperatorBase <ansys.dpf.core.custom_operator.CustomOperatorBase>` class and a call to |
65 | | -the :func:`record_operator() <ansys.dpf.core.custom_operator.record_operator>` method. |
| 78 | +A DPF plugin contains Python modules with declarations of custom Python operators such as seen above. |
| 79 | +However, it also has to define an entry-point for the DPF server to call, |
| 80 | +which records the operators of the plugin into the server registry of available operators. |
66 | 81 |
|
67 | | -This example script shows how you create a basic operator plugin: |
| 82 | +This is done by defining a function (DPF looks for a function named ``load_operators`` by default) |
| 83 | +somewhere in the plugin with signature ``*args`` and a call to the |
| 84 | +:func:`record_operator() <ansys.dpf.core.custom_operator.record_operator>` method for each custom operator. |
| 85 | + |
| 86 | +In this tutorial, the plugin is made of a single operator, in a single Python file. |
| 87 | +You can transform this single Python file into a DPF Python plugin very easily by adding |
| 88 | +``load_operators(*args)`` function with a call to the |
| 89 | +:func:`record_operator() <ansys.dpf.core.custom_operator.record_operator>` method at the end of the file. |
68 | 90 |
|
69 | 91 | .. literalinclude:: custom_operator_example.py |
| 92 | + :start-at: def load_operators(*args): |
70 | 93 |
|
| 94 | +PS: You can declare several custom operator classes in the same file, with as many calls to |
| 95 | +:func:`record_operator() <ansys.dpf.core.custom_operator.record_operator>` as necessary. |
71 | 96 |
|
72 | | -.. code-block:: |
| 97 | +.. _tutorials_custom_operators_and_plugins_custom_operator_load_the_plugin: |
73 | 98 |
|
74 | | - def load_operators(*args): |
75 | | - record_operator(CustomOperator, *args) |
| 99 | +Load the plugin |
| 100 | +--------------- |
76 | 101 |
|
| 102 | +First, start a server in gRPC mode, which is the only server type supported for custom Python plugins. |
77 | 103 |
|
78 | | -In the various properties for the class, you specify the following: |
| 104 | +.. jupyter-execute:: |
79 | 105 |
|
80 | | -- Name for the custom operator |
81 | | -- Description of what the operator does |
82 | | -- Dictionary for each input and output pin, which includes the name, a list of supported types, a description, |
83 | | - and whether it is optional and/or ellipsis (meaning that the specification is valid for pins going from pin |
84 | | - number *x* to infinity) |
85 | | -- List for operator properties, including name to use in the documentation and code generation and the |
86 | | - operator category. The optional ``license`` property allows to define a required license to check out |
87 | | - when running the operator. Set it equal to ``any_dpf_supported_increments`` to allow any license |
88 | | - currently accepted by DPF (see :ref:`here<target_to_ansys_license_increments_list>`) |
| 106 | + import ansys.dpf.core as dpf |
89 | 107 |
|
90 | | -For specific examples on writing operator plugins, see :ref:`python_operators`. |
| 108 | + # Python plugins are not supported in process. |
| 109 | + server = dpf.start_local_server(config=dpf.AvailableServerConfigs.GrpcServer) |
91 | 110 |
|
92 | | -Load the plug-in |
93 | | ----------------- |
94 | 111 |
|
95 | | -Once a custom operator is created, you can use the :func:`load_library() <ansys.dpf.core.core.load_library>` method to load it. |
| 112 | +With the server and custom plugin ready, use the :func:`load_library() <ansys.dpf.core.core.load_library>` method in a PyDPF-Core script to load it. |
96 | 113 |
|
97 | 114 | - The first argument is the path to the directory with the plugin. |
98 | | -- The second argument is ``py_<plugin>``, where <plugin> is the name identifying the plug-in (same name of the Python file). |
99 | | -- The third argument is the function name for recording operators. |
| 115 | +- The second argument is ``py_<plugin>``, where <plugin> is the name identifying the plugin (the name of the Python file for a single file). |
| 116 | +- The third argument is the name of the function in the plugin which records operators (``load_operators`` by default). |
100 | 117 |
|
101 | | -.. code:: |
| 118 | +.. jupyter-execute:: |
102 | 119 |
|
103 | 120 | dpf.load_library( |
104 | | - r"path/to/plugins", |
105 | | - "py_custom_plugin", #if the load_operators function is defined in path/to/plugins/custom_plugin.py |
106 | | - "load_operators") |
| 121 | + filename=r".", # Look into the current directory |
| 122 | + name="py_custom_operator_example", # Look for a Python file named 'custom_operator_example.py' |
| 123 | + symbol="load_operators", # Look for the entry-point where operators are recorded |
| 124 | + server=server, # Load the plugin on the server previously started |
| 125 | + generate_operators=False, # Do not generate the Python module for this operator |
| 126 | + ) |
| 127 | + |
| 128 | +.. _tutorials_custom_operators_and_plugins_custom_operator_use_the_custom_operator: |
| 129 | + |
| 130 | +Use the custom operator |
| 131 | +----------------------- |
| 132 | + |
| 133 | +Once the plugin is loaded, you can instantiate the custom operator based on its name. |
| 134 | + |
| 135 | +.. jupyter-execute:: |
107 | 136 |
|
108 | | -Use custom operators |
109 | | --------------------- |
| 137 | + my_custom_op = dpf.Operator(name="my_custom_operator") # as returned by the ``name`` property |
| 138 | + print(my_custom_op) |
110 | 139 |
|
111 | | -Once the plugin is loaded, you can instantiate the custom operator: |
| 140 | +Finally, run it as any other operator. |
112 | 141 |
|
113 | | -.. code:: |
| 142 | +.. jupyter-execute:: |
114 | 143 |
|
115 | | - new_operator = dpf.Operator("custom_operator") # if "custom_operator" is what is returned by the ``name`` property |
| 144 | + # Create a bogus field to use as input |
| 145 | + in_field = dpf.Field() |
| 146 | + # Give it a name |
| 147 | + in_field.name = "initial name" |
| 148 | + print(in_field) |
| 149 | + # Set it as input of the operator |
| 150 | + my_custom_op.inputs.input_0.connect(in_field) |
| 151 | + # Run the operator by requesting its output |
| 152 | + out_field = my_custom_op.outputs.output_0() |
| 153 | + print(out_field) |
116 | 154 |
|
117 | 155 | References |
118 | 156 | ---------- |
|
0 commit comments