diff --git a/.binder/environment.yml b/.binder/environment.yml index 9e772e2b..b153806e 100644 --- a/.binder/environment.yml +++ b/.binder/environment.yml @@ -7,18 +7,20 @@ dependencies: - cloudpickle =3.0.0 - graphviz =9.0.0 - h5io =0.2.2 -- h5io_browser =0.0.9 -- matplotlib =3.8.3 -- pyiron_base =0.7.9 -- pyiron_contrib =0.1.15 -- pympipool =0.7.13 +- h5io_browser =0.0.12 +- matplotlib =3.8.4 +- pandas =2.2.0 +- pyiron_base =0.8.3 +- pyiron_contrib =0.1.16 +- pympipool =0.8.0 - python-graphviz =0.20.3 - toposort =1.10 -- typeguard =4.1.5 +- typeguard =4.2.1 - ase =3.22.1 -- atomistics =0.1.23 +- atomistics =0.1.27 - lammps -- phonopy =2.21.2 -- pyiron_atomistics =0.4.17 +- matgl = 0.9.2 +- phonopy =2.22.1 +- pyiron_atomistics =0.5.4 - pyiron-data =0.0.29 - numpy =1.26.4 diff --git a/.ci_support/environment-notebooks.yml b/.ci_support/environment-notebooks.yml index 7db01efe..08eacbc4 100644 --- a/.ci_support/environment-notebooks.yml +++ b/.ci_support/environment-notebooks.yml @@ -2,9 +2,10 @@ channels: - conda-forge dependencies: - ase =3.22.1 - - atomistics =0.1.23 + - atomistics =0.1.27 - lammps - - phonopy =2.21.2 - - pyiron_atomistics =0.4.17 + - matgl = 0.9.2 + - phonopy =2.22.1 + - pyiron_atomistics =0.5.4 - pyiron-data =0.0.29 - numpy =1.26.4 \ No newline at end of file diff --git a/.ci_support/environment.yml b/.ci_support/environment.yml index 879c77e0..f445e452 100644 --- a/.ci_support/environment.yml +++ b/.ci_support/environment.yml @@ -7,11 +7,12 @@ dependencies: - cloudpickle =3.0.0 - graphviz =9.0.0 - h5io =0.2.2 -- h5io_browser =0.0.9 -- matplotlib =3.8.3 -- pyiron_base =0.7.9 -- pyiron_contrib =0.1.15 -- pympipool =0.7.13 +- h5io_browser =0.0.12 +- matplotlib =3.8.4 +- pandas =2.2.0 +- pyiron_base =0.8.3 +- pyiron_contrib =0.1.16 +- pympipool =0.8.0 - python-graphviz =0.20.3 - toposort =1.10 -- typeguard =4.1.5 +- typeguard =4.2.1 diff --git a/.gitignore b/.gitignore index 387f0962..f57669c2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ _build/ apidoc/ .ipynb_checkpoints/ test_times.dat +tests/integration/test_notebooks.py .aider* diff --git a/docs/README.md b/docs/README.md index cc3c8ef7..d6082c44 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,7 +26,7 @@ Individual node computations can be shipped off to parallel processes for scalab Once you're happy with a workflow, it can be easily turned it into a macro for use in other workflows. This allows the clean construction of increasingly complex computation graphs by composing simpler graphs. -Nodes (including macros) can be stored in plain text, and registered by future workflows for easy access. This encourages and supports an ecosystem of useful nodes, so you don't need to re-invent the wheel. (This is a beta-feature, with full support of [FAIR](https://en.wikipedia.org/wiki/FAIR_data) principles for node packages planned.) +Nodes (including macros) can be stored in plain text as python code, and registered by future workflows for easy access. This encourages and supports an ecosystem of useful nodes, so you don't need to re-invent the wheel. (This is a beta-feature, with full support of [FAIR](https://en.wikipedia.org/wiki/FAIR_data) principles for node packages planned.) Executed or partially-executed graphs can be stored to file, either by explicit call or automatically after running. When creating a new node(/macro/workflow), the working directory is automatically inspected for a save-file and the node will try to reload itself if one is found. (This is an alpha-feature, so it is currently only possible to save entire graphs at once and not individual nodes within a graph, all the child nodes in a saved graph must have been instantiated by `Workflow.create` (or equivalent, i.e. their code lives in a `.py` file that has been registered), and there are no safety rails to protect you from changing the node source code between saving and loading (which may cause errors/inconsistencies depending on the nature of the changes).) @@ -39,7 +39,7 @@ Nodes can be used by themselves and -- other than being "delayed" in that their ```python >>> from pyiron_workflow import Workflow >>> ->>> @Workflow.wrap_as.function_node() +>>> @Workflow.wrap.as_function_node() ... def add_one(x): ... return x + 1 >>> @@ -54,33 +54,28 @@ But the intent is to collect them together into a workflow and leverage existing >>> from pyiron_workflow import Workflow >>> Workflow.register("pyiron_workflow.node_library.plotting", "plotting") >>> ->>> @Workflow.wrap_as.function_node() +>>> @Workflow.wrap.as_function_node() ... def Arange(n: int): ... import numpy as np ... return np.arange(n) >>> ->>> @Workflow.wrap_as.macro_node("fig") -... def PlotShiftedSquare(macro, shift: int = 0): -... macro.arange = Arange() -... macro.plot = macro.create.plotting.Scatter( -... x=macro.arange + shift, -... y=macro.arange**2 +>>> @Workflow.wrap.as_macro_node("fig") +... def PlotShiftedSquare(self, n: int, shift: int = 0): +... self.arange = Arange(n) +... self.plot = self.create.plotting.Scatter( +... x=self.arange + shift, +... y=self.arange**2 ... ) -... macro.inputs_map = {"arange__n": "n"} # Expose arange input -... return macro.plot +... return self.plot >>> >>> wf = Workflow("plot_with_and_without_shift") >>> wf.n = wf.create.standard.UserInput() ->>> wf.no_shift = PlotShiftedSquare(shift=0, n=10) ->>> wf.shift = PlotShiftedSquare(shift=2, n=10) ->>> wf.inputs_map = { -... "n__user_input": "n", -... "shift__shift": "shift" -... } +>>> wf.no_shift = PlotShiftedSquare(shift=0, n=wf.n) +>>> wf.shift = PlotShiftedSquare(shift=2, n=wf.n) >>> >>> diagram = wf.draw() >>> ->>> out = wf(shift=3, n=10) +>>> out = wf(shift__shift=3, n__user_input=10) ``` diff --git a/docs/environment.yml b/docs/environment.yml index ccfeea72..3998e617 100644 --- a/docs/environment.yml +++ b/docs/environment.yml @@ -12,11 +12,12 @@ dependencies: - cloudpickle =3.0.0 - graphviz =9.0.0 - h5io =0.2.2 -- h5io_browser =0.0.9 -- matplotlib =3.8.3 -- pyiron_base =0.7.9 -- pyiron_contrib =0.1.15 -- pympipool =0.7.13 +- h5io_browser =0.0.12 +- matplotlib =3.8.4 +- pandas =2.2.0 +- pyiron_base =0.8.3 +- pyiron_contrib =0.1.16 +- pympipool =0.8.0 - python-graphviz =0.20.3 - toposort =1.10 -- typeguard =4.1.5 +- typeguard =4.2.1 diff --git a/notebooks/atomistics_nodes.ipynb b/notebooks/atomistics_nodes.ipynb index 2218a532..a3a13818 100644 --- a/notebooks/atomistics_nodes.ipynb +++ b/notebooks/atomistics_nodes.ipynb @@ -37,9 +37,17 @@ "id": "bc0e6187-236d-4cba-8674-de14a0520257", "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/io.py:404: UserWarning: The keyword 'type_hint' was not found among input labels. If you are trying to update a class instance keyword, please use attribute assignment directly instead of calling this method\n", + " warnings.warn(\n" + ] + }, { "data": { - "image/png": "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rYthbztv2LBhWrRokVavXq0XXnhBTU1NKi8v1/79+0O2T8V7LkkrVqzQV199pZtuuilsm1S55ycL/G7b+b0PnGf3nER35MgRzZo1Sz/4wQ/a3Sht4MCBevnll1VTU6PXXntNOTk5GjFihD799NMY9rbzxo4dq1dffVV//OMf9etf/1p//vOfdfnll+vo0aNhz0nF+/7KK6+oR48euvbaa9tt5+R9T4pde2Pt1L8KDcNo9y/FUO1DHU8Wd911l/73f/9X7777brvtzj//fJ1//vmt35eVlWnnzp164okndOmllzrdzagZO3Zs639fcMEFKisr07nnnqtXXnlFM2bMCHlOqt1zSVq4cKHGjh2rgoKCsG1S5Z6HYvf3PtJzEtWxY8d04403yu/3a968ee22HT58eFCi54gRIzRkyBA988wzevrpp53uatRMmjSp9b9LSko0dOhQFRYW6q233mr3gzmV7rskvfjii/qXf/mXDnM/nLzvjIycpHfv3srMzGwT4e7du7dNJBzQp0+fkO27dOmiXr16OdZXp9x9992qqanRO++8o379+tk+f/jw4Un319GpTjvtNF1wwQVhryPV7rkkbd++XX/4wx9066232j432e95YOWUnd/7wHl2z0lUx44d08SJE9XY2Kja2lrb28dnZGToe9/7XlL/HEjmyF9hYWG715FK912S1q9fr48//jii3/1o3neCkZNkZWWptLS0dUVBQG1trcrLy0OeU1ZW1qb922+/raFDh6pr166O9TXaDMPQXXfdpWXLlumPf/yjioqKInqd+vp65efnR7l3sXX06FFt27Yt7HWkyj0/2UsvvaSzzz5b48aNs31ust/zoqIi9enTJ+ietrS0aO3atWF/76XwPwftnZOIAoHIp59+qj/84Q8RBdSGYWjTpk1J/XMgSfv379fOnTvbvY5Uue8BCxcuVGlpqS688ELb50b1vjuSFpvE/uu//svo2rWrsXDhQqOhocGYPn26cdpppxl/+9vfDMMwjFmzZhmTJ09ubf/5558b3bt3N37yk58YDQ0NxsKFC42uXbsaS5YsidclRORHP/qRkZuba6xZs8bweDytj0OHDrW2OfXaf/Ob3xjLly83PvnkE2PLli3GrFmzDEnG0qVL43EJEfvpT39qrFmzxvj888+N999/3/j+979v9OjRI+XveYDP5zMGDBhgzJw5s81zqXLPDxw4YNTX1xv19fWGJOPJJ5806uvrW1eMzJkzx8jNzTWWLVtmbN682fjnf/5nIz8/3/B6va2vMXny5KBVde+9956RmZlpzJkzx9i2bZsxZ84co0uXLsb7778f8+trT3vXfuzYMaOystLo16+fsWnTpqDf/aNHj7a+xqnX/uCDDxqrVq0yPvvsM6O+vt64+eabjS5duhgffPBBPC4xrPau/cCBA8ZPf/pTo66uzmhsbDTeeecdo6yszOjbt2/K3/eA5uZmo3v37sb8+fNDvkYs7zvBSAjPPvusUVhYaGRlZRlDhgwJWt46depUY9SoUUHt16xZY1x00UVGVlaWcc4554S9sYlMUsjHSy+91Nrm1Gt/7LHHjHPPPdfIyckxzjzzTOOSSy4x3nrrrdh3vpMmTZpk5OfnG127djUKCgqMa6+91ti6dWvr86l6zwNWr15tSDI+/vjjNs+lyj0PLEk+9TF16lTDMMzlvQ888IDRp08fIzs727j00kuNzZs3B73GqFGjWtsHvPHGG8b5559vdO3a1Rg4cGBCBmXtXXtjY2PY3/133nmn9TVOvfbp06cbAwYMMLKysoyzzjrLqKioMOrq6mJ/cR1o79oPHTpkVFRUGGeddZbRtWtXY8CAAcbUqVONHTt2BL1GKt73gOeee87o1q2b8dVXX4V8jVjed5dhnMi8AwAAiANyRgAAQFwRjAAAgLgiGAEAAHFFMAIAAOKKYAQAAMQVwQgAAIgrghEAABBXBCMAACCuCEYAAEBcEYwAAIC4IhgBAABxRTACAADi6v8DLdRAq+S5ePgAAAAASUVORK5CYII=", + "image/png": "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K0xXELyurv348eO6//77dckll6hPnz7Ky8vTrbfeqv3793f5mq+88orfn4PW1tYwX4013d3322+/vdM1jBo1qtvXjff7Lsnv/bPZbHriiScCvmY47zvByClWrFihOXPm6IEHHlBtba3Gjh2rSZMmac+ePX7bNzQ0aPLkyRo7dqxqa2v185//XPfee6/efPPNCPe8Z9atW6e7775bH330kaqrq3XixAmVlpbqyJEj3Z776aefyul0djzOP//8CPQ4tC6++GKfa9i+fXvAtolyzyXp73//u891V1dXS5K+973vdXlevN3zI0eO6NJLL9WiRYv8Pv/444/rySef1KJFi/T3v/9dubm5mjBhQse+WP5s2rRJ06dP18yZM/XJJ59o5syZuvHGG/W3v/0tXJcRlK6u/ejRo6qpqdGDDz6ompoarVy5Uv/4xz907bXXdvu62dnZPj8DTqdTWVlZ4biEoHV33yVp4sSJPtdQWVnZ5Wsmwn2X1OnevfTSS7LZbLrhhhu6fN2w3XcDPkaOHGmUlZX5HLvooouMefPm+W3/s5/9zLjooot8jt15553GqFGjwtbHSGhqajIkGevWrQvY5v333zckGV999VXkOhYGCxYsMC699FLT7RP1nhuGYfzoRz8yhgwZYng8Hr/PJ8I9l2S89dZbHV97PB4jNzfXePTRRzuOtba2Gna73Xj++ecDvs6NN95oTJw40efYNddcY8yYMSPkfQ6VU6/dn82bNxuSjC+++CJgm5dfftmw2+2h7VyY+bv22267zbjuuussvU6i3vfrrrvOGD9+fJdtwnnfGRk5ybFjx7RlyxaVlpb6HC8tLdXGjRv9nrNp06ZO7a+55hp9/PHHOn78eNj6Gm4ul0uS1K9fv27bDh8+XA6HQ1dffbXef//9cHctLHbt2qW8vDzl5+drxowZ+uyzzwK2TdR7fuzYMS1btkz//d//3e2GlIlwz70aGhrU2Njoc08zMzM1bty4gL/3UuCfg67OiQcul0s2m02nn356l+2+/vprDR48WGeffbamTp2q2trayHQwxNauXauBAwfqggsu0KxZs9TU1NRl+0S8719++aVWr16tO+64o9u24brvBCMnOXDggNxut3JycnyO5+TkqLGx0e85jY2NftufOHFCBw4cCFtfw8kwDM2dO1dXXnmlCgsLA7ZzOBx64YUX9Oabb2rlypW68MILdfXVV2v9+vUR7G3PXXHFFXr11Vf1l7/8RUuXLlVjY6NGjx6tgwcP+m2fiPdckv70pz/p8OHDuv322wO2SZR7fjLv77aV33vveVbPiXWtra2aN2+ebr755i43Srvooov0yiuv6O2339by5cuVlZWlMWPGaNeuXRHsbc9NmjRJr732mtasWaPf/OY3+vvf/67x48erra0t4DmJeN9///vfq2/fvvrud7/bZbtw3ve42LU30k79q9AwjC7/UvTX3t/xeHHPPfdo27Zt+uCDD7psd+GFF+rCCy/s+LqkpER79+7Vr3/9a1111VXh7mbITJo0qePfl1xyiUpKSjRkyBD9/ve/19y5c/2ek2j3XJJefPFFTZo0SXl5eQHbJMo998fq732w58Sq48ePa8aMGfJ4PFq8eHGXbUeNGuWT6DlmzBgVFRXp2Wef1TPPPBPurobM9OnTO/5dWFioESNGaPDgwVq9enWXH8yJdN8l6aWXXtJ//dd/dZv7Ec77zsjISQYMGKDU1NROEW5TU1OnSNgrNzfXb/u0tDT1798/bH0Nlx/+8Id6++239f777+vss8+2fP6oUaPi7q+jU/Xp00eXXHJJwOtItHsuSV988YXee+89/eAHP7B8brzfc+/KKSu/997zrJ4Tq44fP64bb7xRDQ0Nqq6utrx9fEpKii6//PK4/jmQ2kf+Bg8e3OV1JNJ9l6QNGzbo008/Dep3P5T3nWDkJBkZGSouLu5YUeBVXV2t0aNH+z2npKSkU/t3331XI0aMUHp6etj6GmqGYeiee+7RypUrtWbNGuXn5wf1OrW1tXI4HCHuXWS1tbVp586dAa8jUe75yV5++WUNHDhQU6ZMsXxuvN/z/Px85ebm+tzTY8eOad26dQF/76XAPwddnROLvIHIrl279N577wUVUBuGoa1bt8b1z4EkHTx4UHv37u3yOhLlvnu9+OKLKi4u1qWXXmr53JDe97CkxcaxN954w0hPTzdefPFFo76+3pgzZ47Rp08f4/PPPzcMwzDmzZtnzJw5s6P9Z599ZvTu3du47777jPr6euPFF1800tPTjT/+8Y/RuoSgzJ4927Db7cbatWsNp9PZ8Th69GhHm1Ov/amnnjLeeust4x//+IdRV1dnzJs3z5BkvPnmm9G4hKD9+Mc/NtauXWt89tlnxkcffWRMnTrV6Nu3b8Lfcy+3222cc845xv3339/puUS55y0tLUZtba1RW1trSDKefPJJo7a2tmPFyKOPPmrY7XZj5cqVxvbt242bbrrJcDgcRnNzc8drzJw502dV3YcffmikpqYajz76qLFz507j0UcfNdLS0oyPPvoo4tfXla6u/fjx48a1115rnH322cbWrVt9fvfb2to6XuPUa3/ooYeMqqoqY/fu3UZtba3x/e9/30hLSzP+9re/ReMSA+rq2ltaWowf//jHxsaNG42Ghgbj/fffN0pKSoyzzjor4e+7l8vlMnr37m1UVFT4fY1I3neCET+ee+45Y/DgwUZGRoZRVFTks7z1tttuM8aNG+fTfu3atcbw4cONjIwM49xzzw14Y2OZJL+Pl19+uaPNqdf+2GOPGUOGDDGysrKMM844w7jyyiuN1atXR77zPTR9+nTD4XAY6enpRl5envHd737X2LFjR8fziXrPvf7yl78YkoxPP/2003OJcs+9S5JPfdx2222GYbQv712wYIGRm5trZGZmGldddZWxfft2n9cYN25cR3uvP/zhD8aFF15opKenGxdddFFMBmVdXXtDQ0PA3/3333+/4zVOvfY5c+YY55xzjpGRkWGceeaZRmlpqbFx48bIX1w3urr2o0ePGqWlpcaZZ55ppKenG+ecc45x2223GXv27PF5jUS8715LliwxevXqZRw+fNjva0TyvtsM41+ZdwAAAFFAzggAAIgqghEAABBVBCMAACCqCEYAAEBUEYwAAICoIhgBAABRRTACAACiimAEAABEFcEIAACIKoIRAAAQVQQjAAAgqghGAABAVP3/4IoWwOMu3mkAAAAASUVORK5CYII=", "text/plain": [ "
" ] @@ -91,18 +99,18 @@ "name": "stderr", "output_type": "stream", "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:168: UserWarning: The channel ran was not connected to accumulate_and_run, andthus could not disconnect from it.\n", + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:176: UserWarning: The channel ran was not connected to accumulate_and_run, andthus could not disconnect from it.\n", " warn(\n", - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:168: UserWarning: The channel accumulate_and_run was not connected to ran, andthus could not disconnect from it.\n", + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:176: UserWarning: The channel accumulate_and_run was not connected to ran, andthus could not disconnect from it.\n", " warn(\n", - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:168: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n", + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:176: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n", " warn(\n" ] }, { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 4, @@ -156,7 +164,7 @@ } ], "source": [ - "wf.C" + "wf.C.value" ] }, { @@ -168,7 +176,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 6, @@ -189,6 +197,14 @@ "source": [ "wf.dos_plot()" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4801ddb-7c53-4cc0-a419-413a4c36a247", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/notebooks/deepdive.ipynb b/notebooks/deepdive.ipynb index f9b25f6f..ff371941 100644 --- a/notebooks/deepdive.ipynb +++ b/notebooks/deepdive.ipynb @@ -43,7 +43,7 @@ "metadata": {}, "outputs": [], "source": [ - "from pyiron_workflow.function import Function" + "from pyiron_workflow.function import function_node" ] }, { @@ -56,7 +56,7 @@ "def plus_minus_one(x):\n", " return x+1, x-1\n", "\n", - "pm_node = Function(plus_minus_one)" + "pm_node = function_node(plus_minus_one)" ] }, { @@ -237,7 +237,7 @@ " sum_ = x + y\n", " return sum_\n", "\n", - "adder_node = Function(adder)\n", + "adder_node = function_node(adder)\n", "adder_node.run()\n", "adder_node.outputs.sum_.value # We use `value` to see the data the channel holds" ] @@ -393,7 +393,7 @@ } ], "source": [ - "adder_node = Function(adder, 10, y=20)\n", + "adder_node = function_node(adder, 10, y=20)\n", "adder_node.run()" ] }, @@ -441,26 +441,149 @@ "source": [ "## Reusable node classes\n", "\n", - "If we're going to use a node many times, we may want to define a new sub-class of `Function` to handle this.\n", + "Under the hood, `function_node` is actually dynamically making a new sub-class of `Function` and returning us an instance of that class. If we look at the type, we'll see it's based off the wrapped function (as is the class's `__module__`), and `Function` and the other parent classes appear in the method resolution order:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "09043170-54f1-469e-8975-c013ac11aad0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(__main__.adder, '__main__')" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(adder_node), adder_node.__module__" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "f7be4aa6-5d0d-4e86-9b57-656b7bb16e30", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[__main__.adder,\n", + " pyiron_workflow.snippets.factory._FactoryMade,\n", + " pyiron_workflow.function.Function,\n", + " pyiron_workflow.io_preview.StaticNode,\n", + " pyiron_workflow.node.Node,\n", + " pyiron_workflow.has_to_dict.HasToDict,\n", + " pyiron_workflow.semantics.Semantic,\n", + " pyiron_workflow.run.Runnable,\n", + " pyiron_workflow.injection.HasIOWithInjection,\n", + " pyiron_workflow.io.HasIO,\n", + " pyiron_workflow.has_interface_mixins.UsesState,\n", + " pyiron_workflow.single_output.ExploitsSingleOutput,\n", + " pyiron_workflow.working.HasWorkingDirectory,\n", + " pyiron_workflow.storage.HasH5ioStorage,\n", + " pyiron_workflow.storage.HasTinybaseStorage,\n", + " pyiron_workflow.storage.HasStorage,\n", + " pyiron_workflow.has_interface_mixins.HasLabel,\n", + " pyiron_workflow.has_interface_mixins.HasParent,\n", + " pyiron_workflow.has_interface_mixins.HasRun,\n", + " pyiron_workflow.has_interface_mixins.HasChannel,\n", + " pyiron_workflow.io_preview.ScrapesIO,\n", + " pyiron_workflow.io_preview.HasIOPreview,\n", + " abc.ABC,\n", + " object]" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "adder_node.__class__.mro()" + ] + }, + { + "cell_type": "markdown", + "id": "50370591-e3ac-4593-a9f7-d4bab8b1376f", + "metadata": {}, + "source": [ + "However, there's lots of times where we're going to want a bunch of instances of the same type of node, and we'd really like access to this class directly so we can make new instances more succinctly.\n", + "\n", + "The can be done the traditionaly way directly by inheriting from `Function` and specifying its required `node_function` static method, and (optionally) overriding its `_output_labels` so they are defined by you instead of scraped from the `node_function`" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "4382a4cf-5e64-47a2-938b-39d0674b7ed5", + "metadata": {}, + "outputs": [], + "source": [ + "from pyiron_workflow.function import Function" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "abc26576-3473-4e31-93cb-d5cf2ea31b93", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "class name = MySubtractionChild\n", + "label = MySubtractionChild\n", + "output with default input = {'diff': 1}\n" + ] + } + ], + "source": [ + "class MySubtractionChild(Function):\n", + " _output_labels = [\"diff\"]\n", + " \n", + " @staticmethod\n", + " def node_function(x: int | float = 2, y: int | float = 1) -> int | float:\n", + " return x - y\n", + "\n", + "sn = MySubtractionChild()\n", + "print(\"class name =\", sn.__class__.__name__)\n", + "print(\"label =\", sn.label)\n", "\n", - "The can be done directly by inheriting from `Function` and overriding it's `__init__` function and/or directly defining the `node_function` property so that the core functionality of the node (i.e. the node function and output labels) are set in stone, but even easier is to use the `function_node` decorator to do this for you! \n", + "sn() # Runs without updating input data, but we have defaults so that's fine\n", + "print(\"output with default input = \", sn.outputs.to_value_dict())" + ] + }, + { + "cell_type": "markdown", + "id": "bb875023-6fd4-4fd8-9068-d56bb2660715", + "metadata": {}, + "source": [ + "Even easier is to use the `as_function_node` decorator to do this for you! \n", "\n", - "The decorator also lets us explicitly choose the names of our output channels by passing the `output_labels` argument to the decorator -- as a string to create a single channel for the returned values, or as a list of strings equal to the number of returned values in a returned tuple." + "The decorator lets us easily choose the names of our output channels by passing the `output_labels` argument to the decorator -- as a string to create a single channel for the returned values, or as a list of strings equal to the number of returned values in a returned tuple. The decorator also does nice quality-of-life things like use the decorated function name as the default label for new instances." ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 19, "id": "61b43a9b-8dad-48b7-9194-2045e465793b", "metadata": {}, "outputs": [], "source": [ - "from pyiron_workflow.function import function_node" + "from pyiron_workflow.function import as_function_node" ] }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 20, "id": "647360a9-c971-4272-995c-aa01e5f5bb83", "metadata": {}, "outputs": [ @@ -470,13 +593,13 @@ "text": [ "class name = Subtract\n", "label = Subtract\n", - "default output = -1\n" + "output with default input = {'diff': 1}\n" ] } ], "source": [ - "@function_node(\"diff\")\n", - "def Subtract(x: int | float = 1, y: int | float = 2) -> int | float:\n", + "@as_function_node(\"diff\")\n", + "def Subtract(x: int | float = 2, y: int | float = 1) -> int | float:\n", " return x - y\n", "\n", "sn = Subtract()\n", @@ -484,7 +607,7 @@ "print(\"label =\", sn.label)\n", "\n", "sn() # Runs without updating input data, but we have defaults so that's fine\n", - "print(\"default output =\", sn.outputs.diff.value)" + "print(\"output with default input = \", sn.outputs.to_value_dict())" ] }, { @@ -492,12 +615,42 @@ "id": "77642993-63c3-41a3-a963-a406de33553c", "metadata": {}, "source": [ - "The decorator is just dynamically defining a new child of the `Function` class. These children have their behaviour available in the static method `node_function` so we can access it right from the class level, e.g. to modify the behaviour:" + "Note that we break with python convention and use PascalCase to name our \"function\" here -- that's because by the time the decorator is done with it, it is actually a class! Information about the expected IO is available right at the class level:" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 21, + "id": "122ff192-8a12-4323-bd41-1c1e922a66b3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'inputs': {'x': (int | float, 2), 'y': (int | float, 1)},\n", + " 'outputs': {'diff': int | float}}" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Subtract.preview_io()" + ] + }, + { + "cell_type": "markdown", + "id": "9e40da77-98dc-45d9-bf3a-202f82b38c4a", + "metadata": {}, + "source": [ + "So is the node functionality, so we can still leverage our node as a normal function if we want:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, "id": "b8c845b7-7088-43d7-b106-7a6ba1c571ec", "metadata": {}, "outputs": [ @@ -510,7 +663,7 @@ } ], "source": [ - "@function_node(\"square_diff\")\n", + "@as_function_node(\"square_diff\")\n", "def SubtractAndSqaure(x: int | float = 1, y: int | float = 2) -> int | float:\n", " return Subtract.node_function(x, y)**2\n", " \n", @@ -543,39 +696,29 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 23, "id": "2e418abf-7059-4e1e-9b9f-b3dc0a4b5e35", "metadata": { "tags": [] }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel ran was not connected to run, andthus could not disconnect from it.\n", - " warn(\n", - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n", - " warn(\n" - ] - }, { "data": { "text/plain": [ "2" ] }, - "execution_count": 18, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "@function_node()\n", + "@as_function_node()\n", "def Linear(x):\n", " return x\n", "\n", - "@function_node(\"double\")\n", + "@as_function_node(\"double\")\n", "def TimesTwo(x):\n", " return 2 * x\n", "\n", @@ -599,7 +742,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 24, "id": "f3b0b700-683e-43cb-b374-48735e413bc9", "metadata": {}, "outputs": [ @@ -609,7 +752,7 @@ "4" ] }, - "execution_count": 19, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -635,7 +778,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 25, "id": "59c29856-c77e-48a1-9f17-15d4c58be588", "metadata": {}, "outputs": [ @@ -679,7 +822,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 26, "id": "98312fbb-0e87-417c-9780-d22903cdb3f4", "metadata": {}, "outputs": [ @@ -689,7 +832,7 @@ "9" ] }, - "execution_count": 21, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -712,7 +855,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 27, "id": "b0e8fc87-fba1-4501-882b-f162c4eadf97", "metadata": {}, "outputs": [ @@ -722,7 +865,7 @@ "20" ] }, - "execution_count": 22, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -743,38 +886,7 @@ }, { "cell_type": "code", - "execution_count": 23, - "id": "8a195c41-233e-4076-ad77-008c93297f9c", - "metadata": {}, - "outputs": [], - "source": [ - "foo = [1, 2, 3]" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "6805b0c3-9103-49f4-bc29-569b0b4d6ed0", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 24, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "foo.reverse" - ] - }, - { - "cell_type": "code", - "execution_count": 25, + "execution_count": 28, "id": "7c4cbe66-9b0a-428b-835f-31959a7f75bb", "metadata": {}, "outputs": [ @@ -784,7 +896,7 @@ "[1, 2]" ] }, - "execution_count": 25, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -796,7 +908,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 29, "id": "840d5762-ebb5-45aa-8faf-5443544bdeae", "metadata": {}, "outputs": [ @@ -806,7 +918,7 @@ "[1, 2]" ] }, - "execution_count": 26, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -817,7 +929,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 30, "id": "16c2d0de-de6f-4b33-84e4-aefbe5db4177", "metadata": {}, "outputs": [ @@ -827,7 +939,7 @@ "1" ] }, - "execution_count": 27, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -839,7 +951,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 31, "id": "30b4ed75-bb73-44bb-b6d9-fe525b924652", "metadata": {}, "outputs": [ @@ -849,7 +961,7 @@ "42" ] }, - "execution_count": 28, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -872,7 +984,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 32, "id": "786b1402-b595-4337-8872-fd58687c2725", "metadata": {}, "outputs": [ @@ -882,7 +994,7 @@ "(True, False)" ] }, - "execution_count": 29, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -908,21 +1020,13 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 33, "id": "6569014a-815b-46dd-8b47-4e1cd4584b3b", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n", - " warn(\n" - ] - }, { "data": { - "image/png": "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VF9fv9glLnuxnKPXXntNxcXF+va3v63rrrtON910kx577DH95je/iUfJy04s56i8vFwXLlxQW1ubLMvSBx98oB/84Ae655574lFyUkuIB+UhvoaHhxUOh5WdnR3Rnp2drcHBwXm9x3e/+139+te/1r333rsYJS5rsZyfd999V/v27VNHR4dSU/lnv9hiOUfnz5/XqVOn5HK59MMf/lDDw8P62te+ptHRUa4bWQSxnKPy8nIdP35cVVVVGh8f18cff6wvf/nL+t73vhePkpMaMyOYk8PhiHhtWVZU22xOnDihJ598Ui0tLVqzZs1ilbfszff8hMNh3XfffWpoaNBNN90Ur/Ige/+Gpqam5HA4dPz4cZWUlOjuu+/W008/rZdeeonZkUVk5xz19vZq7969euKJJ9TV1aX29nb19fWpuro6HqUmNf4XCVGysrLkdDqj/u9gaGgo6v8iPq2lpUW7du3SK6+8orvuumsxy1y27J6fsbExvf322+ru7tYjjzwi6dIPn2VZSk1N1euvv6477rgjLrUvF7H8G/J4PLruuusiHreen58vy7J04cIF3XjjjYta83ITyzlqbGzUxo0b9fjjj0uSbrnlFq1cuVKbNm3SU089JY/Hs+h1JytmRhAlLS1NPp9Pfr8/ot3v96u8vHzOcSdOnNADDzygl19+mTXURWT3/GRkZOhnP/uZzp49O3NUV1dr/fr1Onv2rEpLS+NV+rIRy7+hjRs36v3339eHH3440/bOO+8oJSVFa9euXdR6l6NYztFHH32klJTIn02n0ynp0owKroC5a2exlE3f8nb48GGrt7fXqqmpsVauXGn94he/sCzLsvbt22ft2LFjpv/LL79spaamWs8995wVCARmjl/96lemvkJSs3t+Po27aRaf3XM0NjZmrV271vqjP/oj6+c//7n1xhtvWDfeeKO1e/duU18h6dk9Ry+++KKVmppqNTU1We+995516tQpq7i42CopKTH1FZIGYQRzeu6556y8vDwrLS3NKioqst54442Zv91///3W7bffPvP69ttvtyRFHffff3/8C18m7JyfTyOMxIfdc3Tu3DnrrrvuslasWGGtXbvWqq2ttT766KM4V7282D1Hzz77rFVQUGCtWLHC8ng81p/+6Z9aFy5ciHPVycdhWcwtAQAAc7hmBAAAGEUYAQAARhFGAACAUYQRAABgFGEEAAAYRRgBAABGEUYAAIBRhBEAAGAUYQQAABhFGAEAAEYRRgAAgFGEEQAAYNT/BbUnHUOoAWGBAAAAAElFTkSuQmCC", + "image/png": "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qb29XampqQHjqampamho6HGbo0ePas+ePaqurtbWrVv12GOPacuWLbr77rt7vZ+2tja1tLQE3EKF71MAYgevZyA2BHUCq8sVuPvTGNNtrFNHR4dcLpc2bNigvLw83XDDDXrkkUf03HPP6fPPP+9xm9LSUnk8nq5benp6MGV2w/cpALGD1zMQOxyFkWHDhik+Pr7bXpDGxsZue0s6eb1eXXDBBfJ4PF1jY8aMkTFGH374YY/bLF++XH6/v+tWX1/vpMxe8X0KQOzg9QzEDkdhJDExUbm5uaqoqAgYr6io0IQJE3rcZuLEiTp27Jg++eSTrrEjR44oLi5OI0eO7HGbpKQkJScnB9xCge9TAGIHr2cgdjg+TLN06VI988wzWrdunQ4fPqwlS5aorq5ORUVFks7s1Zg7d27X/FmzZmno0KG6/fbbVVNTozfeeEP33XeffvzjH0f8BFa+TwGIHbyegdjheJ2RwsJCNTc3a9WqVfL5fMrOztaOHTuUkZEhSfL5fKqrq+ua/61vfUsVFRX66U9/qrFjx2ro0KGaOXOmHnjggdA9ij7q/D6FBv/JHo8zu3Rm9UC+TwE4+/F6BmKH43VGbAjlOiOdZ99LCngD6zz9lmWMgejB6xk4u/X18/uc+24avk8BiB28noHYcM7tGenEio1A7OD1DJydwrICayzh+xSA2MHrGYhu59xhGgAAcHYhjAAAAKsIIwAAwCrCCAAAsIowAgAArCKMAAAAqwgjAADAKsIIAACwijACAACsIowAAACrCCMAAMAqwggAALCKMAIAAKwijAAAAKsIIwAAwCrCCAAAsIowAgAArCKMAAAAqwgjAADAKsIIAACwijACAACsIowAAACrCCMAAMAqwggAALCKMAIAAKwijAAAAKsIIwAAwCrCCAAAsIowAgAArCKMAAAAqwgjAADAKsIIAACwijACAACsIowAAACrCCMAAMAqwggAALCKMAIAAKwijAAAAKsIIwAAwCrCCAAAsIowAgAArCKMAAAAqwgjAADAKsIIAACwijACAACsIowAAACrCCMAAMAqwggAALCKMAIAAKwijAAAAKsIIwAAwCrCCAAAsIowAgAArCKMAAAAqwgjAADAKsIIAACwijACAACsIowAAACrCCMAAMAqwggAALCKMAIAAKwijAAAAKsIIwAAwCrCCAAAsCqoMFJWVqbM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", "text/plain": [ "
" ] @@ -935,12 +1039,12 @@ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", - "@function_node()\n", + "@as_function_node()\n", "def Noise(length: int = 1):\n", " array = np.random.rand(length)\n", " return array\n", "\n", - "@function_node()\n", + "@as_function_node()\n", "def Plot(x, y):\n", " fig = plt.scatter(x, y)\n", " return fig\n", @@ -951,6 +1055,86 @@ ")()" ] }, + { + "cell_type": "markdown", + "id": "bd95ba27-e439-45cc-87d8-db587dc3b78c", + "metadata": {}, + "source": [ + "## Edge cases\n", + "\n", + "If output labels aren't provided, we try to scrape them from the source code for the function -- but this has limitations, like that the source code needs to be available for inspection and that there's a single return value. \n", + "\n", + "If explicit output labels _are_ provided, we _still_ try to scrape them from the function source code just to make sure that everything lines up nicely. However, there are a couple of edge cases where you may want to tell the workflow code that you really know what you're serious about your labels and just use them without any validation.\n", + "\n", + "(Failing to find the source code to compare with only triggers a warning, so in-memory functions are still OK as long as you provide output labels.)\n", + "\n", + "Turning off this validation comes with some responsibility that your labels make sense and will work. Let's look at a couple examples:\n", + "\n", + "(1) You might want to return a single tuple, and break it appart into channels" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "1a43985b-98d7-4c56-b8fe-e6598298b44b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'x0': 7, 'x1': 10.14}" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "@as_function_node(\"x0\", \"x1\", validate_output_labels=False)\n", + "def ReturnsTuple(x: int) -> tuple[int, float]:\n", + " x = (x, x + 3.14)\n", + " return x\n", + "\n", + "from_tuple = ReturnsTuple(x=7, run_after_init=True)\n", + "from_tuple.outputs.to_value_dict()" + ] + }, + { + "cell_type": "markdown", + "id": "cca66b86-763c-4082-aca1-b19fd7edcc3a", + "metadata": {}, + "source": [ + "(2) To handle multiple return branches -- just be careful that the branches return the same number and type of values, or you may wind up with strange results." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "ab3ad9e6-2a5e-4b0f-82e3-9e7208970d22", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True False\n" + ] + } + ], + "source": [ + "\n", + "@as_function_node(\"bool\", validate_output_labels=False)\n", + "def MultipleBranches(x):\n", + " if x < 10:\n", + " return True\n", + " else:\n", + " return False\n", + "\n", + "switch = MultipleBranches()\n", + "print(switch(3), switch(13))" + ] + }, { "cell_type": "markdown", "id": "5dc12164-b663-405b-872f-756996f628bd", @@ -959,10 +1143,10 @@ "# Workflows\n", "\n", "The case where we have groups of connected nodes working together is our normal, intended use case.\n", - "We offer a formal way to group these objects together as a `Workflow(Node)` object.\n", + "We offer a formal way to group these objects together as a `Workflow` object.\n", "`Workflow` also offers us a single point of entry to the codebase -- i.e. most of the time you shouldn't need the node imports used above, because the decorators are available right on the workflow class.\n", "\n", - "We will also see here that we can rename our node output channels using the `output_labels: Optional[str | list[str] | tuple[str]` kwarg, in case they don't have a convenient name to start with.\n", + "We will also see here that we can rename our node output channels using the `outputs_map: dict[str, str]` kwarg, in case they don't have a convenient name to start with.\n", "This way we can always have convenient dot-based access (and tab completion) instead of having to access things by string-based keys.\n", "\n", "Finally, when a workflow is run, unless its `automate_execution` flag has been set to `False` or the data connections form a cyclic graph, it will _automatically_ build the necessary run signals! That means for all directed acyclic graph (DAG) workflows, all we typically need to worry about is the data connections." @@ -978,14 +1162,14 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 36, "id": "1cd000bd-9b24-4c39-9cac-70a3291d0660", "metadata": {}, "outputs": [], "source": [ "from pyiron_workflow import Workflow\n", "\n", - "@Workflow.wrap_as.function_node(\"is_greater\")\n", + "@Workflow.wrap.as_function_node(\"is_greater\")\n", "def GreaterThanHalf(x: int | float | bool = 0) -> bool:\n", " \"\"\"The functionality doesn't matter here, it's just an example\"\"\"\n", " return x > 0.5" @@ -1005,7 +1189,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 37, "id": "7964df3c-55af-4c25-afc5-9e07accb606a", "metadata": {}, "outputs": [ @@ -1052,7 +1236,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 38, "id": "809178a5-2e6b-471d-89ef-0797db47c5ad", "metadata": {}, "outputs": [ @@ -1060,30 +1244,32 @@ "name": "stdout", "output_type": "stream", "text": [ - "['ax', 'b__x'] ['ay', 'a + b + 2']\n" + "inputs: ['a', 'b']\n", + "outputs: ['a + 1', 'a + b + 2']\n" ] } ], "source": [ "wf = Workflow(\"simple\")\n", "\n", - "@Workflow.wrap_as.function_node()\n", + "@Workflow.wrap.as_function_node()\n", "def AddOne(x):\n", " y = x + 1\n", " return y\n", "\n", - "@Workflow.wrap_as.function_node(\"sum\")\n", + "@Workflow.wrap.as_function_node(\"sum\")\n", "def Add(x, y):\n", " return x + y\n", "\n", "wf.a = AddOne(0)\n", "wf.b = AddOne(0)\n", "wf.sum = Add(wf.a, wf.b) \n", - "wf.inputs_map = {\"a__x\": \"ax\"}\n", - "wf.outputs_map = {\"a__y\": \"ay\", \"sum__sum\": \"a + b + 2\"}\n", + "wf.inputs_map = {\"a__x\": \"a\", \"b__x\": \"b\"}\n", + "wf.outputs_map = {\"a__y\": \"a + 1\", \"sum__sum\": \"a + b + 2\"}\n", "# Remember, with single value nodes we can pass the whole node instead of an output channel!\n", "\n", - "print(wf.inputs.labels, wf.outputs.labels)" + "print(\"inputs:\", wf.inputs.labels)\n", + "print(\"outputs:\", wf.outputs.labels)" ] }, { @@ -1106,23 +1292,23 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 39, "id": "52c48d19-10a2-4c48-ae81-eceea4129a60", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'ay': 3, 'a + b + 2': 7}" + "{'a + 1': 3, 'a + b + 2': 7}" ] }, - "execution_count": 34, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "out = wf(ax=2, b__x=3)\n", + "out = wf(a=2, b=3)\n", "out" ] }, @@ -1139,28 +1325,28 @@ "id": "e3f4b51b-7c28-47f7-9822-b4755e12bd4d", "metadata": {}, "source": [ - "We can see now why we've been trying to give succinct string labels to our `Function` node outputs instead of just arbitrary expressions! The expressions are typically not dot-accessible:" + "We can see now why we've been trying to give succinct string labels to our `Function` node outputs instead of just arbitrary expressions! The expressions are typically not dot-accessible, but can still be grabbed with a key:" ] }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 40, "id": "bb35ba3e-602d-4c9c-b046-32da9401dd1c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "(7, 3)" + "7" ] }, - "execution_count": 35, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "out[\"a + b + 2\"], out.ay" + "out[\"a + b + 2\"]" ] }, { @@ -1168,12 +1354,12 @@ "id": "c67ddcd9-cea0-4f3f-96aa-491da0a4c459", "metadata": {}, "source": [ - "We can also look at our graph:" + "We can also visualize our graph!" ] }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 41, "id": "2b0d2c85-9049-417b-8739-8a8432a1efbe", "metadata": {}, "outputs": [ @@ -1186,12 +1372,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "clustersimple\n", - "\n", - "simple: Workflow\n", + "\n", + "simple: Workflow\n", "\n", "clustersimpleInputs\n", "\n", @@ -1206,24 +1392,24 @@ "\n", "clustersimpleOutputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Outputs\n", "\n", "\n", "clustersimplea\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "a: AddOne\n", + "\n", + "a: AddOne\n", "\n", "\n", "clustersimpleaInputs\n", @@ -1237,26 +1423,26 @@ "Inputs\n", "\n", "\n", - "clustersimpleaOutputs\n", + "clustersimpleaOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clustersimpleb\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "b: AddOne\n", + "\n", + "b: AddOne\n", "\n", "\n", "clustersimplebInputs\n", @@ -1270,48 +1456,48 @@ "Inputs\n", "\n", "\n", - "clustersimplebOutputs\n", + "clustersimplebOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clustersimplesum\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "sum: Add\n", + "\n", + "sum: Add\n", "\n", "\n", "clustersimplesumInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clustersimplesumOutputs\n", + "clustersimplesumOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "\n", @@ -1322,8 +1508,8 @@ "\n", "\n", "clustersimpleOutputsran\n", - "\n", - "ran\n", + "\n", + "ran\n", "\n", "\n", "\n", @@ -1332,11 +1518,11 @@ "\n", "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clustersimpleInputsax\n", + "clustersimpleInputsa\n", "\n", - "ax\n", + "a\n", "\n", "\n", "\n", @@ -1344,18 +1530,18 @@ "\n", "x\n", "\n", - "\n", + "\n", "\n", - "clustersimpleInputsax->clustersimpleaInputsx\n", + "clustersimpleInputsa->clustersimpleaInputsx\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", - "clustersimpleInputsb__x\n", - "\n", - "b__x\n", + "clustersimpleInputsb\n", + "\n", + "b\n", "\n", "\n", "\n", @@ -1363,24 +1549,24 @@ "\n", "x\n", "\n", - "\n", + "\n", "\n", - "clustersimpleInputsb__x->clustersimplebInputsx\n", - "\n", - "\n", - "\n", + "clustersimpleInputsb->clustersimplebInputsx\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clustersimpleOutputsay\n", - "\n", - "ay\n", + "clustersimpleOutputsa + 1\n", + "\n", + "a + 1\n", "\n", "\n", "\n", "clustersimpleOutputsa + b + 2\n", - "\n", - "a + b + 2\n", + "\n", + "a + b + 2\n", "\n", "\n", "\n", @@ -1388,13 +1574,13 @@ "\n", "run\n", "\n", - "\n", + "\n", "\n", - "clustersimpleaOutputsran\n", - "\n", - "ran\n", + "clustersimpleaOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clustersimpleaInputsaccumulate_and_run\n", @@ -1404,41 +1590,41 @@ "\n", "\n", "clustersimplesumInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clustersimpleaOutputsran->clustersimplesumInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clustersimpleaOutputsWithInjectionran->clustersimplesumInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clustersimpleaOutputsy\n", - "\n", - "y\n", + "clustersimpleaOutputsWithInjectiony\n", + "\n", + "y\n", "\n", - "\n", + "\n", "\n", - "clustersimpleaOutputsy->clustersimpleOutputsay\n", - "\n", - "\n", - "\n", + "clustersimpleaOutputsWithInjectiony->clustersimpleOutputsa + 1\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clustersimplesumInputsx\n", - "\n", - "x\n", + "\n", + "x\n", "\n", - "\n", + "\n", "\n", - "clustersimpleaOutputsy->clustersimplesumInputsx\n", - "\n", - "\n", - "\n", + "clustersimpleaOutputsWithInjectiony->clustersimplesumInputsx\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -1446,79 +1632,79 @@ "\n", "run\n", "\n", - "\n", + "\n", "\n", - "clustersimplebOutputsran\n", - "\n", - "ran\n", + "clustersimplebOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clustersimplebInputsaccumulate_and_run\n", "\n", "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clustersimplebOutputsran->clustersimplesumInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clustersimplebOutputsWithInjectionran->clustersimplesumInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clustersimplebOutputsy\n", - "\n", - "y\n", + "clustersimplebOutputsWithInjectiony\n", + "\n", + "y\n", "\n", "\n", "\n", "clustersimplesumInputsy\n", - "\n", - "y\n", + "\n", + "y\n", "\n", - "\n", + "\n", "\n", - "clustersimplebOutputsy->clustersimplesumInputsy\n", - "\n", - "\n", - "\n", + "clustersimplebOutputsWithInjectiony->clustersimplesumInputsy\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clustersimplesumInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clustersimplesumOutputsran\n", - "\n", - "ran\n", + "clustersimplesumOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "clustersimplesumOutputssum\n", - "\n", - "sum\n", + "clustersimplesumOutputsWithInjectionsum\n", + "\n", + "sum\n", "\n", - "\n", + "\n", "\n", - "clustersimplesumOutputssum->clustersimpleOutputsa + b + 2\n", - "\n", - "\n", - "\n", + "clustersimplesumOutputsWithInjectionsum->clustersimpleOutputsa + b + 2\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 36, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -1545,14 +1731,14 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 42, "id": "ae500d5e-e55b-432c-8b5f-d5892193cdf5", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9f930802e2ce4b65b1a35f14ac2c0ec2", + "model_id": "cd6168e3475b4927b067216c5f728d4f", "version_major": 2, "version_minor": 0 }, @@ -1571,7 +1757,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "55042822f5f14a2cbbf59c8a64369307", + "model_id": "90df307a03a84652a9969579b2a05759", "version_major": 2, "version_minor": 0 }, @@ -1585,16 +1771,16 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 37, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAAApkklEQVR4nO3dfXBU133/8c/qaSUUaYsE0mqDTOVErSMv2CAMBjOGhsfUiJ/HnUAMOLhhMpinoBgKJu6MIGNLhkzAydCqY8ZjHFSqTicmMS1RkGNHDgUiRkCDUOuHWLWF2Y0So6yErQcsnd8flBsvQsBKi3RWvF8z94899yvxvQfG+/G9597rMsYYAQAAWCRuqBsAAAC4GgEFAABYh4ACAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA6BBQAAGCdhKFuoD96enp0/vx5paWlyeVyDXU7AADgJhhj1NbWJp/Pp7i4658jicmAcv78eeXm5g51GwAAoB+ampo0ZsyY69bEZEBJS0uTdPkA09PTh7gbAABwM1pbW5Wbm+t8j19PTAaUK5d10tPTCSgAAMSYm1mewSJZAABgHQIKAACwDgEFAABYh4ACAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA6Mfmgtlulu8eotvGCmts6lJWWrMl5GYqP410/AAAMtojPoHz44YdatmyZMjMzNWLECN17772qq6tz9htjtHXrVvl8PqWkpGjmzJk6e/Zs2O/o7OzUunXrNGrUKKWmpmrhwoU6d+7cwI9mAKrqA5q+/XU9uue41lee1qN7jmv69tdVVR8Y0r4AALgdRRRQWlpa9MADDygxMVE/+9nP1NDQoO9///v6sz/7M6dmx44d2rlzp3bv3q0TJ07I6/Vqzpw5amtrc2qKi4t14MABVVZW6siRI7p48aIWLFig7u7uqB1YJKrqA1pVcVKBUEfYeDDUoVUVJwkpAAAMMpcxxtxs8VNPPaX//M//1K9+9atr7jfGyOfzqbi4WJs3b5Z0+WxJdna2tm/frpUrVyoUCmn06NHat2+fFi9eLOlPbyc+dOiQ5s2bd8M+Wltb5fF4FAqFBvwunu4eo+nbX+8VTq5wSfJ6knVk85e53AMAwABE8v0d0RmUV199VZMmTdJXv/pVZWVlacKECdqzZ4+zv7GxUcFgUHPnznXG3G63ZsyYoaNHj0qS6urqdOnSpbAan88nv9/v1Fyts7NTra2tYVu01DZe6DOcSJKRFAh1qLbxQtT+TAAAcH0RBZT33ntP5eXlys/P189//nM98cQT+ta3vqUf/ehHkqRgMChJys7ODvu57OxsZ18wGFRSUpJGjhzZZ83VysrK5PF4nC03NzeStq+rua3vcNKfOgAAMHARBZSenh5NnDhRpaWlmjBhglauXKlvfvObKi8vD6u7+jXKxpgbvlr5ejVbtmxRKBRytqampkjavq6stOSo1gEAgIGLKKDk5OSooKAgbOxLX/qSPvjgA0mS1+uVpF5nQpqbm52zKl6vV11dXWppaemz5mput1vp6elhW7RMzstQjidZfcUnl6Qcz+VbjgEAwOCIKKA88MADeuutt8LG3n77bY0dO1aSlJeXJ6/Xq+rqamd/V1eXampqNG3aNElSYWGhEhMTw2oCgYDq6+udmsEUH+dSSdHl0HV1SLnyuaSogAWyAAAMoogCyre//W0dP35cpaWlevfdd7V//3698MILWrNmjaTLl3aKi4tVWlqqAwcOqL6+Xo8//rhGjBihJUuWSJI8Ho9WrFihDRs26Be/+IVOnTqlZcuWady4cZo9e3b0j/AmzPfnqHzZRHk94ZdxvJ5klS+bqPn+nCHpCwCA21VET5K97777dODAAW3ZskXf/e53lZeXp+eff15Lly51ajZt2qT29natXr1aLS0tmjJlig4fPqy0tDSnZteuXUpISNCiRYvU3t6uWbNmae/evYqPj4/ekUVovj9Hcwq8PEkWAAALRPQcFFtE8zkoAABgcNyy56AAAAAMBgIKAACwDgEFAABYh4ACAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA6BBQAAGAdAgoAALAOAQUAAFiHgAIAAKxDQAEAANYhoAAAAOsQUAAAgHUIKAAAwDoEFAAAYB0CCgAAsA4BBQAAWIeAAgAArENAAQAA1iGgAAAA6xBQAACAdQgoAADAOgQUAABgHQIKAACwDgEFAABYh4ACAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA6BBQAAGAdAgoAALAOAQUAAFiHgAIAAKxDQAEAANYhoAAAAOsQUAAAgHUIKAAAwDoEFAAAYB0CCgAAsA4BBQAAWIeAAgAArENAAQAA1iGgAAAA6xBQAACAdQgoAADAOgQUAABgHQIKAACwDgEFAABYh4ACAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA6BBQAAGAdAgoAALAOAQUAAFgnooCydetWuVyusM3r9Tr7jTHaunWrfD6fUlJSNHPmTJ09ezbsd3R2dmrdunUaNWqUUlNTtXDhQp07dy46RwMAAIaFiM+g3H333QoEAs525swZZ9+OHTu0c+dO7d69WydOnJDX69WcOXPU1tbm1BQXF+vAgQOqrKzUkSNHdPHiRS1YsEDd3d3ROSIAABDzEiL+gYSEsLMmVxhj9Pzzz+vpp5/WI488Ikl6+eWXlZ2drf3792vlypUKhUJ68cUXtW/fPs2ePVuSVFFRodzcXL322muaN2/eAA8HAAAMBxGfQXnnnXfk8/mUl5enr33ta3rvvfckSY2NjQoGg5o7d65T63a7NWPGDB09elSSVFdXp0uXLoXV+Hw++f1+p+ZaOjs71draGrYBAIDhK6KAMmXKFP3oRz/Sz3/+c+3Zs0fBYFDTpk3TRx99pGAwKEnKzs4O+5ns7GxnXzAYVFJSkkaOHNlnzbWUlZXJ4/E4W25ubiRtAwCAGBNRQPnKV76iv/mbv9G4ceM0e/Zs/cd//Ieky5dyrnC5XGE/Y4zpNXa1G9Vs2bJFoVDI2ZqamiJpGwAAxJgB3WacmpqqcePG6Z133nHWpVx9JqS5udk5q+L1etXV1aWWlpY+a67F7XYrPT09bAMAAMPXgAJKZ2en/vu//1s5OTnKy8uT1+tVdXW1s7+rq0s1NTWaNm2aJKmwsFCJiYlhNYFAQPX19U4NAABARHfxbNy4UUVFRbrjjjvU3NysZ555Rq2trVq+fLlcLpeKi4tVWlqq/Px85efnq7S0VCNGjNCSJUskSR6PRytWrNCGDRuUmZmpjIwMbdy40blkBAAAIEUYUM6dO6dHH31Uf/jDHzR69Gjdf//9On78uMaOHStJ2rRpk9rb27V69Wq1tLRoypQpOnz4sNLS0pzfsWvXLiUkJGjRokVqb2/XrFmztHfvXsXHx0f3yAAAQMxyGWPMUDcRqdbWVnk8HoVCIdajAAAQIyL5/uZdPAAAwDoRP0kWuB119xjVNl5Qc1uHstKSNTkvQ/Fx1799HgDQfwQU4Aaq6gPadrBBgVCHM5bjSVZJUYHm+3OGsDMAGL64xANcR1V9QKsqToaFE0kKhjq0quKkquoDQ9QZAAxvBBSgD909RtsONuhaq8ivjG072KDunphbZw4A1iOgAH2obbzQ68zJZxlJgVCHahsvDF5TAHCbIKAAfWhu6zuc9KcOAHDzCChAH7LSkqNaBwC4eQQUoA+T8zKU40lWXzcTu3T5bp7JeRmD2RYA3BYIKEAf4uNcKikqkKReIeXK55KiAp6HAgC3AAEFuI75/hyVL5soryf8Mo7Xk6zyZRN5DgoA3CI8qA24gfn+HM0p8PIkWQAYRAQU4CbEx7k09QuZQ90GANw2uMQDAACswxmUGMdL7AAAwxEBJYbxEjsAwHDFJZ4YxUvsAADDGQElBvESOwDAcEdAiUG8xA4AMNwRUGIQL7EDAAx3BJQYxEvsAADDHQElBvESOwDAcEdAiUG8xA4AMNwRUGIUL7EDAAxnPKgthvESOwDAcEVAiXG8xA4AMBxxiQcAAFiHgAIAAKxDQAEAANYhoAAAAOsQUAAAgHUIKAAAwDoEFAAAYB0CCgAAsA4BBQAAWIeAAgAArENAAQAA1iGgAAAA6xBQAACAdQgoAADAOgQUAABgHQIKAACwDgEFAABYh4ACAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA6BBQAAGAdAgoAALAOAQUAAFiHgAIAAKxDQAEAANYhoAAAAOsQUAAAgHUIKAAAwDoEFAAAYB0CCgAAsM6AAkpZWZlcLpeKi4udMWOMtm7dKp/Pp5SUFM2cOVNnz54N+7nOzk6tW7dOo0aNUmpqqhYuXKhz584NpBUAADCM9DugnDhxQi+88ILGjx8fNr5jxw7t3LlTu3fv1okTJ+T1ejVnzhy1tbU5NcXFxTpw4IAqKyt15MgRXbx4UQsWLFB3d3f/jwQAAAwb/QooFy9e1NKlS7Vnzx6NHDnSGTfG6Pnnn9fTTz+tRx55RH6/Xy+//LI++eQT7d+/X5IUCoX04osv6vvf/75mz56tCRMmqKKiQmfOnNFrr70WnaMCAAAxrV8BZc2aNXrooYc0e/bssPHGxkYFg0HNnTvXGXO73ZoxY4aOHj0qSaqrq9OlS5fCanw+n/x+v1MDAABubwmR/kBlZaVOnjypEydO9NoXDAYlSdnZ2WHj2dnZev/9952apKSksDMvV2qu/PzVOjs71dnZ6XxubW2NtG0AABBDIjqD0tTUpPXr16uiokLJycl91rlcrrDPxpheY1e7Xk1ZWZk8Ho+z5ebmRtI2AACIMREFlLq6OjU3N6uwsFAJCQlKSEhQTU2NfvjDHyohIcE5c3L1mZDm5mZnn9frVVdXl1paWvqsudqWLVsUCoWcrampKZK2YZnuHqNjv/1IPz39oY799iN195ihbgkAYJmILvHMmjVLZ86cCRv727/9W911113avHmz7rzzTnm9XlVXV2vChAmSpK6uLtXU1Gj79u2SpMLCQiUmJqq6ulqLFi2SJAUCAdXX12vHjh3X/HPdbrfcbnfEBwf7VNUHtO1ggwKhDmcsx5OskqICzffnDGFnAACbRBRQ0tLS5Pf7w8ZSU1OVmZnpjBcXF6u0tFT5+fnKz89XaWmpRowYoSVLlkiSPB6PVqxYoQ0bNigzM1MZGRnauHGjxo0b12vRLYaXqvqAVlWc1NXnS4KhDq2qOKnyZRMJKQAASf1YJHsjmzZtUnt7u1avXq2WlhZNmTJFhw8fVlpamlOza9cuJSQkaNGiRWpvb9esWbO0d+9excfHR7sdWKK7x2jbwYZe4USSjCSXpG0HGzSnwKv4uOuvVwIADH8uY0zMLQBobW2Vx+NRKBRSenr6ULeDm3Dstx/p0T3Hb1j3L9+8X1O/kDkIHQEABlsk39+8iweDormt48ZFEdQBAIY3AgoGRVZa37el96cOADC8EVAwKCbnZSjHk6y+Vpe4dPlunsl5GYPZFgDAUgQUDIr4OJdKigokqVdIufK5pKiABbIAAEkEFAyi+f4clS+bKK8n/DKO15PMLcYAgDBRv80YuJ75/hzNKfCqtvGCmts6lJV2+bIOZ04AAJ9FQMGgi49zcSsxAOC6uMQDAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA63MUDDFPdPYbbuQHELAIKMAxV1Qe07WCDAqE/vXwxx5OskqICHogHICZwiQcYZqrqA1pVcTIsnEhSMNShVRUnVVUfGKLOAODmEVCAYaS7x2jbwQaZa+y7MrbtYIO6e65VAQD2IKAAw0ht44VeZ04+y0gKhDpU23hh8JoCgH5gDQowjDS39R1O+lMH4PZjywJ7AgowjGSlJd+4KII6ALcXmxbYc4kHGEYm52Uox5Osvv5fx6XL/7GZnJcxmG0BiAG2LbAnoADDSHycSyVFBZLUK6Rc+VxSVMDzUACEsXGBPQEFGGbm+3NUvmyivJ7wyzheT7LKl03kOSgAerFxgT1rUIBhaL4/R3MKvFYsdANgPxsX2BNQgGEqPs6lqV/IHOo2AMQAGxfYc4kHAIDbnI0L7AkoAADc5mxcYE9AAQAA1i2wZw0KAACQZNcCewIKAABw2LLAnks8AADAOgQUAABgHQIKAACwDgEFAABYh4ACAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA6BBQAAGAdAgoAALAOAQUAAFiHgAIAAKxDQAEAANYhoAAAAOsQUAAAgHUIKAAAwDoEFAAAYB0CCgAAsA4BBQAAWIeAAgAArJMw1A0AADBcdfcY1TZeUHNbh7LSkjU5L0Pxca6hbismEFAAALgFquoD2nawQYFQhzOW40lWSVGB5vtzhrCz2MAlHgAAoqyqPqBVFSfDwokkBUMdWlVxUlX1gSHqLHYQUAAAiKLuHqNtBxtkrrHvyti2gw3q7rlWBa4goAAAEEW1jRd6nTn5LCMpEOpQbeOFwWsqBhFQAACIoua2vsNJf+puVwQUAACiKCstOap1tysCCgAAUTQ5L0M5nmT1dTOxS5fv5pmclzGYbcUcAgoAAFEUH+dSSVGBJPUKKVc+lxQV8DyUG4gooJSXl2v8+PFKT09Xenq6pk6dqp/97GfOfmOMtm7dKp/Pp5SUFM2cOVNnz54N+x2dnZ1at26dRo0apdTUVC1cuFDnzp2LztEAAGCB+f4clS+bKK8n/DKO15Os8mUTeQ7KTXAZY276PqeDBw8qPj5eX/ziFyVJL7/8sr73ve/p1KlTuvvuu7V9+3Y9++yz2rt3r/7iL/5CzzzzjN5880299dZbSktLkyStWrVKBw8e1N69e5WZmakNGzbowoULqqurU3x8/E310draKo/Ho1AopPT09H4cNgAAtx5Pkg0Xyfd3RAHlWjIyMvS9731P3/jGN+Tz+VRcXKzNmzdLuny2JDs7W9u3b9fKlSsVCoU0evRo7du3T4sXL5YknT9/Xrm5uTp06JDmzZsX9QMEAAB2iOT7u99rULq7u1VZWamPP/5YU6dOVWNjo4LBoObOnevUuN1uzZgxQ0ePHpUk1dXV6dKlS2E1Pp9Pfr/fqbmWzs5Otba2hm0AAGD4ijignDlzRp/73Ofkdrv1xBNP6MCBAyooKFAwGJQkZWdnh9VnZ2c7+4LBoJKSkjRy5Mg+a66lrKxMHo/H2XJzcyNtGwAAxJCIA8pf/uVf6vTp0zp+/LhWrVql5cuXq6GhwdnvcoVfWzPG9Bq72o1qtmzZolAo5GxNTU2Rtg0AAGJIxAElKSlJX/ziFzVp0iSVlZXpnnvu0Q9+8AN5vV5J6nUmpLm52Tmr4vV61dXVpZaWlj5rrsXtdjt3Dl3ZAADA8DXg56AYY9TZ2am8vDx5vV5VV1c7+7q6ulRTU6Np06ZJkgoLC5WYmBhWEwgEVF9f79QAAAAkRFL8ne98R1/5yleUm5urtrY2VVZW6pe//KWqqqrkcrlUXFys0tJS5efnKz8/X6WlpRoxYoSWLFkiSfJ4PFqxYoU2bNigzMxMZWRkaOPGjRo3bpxmz559Sw4QAADEnogCyu9+9zs99thjCgQC8ng8Gj9+vKqqqjRnzhxJ0qZNm9Te3q7Vq1erpaVFU6ZM0eHDh51noEjSrl27lJCQoEWLFqm9vV2zZs3S3r17b/oZKAAAYPgb8HNQhgLPQQEAIPYMynNQAAAAbhUCCgAAsA4BBQAAWIeAAgAArENAAQAA1iGgAAAA6xBQAACAdQgoAADAOgQUAABgHQIKAACwDgEFAABYh4ACAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA6BBQAAGAdAgoAALAOAQUAAFiHgAIAAKxDQAEAANYhoAAAAOsQUAAAgHUIKAAAwDoEFAAAYB0CCgAAsA4BBQAAWIeAAgAArENAAQAA1iGgAAAA6xBQAACAdQgoAADAOgQUAABgHQIKAACwDgEFAABYh4ACAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA6BBQAAGAdAgoAALAOAQUAAFiHgAIAAKxDQAEAANYhoAAAAOsQUAAAgHUIKAAAwDoJQ90AAMSy7h6j2sYLam7rUFZasibnZSg+zjXUbQExj4ACAP1UVR/QtoMNCoQ6nLEcT7JKigo0358zhJ0BsY9LPADQD1X1Aa2qOBkWTiQpGOrQqoqTqqoPDFFnwPBAQAGACHX3GG072CBzjX1XxrYdbFB3z7UqANwMAgoAa3T3GB377Uf66ekPdey3H1n7BV/beKHXmZPPMpICoQ7VNl4YvKaAYYY1KACsEEvrOZrb+g4n/akD0BtnUAAMuVhbz5GVlhzVOgC9EVAADKlYXM8xOS9DOZ5k9XUzsUuXz/5MzssYzLaAYYWAAmBIxeJ6jvg4l0qKCiSpV0i58rmkqIDnoQADQEABMKRidT3HfH+OypdNlNcTfhnH60lW+bKJ1q2bAWJNRAGlrKxM9913n9LS0pSVlaWHH35Yb731VliNMUZbt26Vz+dTSkqKZs6cqbNnz4bVdHZ2at26dRo1apRSU1O1cOFCnTt3buBHAyDmxPJ6jvn+HB3Z/GX9yzfv1w++dq/+5Zv368jmLxNOgCiIKKDU1NRozZo1On78uKqrq/Xpp59q7ty5+vjjj52aHTt2aOfOndq9e7dOnDghr9erOXPmqK2tzakpLi7WgQMHVFlZqSNHjujixYtasGCBuru7o3dkAGJCrK/niI9zaeoXMvX/7v28pn4hk8s6QJS4jDH9Xnn2+9//XllZWaqpqdGDDz4oY4x8Pp+Ki4u1efNmSZfPlmRnZ2v79u1auXKlQqGQRo8erX379mnx4sWSpPPnzys3N1eHDh3SvHnzbvjntra2yuPxKBQKKT09vb/tA7DElbt4JIUtlr3yVc8lE2B4iOT7e0BrUEKhkCQpI+Py/9k0NjYqGAxq7ty5To3b7daMGTN09OhRSVJdXZ0uXboUVuPz+eT3+50aALcX1nMAuFq/H9RmjNGTTz6p6dOny+/3S5KCwaAkKTs7O6w2Oztb77//vlOTlJSkkSNH9qq58vNX6+zsVGdnp/O5tbW1v20DsNR8f47mFHh5MzAASQMIKGvXrtVvfvMbHTlypNc+lyv8PyjGmF5jV7teTVlZmbZt29bfVgHEiCvrOQCgX5d41q1bp1dffVVvvPGGxowZ44x7vV5J6nUmpLm52Tmr4vV61dXVpZaWlj5rrrZlyxaFQiFna2pq6k/bAAAgRkQUUIwxWrt2rV555RW9/vrrysvLC9ufl5cnr9er6upqZ6yrq0s1NTWaNm2aJKmwsFCJiYlhNYFAQPX19U7N1dxut9LT08M2AAAwfEV0iWfNmjXav3+/fvrTnyotLc05U+LxeJSSkiKXy6Xi4mKVlpYqPz9f+fn5Ki0t1YgRI7RkyRKndsWKFdqwYYMyMzOVkZGhjRs3aty4cZo9e3b0jxAAAMSciAJKeXm5JGnmzJlh4y+99JIef/xxSdKmTZvU3t6u1atXq6WlRVOmTNHhw4eVlpbm1O/atUsJCQlatGiR2tvbNWvWLO3du1fx8fEDOxoAADAsDOg5KEOF56AAABB7Bu05KAAAALcCAQUAAFiHgAIAAKxDQAEAANYhoAAAAOsQUAAAgHUIKAAAwDoEFAAAYB0CCgAAsA4BBQAAWIeAAgAArENAAQAA1iGgAAAA6xBQAACAdQgoAADAOgQUAABgHQIKAACwDgEFAABYh4ACAACsQ0ABAADWSRjqBgAAg6+7x6i28YKa2zqUlZasyXkZio9zDXVbgIOAAgC3mar6gLYdbFAg1OGM5XiSVVJUoPn+nCHsDPgTLvEAwG2kqj6gVRUnw8KJJAVDHVpVcVJV9YEh6gwIR0ABgNtEd4/RtoMNMtfYd2Vs28EGdfdcqwIYXAQUALhN1DZe6HXm5LOMpECoQ7WNFwavKaAPBBQAuE00t/UdTvpTB9xKBBQAuE1kpSVHtQ64lQgoAHCbmJyXoRxPsvq6mdily3fzTM7LGMy2gGsioADAbSI+zqWSogJJ6hVSrnwuKSrgeSiwAgEFAG4j8/05Kl82UV5P+GUcrydZ5csm8hwUWIMHtQHAbWa+P0dzCrw8SRZWI6AAwG0oPs6lqV/IHOo2gD5xiQcAAFiHgAIAAKxDQAEAANYhoAAAAOsQUAAAgHUIKAAAwDoEFAAAYB0CCgAAsA4BBQAAWIeAAgAArENAAQAA1iGgAAAA6xBQAACAdQgoAADAOglD3QAAADeju8eotvGCmts6lJWWrMl5GYqPcw11W7hFCCgAAOtV1Qe07WCDAqEOZyzHk6ySogLN9+cMYWe4VbjEAwCwWlV9QKsqToaFE0kKhjq0quKkquoDQ9QZbiUCCgDAWt09RtsONshcY9+VsW0HG9Tdc60KxDICCgDAWrWNF3qdOfksIykQ6lBt44XBawqDgoACALBWc1vf4aQ/dYgdBBQAgLWy0pKjWofYQUABAFhrcl6GcjzJ6utmYpcu380zOS9jMNvCICCgAACsFR/nUklRgST1CilXPpcUFfA8lGGIgAIAsNp8f47Kl02U1xN+GcfrSVb5sok8B2WY4kFtAADrzffnaE6BlyfJ3kYIKACAmBAf59LUL2QOdRsYJFziAQAA1ok4oLz55psqKiqSz+eTy+XST37yk7D9xhht3bpVPp9PKSkpmjlzps6ePRtW09nZqXXr1mnUqFFKTU3VwoULde7cuQEdCAAAGD4iDigff/yx7rnnHu3evfua+3fs2KGdO3dq9+7dOnHihLxer+bMmaO2tjanpri4WAcOHFBlZaWOHDmiixcvasGCBeru7u7/kQAAgGHDZYzp9wsMXC6XDhw4oIcffljS5bMnPp9PxcXF2rx5s6TLZ0uys7O1fft2rVy5UqFQSKNHj9a+ffu0ePFiSdL58+eVm5urQ4cOad68eTf8c1tbW+XxeBQKhZSent7f9gEAwCCK5Ps7qmtQGhsbFQwGNXfuXGfM7XZrxowZOnr0qCSprq5Oly5dCqvx+Xzy+/1OzdU6OzvV2toatgEAgOErqgElGAxKkrKzs8PGs7OznX3BYFBJSUkaOXJknzVXKysrk8fjcbbc3Nxotg0AACxzS+7icbnC70s3xvQau9r1arZs2aJQKORsTU1NUesVAADYJ6oBxev1SlKvMyHNzc3OWRWv16uuri61tLT0WXM1t9ut9PT0sA0AAAxfUQ0oeXl58nq9qq6udsa6urpUU1OjadOmSZIKCwuVmJgYVhMIBFRfX+/UAACA21vET5K9ePGi3n33XedzY2OjTp8+rYyMDN1xxx0qLi5WaWmp8vPzlZ+fr9LSUo0YMUJLliyRJHk8Hq1YsUIbNmxQZmamMjIytHHjRo0bN06zZ8++qR6u3HjEYlkAAGLHle/tm7qB2ETojTfeMJJ6bcuXLzfGGNPT02NKSkqM1+s1brfbPPjgg+bMmTNhv6O9vd2sXbvWZGRkmJSUFLNgwQLzwQcf3HQPTU1N1+yBjY2NjY2Nzf6tqanpht/1A3oOylDp6enR+fPnlZaWdsPFt5FqbW1Vbm6umpqaWOtyCzHPg4N5HhzM8+BhrgfHrZpnY4za2trk8/kUF3f9VSYx+bLAuLg4jRkz5pb+GSzGHRzM8+BgngcH8zx4mOvBcSvm2ePx3FQdLwsEAADWIaAAAADrEFCu4na7VVJSIrfbPdStDGvM8+BgngcH8zx4mOvBYcM8x+QiWQAAMLxxBgUAAFiHgAIAAKxDQAEAANYhoAAAAOsQUD7jH//xH5WXl6fk5GQVFhbqV7/61VC3FFPKysp03333KS0tTVlZWXr44Yf11ltvhdUYY7R161b5fD6lpKRo5syZOnv2bFhNZ2en1q1bp1GjRik1NVULFy7UuXPnBvNQYkpZWZlcLpeKi4udMeY5Oj788EMtW7ZMmZmZGjFihO69917V1dU5+5nngfv000/193//98rLy1NKSoruvPNOffe731VPT49Twzz3z5tvvqmioiL5fD65XC795Cc/CdsfrXltaWnRY489Jo/HI4/Ho8cee0x//OMfB34AN/0CnGGusrLSJCYmmj179piGhgazfv16k5qaat5///2hbi1mzJs3z7z00kumvr7enD592jz00EPmjjvuMBcvXnRqnnvuOZOWlmZ+/OMfmzNnzpjFixebnJwc09ra6tQ88cQT5vOf/7yprq42J0+eNH/1V39l7rnnHvPpp58OxWFZrba21vz5n/+5GT9+vFm/fr0zzjwP3IULF8zYsWPN448/bn7961+bxsZG89prr5l3333XqWGeB+6ZZ54xmZmZ5t///d9NY2Oj+bd/+zfzuc99zjz//PNODfPcP4cOHTJPP/20+fGPf2wkmQMHDoTtj9a8zp8/3/j9fnP06FFz9OhR4/f7zYIFCwbcPwHl/0yePNk88cQTYWN33XWXeeqpp4aoo9jX3NxsJJmamhpjzOUXSXq9XvPcc885NR0dHcbj8Zh/+qd/MsYY88c//tEkJiaayspKp+bDDz80cXFxpqqqanAPwHJtbW0mPz/fVFdXmxkzZjgBhXmOjs2bN5vp06f3uZ95jo6HHnrIfOMb3wgbe+SRR8yyZcuMMcxztFwdUKI1rw0NDUaSOX78uFNz7NgxI8n8z//8z4B65hKPpK6uLtXV1Wnu3Llh43PnztXRo0eHqKvYFwqFJEkZGRmSpMbGRgWDwbB5drvdmjFjhjPPdXV1unTpUliNz+eT3+/n7+Iqa9as0UMPPaTZs2eHjTPP0fHqq69q0qRJ+upXv6qsrCxNmDBBe/bscfYzz9Exffp0/eIXv9Dbb78tSfqv//ovHTlyRH/9138tiXm+VaI1r8eOHZPH49GUKVOcmvvvv18ej2fAcx+TLwuMtj/84Q/q7u5WdnZ22Hh2draCweAQdRXbjDF68sknNX36dPn9fkly5vJa8/z+++87NUlJSRo5cmSvGv4u/qSyslInT57UiRMneu1jnqPjvffeU3l5uZ588kl95zvfUW1trb71rW/J7Xbr61//OvMcJZs3b1YoFNJdd92l+Ph4dXd369lnn9Wjjz4qiX/Pt0q05jUYDCorK6vX78/Kyhrw3BNQPsPlcoV9Nsb0GsPNWbt2rX7zm9/oyJEjvfb1Z575u/iTpqYmrV+/XocPH1ZycnKfdczzwPT09GjSpEkqLS2VJE2YMEFnz55VeXm5vv71rzt1zPPA/Ou//qsqKiq0f/9+3X333Tp9+rSKi4vl8/m0fPlyp455vjWiMa/Xqo/G3HOJR9KoUaMUHx/fK+01Nzf3Spe4sXXr1unVV1/VG2+8oTFjxjjjXq9Xkq47z16vV11dXWppaemz5nZXV1en5uZmFRYWKiEhQQkJCaqpqdEPf/hDJSQkOPPEPA9MTk6OCgoKwsa+9KUv6YMPPpDEv+do+bu/+zs99dRT+trXvqZx48bpscce07e//W2VlZVJYp5vlWjNq9fr1e9+97tev//3v//9gOeegCIpKSlJhYWFqq6uDhuvrq7WtGnThqir2GOM0dq1a/XKK6/o9ddfV15eXtj+vLw8eb3esHnu6upSTU2NM8+FhYVKTEwMqwkEAqqvr+fv4v/MmjVLZ86c0enTp51t0qRJWrp0qU6fPq0777yTeY6CBx54oNdt8m+//bbGjh0riX/P0fLJJ58oLi78qyg+Pt65zZh5vjWiNa9Tp05VKBRSbW2tU/PrX/9aoVBo4HM/oCW2w8iV24xffPFF09DQYIqLi01qaqr53//936FuLWasWrXKeDwe88tf/tIEAgFn++STT5ya5557zng8HvPKK6+YM2fOmEcfffSat7WNGTPGvPbaa+bkyZPmy1/+8m1/u+CNfPYuHmOY52iora01CQkJ5tlnnzXvvPOO+ed//mczYsQIU1FR4dQwzwO3fPly8/nPf965zfiVV14xo0aNMps2bXJqmOf+aWtrM6dOnTKnTp0ykszOnTvNqVOnnMdnRGte58+fb8aPH2+OHTtmjh07ZsaNG8dtxtH2D//wD2bs2LEmKSnJTJw40bk9FjdH0jW3l156yanp6ekxJSUlxuv1GrfbbR588EFz5syZsN/T3t5u1q5dazIyMkxKSopZsGCB+eCDDwb5aGLL1QGFeY6OgwcPGr/fb9xut7nrrrvMCy+8ELafeR641tZWs379enPHHXeY5ORkc+edd5qnn37adHZ2OjXMc/+88cYb1/xv8vLly40x0ZvXjz76yCxdutSkpaWZtLQ0s3TpUtPS0jLg/l3GGDOwczAAAADRxRoUAABgHQIKAACwDgEFAABYh4ACAACsQ0ABAADWIaAAAADrEFAAAIB1CCgAAMA6BBQAAGAdAgoAALAOAQUAAFiHgAIAAKzz/wH8F5zKaZrpTwAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -1631,7 +1817,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 43, "id": "2114d0c3-cdad-43c7-9ffa-50c36d56d18f", "metadata": {}, "outputs": [ @@ -1644,9 +1830,9 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "clusterwith_prebuilt\n", "\n", "with_prebuilt: Workflow\n", @@ -1845,16 +2031,16 @@ "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 38, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "wf.draw(depth=0)" + "wf.draw(depth=0, size=(10, 10))" ] }, { @@ -1880,66 +2066,71 @@ "source": [ "# Macros\n", "\n", - "Once you have a workflow that you're happy with, you may want to store it as a macro so it can be stored in a human-readable way, reused, shared, and executed with more efficiency than the \"living\" `Workflow` instance. Automated conversion of an existing `Workflow` instance into a `Macro` subclass is still on the TODO list, but defining a new macro is pretty easy: they are just composite nodes that have a function defining their graph setup:" + "Once you have a workflow that you're happy with, you may want to store it as a macro so it can be stored in a human-readable way, reused, shared, and executed with more efficiency than the \"living\" `Workflow` instance. Defining a new macro is pretty easy -- they are just composite nodes that have a function defining their input, graph setup, and output analogous to how `Function` nodes define their node function" ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 44, "id": "c71a8308-f8a1-4041-bea0-1c841e072a6d", "metadata": {}, "outputs": [], "source": [ - "from pyiron_workflow.macro import Macro" + "from pyiron_workflow.macro import macro_node" ] }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 45, "id": "2b9bb21a-73cd-444e-84a9-100e202aa422", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n", - " warn(\n" - ] - }, { "data": { "text/plain": [ "13" ] }, - "execution_count": 40, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "@Workflow.wrap_as.function_node(\"result\")\n", + "@Workflow.wrap.as_function_node(\"result\")\n", "def AddOne(x):\n", " return x + 1\n", "\n", - "def add_three_macro(macro: Macro) -> None:\n", + "def add_three_macro(self, x):\n", " \"\"\"\n", - " The graph constructor a Macro expects must take the macro as its only argument\n", - " (i.e. \"self\" from the macro's perspective) and return nothing.\n", - " Inside, it should add nodes to the macro, wire their connections, etc.\n", + " The macro constructor `macro_node` expects the provided function \n", + " to take a `Macro` instance as its first argument, followed by any input data,\n", + " and for output data to be returned `HasChannel` objects (i.e. a single-value \n", + " `Node` or an output data channel) \n", + " In the function body, it should add nodes to the macro, wire their connections, etc.\n", " \"\"\"\n", - " macro.add_one = AddOne(0)\n", - " macro.add_two = AddOne(macro.add_one)\n", - " macro.add_three = AddOne(macro.add_two)\n", + " self.add_one = AddOne(x)\n", + " self.add_two = AddOne(self.add_one)\n", + " self.add_three = AddOne(self.add_two)\n", " # Just like workflows, for simple DAG macros we don't _need_\n", " # to set signals and starting nodes -- the macro will build them\n", " # automatically. But, if you do set both then the macro will use them\n", - " macro.add_one >> macro.add_two >> macro.add_three\n", - " macro.starting_nodes = [macro.add_one] \n", + " self.add_one >> self.add_two >> self.add_three\n", + " self.starting_nodes = [self.add_one] \n", + " # We _do_ need to specify the output of our macro,\n", + " # which will typically be output channel(s) and/or single-return node(s)\n", + " return self.add_three\n", " \n", - "macro = Macro(add_three_macro)\n", - "macro(add_one__x=10).add_three__result" + "macro = macro_node(add_three_macro)\n", + "macro(x=10).add_three" + ] + }, + { + "cell_type": "markdown", + "id": "28323b14-22c2-4bab-bea7-168505677ebf", + "metadata": {}, + "source": [ + "Note that the input and output channel labels are scraped from the decorated function. This is just like for `function_node`, but with one big exception: the `\"self.\"` has been stripped off the returned value! Since the most likely thing to return is some child node, this is just a quality of life shortcut. Passing `output_labels=...` still works just like for `function_node`." ] }, { @@ -1947,7 +2138,7 @@ "id": "d4f797d6-8d88-415f-bb9c-00f3e1b15e37", "metadata": {}, "source": [ - "Even in the abscence of an automated converter, it should be easy to take the workflow you've been developing and copy-paste that code into a function -- then bam, you've got a macro!" + "It will often be the case that this new macro will be made by copying and pasting some `wf = Workflow(...); ...` code that was explored. The use of `self` here reflects the canonical name for the own-instance argument, but for the sake of defining the function any variable will do! So you can use defintions like `def add_three_macro(wf, x):` just fine if that makes copy-pasting easier." ] }, { @@ -1955,45 +2146,46 @@ "id": "bd5099c4-1c01-4a45-a5bb-e5087595db9f", "metadata": {}, "source": [ - "Of course, we can also use a decorator like for other node types. Just like workflows, we can use `inputs_map` and `outputs_map` to control macro-level IO, but macros also allow us to use a more function-like interface where the callable that creates the graph has args and/or kwargs, and/or has return values and output labels. In these cases, the I/O switches over to a \"whitelist\" paradigm where all the child IO we _don't explicitly mention_ gets _disabled and hidden_. The maps always take precedence, and both approaches are equivalent, so it's really just a question of whether it's less typing to use the maps to turn off/relabel stuff you _don't_ want, or use the callable definition to specify which stuff you _do_ want. Typically the latter is easier.\n", - "\n", - "Let's take a look at this, where we use the function defintion to control most of our IO, but still leverage the map to expose something that would normally be hidden (even in the workflows, since it's connected):" + "Of course, we can also use a decorator like for funciton nodes:" ] }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 46, "id": "3668f9a9-adca-48a4-84ea-13add965897c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'intermediate': 102, 'plus_three': 103}" + "{'add_two': 102, 'add_three': 103}" ] }, - "execution_count": 41, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "@Workflow.wrap_as.macro_node(\"plus_three\")\n", - "def AddThree(macro: Macro, x: int = 0):\n", + "@Workflow.wrap.as_macro_node()\n", + "def AddThree(macro, x: int = 0):\n", " \"\"\"\n", - " The graph constructor a Macro expects must take the macro as its only argument\n", - " (i.e. \"self\" from the macro's perspective) and return nothing.\n", - " Inside, it should add nodes to the macro, wire their connections, etc.\n", + " The function decorator `as_macro_node` expects the decorated function \n", + " to take a `Macro` instance as its first argument, followed by any input data,\n", + " and for output data to be returned `HasChannel` objects (i.e. a single-value \n", + " `Node` or an output data channel) \n", + " In the function body, it should add nodes to the macro, wire their connections, etc.\n", " \"\"\"\n", " macro.add_one = AddOne(x) # Directly use the input from the signature\n", " # Under the hood a new `UserInput` node is being created and used\n", " macro.add_two = AddOne(macro.add_one)\n", " macro.add_three = AddOne(macro.add_two)\n", " macro.outputs_map = {\"add_two__result\": \"intermediate\"}\n", - " # return macro.add_three.outputs.result\n", " # We need to return something like output channels, but since AddOne has \n", " # only a single output channel, we can return it directly.\n", - " return macro.add_three\n", + " # We also return an intermediate value that would not normally be \n", + " # exposed if this were a workflow since it's connected to other child channels\n", + " return macro.add_two, macro.add_three\n", " \n", "macro = AddThree()\n", "macro(x=100)\n", @@ -2014,26 +2206,55 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 47, "id": "9aaeeec0-5f88-4c94-a6cc-45b56d2f0111", "metadata": {}, "outputs": [], "source": [ - "@Workflow.wrap_as.macro_node(\"structure\", \"energy\")\n", - "def LammpsMinimize(macro, element: str, crystalstructure: str, lattice_guess: float | int):\n", - " macro.structure = macro.create.pyiron_atomistics.Bulk(\n", + "@Workflow.wrap.as_macro_node(\"structure\", \"energy\")\n", + "def LammpsMinimize(self, element: str, crystalstructure: str, lattice_guess: float | int):\n", + " self.structure = self.create.pyiron_atomistics.Bulk(\n", " name=element,\n", " crystalstructure=crystalstructure,\n", " a=lattice_guess\n", " )\n", - " macro.engine = macro.create.pyiron_atomistics.Lammps(structure=macro.structure)\n", - " macro.calc = macro.create.pyiron_atomistics.CalcMin(job=macro.engine, pressure=0)\n", - " return macro.structure, macro.calc.outputs.energy_pot" + " self.engine = self.create.pyiron_atomistics.Lammps(structure=self.structure)\n", + " self.calc = self.create.pyiron_atomistics.CalcMin(job=self.engine, pressure=0)\n", + " return self.structure, self.calc.outputs.energy_pot" ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 48, + "id": "26a080dc-acaf-45bb-9935-7a42ff8d9552", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'structure': None, 'energy': None}" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "LammpsMinimize.preview_outputs()" + ] + }, + { + "cell_type": "markdown", + "id": "4dfe9c0c-e9e7-4d5f-ad34-e19fd0382670", + "metadata": {}, + "source": [ + "Note that while `\"self.\"` will get stripped off our return channel names, we're not allowed to have other `\".\"` characters in what remains -- so here where we're mixing and matching a returned (single-return-value) node and an explicit output channel (from a node with more than one output), we need to provide output labels. We could alternatively have given a nicely named local variable, e.g. `energy = self.calc.outputs.energy_pot; return return self.structure, energy` to get the same result." + ] + }, + { + "cell_type": "code", + "execution_count": 49, "id": "a832e552-b3cc-411a-a258-ef21574fc439", "metadata": {}, "outputs": [], @@ -2052,7 +2273,7 @@ "\n", "# Or we could write a node to do that:\n", "\n", - "# @Workflow.wrap_as.function_node()\n", + "# @Workflow.wrap.as_function_node()\n", "# def PerAtomEnergyDifference(structure1, energy1, structure2, energy2):\n", "# # The unrelaxed structure is fine, we're just using it to get n_atoms\n", "# sub = (energy2[-1]/len(structure2)) - (energy1[-1]/len(structure1))\n", @@ -2076,7 +2297,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 50, "id": "b764a447-236f-4cb7-952a-7cba4855087d", "metadata": {}, "outputs": [ @@ -2089,12 +2310,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "clusterphase_preference\n", - "\n", - "phase_preference: Workflow\n", + "\n", + "phase_preference: Workflow\n", "\n", "clusterphase_preferenceInputs\n", "\n", @@ -2109,24 +2330,24 @@ "\n", "clusterphase_preferenceOutputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Outputs\n", "\n", "\n", "clusterphase_preferenceelement\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "element: UserInput\n", + "\n", + "element: UserInput\n", "\n", "\n", "clusterphase_preferenceelementInputs\n", @@ -2140,312 +2361,312 @@ "Inputs\n", "\n", "\n", - "clusterphase_preferenceelementOutputs\n", + "clusterphase_preferenceelementOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterphase_preferencemin_phase1\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "min_phase1: LammpsMinimize\n", + "\n", + "min_phase1: LammpsMinimize\n", "\n", "\n", "clusterphase_preferencemin_phase1Inputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterphase_preferencemin_phase1Outputs\n", + "clusterphase_preferencemin_phase1OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterphase_preferencemin_phase2\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "min_phase2: LammpsMinimize\n", + "\n", + "min_phase2: LammpsMinimize\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterphase_preferencemin_phase2Outputs\n", + "clusterphase_preferencemin_phase2OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterphase_preferencee1\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "e1: GetItem\n", + "\n", + "e1: GetItem\n", "\n", "\n", "clusterphase_preferencee1Inputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterphase_preferencee1Outputs\n", + "clusterphase_preferencee1OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterphase_preferencen1\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "n1: Length\n", + "\n", + "n1: Length\n", "\n", "\n", "clusterphase_preferencen1Inputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterphase_preferencen1Outputs\n", + "clusterphase_preferencen1OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterphase_preferencee2\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "e2: GetItem\n", + "\n", + "e2: GetItem\n", "\n", "\n", "clusterphase_preferencee2Inputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterphase_preferencee2Outputs\n", + "clusterphase_preferencee2OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterphase_preferencen2\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "n2: Length\n", + "\n", + "n2: Length\n", "\n", "\n", "clusterphase_preferencen2Inputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterphase_preferencen2Outputs\n", + "clusterphase_preferencen2OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterphase_preferencee2__getitem_Divide_n2__len\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "e2__getitem_Divide_n2__len: Divide\n", + "\n", + "e2__getitem_Divide_n2__len: Divide\n", "\n", "\n", "clusterphase_preferencee2__getitem_Divide_n2__lenInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterphase_preferencee2__getitem_Divide_n2__lenOutputs\n", + "clusterphase_preferencee2__getitem_Divide_n2__lenOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterphase_preferencee1__getitem_Divide_n1__len\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "e1__getitem_Divide_n1__len: Divide\n", + "\n", + "e1__getitem_Divide_n1__len: Divide\n", "\n", "\n", "clusterphase_preferencee1__getitem_Divide_n1__lenInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterphase_preferencee1__getitem_Divide_n1__lenOutputs\n", + "clusterphase_preferencee1__getitem_Divide_n1__lenOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterphase_preferencecompare\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "compare: Subtract\n", + "\n", + "compare: Subtract\n", "\n", "\n", "clusterphase_preferencecompareInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterphase_preferencecompareOutputs\n", + "clusterphase_preferencecompareOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "\n", @@ -2456,8 +2677,8 @@ "\n", "\n", "clusterphase_preferenceOutputsran\n", - "\n", - "ran\n", + "\n", + "ran\n", "\n", "\n", "\n", @@ -2494,15 +2715,15 @@ "\n", "\n", "clusterphase_preferencemin_phase1Inputscrystalstructure\n", - "\n", - "crystalstructure: str\n", + "\n", + "crystalstructure: str\n", "\n", "\n", "\n", "clusterphase_preferenceInputsphase1->clusterphase_preferencemin_phase1Inputscrystalstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -2513,15 +2734,15 @@ "\n", "\n", "clusterphase_preferencemin_phase1Inputslattice_guess\n", - "\n", - "lattice_guess\n", + "\n", + "lattice_guess\n", "\n", "\n", "\n", "clusterphase_preferenceInputslattice_guess1->clusterphase_preferencemin_phase1Inputslattice_guess\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -2532,15 +2753,15 @@ "\n", "\n", "clusterphase_preferencemin_phase2Inputscrystalstructure\n", - "\n", - "crystalstructure: str\n", + "\n", + "crystalstructure: str\n", "\n", "\n", "\n", "clusterphase_preferenceInputsphase2->clusterphase_preferencemin_phase2Inputscrystalstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -2551,15 +2772,15 @@ "\n", "\n", "clusterphase_preferencemin_phase2Inputslattice_guess\n", - "\n", - "lattice_guess\n", + "\n", + "lattice_guess\n", "\n", "\n", "\n", "clusterphase_preferenceInputslattice_guess2->clusterphase_preferencemin_phase2Inputslattice_guess\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -2570,15 +2791,15 @@ "\n", "\n", "clusterphase_preferencee1Inputsitem\n", - "\n", - "item\n", + "\n", + "item\n", "\n", "\n", "\n", "clusterphase_preferenceInputse1__item->clusterphase_preferencee1Inputsitem\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -2589,21 +2810,21 @@ "\n", "\n", "clusterphase_preferencee2Inputsitem\n", - "\n", - "item\n", + "\n", + "item\n", "\n", "\n", "\n", "clusterphase_preferenceInputse2__item->clusterphase_preferencee2Inputsitem\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferenceOutputscompare__sub\n", - "\n", - "compare__sub\n", + "\n", + "compare__sub\n", "\n", "\n", "\n", @@ -2611,433 +2832,433 @@ "\n", "run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferenceelementOutputsran\n", - "\n", - "ran\n", + "clusterphase_preferenceelementOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterphase_preferenceelementInputsaccumulate_and_run\n", "\n", "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferenceelementOutputsuser_input\n", - "\n", - "user_input\n", + "clusterphase_preferenceelementOutputsWithInjectionuser_input\n", + "\n", + "user_input\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase1Inputselement\n", - "\n", - "element: str\n", + "\n", + "element: str\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferenceelementOutputsuser_input->clusterphase_preferencemin_phase1Inputselement\n", - "\n", - "\n", - "\n", + "clusterphase_preferenceelementOutputsWithInjectionuser_input->clusterphase_preferencemin_phase1Inputselement\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputselement\n", - "\n", - "element: str\n", + "\n", + "element: str\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferenceelementOutputsuser_input->clusterphase_preferencemin_phase2Inputselement\n", - "\n", - "\n", - "\n", + "clusterphase_preferenceelementOutputsWithInjectionuser_input->clusterphase_preferencemin_phase2Inputselement\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase1Inputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencemin_phase1Outputsran\n", - "\n", - "ran\n", + "clusterphase_preferencemin_phase1OutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterphase_preferencemin_phase1Inputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencemin_phase1Outputsstructure\n", - "\n", - "structure\n", + "clusterphase_preferencemin_phase1OutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", "\n", "\n", "clusterphase_preferencen1Inputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencemin_phase1Outputsstructure->clusterphase_preferencen1Inputsobj\n", - "\n", - "\n", - "\n", + "clusterphase_preferencemin_phase1OutputsWithInjectionstructure->clusterphase_preferencen1Inputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencemin_phase1Outputsenergy\n", - "\n", - "energy\n", + "clusterphase_preferencemin_phase1OutputsWithInjectionenergy\n", + "\n", + "energy\n", "\n", "\n", "\n", "clusterphase_preferencee1Inputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencemin_phase1Outputsenergy->clusterphase_preferencee1Inputsobj\n", - "\n", - "\n", - "\n", + "clusterphase_preferencemin_phase1OutputsWithInjectionenergy->clusterphase_preferencee1Inputsobj\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencemin_phase2Outputsran\n", - "\n", - "ran\n", + "clusterphase_preferencemin_phase2OutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencemin_phase2Outputsstructure\n", - "\n", - "structure\n", + "clusterphase_preferencemin_phase2OutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", "\n", "\n", "clusterphase_preferencen2Inputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencemin_phase2Outputsstructure->clusterphase_preferencen2Inputsobj\n", - "\n", - "\n", - "\n", + "clusterphase_preferencemin_phase2OutputsWithInjectionstructure->clusterphase_preferencen2Inputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencemin_phase2Outputsenergy\n", - "\n", - "energy\n", + "clusterphase_preferencemin_phase2OutputsWithInjectionenergy\n", + "\n", + "energy\n", "\n", "\n", "\n", "clusterphase_preferencee2Inputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencemin_phase2Outputsenergy->clusterphase_preferencee2Inputsobj\n", - "\n", - "\n", - "\n", + "clusterphase_preferencemin_phase2OutputsWithInjectionenergy->clusterphase_preferencee2Inputsobj\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencee1Inputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee1Outputsran\n", - "\n", - "ran\n", + "clusterphase_preferencee1OutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterphase_preferencee1Inputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee1Outputsgetitem\n", - "\n", - "getitem\n", + "clusterphase_preferencee1OutputsWithInjectiongetitem\n", + "\n", + "getitem\n", "\n", "\n", "\n", "clusterphase_preferencee1__getitem_Divide_n1__lenInputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee1Outputsgetitem->clusterphase_preferencee1__getitem_Divide_n1__lenInputsobj\n", - "\n", - "\n", - "\n", + "clusterphase_preferencee1OutputsWithInjectiongetitem->clusterphase_preferencee1__getitem_Divide_n1__lenInputsobj\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencen1Inputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencen1Outputsran\n", - "\n", - "ran\n", + "clusterphase_preferencen1OutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterphase_preferencen1Inputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencen1Outputslen\n", - "\n", - "len\n", + "clusterphase_preferencen1OutputsWithInjectionlen\n", + "\n", + "len\n", "\n", "\n", "\n", "clusterphase_preferencee1__getitem_Divide_n1__lenInputsother\n", - "\n", - "other\n", + "\n", + "other\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencen1Outputslen->clusterphase_preferencee1__getitem_Divide_n1__lenInputsother\n", - "\n", - "\n", - "\n", + "clusterphase_preferencen1OutputsWithInjectionlen->clusterphase_preferencee1__getitem_Divide_n1__lenInputsother\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencee2Inputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee2Outputsran\n", - "\n", - "ran\n", + "clusterphase_preferencee2OutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterphase_preferencee2Inputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee2Outputsgetitem\n", - "\n", - "getitem\n", + "clusterphase_preferencee2OutputsWithInjectiongetitem\n", + "\n", + "getitem\n", "\n", "\n", "\n", "clusterphase_preferencee2__getitem_Divide_n2__lenInputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee2Outputsgetitem->clusterphase_preferencee2__getitem_Divide_n2__lenInputsobj\n", - "\n", - "\n", - "\n", + "clusterphase_preferencee2OutputsWithInjectiongetitem->clusterphase_preferencee2__getitem_Divide_n2__lenInputsobj\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencen2Inputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencen2Outputsran\n", - "\n", - "ran\n", + "clusterphase_preferencen2OutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterphase_preferencen2Inputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencen2Outputslen\n", - "\n", - "len\n", + "clusterphase_preferencen2OutputsWithInjectionlen\n", + "\n", + "len\n", "\n", "\n", "\n", "clusterphase_preferencee2__getitem_Divide_n2__lenInputsother\n", - "\n", - "other\n", + "\n", + "other\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencen2Outputslen->clusterphase_preferencee2__getitem_Divide_n2__lenInputsother\n", - "\n", - "\n", - "\n", + "clusterphase_preferencen2OutputsWithInjectionlen->clusterphase_preferencee2__getitem_Divide_n2__lenInputsother\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencee2__getitem_Divide_n2__lenInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee2__getitem_Divide_n2__lenOutputsran\n", - "\n", - "ran\n", + "clusterphase_preferencee2__getitem_Divide_n2__lenOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterphase_preferencee2__getitem_Divide_n2__lenInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee2__getitem_Divide_n2__lenOutputstruediv\n", - "\n", - "truediv\n", + "clusterphase_preferencee2__getitem_Divide_n2__lenOutputsWithInjectiontruediv\n", + "\n", + "truediv\n", "\n", "\n", "\n", "clusterphase_preferencecompareInputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee2__getitem_Divide_n2__lenOutputstruediv->clusterphase_preferencecompareInputsobj\n", - "\n", - "\n", - "\n", + "clusterphase_preferencee2__getitem_Divide_n2__lenOutputsWithInjectiontruediv->clusterphase_preferencecompareInputsobj\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencee1__getitem_Divide_n1__lenInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee1__getitem_Divide_n1__lenOutputsran\n", - "\n", - "ran\n", + "clusterphase_preferencee1__getitem_Divide_n1__lenOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterphase_preferencee1__getitem_Divide_n1__lenInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee1__getitem_Divide_n1__lenOutputstruediv\n", - "\n", - "truediv\n", + "clusterphase_preferencee1__getitem_Divide_n1__lenOutputsWithInjectiontruediv\n", + "\n", + "truediv\n", "\n", "\n", "\n", "clusterphase_preferencecompareInputsother\n", - "\n", - "other\n", + "\n", + "other\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencee1__getitem_Divide_n1__lenOutputstruediv->clusterphase_preferencecompareInputsother\n", - "\n", - "\n", - "\n", + "clusterphase_preferencee1__getitem_Divide_n1__lenOutputsWithInjectiontruediv->clusterphase_preferencecompareInputsother\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencecompareInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencecompareOutputsran\n", - "\n", - "ran\n", + "clusterphase_preferencecompareOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterphase_preferencecompareInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencecompareOutputssub\n", - "\n", - "sub\n", + "clusterphase_preferencecompareOutputsWithInjectionsub\n", + "\n", + "sub\n", "\n", - "\n", + "\n", "\n", - "clusterphase_preferencecompareOutputssub->clusterphase_preferenceOutputscompare__sub\n", - "\n", - "\n", - "\n", + "clusterphase_preferencecompareOutputsWithInjectionsub->clusterphase_preferenceOutputscompare__sub\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 44, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -3048,7 +3269,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 51, "id": "b51bef25-86c5-4d57-80c1-ab733e703caf", "metadata": {}, "outputs": [ @@ -3062,7 +3283,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "10bc7d78116d405f9445576999089d6e", + "model_id": "96a6cca822b941fcb90fe69ff872c8e1", "version_major": 2, "version_minor": 0 }, @@ -3083,7 +3304,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "3a33a223220d475b805db9af950b068a", + "model_id": "6abd71c361d048378a883dcd6cc7fbfc", "version_major": 2, "version_minor": 0 }, @@ -3109,18 +3330,10 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 52, "id": "091e2386-0081-436c-a736-23d019bd9b91", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel ran was not connected to accumulate_and_run, andthus could not disconnect from it.\n", - " warn(\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -3131,7 +3344,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "5d2630241a0544b68deadcb144311159", + "model_id": "346248ad2b6d4d59b6074a2fe1c95d3c", "version_major": 2, "version_minor": 0 }, @@ -3152,7 +3365,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "cf41692ecd294d789f9d62c90fc7713c", + "model_id": "d84076a513ab47ceb3f2509ba49f3f2c", "version_major": 2, "version_minor": 0 }, @@ -3190,30 +3403,13 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 53, "id": "4cdffdca-48d3-4486-9045-48102c7e5f31", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel job was not connected to job, andthus could not disconnect from it.\n", - " warn(\n", - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel accumulate_and_run was not connected to ran, andthus could not disconnect from it.\n", - " warn(\n", - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel element was not connected to user_input, andthus could not disconnect from it.\n", - " warn(\n", - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel structure was not connected to obj, andthus could not disconnect from it.\n", - " warn(\n", - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel energy was not connected to obj, andthus could not disconnect from it.\n", - " warn(\n" - ] - } - ], + "outputs": [], "source": [ "replacee = wf.min_phase1.calc \n", - "wf.min_phase1.calc = Macro.create.pyiron_atomistics.CalcStatic" + "wf.min_phase1.calc = Workflow.create.pyiron_atomistics.CalcStatic" ] }, { @@ -3228,18 +3424,10 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 54, "id": "ed4a3a22-fc3a-44c9-9d4f-c65bc1288889", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel ran was not connected to accumulate_and_run, andthus could not disconnect from it.\n", - " warn(\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -3250,7 +3438,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "16ff55bb9a8a49a88d5c71db0f560958", + "model_id": "a808a4f83e4d4ae2aaea4e6f2dc2590c", "version_major": 2, "version_minor": 0 }, @@ -3271,7 +3459,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0686c706f5324b8796eff8970af59900", + "model_id": "5c1f7a32a7934cd5a3af65403789d94d", "version_major": 2, "version_minor": 0 }, @@ -3298,18 +3486,10 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 55, "id": "5a985cbf-c308-4369-9223-b8a37edb8ab1", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel ran was not connected to accumulate_and_run, andthus could not disconnect from it.\n", - " warn(\n" - ] - }, { "name": "stdout", "output_type": "stream", @@ -3320,7 +3500,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "315bfc47d76a4d9aa7ce95fad9ff6fdf", + "model_id": "e2e4269fb9a44bc8b9d6c6293830973e", "version_major": 2, "version_minor": 0 }, @@ -3341,7 +3521,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "59716bf30a52447eb55ed57663789f9b", + "model_id": "93b31e4a2a0e410db108371bde980633", "version_major": 2, "version_minor": 0 }, @@ -3389,14 +3569,16 @@ "source": [ "## Parallelization\n", "\n", - "You can currently run nodes in different process by setting that node's `executor` to an instance of a compliant executor object -- i.e. that takes the standard `submit` method of `concurrent.futures.Executor`, returns a `concurrent.futures.Future` (or sub-class), and can handle serializing dynamically defined objects. We make a handful of such compliant executors available on the creator. There is a toy `CloudpickleProcessPoolExecutor` which is a minimal example of compliance and useful for learning, but we also link to the executors provided by `pympipool`. Depending on your installation of `pympipool`, it's possible that only the `PyMPIExecutor` will be available, and this is what is provided by default under the creator's `Executor` attribute. \n", + "You can currently run nodes in different process by setting that node's `executor` to an instance of a compliant executor object -- i.e. that takes the standard `submit` method of `concurrent.futures.Executor`, returns a `concurrent.futures.Future` (or sub-class). The built-in `Node` instances (workflows, macros, function nodes, etc.) are `pickle`-compliant, and thus will work with a standard `ProcessPoolExecutor` or `ThreadPoolExecutor` from `concurrent.futures`.\n", + "\n", + "For the case of `ProcessPoolExecutor`, the other process needs to be able to find an import the nodes, so they can't have been created in `__main__` (e.g. here in notebook) but need to be in some importable `.py` file. You might also want to have a node holding un-pickleable data. For these cases, we make a couple more flexible executors available on the creator. There is a toy `CloudpickleProcessPoolExecutor` which is a minimal example of handling dynamically defined/un-picklable data and useful for learning, but we also link to `pympipool.Executor`, which is both flexible and powerful. This is the default `Workflow.create.Executor`.\n", "\n", "Here's a simple example of executor usage:" ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 56, "id": "aa575249-b209-4e0c-9ea6-a82bc69dc833", "metadata": {}, "outputs": [ @@ -3405,7 +3587,7 @@ "output_type": "stream", "text": [ "None 1\n", - " NOT_DATA\n" + " 5\n" ] } ], @@ -3432,7 +3614,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 57, "id": "c1b7b4e9-1c76-470c-ba6e-a58ea3f611f6", "metadata": {}, "outputs": [ @@ -3441,7 +3623,11 @@ "output_type": "stream", "text": [ "Finally 5\n", - "b (Add) output single-value: 6\n" + "b (Add):\n", + "Inputs ['obj', 'other']\n", + "OutputsWithInjection ['add']\n", + "InputSignals ['run', 'accumulate_and_run']\n", + "OutputSignals ['ran']\n" ] } ], @@ -3460,7 +3646,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 58, "id": "7e98058b-a791-4cb1-ae2c-864ad7e56cee", "metadata": {}, "outputs": [], @@ -3478,7 +3664,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 59, "id": "0d1b4005-488e-492f-adcb-8ad7235e4fe3", "metadata": {}, "outputs": [ @@ -3487,9 +3673,13 @@ "output_type": "stream", "text": [ "None 1\n", - " NOT_DATA\n", + " 5\n", "Finally 5\n", - "b (Add) output single-value: 6\n" + "b (Add):\n", + "Inputs ['obj', 'other']\n", + "OutputsWithInjection ['add']\n", + "InputSignals ['run', 'accumulate_and_run']\n", + "OutputSignals ['ran']\n" ] } ], @@ -3522,7 +3712,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 60, "id": "d03ca074-35a0-4e0d-9377-d4eaa5521f85", "metadata": {}, "outputs": [], @@ -3530,18 +3720,13 @@ "from time import perf_counter, sleep\n", "\n", "from pyiron_workflow.channels import NOT_DATA\n", - "\n", - "@Workflow.wrap_as.function_node()\n", - "def Wait(t):\n", - " sleep(t)\n", - " return True\n", " \n", "t = 2" ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 61, "id": "a7c07aa0-84fc-4f43-aa4f-6498c0837d76", "metadata": {}, "outputs": [ @@ -3549,15 +3734,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "6.018623271003889\n" + "6.00604566000402\n" ] } ], "source": [ "wf = Workflow(\"serial\")\n", - "wf.a = Wait(t)\n", - "wf.b = Wait(t)\n", - "wf.c = Wait(t)\n", + "wf.a = Workflow.create.standard.Sleep(t)\n", + "wf.b = Workflow.create.standard.Sleep(t)\n", + "wf.c = Workflow.create.standard.Sleep(t)\n", "wf.d = wf.create.standard.UserInput(t)\n", "wf.automate_execution = False\n", "wf.a >> wf.b >> wf.c >> wf.d\n", @@ -3573,7 +3758,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 62, "id": "b062ab5f-9b98-4843-8925-b93bf4c173f8", "metadata": {}, "outputs": [ @@ -3581,22 +3766,25 @@ "name": "stdout", "output_type": "stream", "text": [ - "2.342073881998658\n" + "2.927992068012827\n" ] } ], "source": [ "wf = Workflow(\"parallel\")\n", - "wf.a = Wait(t)\n", - "wf.b = Wait(t)\n", - "wf.c = Wait(t)\n", + "wf.a = Workflow.create.standard.Sleep(t)\n", + "wf.b = Workflow.create.standard.Sleep(t)\n", + "wf.c = Workflow.create.standard.Sleep(t)\n", "wf.d = wf.create.standard.UserInput(t)\n", "wf.automate_execution = False\n", "wf.d << (wf.a, wf.b, wf.c)\n", "wf.starting_nodes = [wf.a, wf.b, wf.c]\n", "\n", "\n", - "with wf.create.Executor(max_workers=3, cores_per_worker=1) as executor:\n", + "# with wf.create.Executor(max_workers=3) as executor:\n", + "# pympipool.Executor does not conform to the concurrent.futures.Executor signature\n", + "# use max_cores instead of max_workers\n", + "with wf.create.Executor(max_cores=3) as executor:\n", " wf.a.executor = executor\n", " wf.b.executor = executor\n", " wf.c.executor = executor\n", @@ -3628,6 +3816,501 @@ "Unfortunately, _nested_ executors are not yet working. So if you set a macro to use an executor, none of its (grand...)children may specify an executor." ] }, + { + "cell_type": "markdown", + "id": "4d3f2d37-9e35-425b-93a1-2c327685bbf4", + "metadata": {}, + "source": [ + "# For-loops\n", + "\n", + "Any node with an IO signature that is fixed at the class level (i.e. every `StaticNode`, which is all the standard ones except for a `Workflow` instance) can be transformed into a macro that loops over that node using the `Workflow.create.for_node` interface. Any input that is not explicity scattered using the `iter_on` or `zip_on` gets gets broadcast to _all_ copies of the body node. The result is a dataframe coupling looped input to body node output:" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "e3538139-f814-43ba-aad2-f35be0dc2721", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Al: [0. 0. 0.]\n", + "tags: \n", + " indices: [0]\n", + "pbc: [ True True True]\n", + "cell: \n", + "Cell([[0.0, 2.05, 2.05], [2.05, 0.0, 2.05], [2.05, 2.05, 0.0]])" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "n = Workflow.create.pyiron_atomistics.Bulk(name=\"Al\", a=4.1)\n", + "n()" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "id": "0b373764-b389-4c24-8086-f3d33a4f7fd7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
astructure
03.90[Atom('Al', [0.0, 0.0, 0.0], index=0)]
13.95[Atom('Al', [0.0, 0.0, 0.0], index=0)]
24.00[Atom('Al', [0.0, 0.0, 0.0], index=0)]
34.05[Atom('Al', [0.0, 0.0, 0.0], index=0)]
44.10[Atom('Al', [0.0, 0.0, 0.0], index=0)]
\n", + "
" + ], + "text/plain": [ + " a structure\n", + "0 3.90 [Atom('Al', [0.0, 0.0, 0.0], index=0)]\n", + "1 3.95 [Atom('Al', [0.0, 0.0, 0.0], index=0)]\n", + "2 4.00 [Atom('Al', [0.0, 0.0, 0.0], index=0)]\n", + "3 4.05 [Atom('Al', [0.0, 0.0, 0.0], index=0)]\n", + "4 4.10 [Atom('Al', [0.0, 0.0, 0.0], index=0)]" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bulk_loop = Workflow.create.for_node(\n", + " Workflow.create.pyiron_atomistics.Bulk,\n", + " iter_on=(\"a\",),\n", + " name=\"Al\",\n", + " a=np.linspace(3.9, 4.1, 5).tolist()\n", + ")\n", + "\n", + "out = bulk_loop()\n", + "out.df" + ] + }, + { + "cell_type": "markdown", + "id": "c8481efb-d7a5-4395-9e46-aab6a0e004eb", + "metadata": {}, + "source": [ + "Any number of input channels can be specified to make a nested list over, and/or zipped over by passing the channel labels as tuples to the `iter_on` and `zip_on` arguments respectively. In case the body node uses the same labels for both (looped) input channels _and_ output channels, you will need to provide a map to the for-loop to prevent the resulting dataframe from having degenerate column names:" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "id": "6f486c87-f3d4-405f-a759-2ada12cb45e2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
abcdout_aout_bout_cout_de
013791379e
11381013810e
214791479e
31481014810e
415791579e
51581015810e
616791679e
71681016810e
823792379e
92381023810e
1024792479e
112481024810e
1225792579e
132581025810e
1426792679e
152681026810e
\n", + "
" + ], + "text/plain": [ + " a b c d out_a out_b out_c out_d e\n", + "0 1 3 7 9 1 3 7 9 e\n", + "1 1 3 8 10 1 3 8 10 e\n", + "2 1 4 7 9 1 4 7 9 e\n", + "3 1 4 8 10 1 4 8 10 e\n", + "4 1 5 7 9 1 5 7 9 e\n", + "5 1 5 8 10 1 5 8 10 e\n", + "6 1 6 7 9 1 6 7 9 e\n", + "7 1 6 8 10 1 6 8 10 e\n", + "8 2 3 7 9 2 3 7 9 e\n", + "9 2 3 8 10 2 3 8 10 e\n", + "10 2 4 7 9 2 4 7 9 e\n", + "11 2 4 8 10 2 4 8 10 e\n", + "12 2 5 7 9 2 5 7 9 e\n", + "13 2 5 8 10 2 5 8 10 e\n", + "14 2 6 7 9 2 6 7 9 e\n", + "15 2 6 8 10 2 6 8 10 e" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "@Workflow.wrap.as_function_node()\n", + "def FiveApart(a: int, b: int, c: int, d: int, e: str = \"foobar\"):\n", + " return a, b, c, d, e,\n", + "\n", + "for_instance = Workflow.create.for_node(\n", + " FiveApart,\n", + " iter_on=(\"a\", \"b\"),\n", + " zip_on=(\"c\", \"d\"),\n", + " a=[1, 2],\n", + " b=[3, 4, 5, 6],\n", + " c=[7, 8],\n", + " d=[9, 10, 11],\n", + " e=\"e\",\n", + " output_column_map={\n", + " \"a\": \"out_a\",\n", + " \"b\": \"out_b\",\n", + " \"c\": \"out_c\",\n", + " \"d\": \"out_d\"\n", + " }\n", + ")\n", + "\n", + "out = for_instance()\n", + "out.df" + ] + }, + { + "cell_type": "markdown", + "id": "22b688e2-d203-4795-a409-dfeaa978b595", + "metadata": {}, + "source": [ + "Once set, these inputs will _always_ be iterated on, and thus require list input, but the length of the input can be varied between runs of the node. Under the hood, the macro is destroying and recreating (many of) its subgraph nodes at each runtime -- so the interface is fixed, but the internal structure can vary. Note that we use the same standard as python, and zipped input is always truncated to the shortest zipping partner:" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "id": "10f284c8-9210-465f-b4d6-9aa4c0909b08", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
abcdout_aout_bout_cout_de
013791379e
\n", + "
" + ], + "text/plain": [ + " a b c d out_a out_b out_c out_d e\n", + "0 1 3 7 9 1 3 7 9 e" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for_instance(a=[1], b=[3], c=[7]).df" + ] + }, { "cell_type": "markdown", "id": "f447531e-3e8c-4c7e-a579-5f9c56b75a5b", @@ -3672,7 +4355,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 67, "id": "c8196054-aff3-4d39-a872-b428d329dac9", "metadata": {}, "outputs": [], @@ -3682,7 +4365,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 68, "id": "ffd741a3-b086-4ed0-9a62-76143a3705b2", "metadata": {}, "outputs": [], @@ -3699,7 +4382,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 69, "id": "3a22c622-f8c1-449b-a910-c52beb6a09c3", "metadata": {}, "outputs": [ @@ -3707,7 +4390,15 @@ "name": "stderr", "output_type": "stream", "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/node.py:340: UserWarning: A saved file was found for the node save_demo -- attempting to load it...(To delete the saved file instead, use `overwrite_save=True`)\n", + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/node.py:376: UserWarning: A saved file was found for the node save_demo -- attempting to load it...(To delete the saved file instead, use `overwrite_save=True`)\n", + " warnings.warn(\n", + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/node.py:376: UserWarning: A saved file was found for the node inp -- attempting to load it...(To delete the saved file instead, use `overwrite_save=True`)\n", + " warnings.warn(\n", + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/node.py:376: UserWarning: A saved file was found for the node middle -- attempting to load it...(To delete the saved file instead, use `overwrite_save=True`)\n", + " warnings.warn(\n", + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/node.py:376: UserWarning: A saved file was found for the node end -- attempting to load it...(To delete the saved file instead, use `overwrite_save=True`)\n", + " warnings.warn(\n", + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/node.py:376: UserWarning: A saved file was found for the node out -- attempting to load it...(To delete the saved file instead, use `overwrite_save=True`)\n", " warnings.warn(\n" ] } @@ -3730,7 +4421,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 70, "id": "0999d3e8-3a5a-451d-8667-a01dae7c1193", "metadata": {}, "outputs": [], @@ -3739,81 +4430,23 @@ " reloaded.storage.delete()" ] }, - { - "cell_type": "markdown", - "id": "1f012460-19af-45f7-98aa-a0ad5b8e6faa", - "metadata": {}, - "source": [ - "## Meta-nodes and flow control\n", - "\n", - "A meta-node is a function that produces a node _class_ instedad of a node _instance_.\n", - "Right now, these are used to produce parameterized flow-control nodes, which take an node class as input and return a new macro class that builds some graph using the passed node class, e.g. for- and while-loops.\n", - "\n", - "### For-loops\n", - "\n", - "One meta node is a for-loop builder, which creates a macro with $n$ internal instances of the \"loop body\" node class, and a new IO interface.\n", - "The new input allows you to specify which input channels are being looped over -- such that the macro input for this channel is interpreted as list-like and distributed to all the copies of the nodes separately --, and which is _not_ being looped over -- and thus interpreted as the loop body node would normally interpret the input and passed to all copies equally.\n", - "All of the loop body outputs are then collected as a list of length $n$.\n", - "\n", - "We follow a convention that inputs and outputs being looped over are indicated by their channel labels being ALL CAPS.\n", - "\n", - "In the example below, we loop over the bulk structure node to create structures with different lattice constants:" - ] - }, - { - "cell_type": "code", - "execution_count": 61, - "id": "0b373764-b389-4c24-8086-f3d33a4f7fd7", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[14.829749999999995,\n", - " 15.407468749999998,\n", - " 15.999999999999998,\n", - " 16.60753125,\n", - " 17.230249999999995]" - ] - }, - "execution_count": 61, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "n = 5\n", - "\n", - "bulk_loop = Workflow.create.meta.for_loop(\n", - " Workflow.create.pyiron_atomistics.Bulk,\n", - " n,\n", - " iterate_on=(\"a\",),\n", - ")()\n", - "\n", - "out = bulk_loop(\n", - " name=\"Al\", # Sent equally to each body node\n", - " A=np.linspace(3.9, 4.1, n).tolist(), # Distributed across body nodes\n", - ")\n", - "\n", - "[struct.cell.volume for struct in out.STRUCTURE] \n", - "# output is a list collected from copies of the body node, as indicated by CAPS label" - ] - }, { "cell_type": "markdown", "id": "4e7ed210-dbc2-4afa-825e-b91168baff25", "metadata": {}, "source": [ - "## While-loops\n", + "# While-loops\n", + "\n", + "Similar to for-loops, we can also create a while-loop, which takes both a body node and a condition node. The condition node must be a single-output `Function` node returning a `bool` type. Instead of creating copies of the body node, the body node gets re-run until the condition node returns `False`.\n", "\n", - "We can also create a while-loop, which takes both a body node and a condition node. The condition node must be a single-output `Function` node returning a `bool` type. Instead of creating copies of the body node, the body node gets re-run until the condition node returns `False`.\n", + "You _must_ specify the data connection so that the body node passes information to the condition node. You may optionally also loop output of the body node back to input of the body node to change the input at each iteration. Right now this is done with horribly ugly string tuples, but we're working on improving this interface and making it more like the for-loop.\n", "\n", - "You _must_ specify the data connection so that the body node passes information to the condition node. You may optionally also loop output of the body node back to input of the body node to change the input at each iteration. Right now this is done with horribly ugly string tuples, but we're still working on it." + "Note: The body (and condition) node classes passed to while-loops must be importable, i.e. they can come from a node package, or be defined here in the notebook (importable from `__main__`), but you can't use, e.g., a node defined _inside_ the scope of some other function." ] }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 71, "id": "0dd04b4c-e3e7-4072-ad34-58f2c1e4f596", "metadata": {}, "outputs": [ @@ -3821,36 +4454,30 @@ "name": "stderr", "output_type": "stream", "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel run was not connected to true, andthus could not disconnect from it.\n", - " warn(\n", - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n", - " warn(\n" + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/io_preview.py:261: OutputLabelsNotValidated: Could not find the source code to validate AddWhileLessThan_4075744974569251116 output labels against the number of returned values -- proceeding without validation\n", + " warnings.warn(\n" ] } ], "source": [ - "@Workflow.wrap_as.function_node()\n", + "@Workflow.wrap.as_function_node(\"sum\")\n", "def Add(a, b):\n", " print(f\"{a} + {b} = {a + b}\")\n", " return a + b\n", "\n", - "@Workflow.wrap_as.function_node()\n", - "def LessThanTen(value):\n", - " return value < 10\n", - "\n", "AddWhile = Workflow.create.meta.while_loop(\n", " loop_body_class=Add,\n", - " condition_class=LessThanTen,\n", + " condition_class=Workflow.create.standard.LessThan,\n", " internal_connection_map=[\n", - " (\"Add\", \"a + b\", \"LessThanTen\", \"value\"),\n", - " (\"Add\", \"a + b\", \"Add\", \"a\")\n", + " (\"Add\", \"sum\", \"LessThan\", \"obj\"),\n", + " (\"Add\", \"sum\", \"Add\", \"a\")\n", " ],\n", - " inputs_map={\"Add__a\": \"a\", \"Add__b\": \"b\"},\n", - " outputs_map={\"Add__a + b\": \"total\"}\n", + " inputs_map={\"Add__a\": \"a\", \"Add__b\": \"b\", \"LessThan__other\": \"threshold\"},\n", + " outputs_map={\"Add__sum\": \"total\"}\n", ")\n", "\n", "wf = Workflow(\"do_while\")\n", - "wf.add_while = AddWhile()\n", + "wf.add_while = AddWhile(threshold=10)\n", "\n", "wf.inputs_map = {\n", " \"add_while__a\": \"a\",\n", @@ -3872,7 +4499,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 72, "id": "2dfb967b-41ac-4463-b606-3e315e617f2a", "metadata": {}, "outputs": [ @@ -3896,7 +4523,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 73, "id": "2e87f858-b327-4f6b-9237-c8a557f29aeb", "metadata": {}, "outputs": [ @@ -3904,22 +4531,28 @@ "name": "stdout", "output_type": "stream", "text": [ - "0.270 > 0.2\n", - "0.853 > 0.2\n", - "0.677 > 0.2\n", - "0.752 > 0.2\n", - "0.644 > 0.2\n", - "0.064 <= 0.2\n", - "Finally 0.064\n" + "0.692 > 0.2\n", + "0.305 > 0.2\n", + "0.890 > 0.2\n", + "0.140 <= 0.2\n", + "Finally 0.140\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/io_preview.py:261: OutputLabelsNotValidated: Could not find the source code to validate RandomWhileGreaterThan_1056577651091147451 output labels against the number of returned values -- proceeding without validation\n", + " warnings.warn(\n" ] } ], "source": [ - "@Workflow.wrap_as.function_node(\"random\")\n", + "@Workflow.wrap.as_function_node(\"random\")\n", "def Random(length: int | None = None):\n", " return np.random.random(length)\n", "\n", - "@Workflow.wrap_as.function_node()\n", + "@Workflow.wrap.as_function_node()\n", "def GreaterThan(x: float, threshold: float):\n", " gt = x > threshold\n", " symbol = \">\" if gt else \"<=\"\n", @@ -3930,6 +4563,7 @@ " loop_body_class=Random,\n", " condition_class=GreaterThan,\n", " internal_connection_map=[(\"Random\", \"random\", \"GreaterThan\", \"x\")],\n", + " inputs_map={\"GreaterThan__threshold\": \"threshold\"},\n", " outputs_map={\"Random__random\": \"capped_result\"}\n", ")\n", "\n", @@ -3942,7 +4576,7 @@ "wf.random_while = RandomWhile()\n", "\n", "## Give convenient labels\n", - "wf.inputs_map = {\"random_while__GreaterThan__threshold\": \"threshold\"}\n", + "wf.inputs_map = {\"random_while__threshold\": \"threshold\"}\n", "wf.outputs_map = {\"random_while__capped_result\": \"capped_result\"}\n", "\n", "print(f\"Finally {wf(threshold=0.2).capped_result:.3f}\")" @@ -3973,7 +4607,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/notebooks/phonopy_wf.ipynb b/notebooks/phonopy_wf.ipynb index 923d3318..c172eeac 100644 --- a/notebooks/phonopy_wf.ipynb +++ b/notebooks/phonopy_wf.ipynb @@ -18,8 +18,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 1.33 s, sys: 264 ms, total: 1.59 s\n", - "Wall time: 458 ms\n" + "CPU times: user 615 ms, sys: 208 ms, total: 823 ms\n", + "Wall time: 1.31 s\n" ] } ], @@ -49,7 +49,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "6adf84964e5445bc91c3437059885cda", + "model_id": "378a38defb454d16b483480640de6674", "version_major": 2, "version_minor": 0 }, @@ -62,8 +62,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 824 ms, sys: 201 ms, total: 1.02 s\n", - "Wall time: 1.63 s\n" + "CPU times: user 1.69 s, sys: 671 ms, total: 2.36 s\n", + "Wall time: 3.45 s\n" ] } ], @@ -81,7 +81,7 @@ "outputs": [], "source": [ "wf = Workflow('test')\n", - "wf.structure = wf.create.atomistic.structure.build.bulk('Al')" + "wf.structure = wf.create.atomistic.structure.build.Bulk('Al')" ] }, { @@ -91,22 +91,23 @@ "metadata": {}, "outputs": [], "source": [ - "@Workflow.wrap_as.macro_node(\"structure\")\n", - "def bulk_rotation(wf, name='Al', cubic: bool=True, repeat_cell=2, angle=0, axis=[0,0,1]):\n", - " wf.structure = wf.create.atomistic.structure.build.bulk(name=name, cubic=cubic)\n", - " wf.repeat = wf.create.atomistic.structure.transform.repeat(structure=wf.structure, repeat_scalar=repeat_cell)\n", - " wf.rotate = wf.create.atomistic.structure.transform.rotate_axis_angle(structure=wf.repeat, angle=angle, axis=axis)\n", + "@Workflow.wrap.as_macro_node(\"structure\")\n", + "def BulkRotation(wf, name='Al', cubic: bool=True, repeat_cell=2, angle=0, axis=(0,0,1)):\n", + " wf.structure = wf.create.atomistic.structure.build.Bulk(\n", + " name=name, \n", + " cubic=cubic\n", + " )\n", + " wf.repeat = wf.create.atomistic.structure.transform.Repeat(\n", + " structure=wf.structure, \n", + " repeat_scalar=repeat_cell\n", + " )\n", + " wf.rotate = wf.create.atomistic.structure.transform.RotateAxisAngle(\n", + " structure=wf.repeat, \n", + " angle=angle, axis=axis\n", + " )\n", " return wf.rotate" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "f22af304-5950-49f6-ae06-a5a9b69e036d", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": 5, @@ -116,7 +117,7 @@ { "data": { "text/plain": [ - "(NOT_DATA, False)" + "('Al', True)" ] }, "execution_count": 5, @@ -125,7 +126,7 @@ } ], "source": [ - "br = bulk_rotation()\n", + "br = BulkRotation()\n", "br.structure.inputs.name.value, br.structure.inputs.cubic.value" ] }, @@ -143,819 +144,429 @@ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotation\n", - "\n", - "bulk_rotation: bulk_rotation\n", - "\n", - "clusterbulk_rotationname\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "name: UserInput\n", - "\n", - "\n", - "clusterbulk_rotationnameInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulk_rotationnameOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulk_rotationaxis\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "axis: UserInput\n", - "\n", - "\n", - "clusterbulk_rotationaxisInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulk_rotationaxisOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulk_rotationangle\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "angle: UserInput\n", - "\n", - "\n", - "clusterbulk_rotationangleInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulk_rotationangleOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulk_rotationstructure\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "structure: bulk\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulk_rotationstructureOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulk_rotationrepeat\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "repeat: repeat\n", - "\n", - "\n", - "clusterbulk_rotationrepeatInputs\n", + "\n", + "\n", + "\n", + "clusterBulkRotation\n", + "\n", + "BulkRotation: BulkRotation\n", + "\n", + "clusterBulkRotationInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterbulk_rotationrepeatOutputs\n", + "\n", + "clusterBulkRotationOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterbulk_rotationrotate\n", + "\n", + "clusterBulkRotationstructure\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "rotate: rotate_axis_angle\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulk_rotationrotateOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", + "\n", + "structure: Bulk\n", "\n", - "\n", - "clusterbulk_rotationInputs\n", + "\n", + "clusterBulkRotationstructureInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterbulk_rotationOutputs\n", + "\n", + "clusterBulkRotationstructureOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterbulk_rotationcubic\n", + "clusterBulkRotationrepeat\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "cubic: UserInput\n", + "\n", + "repeat: Repeat\n", "\n", "\n", - "clusterbulk_rotationcubicInputs\n", + "clusterBulkRotationrepeatInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterbulk_rotationcubicOutputs\n", + "clusterBulkRotationrepeatOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterbulk_rotationrepeat_cell\n", + "clusterBulkRotationrotate\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "repeat_cell: UserInput\n", + "\n", + "rotate: RotateAxisAngle\n", "\n", - "\n", - "clusterbulk_rotationrepeat_cellOutputs\n", + "\n", + "clusterBulkRotationrotateInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterbulk_rotationrepeat_cellInputs\n", + "\n", + "clusterBulkRotationrotateOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationInputsrun\n", - "\n", - "run\n", + "clusterBulkRotationInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationOutputsran\n", - "\n", - "ran\n", + "clusterBulkRotationOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "clusterbulk_rotationInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "clusterBulkRotationInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationInputsname\n", - "\n", - "name\n", + "clusterBulkRotationInputsname\n", + "\n", + "name\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationnameInputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationInputsname->clusterbulk_rotationnameInputsuser_input\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "clusterBulkRotationstructureInputsname\n", + "\n", + "name\n", + "\n", + "\n", + "\n", + "clusterBulkRotationInputsname->clusterBulkRotationstructureInputsname\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "clusterbulk_rotationInputscubic\n", - "\n", - "cubic: bool\n", + "clusterBulkRotationInputscubic\n", + "\n", + "cubic: bool\n", "\n", - "\n", - "\n", - "clusterbulk_rotationcubicInputsuser_input\n", - "\n", - "user_input: bool\n", + "\n", + "\n", + "clusterBulkRotationstructureInputscubic\n", + "\n", + "cubic\n", "\n", - "\n", - "\n", - "clusterbulk_rotationInputscubic->clusterbulk_rotationcubicInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationInputscubic->clusterBulkRotationstructureInputscubic\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationInputsrepeat_cell\n", - "\n", - "repeat_cell\n", + "clusterBulkRotationInputsrepeat_cell\n", + "\n", + "repeat_cell\n", "\n", - "\n", - "\n", - "clusterbulk_rotationrepeat_cellInputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterBulkRotationrepeatInputsrepeat_scalar\n", + "\n", + "repeat_scalar: int\n", "\n", - "\n", - "\n", - "clusterbulk_rotationInputsrepeat_cell->clusterbulk_rotationrepeat_cellInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationInputsrepeat_cell->clusterBulkRotationrepeatInputsrepeat_scalar\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationInputsangle\n", - "\n", - "angle\n", + "clusterBulkRotationInputsangle\n", + "\n", + "angle\n", "\n", - "\n", - "\n", - "clusterbulk_rotationangleInputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterBulkRotationrotateInputsangle\n", + "\n", + "angle\n", "\n", - "\n", - "\n", - "clusterbulk_rotationInputsangle->clusterbulk_rotationangleInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationInputsangle->clusterBulkRotationrotateInputsangle\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationInputsaxis\n", - "\n", - "axis\n", + "clusterBulkRotationInputsaxis\n", + "\n", + "axis\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationaxisInputsuser_input\n", - "\n", - "user_input\n", + "clusterBulkRotationrotateInputsaxis\n", + "\n", + "axis\n", "\n", - "\n", - "\n", - "clusterbulk_rotationInputsaxis->clusterbulk_rotationaxisInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationInputsaxis->clusterBulkRotationrotateInputsaxis\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationOutputsstructure\n", - "\n", - "structure\n", + "clusterBulkRotationOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationnameInputsrun\n", - "\n", - "run\n", + "clusterBulkRotationstructureInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterbulk_rotationnameOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterBulkRotationstructureOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "clusterbulk_rotationnameInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "clusterBulkRotationstructureInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationnameOutputsran->clusterbulk_rotationstructureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationstructureInputscrystalstructure\n", + "\n", + "crystalstructure\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationnameOutputsuser_input\n", - "\n", - "user_input\n", + "clusterBulkRotationstructureInputsa\n", + "\n", + "a\n", "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsname\n", - "\n", - "name\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationnameOutputsuser_input->clusterbulk_rotationstructureInputsname\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationcubicInputsrun\n", - "\n", - "run\n", + "clusterBulkRotationstructureInputsc\n", + "\n", + "c\n", "\n", - "\n", - "\n", - "clusterbulk_rotationcubicOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationcubicInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationcubicOutputsran->clusterbulk_rotationstructureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clusterBulkRotationstructureInputscovera\n", + "\n", + "covera\n", "\n", - "\n", - "\n", - "clusterbulk_rotationcubicOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputscubic\n", - "\n", - "cubic\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationcubicOutputsuser_input->clusterbulk_rotationstructureInputscubic\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationstructureInputsu\n", + "\n", + "u\n", "\n", - "\n", - "\n", - "clusterbulk_rotationrepeat_cellInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterBulkRotationstructureInputsorthorhombic\n", + "\n", + "orthorhombic\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationrepeat_cellOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", + "clusterBulkRotationrepeatInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterBulkRotationstructureOutputsWithInjectionran->clusterBulkRotationrepeatInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "clusterbulk_rotationrepeat_cellInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeat_cellOutputsran->clusterbulk_rotationrepeatInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clusterBulkRotationstructureOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationrepeat_cellOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatInputsrepeat_scalar\n", - "\n", - "repeat_scalar: int\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeat_cellOutputsuser_input->clusterbulk_rotationrepeatInputsrepeat_scalar\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationangleInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationangleOutputsran\n", - "\n", - "ran\n", + "clusterBulkRotationrepeatInputsstructure\n", + "\n", + "structure: Atoms\n", + "\n", + "\n", + "\n", + "clusterBulkRotationstructureOutputsWithInjectionstructure->clusterBulkRotationrepeatInputsstructure\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrepeatInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationangleInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "clusterBulkRotationrepeatOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterbulk_rotationangleOutputsran->clusterbulk_rotationrotateInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "clusterbulk_rotationangleOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsangle\n", - "\n", - "angle\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationangleOutputsuser_input->clusterbulk_rotationrotateInputsangle\n", - "\n", - "\n", - "\n", + "clusterBulkRotationrotateInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrepeatOutputsWithInjectionran->clusterBulkRotationrotateInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrepeatOutputsWithInjectionstructure\n", + "\n", + "structure: Atoms\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationaxisInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationaxisOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationaxisInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationaxisOutputsran->clusterbulk_rotationrotateInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationaxisOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsaxis\n", - "\n", - "axis: list\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationaxisOutputsuser_input->clusterbulk_rotationrotateInputsaxis\n", - "\n", - "\n", - "\n", + "clusterBulkRotationrotateInputsstructure\n", + "\n", + "structure: Atoms\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrepeatOutputsWithInjectionstructure->clusterBulkRotationrotateInputsstructure\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrotateInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationstructureInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputscrystalstructure\n", - "\n", - "crystalstructure\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsa\n", - "\n", - "a\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsc\n", - "\n", - "c\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputscovera\n", - "\n", - "covera\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsu\n", - "\n", - "u\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsorthorhombic\n", - "\n", - "orthorhombic\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureOutputsran->clusterbulk_rotationrepeatInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureOutputsstructure\n", - "\n", - "structure\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatInputsstructure\n", - "\n", - "structure: Atoms\n", + "clusterBulkRotationrotateOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterbulk_rotationstructureOutputsstructure->clusterbulk_rotationrepeatInputsstructure\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatOutputsran->clusterbulk_rotationrotateInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatOutputsstructure\n", - "\n", - "structure: Atoms\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsstructure\n", - "\n", - "structure: Atoms\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatOutputsstructure->clusterbulk_rotationrotateInputsstructure\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputscenter\n", - "\n", - "center\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrotateInputscenter\n", + "\n", + "center\n", "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsrotate_cell\n", - "\n", - "rotate_cell: bool\n", + "\n", + "\n", + "clusterBulkRotationrotateInputsrotate_cell\n", + "\n", + "rotate_cell: bool\n", "\n", - "\n", - "\n", - "clusterbulk_rotationrotateOutputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "clusterBulkRotationrotateOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", - "\n", - "\n", - "clusterbulk_rotationrotateOutputsstructure->clusterbulk_rotationOutputsstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationrotateOutputsWithInjectionstructure->clusterBulkRotationOutputsWithInjectionstructure\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, "execution_count": 6, @@ -964,7 +575,7 @@ } ], "source": [ - "br.draw()" + "br.draw(size=(10, 10))" ] }, { @@ -984,7 +595,7 @@ "value": "'Al'" }, "text/plain": [ - "" + "" ] }, "execution_count": 7, @@ -1050,7 +661,7 @@ } ], "source": [ - "br = bulk_rotation(name='Fe')\n", + "br = BulkRotation(name='Fe')\n", "br.structure.inputs.name = 'Al'\n", "br.inputs.name = 'Al'\n", "br.run()" @@ -1070,819 +681,429 @@ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n", "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotation\n", - "\n", - "bulk_rotation: bulk_rotation\n", - "\n", - "clusterbulk_rotationstructure\n", + "\n", + "\n", + "\n", + "clusterBulkRotation\n", + "\n", + "BulkRotation: BulkRotation\n", + "\n", + "clusterBulkRotationInputs\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "structure: bulk\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterbulk_rotationstructureInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulk_rotationstructureOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulk_rotationrepeat\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "repeat: repeat\n", - "\n", - "\n", - "clusterbulk_rotationrepeatInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulk_rotationrepeatOutputs\n", + "\n", + "clusterBulkRotationOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterbulk_rotationrotate\n", + "\n", + "clusterBulkRotationstructure\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "rotate: rotate_axis_angle\n", + "\n", + "structure: Bulk\n", "\n", - "\n", - "clusterbulk_rotationrotateInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulk_rotationrotateOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulk_rotationInputs\n", + "\n", + "clusterBulkRotationstructureInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterbulk_rotationOutputs\n", + "\n", + "clusterBulkRotationstructureOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterbulk_rotationcubic\n", + "clusterBulkRotationrepeat\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "cubic: UserInput\n", + "\n", + "repeat: Repeat\n", "\n", "\n", - "clusterbulk_rotationcubicInputs\n", + "clusterBulkRotationrepeatInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterbulk_rotationcubicOutputs\n", + "clusterBulkRotationrepeatOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterbulk_rotationrepeat_cell\n", + "clusterBulkRotationrotate\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "repeat_cell: UserInput\n", + "\n", + "rotate: RotateAxisAngle\n", "\n", "\n", - "clusterbulk_rotationrepeat_cellInputs\n", + "clusterBulkRotationrotateInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterbulk_rotationrepeat_cellOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulk_rotationangle\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "angle: UserInput\n", - "\n", - "\n", - "clusterbulk_rotationangleInputs\n", + "clusterBulkRotationrotateOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterbulk_rotationangleOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulk_rotationname\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "name: UserInput\n", - "\n", - "\n", - "clusterbulk_rotationnameInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulk_rotationnameOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulk_rotationaxis\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "axis: UserInput\n", - "\n", - "\n", - "clusterbulk_rotationaxisInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulk_rotationaxisOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationInputsrun\n", - "\n", - "run\n", + "clusterBulkRotationInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationOutputsran\n", - "\n", - "ran\n", + "clusterBulkRotationOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "clusterbulk_rotationInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "clusterBulkRotationInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationInputsname\n", - "\n", - "name\n", + "clusterBulkRotationInputsname\n", + "\n", + "name\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationnameInputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationInputsname->clusterbulk_rotationnameInputsuser_input\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "clusterBulkRotationstructureInputsname\n", + "\n", + "name\n", + "\n", + "\n", + "\n", + "clusterBulkRotationInputsname->clusterBulkRotationstructureInputsname\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "clusterbulk_rotationInputscubic\n", - "\n", - "cubic: bool\n", + "clusterBulkRotationInputscubic\n", + "\n", + "cubic: bool\n", "\n", - "\n", - "\n", - "clusterbulk_rotationcubicInputsuser_input\n", - "\n", - "user_input: bool\n", + "\n", + "\n", + "clusterBulkRotationstructureInputscubic\n", + "\n", + "cubic\n", "\n", - "\n", - "\n", - "clusterbulk_rotationInputscubic->clusterbulk_rotationcubicInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationInputscubic->clusterBulkRotationstructureInputscubic\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationInputsrepeat_cell\n", - "\n", - "repeat_cell\n", + "clusterBulkRotationInputsrepeat_cell\n", + "\n", + "repeat_cell\n", "\n", - "\n", - "\n", - "clusterbulk_rotationrepeat_cellInputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterBulkRotationrepeatInputsrepeat_scalar\n", + "\n", + "repeat_scalar: int\n", "\n", - "\n", - "\n", - "clusterbulk_rotationInputsrepeat_cell->clusterbulk_rotationrepeat_cellInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationInputsrepeat_cell->clusterBulkRotationrepeatInputsrepeat_scalar\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationInputsangle\n", - "\n", - "angle\n", + "clusterBulkRotationInputsangle\n", + "\n", + "angle\n", "\n", - "\n", - "\n", - "clusterbulk_rotationangleInputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterBulkRotationrotateInputsangle\n", + "\n", + "angle\n", "\n", - "\n", - "\n", - "clusterbulk_rotationInputsangle->clusterbulk_rotationangleInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationInputsangle->clusterBulkRotationrotateInputsangle\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationInputsaxis\n", - "\n", - "axis\n", + "clusterBulkRotationInputsaxis\n", + "\n", + "axis\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationaxisInputsuser_input\n", - "\n", - "user_input\n", + "clusterBulkRotationrotateInputsaxis\n", + "\n", + "axis\n", "\n", - "\n", - "\n", - "clusterbulk_rotationInputsaxis->clusterbulk_rotationaxisInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationInputsaxis->clusterBulkRotationrotateInputsaxis\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationOutputsstructure\n", - "\n", - "structure\n", + "clusterBulkRotationOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationnameInputsrun\n", - "\n", - "run\n", + "clusterBulkRotationstructureInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterbulk_rotationnameOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterBulkRotationstructureOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "clusterbulk_rotationnameInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "clusterBulkRotationstructureInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationnameOutputsran->clusterbulk_rotationstructureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationstructureInputscrystalstructure\n", + "\n", + "crystalstructure\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationnameOutputsuser_input\n", - "\n", - "user_input\n", + "clusterBulkRotationstructureInputsa\n", + "\n", + "a\n", "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsname\n", - "\n", - "name\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationnameOutputsuser_input->clusterbulk_rotationstructureInputsname\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationcubicInputsrun\n", - "\n", - "run\n", + "clusterBulkRotationstructureInputsc\n", + "\n", + "c\n", "\n", - "\n", - "\n", - "clusterbulk_rotationcubicOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationcubicInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationcubicOutputsran->clusterbulk_rotationstructureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clusterBulkRotationstructureInputscovera\n", + "\n", + "covera\n", "\n", - "\n", - "\n", - "clusterbulk_rotationcubicOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputscubic\n", - "\n", - "cubic\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationcubicOutputsuser_input->clusterbulk_rotationstructureInputscubic\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationstructureInputsu\n", + "\n", + "u\n", "\n", - "\n", - "\n", - "clusterbulk_rotationrepeat_cellInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterBulkRotationstructureInputsorthorhombic\n", + "\n", + "orthorhombic\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationrepeat_cellOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", + "clusterBulkRotationrepeatInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterBulkRotationstructureOutputsWithInjectionran->clusterBulkRotationrepeatInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "clusterbulk_rotationrepeat_cellInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeat_cellOutputsran->clusterbulk_rotationrepeatInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clusterBulkRotationstructureOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationrepeat_cellOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatInputsrepeat_scalar\n", - "\n", - "repeat_scalar: int\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeat_cellOutputsuser_input->clusterbulk_rotationrepeatInputsrepeat_scalar\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationangleInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationangleOutputsran\n", - "\n", - "ran\n", + "clusterBulkRotationrepeatInputsstructure\n", + "\n", + "structure: Atoms\n", + "\n", + "\n", + "\n", + "clusterBulkRotationstructureOutputsWithInjectionstructure->clusterBulkRotationrepeatInputsstructure\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrepeatInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationangleInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "clusterBulkRotationrepeatOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterbulk_rotationangleOutputsran->clusterbulk_rotationrotateInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "clusterbulk_rotationangleOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsangle\n", - "\n", - "angle\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationangleOutputsuser_input->clusterbulk_rotationrotateInputsangle\n", - "\n", - "\n", - "\n", + "clusterBulkRotationrotateInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrepeatOutputsWithInjectionran->clusterBulkRotationrotateInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrepeatOutputsWithInjectionstructure\n", + "\n", + "structure: Atoms\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationaxisInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationaxisOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationaxisInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationaxisOutputsran->clusterbulk_rotationrotateInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationaxisOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsaxis\n", - "\n", - "axis: list\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationaxisOutputsuser_input->clusterbulk_rotationrotateInputsaxis\n", - "\n", - "\n", - "\n", + "clusterBulkRotationrotateInputsstructure\n", + "\n", + "structure: Atoms\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrepeatOutputsWithInjectionstructure->clusterBulkRotationrotateInputsstructure\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrotateInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterbulk_rotationstructureInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputscrystalstructure\n", - "\n", - "crystalstructure\n", + "clusterBulkRotationrotateOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsa\n", - "\n", - "a\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsc\n", - "\n", - "c\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputscovera\n", - "\n", - "covera\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsu\n", - "\n", - "u\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureInputsorthorhombic\n", - "\n", - "orthorhombic\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureOutputsran->clusterbulk_rotationrepeatInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureOutputsstructure\n", - "\n", - "structure\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatInputsstructure\n", - "\n", - "structure: Atoms\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationstructureOutputsstructure->clusterbulk_rotationrepeatInputsstructure\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatOutputsran->clusterbulk_rotationrotateInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatOutputsstructure\n", - "\n", - "structure: Atoms\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsstructure\n", - "\n", - "structure: Atoms\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrepeatOutputsstructure->clusterbulk_rotationrotateInputsstructure\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputscenter\n", - "\n", - "center\n", + "\n", + "\n", + "\n", + "clusterBulkRotationrotateInputscenter\n", + "\n", + "center\n", "\n", - "\n", - "\n", - "clusterbulk_rotationrotateInputsrotate_cell\n", - "\n", - "rotate_cell: bool\n", + "\n", + "\n", + "clusterBulkRotationrotateInputsrotate_cell\n", + "\n", + "rotate_cell: bool\n", "\n", - "\n", - "\n", - "clusterbulk_rotationrotateOutputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "clusterBulkRotationrotateOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", - "\n", - "\n", - "clusterbulk_rotationrotateOutputsstructure->clusterbulk_rotationOutputsstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterBulkRotationrotateOutputsWithInjectionstructure->clusterBulkRotationOutputsWithInjectionstructure\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, "execution_count": 9, @@ -1891,7 +1112,7 @@ } ], "source": [ - "br.draw()" + "br.draw(size=(10, 10))" ] }, { @@ -1901,7 +1122,7 @@ "metadata": {}, "outputs": [], "source": [ - "from pyiron_workflow.node_library.atomistic.property.phonons import InputPhonopyGenerateSupercells" + "from pyiron_workflow.node_library.atomistic.property.phonons import GenerateSupercellsParameters" ] }, { @@ -1927,50 +1148,57 @@ "metadata": {}, "outputs": [], "source": [ - "@Workflow.wrap_as.macro_node(\n", + "@Workflow.wrap.as_macro_node(\n", " \"imaginary_modes\",\n", " \"total_dos\",\n", " \"energy_relaxed\",\n", " \"energy_initial\",\n", " \"energy_displaced\",\n", ")\n", - "def run_phonopy(\n", + "def RunPhonopy(\n", " wf,\n", " element: str,\n", " cell_size: int = 2,\n", " vacancy_index: int | None = None,\n", " displacement: float = 0.01,\n", - " max_workers: int = 1\n", "):\n", "\n", " # wf.engine = wf.create.engine.ase.M3GNet()\n", " wf.engine = wf.create.atomistic.engine.ase.EMT()\n", " \n", - " wf.structure = wf.create.atomistic.structure.build.cubic_bulk_cell(\n", - " element=element, cell_size=cell_size, vacancy_index=vacancy_index\n", + " wf.structure = wf.create.atomistic.structure.build.CubicBulkCell(\n", + " element=element, \n", + " cell_size=cell_size,\n", + " vacancy_index=vacancy_index\n", " )\n", - " # explicit output needed since macro and not single_value_node (we should have also a single_value_macro)\n", - " wf.relaxed_structure = wf.create.atomistic.calculator.ase.minimize(\n", - " atoms=wf.structure.outputs.structure,\n", + " wf.relaxed_structure = wf.create.atomistic.calculator.ase.Minimize(\n", + " atoms=wf.structure,\n", " engine=wf.engine,\n", " )\n", " \n", - " wf.phonopy_input = wf.create.atomistic.property.phonons.PhonopyParameters(distance=displacement)\n", + " # Dataclass node access is awkward -- rethink altering the name\n", + " wf.phonopy_input = Workflow.create.transformer.dataclass_node(\n", + " GenerateSupercellsParameters,\n", + " distance=displacement\n", + " )\n", "\n", - " wf.phonopy = wf.create.atomistic.property.phonons.create_phonopy(\n", + " wf.phonopy = wf.create.atomistic.property.phonons.CreatePhonopy(\n", " structure=wf.relaxed_structure.outputs.structure,\n", - " parameters=wf.phonopy_input,\n", + " generate_supercells_parameters=wf.phonopy_input,\n", " engine=wf.engine,\n", - " max_workers=max_workers,\n", " )\n", - " # print ('test: ', displacement.run())\n", "\n", - " wf.check_consistency = wf.create.atomistic.property.phonons.check_consistency(\n", + " wf.check_consistency = wf.create.atomistic.property.phonons.CheckConsistency(\n", + " phonopy=wf.phonopy.outputs.phonopy\n", + " )\n", + " wf.total_dos = wf.create.atomistic.property.phonons.GetTotalDos(\n", " phonopy=wf.phonopy.outputs.phonopy\n", " )\n", - " wf.total_dos = wf.create.atomistic.property.phonons.get_total_dos(phonopy=wf.phonopy.outputs.phonopy)\n", "\n", - " # iterate over all nodes, extract the log_output and store it in hdf5\n", + " wf.energies = Workflow.create.atomistic.property.phonons.DictsToList(\n", + " wf.phonopy.outputs.df[\"out\"],\n", + " \"energy\"\n", + " )\n", " # control the amount of output via log_level\n", "\n", " return (\n", @@ -1978,7 +1206,7 @@ " wf.total_dos,\n", " wf.relaxed_structure.outputs.out.final.energy,\n", " wf.relaxed_structure.outputs.out.initial.energy,\n", - " wf.phonopy.outputs.out[\"energies\"],\n", + " wf.energies,\n", " )" ] }, @@ -2000,1707 +1228,1400 @@ "name": "stdout", "output_type": "stream", "text": [ - "energy: 0.8013167095856435 0.7996059979142878\n", - "max_workers: 1\n", - "WARNING: 3 imaginary modes exist\n", - "CPU times: user 14.1 s, sys: 1.61 s, total: 15.7 s\n", - "Wall time: 5.23 s\n" + "CPU times: user 241 ms, sys: 101 ms, total: 342 ms\n", + "Wall time: 351 ms\n" ] } ], "source": [ "%%time\n", - "wf = run_phonopy(element='Al', cell_size=3, vacancy_index=0, displacement=0.1, max_workers=1)\n", - "out = wf.run()" + "wf = RunPhonopy(element='Al', cell_size=3, vacancy_index=0, displacement=0.1)" ] }, { "cell_type": "code", "execution_count": 13, - "id": "f904f475-dc8c-4f82-8d7d-507a7cafa4f7", + "id": "87cef12e-7de1-4794-924a-d4395daf2474", "metadata": {}, "outputs": [ { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopy\n", - "\n", - "run_phonopy: run_phonopy\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energy\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "relaxed_structure__out_GetAttr_final__getattr_GetAttr_energy: GetAttr\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energy\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "relaxed_structure__out_GetAttr_initial__getattr_GetAttr_energy: GetAttr\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputs\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 14.3 s, sys: 1 s, total: 15.4 s\n", + "Wall time: 9.54 s\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/node_library/atomistic/property/phonons.py:129: UserWarning: WARNING: 3 imaginary modes exist\n", + " warnings.warn(f\"WARNING: {n_imaginary_nodes} imaginary modes exist\")\n" + ] + } + ], + "source": [ + "%%time\n", + "out = wf.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "f904f475-dc8c-4f82-8d7d-507a7cafa4f7", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterRunPhonopy\n", + "\n", + "RunPhonopy: RunPhonopy\n", + "\n", + "clusterRunPhonopyInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyOutputs\n", + "\n", + "clusterRunPhonopyOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopymax_workers\n", + "\n", + "clusterRunPhonopyengine\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "max_workers: UserInput\n", + "\n", + "engine: EMT\n", "\n", - "\n", - "clusterrun_phonopymax_workersInputs\n", + "\n", + "clusterRunPhonopyengineInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopymax_workersOutputs\n", + "\n", + "clusterRunPhonopyengineOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopystructure\n", + "\n", + "clusterRunPhonopystructure\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "structure: cubic_bulk_cell\n", + "\n", + "structure: CubicBulkCell\n", "\n", - "\n", - "clusterrun_phonopystructureInputs\n", + "\n", + "clusterRunPhonopystructureInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopystructureOutputs\n", + "\n", + "clusterRunPhonopystructureOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopyrelaxed_structure\n", + "\n", + "clusterRunPhonopyrelaxed_structure\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "relaxed_structure: minimize\n", + "\n", + "relaxed_structure: Minimize\n", "\n", - "\n", - "clusterrun_phonopyrelaxed_structureInputs\n", + "\n", + "clusterRunPhonopyrelaxed_structureInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopyrelaxed_structureOutputs\n", + "\n", + "clusterRunPhonopyrelaxed_structureOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopyphonopy_input\n", + "\n", + "clusterRunPhonopyphonopy_input\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "phonopy_input: PhonopyParameters\n", + "\n", + "phonopy_input: DataclassNodeGenerateSupercellsParameters\n", "\n", - "\n", - "clusterrun_phonopyphonopy_inputInputs\n", + "\n", + "clusterRunPhonopyphonopy_inputInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopyphonopy_inputOutputs\n", + "\n", + "clusterRunPhonopyphonopy_inputOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopyphonopy\n", + "\n", + "clusterRunPhonopyphonopy\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "phonopy: create_phonopy\n", + "\n", + "phonopy: CreatePhonopy\n", "\n", - "\n", - "clusterrun_phonopyphonopyInputs\n", + "\n", + "clusterRunPhonopyphonopyInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopyphonopyOutputs\n", + "\n", + "clusterRunPhonopyphonopyOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopycheck_consistency\n", + "\n", + "clusterRunPhonopycheck_consistency\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "check_consistency: check_consistency\n", + "\n", + "check_consistency: CheckConsistency\n", "\n", - "\n", - "clusterrun_phonopycheck_consistencyInputs\n", + "\n", + "clusterRunPhonopycheck_consistencyInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopycheck_consistencyOutputs\n", + "\n", + "clusterRunPhonopycheck_consistencyOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopytotal_dos\n", + "\n", + "clusterRunPhonopytotal_dos\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "total_dos: get_total_dos\n", + "\n", + "total_dos: GetTotalDos\n", "\n", - "\n", - "clusterrun_phonopytotal_dosOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterrun_phonopytotal_dosInputs\n", + "\n", + "clusterRunPhonopytotal_dosInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopyOutputs\n", + "\n", + "clusterRunPhonopytotal_dosOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopyelement\n", + "\n", + "clusterRunPhonopyphonopy__df_GetItem_out\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "element: UserInput\n", + "\n", + "phonopy__df_GetItem_out: GetItem\n", "\n", - "\n", - "clusterrun_phonopyelementInputs\n", + "\n", + "clusterRunPhonopyphonopy__df_GetItem_outInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopyelementOutputs\n", + "\n", + "clusterRunPhonopyphonopy__df_GetItem_outOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopycell_size\n", + "\n", + "clusterRunPhonopyenergies\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "cell_size: UserInput\n", + "\n", + "energies: DictsToList\n", "\n", - "\n", - "clusterrun_phonopycell_sizeInputs\n", + "\n", + "clusterRunPhonopyenergiesInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopycell_sizeOutputs\n", + "\n", + "clusterRunPhonopyenergiesOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopyvacancy_index\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "vacancy_index: UserInput\n", + "\n", + "relaxed_structure__out_GetAttr_final: GetAttr\n", "\n", - "\n", - "clusterrun_phonopyvacancy_indexInputs\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_finalInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopyvacancy_indexOutputs\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_finalOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopydisplacement\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energy\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "displacement: UserInput\n", + "\n", + "relaxed_structure__out_GetAttr_final__getattr_GetAttr_energy: GetAttr\n", "\n", - "\n", - "clusterrun_phonopydisplacementInputs\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopydisplacementOutputs\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "relaxed_structure__out_GetAttr_final: GetAttr\n", + "\n", + "relaxed_structure__out_GetAttr_initial: GetAttr\n", "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_finalInputs\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initialInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_finalOutputs\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initialOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energy\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "relaxed_structure__out_GetAttr_initial: GetAttr\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initialInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", + "\n", + "relaxed_structure__out_GetAttr_initial__getattr_GetAttr_energy: GetAttr\n", "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initialOutputs\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterrun_phonopyphonopy__out_GetItem_energies\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "phonopy__out_GetItem_energies: GetItem\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterrun_phonopyphonopy__out_GetItem_energiesInputs\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterrun_phonopyphonopy__out_GetItem_energiesOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", + "\n", + "\n", + "clusterRunPhonopyInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "clusterrun_phonopyInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", + "\n", + "\n", + "clusterRunPhonopyOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "clusterrun_phonopyengine\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "engine: EMT\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "clusterrun_phonopyengineInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", + "\n", + "\n", + "clusterRunPhonopyInputselement\n", + "\n", + "element: str\n", "\n", - "\n", - "clusterrun_phonopyengineOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", + "\n", + "\n", + "clusterRunPhonopystructureInputselement\n", + "\n", + "element: str\n", "\n", - "\n", - "\n", - "clusterrun_phonopyInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterRunPhonopyInputselement->clusterRunPhonopystructureInputselement\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterRunPhonopyInputscell_size\n", + "\n", + "cell_size: int\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopystructureInputscell_size\n", + "\n", + "cell_size: int\n", "\n", - "\n", - "\n", - "clusterrun_phonopyInputselement\n", - "\n", - "element: str\n", + "\n", + "\n", + "clusterRunPhonopyInputscell_size->clusterRunPhonopystructureInputscell_size\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyelementInputsuser_input\n", - "\n", - "user_input: str\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyInputselement->clusterrun_phonopyelementInputsuser_input\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyInputscell_size\n", - "\n", - "cell_size: int\n", + "\n", + "\n", + "clusterRunPhonopyInputsvacancy_index\n", + "\n", + "vacancy_index\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopycell_sizeInputsuser_input\n", - "\n", - "user_input: int\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyInputscell_size->clusterrun_phonopycell_sizeInputsuser_input\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyInputsvacancy_index\n", - "\n", - "vacancy_index\n", + "clusterRunPhonopystructureInputsvacancy_index\n", + "\n", + "vacancy_index\n", "\n", - "\n", - "\n", - "clusterrun_phonopyvacancy_indexInputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyInputsvacancy_index->clusterrun_phonopyvacancy_indexInputsuser_input\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyInputsvacancy_index->clusterRunPhonopystructureInputsvacancy_index\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "clusterrun_phonopyInputsdisplacement\n", - "\n", - "displacement: float\n", + "clusterRunPhonopyInputsdisplacement\n", + "\n", + "displacement: float\n", "\n", - "\n", - "\n", - "clusterrun_phonopydisplacementInputsuser_input\n", - "\n", - "user_input: float\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyInputsdisplacement->clusterrun_phonopydisplacementInputsuser_input\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyInputsmax_workers\n", - "\n", - "max_workers: int\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputInputsdistance\n", + "\n", + "distance: float\n", "\n", - "\n", - "\n", - "clusterrun_phonopymax_workersInputsuser_input\n", - "\n", - "user_input: int\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyInputsmax_workers->clusterrun_phonopymax_workersInputsuser_input\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyInputsdisplacement->clusterRunPhonopyphonopy_inputInputsdistance\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyOutputsWithInjectionimaginary_modes\n", + "\n", + "imaginary_modes\n", + "\n", + "\n", "\n", - "clusterrun_phonopyOutputsimaginary_modes\n", - "\n", - "imaginary_modes\n", + "clusterRunPhonopyOutputsWithInjectiontotal_dos\n", + "\n", + "total_dos\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyOutputstotal_dos\n", - "\n", - "total_dos\n", + "clusterRunPhonopyOutputsWithInjectionenergy_relaxed\n", + "\n", + "energy_relaxed\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyOutputsenergy_relaxed\n", - "\n", - "energy_relaxed\n", + "clusterRunPhonopyOutputsWithInjectionenergy_initial\n", + "\n", + "energy_initial\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyOutputsenergy_initial\n", - "\n", - "energy_initial\n", + "clusterRunPhonopyOutputsWithInjectionenergy_displaced\n", + "\n", + "energy_displaced\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyOutputsenergy_displaced\n", - "\n", - "energy_displaced\n", + "clusterRunPhonopyengineInputsrun\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyengineOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterrun_phonopyelementInputsrun\n", - "\n", - "run\n", + "clusterRunPhonopyengineInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyelementOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyelementInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyengineOutputsWithInjectionran->clusterRunPhonopyrelaxed_structureInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopystructureInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyphonopyInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyelementOutputsran->clusterrun_phonopystructureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyengineOutputsWithInjectionran->clusterRunPhonopyphonopyInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyelementOutputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterRunPhonopyengineOutputsWithInjectionengine\n", + "\n", + "engine\n", "\n", - "\n", - "\n", - "clusterrun_phonopystructureInputselement\n", - "\n", - "element: str\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureInputsengine\n", + "\n", + "engine\n", "\n", - "\n", - "\n", - "clusterrun_phonopyelementOutputsuser_input->clusterrun_phonopystructureInputselement\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyengineOutputsWithInjectionengine->clusterRunPhonopyrelaxed_structureInputsengine\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopycell_sizeInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterRunPhonopyphonopyInputsengine\n", + "\n", + "engine\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyengineOutputsWithInjectionengine->clusterRunPhonopyphonopyInputsengine\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterRunPhonopystructureInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopycell_sizeOutputsran\n", - "\n", - "ran\n", + "clusterRunPhonopystructureOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopycell_sizeInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterRunPhonopystructureInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterrun_phonopycell_sizeOutputsran->clusterrun_phonopystructureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopystructureOutputsWithInjectionran->clusterRunPhonopyrelaxed_structureInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopycell_sizeOutputsuser_input\n", - "\n", - "user_input\n", + "clusterRunPhonopystructureOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", - "\n", - "\n", - "clusterrun_phonopystructureInputscell_size\n", - "\n", - "cell_size: int\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureInputsatoms\n", + "\n", + "atoms\n", "\n", - "\n", - "\n", - "clusterrun_phonopycell_sizeOutputsuser_input->clusterrun_phonopystructureInputscell_size\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopystructureOutputsWithInjectionstructure->clusterRunPhonopyrelaxed_structureInputsatoms\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyvacancy_indexInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyvacancy_indexOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyvacancy_indexInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "clusterRunPhonopyrelaxed_structureInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyvacancy_indexOutputsran->clusterrun_phonopystructureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterrun_phonopyvacancy_indexOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterrun_phonopystructureInputsvacancy_index\n", - "\n", - "vacancy_index\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyvacancy_indexOutputsuser_input->clusterrun_phonopystructureInputsvacancy_index\n", - "\n", - "\n", - "\n", + "clusterRunPhonopyrelaxed_structureInputsfmax\n", + "\n", + "fmax\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopydisplacementInputsrun\n", - "\n", - "run\n", + "clusterRunPhonopyrelaxed_structureInputslog_file\n", + "\n", + "log_file\n", "\n", - "\n", - "\n", - "clusterrun_phonopydisplacementOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureOutputsWithInjectionran->clusterRunPhonopyphonopyInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopydisplacementInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_finalInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureOutputsWithInjectionran->clusterRunPhonopyrelaxed_structure__out_GetAttr_finalInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopydisplacementOutputsran->clusterrun_phonopyphonopy_inputInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initialInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterrun_phonopydisplacementOutputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureOutputsWithInjectionran->clusterRunPhonopyrelaxed_structure__out_GetAttr_initialInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputInputsdistance\n", - "\n", - "distance: float\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", - "\n", - "\n", - "clusterrun_phonopydisplacementOutputsuser_input->clusterrun_phonopyphonopy_inputInputsdistance\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyphonopyInputsstructure\n", + "\n", + "structure\n", "\n", - "\n", - "\n", - "clusterrun_phonopymax_workersInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureOutputsWithInjectionstructure->clusterRunPhonopyphonopyInputsstructure\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopymax_workersOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureOutputsWithInjectionout\n", + "\n", + "out\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopymax_workersInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_finalInputsobj\n", + "\n", + "obj\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureOutputsWithInjectionout->clusterRunPhonopyrelaxed_structure__out_GetAttr_finalInputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopymax_workersOutputsran->clusterrun_phonopyphonopyInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initialInputsobj\n", + "\n", + "obj\n", "\n", - "\n", - "\n", - "clusterrun_phonopymax_workersOutputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structureOutputsWithInjectionout->clusterRunPhonopyrelaxed_structure__out_GetAttr_initialInputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyInputsmax_workers\n", - "\n", - "max_workers\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterrun_phonopymax_workersOutputsuser_input->clusterrun_phonopyphonopyInputsmax_workers\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterrun_phonopyengineInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyengineOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputInputsis_plusminus\n", + "\n", + "is_plusminus: Union\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyengineInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputInputsis_diagonal\n", + "\n", + "is_diagonal: bool\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputInputsis_trigonal\n", + "\n", + "is_trigonal: bool\n", "\n", - "\n", - "\n", - "clusterrun_phonopyengineOutputsran->clusterrun_phonopyrelaxed_structureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputInputsnumber_of_snapshots\n", + "\n", + "number_of_snapshots: Optional\n", "\n", - "\n", - "\n", - "clusterrun_phonopyengineOutputsran->clusterrun_phonopyphonopyInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputInputsrandom_seed\n", + "\n", + "random_seed: Optional\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputInputstemperature\n", + "\n", + "temperature: Optional\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyengineOutputsengine\n", - "\n", - "engine\n", + "clusterRunPhonopyphonopy_inputInputscutoff_frequency\n", + "\n", + "cutoff_frequency: Optional\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureInputsengine\n", - "\n", - "engine\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputInputsmax_distance\n", + "\n", + "max_distance: Optional\n", "\n", - "\n", - "\n", - "clusterrun_phonopyengineOutputsengine->clusterrun_phonopyrelaxed_structureInputsengine\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputOutputsWithInjectionran->clusterRunPhonopyphonopyInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyInputsengine\n", - "\n", - "engine\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputOutputsWithInjectiondataclass\n", + "\n", + "dataclass: GenerateSupercellsParameters\n", "\n", - "\n", - "\n", - "clusterrun_phonopyengineOutputsengine->clusterrun_phonopyphonopyInputsengine\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyphonopyInputsgenerate_supercells_parameters\n", + "\n", + "generate_supercells_parameters: GenerateSupercellsParameters\n", "\n", - "\n", - "\n", - "clusterrun_phonopystructureInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterRunPhonopyphonopy_inputOutputsWithInjectiondataclass->clusterRunPhonopyphonopyInputsgenerate_supercells_parameters\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopystructureOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterRunPhonopyphonopyInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopystructureOutputsran->clusterrun_phonopyrelaxed_structureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyphonopyOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterrun_phonopystructureOutputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "\n", + "clusterRunPhonopycheck_consistencyInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureInputsatoms\n", - "\n", - "atoms\n", + "\n", + "\n", + "clusterRunPhonopyphonopyOutputsWithInjectionran->clusterRunPhonopycheck_consistencyInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopystructureOutputsstructure->clusterrun_phonopyrelaxed_structureInputsatoms\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopytotal_dosInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterRunPhonopyphonopyOutputsWithInjectionran->clusterRunPhonopytotal_dosInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterRunPhonopyphonopy__df_GetItem_outInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureInputsfmax\n", - "\n", - "fmax\n", + "\n", + "\n", + "clusterRunPhonopyphonopyOutputsWithInjectionran->clusterRunPhonopyphonopy__df_GetItem_outInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureInputslog_file\n", - "\n", - "log_file\n", + "\n", + "\n", + "clusterRunPhonopyphonopyOutputsWithInjectionphonopy\n", + "\n", + "phonopy\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureOutputsran->clusterrun_phonopyphonopyInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopycheck_consistencyInputsphonopy\n", + "\n", + "phonopy\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_finalInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyphonopyOutputsWithInjectionphonopy->clusterRunPhonopycheck_consistencyInputsphonopy\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureOutputsran->clusterrun_phonopyrelaxed_structure__out_GetAttr_finalInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initialInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureOutputsran->clusterrun_phonopyrelaxed_structure__out_GetAttr_initialInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopytotal_dosInputsphonopy\n", + "\n", + "phonopy\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureOutputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "clusterRunPhonopyphonopyOutputsWithInjectionphonopy->clusterRunPhonopytotal_dosInputsphonopy\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyInputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "clusterRunPhonopyphonopyOutputsWithInjectiondf\n", + "\n", + "df\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureOutputsstructure->clusterrun_phonopyphonopyInputsstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyphonopy__df_GetItem_outInputsobj\n", + "\n", + "obj\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureOutputsout\n", - "\n", - "out\n", + "\n", + "\n", + "clusterRunPhonopyphonopyOutputsWithInjectiondf->clusterRunPhonopyphonopy__df_GetItem_outInputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_finalInputsobj\n", - "\n", - "obj\n", + "\n", + "\n", + "clusterRunPhonopycheck_consistencyInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureOutputsout->clusterrun_phonopyrelaxed_structure__out_GetAttr_finalInputsobj\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initialInputsobj\n", - "\n", - "obj\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structureOutputsout->clusterrun_phonopyrelaxed_structure__out_GetAttr_initialInputsobj\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopycheck_consistencyOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "\n", + "clusterRunPhonopycheck_consistencyInputstolerance\n", + "\n", + "tolerance: float\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterRunPhonopycheck_consistencyOutputsWithInjectionhas_imaginary_modes\n", + "\n", + "has_imaginary_modes\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputInputsis_plusminus\n", - "\n", - "is_plusminus: Union\n", + "\n", + "\n", + "clusterRunPhonopycheck_consistencyOutputsWithInjectionhas_imaginary_modes->clusterRunPhonopyOutputsWithInjectionimaginary_modes\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputInputsis_diagonal\n", - "\n", - "is_diagonal: bool\n", + "\n", + "\n", + "clusterRunPhonopytotal_dosInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyphonopy_inputInputsis_trigonal\n", - "\n", - "is_trigonal: bool\n", + "clusterRunPhonopytotal_dosOutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clusterRunPhonopytotal_dosInputsmesh\n", + "\n", + "mesh: Optional\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyphonopy_inputInputsnumber_of_snapshots\n", - "\n", - "number_of_snapshots: Optional\n", + "clusterRunPhonopytotal_dosOutputsWithInjectiontotal_dos\n", + "\n", + "total_dos\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputInputsrandom_seed\n", - "\n", - "random_seed: Optional\n", + "\n", + "\n", + "clusterRunPhonopytotal_dosOutputsWithInjectiontotal_dos->clusterRunPhonopyOutputsWithInjectiontotal_dos\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputInputstemperature\n", - "\n", - "temperature: Optional\n", + "\n", + "\n", + "clusterRunPhonopyphonopy__df_GetItem_outInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputInputscutoff_frequency\n", - "\n", - "cutoff_frequency: Optional\n", + "\n", + "\n", + "clusterRunPhonopyphonopy__df_GetItem_outOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterrun_phonopyphonopy_inputInputsmax_distance\n", - "\n", - "max_distance: Optional\n", + "clusterRunPhonopyphonopy__df_GetItem_outInputsitem\n", + "\n", + "item\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputOutputsran->clusterrun_phonopyphonopyInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyenergiesInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyphonopy__df_GetItem_outOutputsWithInjectionran->clusterRunPhonopyenergiesInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyphonopy_inputOutputsparameters\n", - "\n", - "parameters: dict\n", + "clusterRunPhonopyphonopy__df_GetItem_outOutputsWithInjectiongetitem\n", + "\n", + "getitem\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyInputsparameters\n", - "\n", - "parameters: Union\n", + "\n", + "\n", + "clusterRunPhonopyenergiesInputsdictionaries\n", + "\n", + "dictionaries\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy_inputOutputsparameters->clusterrun_phonopyphonopyInputsparameters\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyphonopy__df_GetItem_outOutputsWithInjectiongetitem->clusterRunPhonopyenergiesInputsdictionaries\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyphonopyInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyOutputsran\n", - "\n", - "ran\n", + "clusterRunPhonopyenergiesInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyphonopyInputsexecutor\n", - "\n", - "executor\n", + "clusterRunPhonopyenergiesOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterrun_phonopycheck_consistencyInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyenergiesInputskey\n", + "\n", + "key\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyOutputsran->clusterrun_phonopycheck_consistencyInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyenergiesOutputsWithInjection[d[key] for d in dictionaries]\n", + "\n", + "[d[key] for d in dictionaries]\n", "\n", - "\n", - "\n", - "clusterrun_phonopytotal_dosInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyenergiesOutputsWithInjection[d[key] for d in dictionaries]->clusterRunPhonopyOutputsWithInjectionenergy_displaced\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyOutputsran->clusterrun_phonopytotal_dosInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy__out_GetItem_energiesInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyOutputsran->clusterrun_phonopyphonopy__out_GetItem_energiesInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_finalInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyOutputsphonopy\n", - "\n", - "phonopy\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_finalOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterrun_phonopycheck_consistencyInputsphonopy\n", - "\n", - "phonopy\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_finalInputsname\n", + "\n", + "name\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyOutputsphonopy->clusterrun_phonopycheck_consistencyInputsphonopy\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterrun_phonopytotal_dosInputsphonopy\n", - "\n", - "phonopy\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_finalOutputsWithInjectionran->clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyOutputsphonopy->clusterrun_phonopytotal_dosInputsphonopy\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_finalOutputsWithInjectiongetattr\n", + "\n", + "getattr\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyOutputsout\n", - "\n", - "out\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsobj\n", + "\n", + "obj\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy__out_GetItem_energiesInputsobj\n", - "\n", - "obj\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_finalOutputsWithInjectiongetattr->clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyphonopyOutputsout->clusterrun_phonopyphonopy__out_GetItem_energiesInputsobj\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterrun_phonopycheck_consistencyInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterrun_phonopycheck_consistencyOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsname\n", + "\n", + "name\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopycheck_consistencyInputstolerance\n", - "\n", - "tolerance: float\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyOutputsWithInjectiongetattr\n", + "\n", + "getattr\n", "\n", - "\n", - "\n", - "clusterrun_phonopycheck_consistencyOutputshas_imaginary_modes\n", - "\n", - "has_imaginary_modes\n", - "\n", - "\n", - "\n", - "clusterrun_phonopycheck_consistencyOutputshas_imaginary_modes->clusterrun_phonopyOutputsimaginary_modes\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopytotal_dosInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyOutputsWithInjectiongetattr->clusterRunPhonopyOutputsWithInjectionenergy_relaxed\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopytotal_dosOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initialInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopytotal_dosInputsmesh\n", - "\n", - "mesh\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initialOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterrun_phonopytotal_dosOutputstotal_dos\n", - "\n", - "total_dos\n", - "\n", - "\n", - "\n", - "clusterrun_phonopytotal_dosOutputstotal_dos->clusterrun_phonopyOutputstotal_dos\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_finalInputsrun\n", - "\n", - "run\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initialInputsname\n", + "\n", + "name\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initialOutputsWithInjectionran->clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initialOutputsWithInjectiongetattr\n", + "\n", + "getattr\n", "\n", - "\n", + "\n", "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_finalOutputsran\n", - "\n", - "ran\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsobj\n", + "\n", + "obj\n", "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_finalInputsname\n", - "\n", - "name\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initialOutputsWithInjectiongetattr->clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_finalOutputsran->clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_finalOutputsgetattr\n", - "\n", - "getattr\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsobj\n", - "\n", - "obj\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_finalOutputsgetattr->clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsobj\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyInputsname\n", - "\n", - "name\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyOutputsgetattr\n", - "\n", - "getattr\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_final__getattr_GetAttr_energyOutputsgetattr->clusterrun_phonopyOutputsenergy_relaxed\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initialInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initialOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initialInputsname\n", - "\n", - "name\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initialOutputsran->clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initialOutputsgetattr\n", - "\n", - "getattr\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsobj\n", - "\n", - "obj\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initialOutputsgetattr->clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsobj\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsname\n", - "\n", - "name\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyOutputsgetattr\n", - "\n", - "getattr\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyOutputsgetattr->clusterrun_phonopyOutputsenergy_initial\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy__out_GetItem_energiesInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy__out_GetItem_energiesOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy__out_GetItem_energiesInputsitem\n", - "\n", - "item\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy__out_GetItem_energiesOutputsgetitem\n", - "\n", - "getitem\n", - "\n", - "\n", - "\n", - "clusterrun_phonopyphonopy__out_GetItem_energiesOutputsgetitem->clusterrun_phonopyOutputsenergy_displaced\n", - "\n", - "\n", - "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyInputsname\n", + "\n", + "name\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyOutputsWithInjectiongetattr\n", + "\n", + "getattr\n", + "\n", + "\n", + "\n", + "clusterRunPhonopyrelaxed_structure__out_GetAttr_initial__getattr_GetAttr_energyOutputsWithInjectiongetattr->clusterRunPhonopyOutputsWithInjectionenergy_initial\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 13, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "wf.draw()" + "wf.draw(size=(10, 10))" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "id": "49ac188a-6b86-4650-a1d9-96d6e80f7d24", "metadata": {}, "outputs": [], @@ -3710,7 +2631,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "id": "0c979de8-9250-4db9-a5a4-fe0ea2dcaba6", "metadata": {}, "outputs": [ @@ -3736,329 +2657,174 @@ "
\n", " \n", " atoms\n", - " energy_pot\n", - " force\n", - " stress\n", - " structure\n", - " atomic_energies\n", - " energy\n", - " forces\n", + " out\n", "
\n", " \n", " \n", "
\n", " 0\n", " (Atom('Al', [12.220710678118651, 2.08210908380...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.816457\n", - " [[-0.23174580883329354, -0.19678867725572524, ...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 1\n", " (Atom('Al', [12.079289321881344, 1.94068772756...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.816327\n", - " [[0.23692289961947186, 0.19206047173841828, -0...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 2\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817278\n", - " [[-0.0005872298862959757, -0.02996349715915251...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 3\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817907\n", - " [[0.0028026803072459007, 0.032890474969283864,...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 4\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.816751\n", - " [[0.0007383933558697698, -0.000762530426247583...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 5\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.818430\n", - " [[-0.0009088635560401473, -0.00237638063938402...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 6\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817795\n", - " [[0.03740766557096517, -0.0010937097702614293,...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 7\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.816850\n", - " [[-0.04661644245198476, -0.001978463773318246,...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 8\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.818396\n", - " [[0.03786045149966151, -0.0012044959255991111,...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 9\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.816223\n", - " [[-0.05168042888587365, -0.0018528473275270692...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 10\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.818146\n", - " [[-0.00033051875777549873, 0.02594564295422879...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 11\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.816604\n", - " [[0.002422288076770966, -0.03244830791908933, ...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 12\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817426\n", - " [[-4.780355604225989e-07, -0.00146815216264624...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 13\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817452\n", - " [[-0.0006781361956882913, -0.00232068846764893...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 14\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817448\n", - " [[0.0007905956169700513, -0.000446523484563542...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 15\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817074\n", - " [[-0.0004158699915945644, -0.00166271538182353...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 16\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817817\n", - " [[0.00045048659760040487, -0.00132873998851663...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 17\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817147\n", - " [[-0.0013705275391655818, -0.00229917035819127...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 18\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817694\n", - " [[0.0016049072496653483, -0.000505528307749161...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 19\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817430\n", - " [[6.755310123750247e-07, -0.001483325282606008...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", " 20\n", " (Atom('Al', [12.149999999999997, 2.01139840568...\n", - " None\n", - " None\n", - " None\n", - " None\n", - " None\n", - " 0.817439\n", - " [[-5.078224998809888e-07, -0.00148448447449132...\n", + " {'energy_pot': None, 'force': None, 'stress': ...\n", "
\n", "
\n", "\n", "" ], "text/plain": [ - " atoms energy_pot force stress \\\n", - "0 (Atom('Al', [12.220710678118651, 2.08210908380... None None None \n", - "1 (Atom('Al', [12.079289321881344, 1.94068772756... None None None \n", - "2 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "3 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "4 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "5 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "6 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "7 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "8 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "9 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "10 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "11 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "12 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "13 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "14 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "15 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "16 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "17 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "18 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "19 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "20 (Atom('Al', [12.149999999999997, 2.01139840568... None None None \n", - "\n", - " structure atomic_energies energy \\\n", - "0 None None 0.816457 \n", - "1 None None 0.816327 \n", - "2 None None 0.817278 \n", - "3 None None 0.817907 \n", - "4 None None 0.816751 \n", - "5 None None 0.818430 \n", - "6 None None 0.817795 \n", - "7 None None 0.816850 \n", - "8 None None 0.818396 \n", - "9 None None 0.816223 \n", - "10 None None 0.818146 \n", - "11 None None 0.816604 \n", - "12 None None 0.817426 \n", - "13 None None 0.817452 \n", - "14 None None 0.817448 \n", - "15 None None 0.817074 \n", - "16 None None 0.817817 \n", - "17 None None 0.817147 \n", - "18 None None 0.817694 \n", - "19 None None 0.817430 \n", - "20 None None 0.817439 \n", + " atoms \\\n", + "0 (Atom('Al', [12.220710678118651, 2.08210908380... \n", + "1 (Atom('Al', [12.079289321881344, 1.94068772756... \n", + "2 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "3 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "4 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "5 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "6 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "7 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "8 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "9 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "10 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "11 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "12 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "13 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "14 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "15 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "16 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "17 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "18 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "19 (Atom('Al', [12.149999999999997, 2.01139840568... \n", + "20 (Atom('Al', [12.149999999999997, 2.01139840568... \n", "\n", - " forces \n", - "0 [[-0.23174580883329354, -0.19678867725572524, ... \n", - "1 [[0.23692289961947186, 0.19206047173841828, -0... \n", - "2 [[-0.0005872298862959757, -0.02996349715915251... \n", - "3 [[0.0028026803072459007, 0.032890474969283864,... \n", - "4 [[0.0007383933558697698, -0.000762530426247583... \n", - "5 [[-0.0009088635560401473, -0.00237638063938402... \n", - "6 [[0.03740766557096517, -0.0010937097702614293,... \n", - "7 [[-0.04661644245198476, -0.001978463773318246,... \n", - "8 [[0.03786045149966151, -0.0012044959255991111,... \n", - "9 [[-0.05168042888587365, -0.0018528473275270692... \n", - "10 [[-0.00033051875777549873, 0.02594564295422879... \n", - "11 [[0.002422288076770966, -0.03244830791908933, ... \n", - "12 [[-4.780355604225989e-07, -0.00146815216264624... \n", - "13 [[-0.0006781361956882913, -0.00232068846764893... \n", - "14 [[0.0007905956169700513, -0.000446523484563542... \n", - "15 [[-0.0004158699915945644, -0.00166271538182353... \n", - "16 [[0.00045048659760040487, -0.00132873998851663... \n", - "17 [[-0.0013705275391655818, -0.00229917035819127... \n", - "18 [[0.0016049072496653483, -0.000505528307749161... \n", - "19 [[6.755310123750247e-07, -0.001483325282606008... \n", - "20 [[-5.078224998809888e-07, -0.00148448447449132... " + " out \n", + "0 {'energy_pot': None, 'force': None, 'stress': ... \n", + "1 {'energy_pot': None, 'force': None, 'stress': ... \n", + "2 {'energy_pot': None, 'force': None, 'stress': ... \n", + "3 {'energy_pot': None, 'force': None, 'stress': ... \n", + "4 {'energy_pot': None, 'force': None, 'stress': ... \n", + "5 {'energy_pot': None, 'force': None, 'stress': ... \n", + "6 {'energy_pot': None, 'force': None, 'stress': ... \n", + "7 {'energy_pot': None, 'force': None, 'stress': ... \n", + "8 {'energy_pot': None, 'force': None, 'stress': ... \n", + "9 {'energy_pot': None, 'force': None, 'stress': ... \n", + "10 {'energy_pot': None, 'force': None, 'stress': ... \n", + "11 {'energy_pot': None, 'force': None, 'stress': ... \n", + "12 {'energy_pot': None, 'force': None, 'stress': ... \n", + "13 {'energy_pot': None, 'force': None, 'stress': ... \n", + "14 {'energy_pot': None, 'force': None, 'stress': ... \n", + "15 {'energy_pot': None, 'force': None, 'stress': ... \n", + "16 {'energy_pot': None, 'force': None, 'stress': ... \n", + "17 {'energy_pot': None, 'force': None, 'stress': ... \n", + "18 {'energy_pot': None, 'force': None, 'stress': ... \n", + "19 {'energy_pot': None, 'force': None, 'stress': ... \n", + "20 {'energy_pot': None, 'force': None, 'stress': ... " ] }, - "execution_count": 15, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "wf.phonopy.outputs.out.value['df']" + "wf.phonopy.outputs.df.value" ] }, { @@ -4071,61 +2837,40 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "id": "7bc34f3a-df8a-4cbb-9aa5-7b201d9e9f28", "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/node_library/atomistic/property/phonons.py:129: UserWarning: WARNING: 3 imaginary modes exist\n", + " warnings.warn(f\"WARNING: {n_imaginary_nodes} imaginary modes exist\")\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "max_workers: 1\n", - "energy: -0.006008190344925168 -0.006008190344925168\n", - "max_workers: 1\n", - "energy: -0.006008190344925168 -0.006008190344925168\n", - "max_workers: 1\n", - "energy: 0.8712882553372374 0.8712882553372374\n", - "max_workers: 1\n", - "WARNING: 3 imaginary modes exist\n", - "energy: 0.8712882553372374 0.8712882553372374\n", - "max_workers: 1\n", - "WARNING: 3 imaginary modes exist\n", - "energy: -0.0480655227588862 -0.0480655227588862\n", - "max_workers: 1\n", - "energy: -0.0480655227588862 -0.0480655227588862\n", - "max_workers: 1\n", - "energy: 0.9186046985116931 0.9179414222257538\n", - "max_workers: 1\n", - "WARNING: 3 imaginary modes exist\n", - "energy: 0.9186046985116931 0.9179414222257538\n", - "max_workers: 1\n", - "WARNING: 3 imaginary modes exist\n", - "energy: -0.16222113933213578 -0.16222113933213578\n", - "max_workers: 1\n", - "energy: -0.16222113933213578 -0.16222113933213578\n", - "max_workers: 1\n", - "energy: 0.8013167095856435 0.7996059979142878\n", - "max_workers: 1\n", - "energy: 0.8013167095856435 0.7996059979142878\n", - "max_workers: 1\n", - "WARNING: 3 imaginary modes exist\n", - "CPU times: user 58.2 s, sys: 6.67 s, total: 1min 4s\n", - "Wall time: 11.7 s\n" + "CPU times: user 1min 2s, sys: 23.8 s, total: 1min 25s\n", + "Wall time: 56.5 s\n" ] } ], "source": [ "%%time\n", - "df = wf.iter(cell_size=list(range(1,4)), \n", - " element=['Al'], \n", - " vacancy_index=[None, 0], \n", - " displacement=[0.01, 0.1]\n", - " ) #, Cu, Pd, Ag, Pt and Au])" + "df = wf.iter(\n", + " cell_size=list(range(1,4)), \n", + " # element= [Al, Cu, Pd, Ag, Pt, Au] # Takes about a minute without looping over species\n", + " vacancy_index=[None, 0], \n", + " displacement=[0.01, 0.1],\n", + ") " ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "id": "5649c8f1-00b2-44ac-8f92-df425551075e", "metadata": {}, "outputs": [ @@ -4151,7 +2896,6 @@ "
\n", " \n", " cell_size\n", - " element\n", " vacancy_index\n", " displacement\n", " imaginary_modes\n", @@ -4165,7 +2909,6 @@ "
\n", " 0\n", " 1\n", - " Al\n", " NaN\n", " 0.01\n", " False\n", @@ -4173,13 +2916,11 @@ "0 ...\n", " -0.006008\n", " -0.006008\n", - " 0 -0.005843\n", - "Name: energy, dtype: float64\n", + " [-0.005842915427074047]\n", "
\n", "
\n", " 1\n", " 1\n", - " Al\n", " NaN\n", " 0.10\n", " False\n", @@ -4187,13 +2928,11 @@ "0 ...\n", " -0.006008\n", " -0.006008\n", - " 0 0.010553\n", - "Name: energy, dtype: float64\n", + " [0.010553270429960904]\n", "
\n", "
\n", " 2\n", " 1\n", - " Al\n", " 0.0\n", " 0.01\n", " True\n", @@ -4201,13 +2940,11 @@ "0 ...\n", " 0.871288\n", " 0.871288\n", - " 0 0.871408\n", - "Name: energy, dtype: float64\n", + " [0.8714080922708654]\n", "
\n", "
\n", " 3\n", " 1\n", - " Al\n", " 0.0\n", " 0.10\n", " True\n", @@ -4215,13 +2952,11 @@ "0 ...\n", " 0.871288\n", " 0.871288\n", - " 0 0.883371\n", - "Name: energy, dtype: float64\n", + " [0.8833706262545284]\n", "
\n", "
\n", " 4\n", " 2\n", - " Al\n", " NaN\n", " 0.01\n", " False\n", @@ -4229,13 +2964,11 @@ "0 -...\n", " -0.048066\n", " -0.048066\n", - " 0 -0.047905\n", - "Name: energy, dtype: float64\n", + " [-0.04790513998490198]\n", "
\n", "
\n", " 5\n", " 2\n", - " Al\n", " NaN\n", " 0.10\n", " False\n", @@ -4243,13 +2976,11 @@ "0 -...\n", " -0.048066\n", " -0.048066\n", - " 0 -0.031995\n", - "Name: energy, dtype: float64\n", + " [-0.03199460815804045]\n", "
\n", "
\n", " 6\n", " 2\n", - " Al\n", " 0.0\n", " 0.01\n", " True\n", @@ -4257,15 +2988,11 @@ "0 -...\n", " 0.918605\n", " 0.917941\n", - " 0 0.918776\n", - "1 0.918736\n", - "2 0.918768\n", - "3 ...\n", + " [0.9187758684410561, 0.918735616401376, 0.9187...\n", "
\n", "
\n", " 7\n", " 2\n", - " Al\n", " 0.0\n", " 0.10\n", " True\n", @@ -4273,15 +3000,11 @@ "0 -...\n", " 0.918605\n", " 0.917941\n", - " 0 0.933953\n", - "1 0.933630\n", - "2 0.935058\n", - "3 ...\n", + " [0.9339528274357036, 0.9336300229312133, 0.935...\n", "
\n", "
\n", " 8\n", " 3\n", - " Al\n", " NaN\n", " 0.01\n", " False\n", @@ -4289,13 +3012,11 @@ "0 -...\n", " -0.162221\n", " -0.162221\n", - " 0 -0.162061\n", - "Name: energy, dtype: float64\n", + " [-0.16206075641027518]\n", "
\n", "
\n", " 9\n", " 3\n", - " Al\n", " NaN\n", " 0.10\n", " False\n", @@ -4303,13 +3024,11 @@ "0 -...\n", " -0.162221\n", " -0.162221\n", - " 0 -0.14615\n", - "Name: energy, dtype: float64\n", + " [-0.14615021022018482]\n", "
\n", "
\n", " 10\n", " 3\n", - " Al\n", " 0.0\n", " 0.01\n", " False\n", @@ -4317,15 +3036,11 @@ "0 -...\n", " 0.801317\n", " 0.799606\n", - " 0 0.801477\n", - "1 0.801456\n", - "2 0.801444\n", - "3...\n", + " [0.8014770899771015, 0.801456182029046, 0.8014...\n", "
\n", "
\n", " 11\n", " 3\n", - " Al\n", " 0.0\n", " 0.10\n", " True\n", @@ -4333,29 +3048,26 @@ "0 -...\n", " 0.801317\n", " 0.799606\n", - " 0 0.816457\n", - "1 0.816327\n", - "2 0.817278\n", - "3...\n", + " [0.8164570512616045, 0.816327055467557, 0.8172...\n", "
\n", " \n", "\n", "" ], "text/plain": [ - " cell_size element vacancy_index displacement imaginary_modes \\\n", - "0 1 Al NaN 0.01 False \n", - "1 1 Al NaN 0.10 False \n", - "2 1 Al 0.0 0.01 True \n", - "3 1 Al 0.0 0.10 True \n", - "4 2 Al NaN 0.01 False \n", - "5 2 Al NaN 0.10 False \n", - "6 2 Al 0.0 0.01 True \n", - "7 2 Al 0.0 0.10 True \n", - "8 3 Al NaN 0.01 False \n", - "9 3 Al NaN 0.10 False \n", - "10 3 Al 0.0 0.01 False \n", - "11 3 Al 0.0 0.10 True \n", + " cell_size vacancy_index displacement imaginary_modes \\\n", + "0 1 NaN 0.01 False \n", + "1 1 NaN 0.10 False \n", + "2 1 0.0 0.01 True \n", + "3 1 0.0 0.10 True \n", + "4 2 NaN 0.01 False \n", + "5 2 NaN 0.10 False \n", + "6 2 0.0 0.01 True \n", + "7 2 0.0 0.10 True \n", + "8 3 NaN 0.01 False \n", + "9 3 NaN 0.10 False \n", + "10 3 0.0 0.01 False \n", + "11 3 0.0 0.10 True \n", "\n", " total_dos energy_relaxed \\\n", "0 frequency_points total_dos\n", @@ -4384,41 +3096,21 @@ "0 -... 0.801317 \n", "\n", " energy_initial energy_displaced \n", - "0 -0.006008 0 -0.005843\n", - "Name: energy, dtype: float64 \n", - "1 -0.006008 0 0.010553\n", - "Name: energy, dtype: float64 \n", - "2 0.871288 0 0.871408\n", - "Name: energy, dtype: float64 \n", - "3 0.871288 0 0.883371\n", - "Name: energy, dtype: float64 \n", - "4 -0.048066 0 -0.047905\n", - "Name: energy, dtype: float64 \n", - "5 -0.048066 0 -0.031995\n", - "Name: energy, dtype: float64 \n", - "6 0.917941 0 0.918776\n", - "1 0.918736\n", - "2 0.918768\n", - "3 ... \n", - "7 0.917941 0 0.933953\n", - "1 0.933630\n", - "2 0.935058\n", - "3 ... \n", - "8 -0.162221 0 -0.162061\n", - "Name: energy, dtype: float64 \n", - "9 -0.162221 0 -0.14615\n", - "Name: energy, dtype: float64 \n", - "10 0.799606 0 0.801477\n", - "1 0.801456\n", - "2 0.801444\n", - "3... \n", - "11 0.799606 0 0.816457\n", - "1 0.816327\n", - "2 0.817278\n", - "3... " + "0 -0.006008 [-0.005842915427074047] \n", + "1 -0.006008 [0.010553270429960904] \n", + "2 0.871288 [0.8714080922708654] \n", + "3 0.871288 [0.8833706262545284] \n", + "4 -0.048066 [-0.04790513998490198] \n", + "5 -0.048066 [-0.03199460815804045] \n", + "6 0.917941 [0.9187758684410561, 0.918735616401376, 0.9187... \n", + "7 0.917941 [0.9339528274357036, 0.9336300229312133, 0.935... \n", + "8 -0.162221 [-0.16206075641027518] \n", + "9 -0.162221 [-0.14615021022018482] \n", + "10 0.799606 [0.8014770899771015, 0.801456182029046, 0.8014... \n", + "11 0.799606 [0.8164570512616045, 0.816327055467557, 0.8172... " ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -4427,6 +3119,36 @@ "df" ] }, + { + "cell_type": "code", + "execution_count": 19, + "id": "6d8b06ef-35d6-4456-8087-8b277ec538e7", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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DOWUl5ejqioNDQ1ZLkp1dTXnnXdezs4NMHDgQC6++GJWrlzJueeey/PPP8/tt99u73/xxRf53ve+x+LFi5k0aRLBYJD/+I//4P333wfAm8OfhUDQHrotUNoWAuG4uV9Nds+ZaIxYAqWTNyxdj5PACUAC52Hb3Ve1fAWMtx/HO0h2F3SBA5th6x9JRL5mV/BEcCMlDA56B9iHqbKzlUBJouIkltAo9DqP9lX3afqGQJGkvIZZjhYlJSWUlJRkbZs4cSJOp5M1a9bY4qWyspKtW7fy6KOP5vwarr/+en74wx/y7W9/mx07dnDttdfa+9577z3OO+887rzzTnvbjh3pSopgMMjw4cN56623uPjii3N+bQIBHN5BCcfNG368kw5Ia5pSDkonm3fpupohUFyHHRhYFcp2RlVdCJSc8M6jsP3PjC6T+CdmaC+KC1lNUunvZx+WkJ0gp91n0e6+5xAhnl5EKBRi8+bNbN68GYCdO3eyefPmDkM5hYWFzJ49m3vuuYe33nqLTZs2ccMNNzBu3DimTp1qHzdlypSskuLunAvg6quvprm5mTvuuIOLL76YQYMG2ftGjhzJhg0beOONN/j888+59957+eCDD7LWL1y4kMWLF/OLX/yCL774gg8//JClS5d29kckEBwWQz+cQDH31zZ81K3nb4iYzkxnHRRDV0lYjb90SUHtIMSj6wlqIjVZ20QOSo6ImdOL3WqDXWIcNdygGhyQ0h/8ErILRTHQDVOkuEQvlB5DCJRexIYNG5gwYQITJkwAYP78+UyYMIH77ruvw3WPP/44V155JbNmzeL888/H5/Px6quvoiiKfcyOHTuora094nMVFBRw2WWX8dFHH3H99ddn7ZszZw5XX30111xzDeeccw51dXVZbgrATTfdxJIlS3jqqac49dRTmTlzJl988cXhfzgCQSc5nIMSipsORnVd58K+jRGVxX/dzk4rObbrIZ60gwIQS7afUxKJfEXIcGdtEwIlRyQiACiJcEaIxwM6fJkoTR8mu3DKut2srchrChUhUI4+fSLEc6wwefLkbpUlezweli5d2qETsWvXrpycC8gqG87E7XbzzDPP8Mwzz2Rtf+ihh7Ie33777Vm5KwJBLknleLSVgxJPaqRST+KdzD19edN+lv7tSyqbYvznt8anQzydzGHRMxwUgJjWtnACiMUrsyp4AFRR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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "for size, displacement, vacancy, total_dos in zip(\n", + " df[\"cell_size\"], df[\"displacement\"], df[\"vacancy_index\"], df[\"total_dos\"]\n", + "):\n", + " plt.plot(\n", + " total_dos[\"frequency_points\"], \n", + " total_dos[\"total_dos\"],\n", + " label=f\"{size}-{displacement} {'vac' if vacancy == 0 else ''}\"\n", + " )\n", + "plt.legend()\n", + "plt.show()" + ] + }, { "cell_type": "markdown", "id": "22ccd6eb-22f9-4a26-a2e8-8edb14e05162", @@ -4437,7 +3159,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "id": "f5db8f36-a493-4d0c-b530-56a050134d6b", "metadata": {}, "outputs": [ @@ -4445,8 +3167,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 5.7 ms, sys: 1.83 ms, total: 7.53 ms\n", - "Wall time: 1.08 ms\n" + "CPU times: user 2.51 ms, sys: 80 µs, total: 2.59 ms\n", + "Wall time: 2.7 ms\n" ] } ], @@ -4463,7 +3185,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 21, "id": "cd33bf3e-7452-4d3b-a96e-5d1ed9eb538b", "metadata": {}, "outputs": [ @@ -4471,8 +3193,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 9 µs, sys: 0 ns, total: 9 µs\n", - "Wall time: 3.1 µs\n" + "CPU times: user 4 µs, sys: 1 µs, total: 5 µs\n", + "Wall time: 8.82 µs\n" ] } ], @@ -4485,7 +3207,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 95, "id": "a28ee850-afff-4036-8a05-e86136511514", "metadata": {}, "outputs": [ @@ -4493,13 +3215,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "File downloaded successfully\n" + "Failed to download file: 403\n" ] } ], "source": [ - "# Get the source data\n", - "# from Ref. de Jong et al. https://www.nature.com/articles/sdata20159#MOESM77\n", + "# # Get the source data\n", + "# # from Ref. de Jong et al. https://www.nature.com/articles/sdata20159#MOESM77\n", "\n", "import requests\n", "\n", @@ -4512,23 +3234,31 @@ " f.write(response.content)\n", " print('File downloaded successfully')\n", "else:\n", - " print('Failed to download file:', response.status_code)" + " print('Failed to download file:', response.status_code)\n", + "\n", + "# The link works fine, but downloading via python yields a 403\n", + "# I guess they are blocking scraping. \n", + "# The file is multiple MB and I don't want to add it to the repo\n", + "# This section will no longer get automated testing\n", + "import os\n", + "SOURCE_DATA_PRESENT = os.path.exists(destination_file)" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "id": "10ad0c84-0846-4d9d-91b8-cee47f5efef3", "metadata": {}, "outputs": [], "source": [ - "wf = Workflow('elastic')\n", - "wf.data = wf.create.databases.elasticity.de_jong()" + "if SOURCE_DATA_PRESENT:\n", + " wf = Workflow('elastic')\n", + " wf.data = wf.create.databases.elasticity.DeJong()" ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 24, "id": "37b0972d-0ad3-4f85-8897-36f1d8d76fc2", "metadata": {}, "outputs": [ @@ -4536,19 +3266,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 21.6 s, sys: 3.19 s, total: 24.7 s\n", - "Wall time: 3.34 s\n" + "CPU times: user 5.88 s, sys: 168 ms, total: 6.04 s\n", + "Wall time: 6.07 s\n" ] } ], "source": [ "%%time\n", - "df_data = wf.run()['data__dataframe']" + "if SOURCE_DATA_PRESENT:\n", + " df_data = wf.run()['data__dataframe']" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 25, "id": "25a0f789-1248-4280-a09a-f2c9903b06f1", "metadata": {}, "outputs": [ @@ -4755,7 +3486,7 @@ " 194\n", " #\\#CIF1.1\\n###################################...\n", " 121.520152\n", - " (Atom('Ti', [1.5517152117138742, 0.89588144411...\n", + " (Atom('Ti', [1.5517152117138744, 0.89588144411...\n", "
\n", "
\n", " 1177\n", @@ -4953,7 +3684,7 @@ "3 (Atom('Ga', [0.0, 1.09045796233546, 0.84078379... \n", "4 (Atom('Si', [1.0094264625, 4.247717077057611, ... \n", "... ... \n", - "1176 (Atom('Ti', [1.5517152117138742, 0.89588144411... \n", + "1176 (Atom('Ti', [1.5517152117138744, 0.89588144411... \n", "1177 (Atom('Sc', [0.0, 8.534175787117318, 0.9174096... \n", "1178 (Atom('Y', [0.0, 9.084548591046719, 0.96092093... \n", "1179 (Atom('Al', [5.1103587633892795, 2.07486753338... \n", @@ -4962,26 +3693,28 @@ "[1181 rows x 20 columns]" ] }, - "execution_count": 23, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "df_data" + "if SOURCE_DATA_PRESENT:\n", + " df_data" ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 26, "id": "0ba2176d-dfa3-4522-af61-fd7e3411d9ef", "metadata": {}, "outputs": [], "source": [ - "unaries = df_data[df_data.formula.str.len() == 2]\n", - "K_Reuss = unaries.K_Reuss.values\n", - "K_Voigt = unaries.K_Voigt\n", - "structures = unaries.atoms.values" + "if SOURCE_DATA_PRESENT:\n", + " unaries = df_data[df_data.formula.str.len() == 2]\n", + " K_Reuss = unaries.K_Reuss.values\n", + " K_Voigt = unaries.K_Voigt\n", + " structures = unaries.atoms.values" ] }, { @@ -4996,32 +3729,59 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 27, "id": "10bbf3ba-4c61-455b-a071-35efcbadfd92", "metadata": {}, + "outputs": [], + "source": [ + "if SOURCE_DATA_PRESENT:\n", + " table_M3GNet = Workflow.create.atomistic.structure.calc.Volume().iter(\n", + " structure=structures.tolist(),\n", + " ) # TODO: load rather than run" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "adb8c0a2-d251-41fd-973c-accf3376141b", + "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "max_workers: 1\n", - "iter: add label\n" - ] + "data": { + "text/plain": [ + "0 76.721433\n", + "1 58.258386\n", + "2 73.918650\n", + "3 40.523308\n", + "4 43.685532\n", + " ... \n", + "62 15.850527\n", + "63 23.161980\n", + "64 45.915955\n", + "65 107.690974\n", + "66 43.223810\n", + "Name: volume, Length: 67, dtype: float64" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "table_M3GNet = Workflow.create.atomistic.structure.calc.volume().iter(structure=structures) # TODO: load rather than run" + "if SOURCE_DATA_PRESENT:\n", + " table_M3GNet[\"volume\"]" ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 29, "id": "9249849d-4685-4b33-b6ef-2536f4ab2a94", "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -5031,104 +3791,31 @@ } ], "source": [ - "plt.scatter(unaries.volume, table_M3GNet.volume);" + "if SOURCE_DATA_PRESENT:\n", + " plt.scatter(unaries[\"volume\"], table_M3GNet[\"volume\"]);" ] }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 30, "id": "8a36d2f6-3937-4c90-93fb-62eb54acca65", "metadata": {}, "outputs": [], "source": [ "try:\n", " import matgl\n", - " MATGL_PRESENT = True\n", + " MATGL_PRESENT = SOURCE_DATA_PRESENT\n", + " # We also leverage the data from above, so we need that too\n", "except ModuleNotFoundError:\n", " MATGL_PRESENT = False" ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 31, "id": "f6710a6d-e1d6-4807-b1e4-88fa14aad86a", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n", - "max_workers: 1\n" - ] - } - ], + "outputs": [], "source": [ "if MATGL_PRESENT:\n", " import warnings\n", @@ -5139,21 +3826,10 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 32, "id": "3e6f8e28-9bb2-4b8d-ab29-29b0781c536c", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "if MATGL_PRESENT:\n", " x = np.linspace(0, 500, 101)\n", @@ -5180,7 +3856,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 33, "id": "560c2439-b831-452c-b0f3-c42d17fb5591", "metadata": {}, "outputs": [], @@ -5199,7 +3875,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 34, "id": "5f9c9dae-7a97-47e5-9750-c9adfb9c1072", "metadata": {}, "outputs": [], @@ -5212,7 +3888,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 35, "id": "53847c37-2f8b-4ba1-bd3a-c27c9eee958a", "metadata": {}, "outputs": [], @@ -5224,7 +3900,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 36, "id": "24632194-6f3b-44c4-a8dd-c9ee1b9147f5", "metadata": {}, "outputs": [], @@ -5246,7 +3922,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 37, "id": "47cb2507-7b1e-4172-b423-4a055f0256b4", "metadata": {}, "outputs": [], @@ -5257,21 +3933,10 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 38, "id": "54dfd61f-4195-41b2-98b4-293957c7be70", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjMAAAGdCAYAAADnrPLBAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAABlZElEQVR4nO3deXxU5dk//s+ZJZOF7CGQkJUl7CCrgCDiAihVqbVWrKLlZ1v61WqljwVtH/VptdBitbXr09pH20pJsaBgFQXZXBDDEvadQBKyELJO1lnP74+ZczITZpJZzpkFPu/XK6/aZJKcHJK5r7nu674uQRRFEURERERRShPuCyAiIiIKBoMZIiIiimoMZoiIiCiqMZghIiKiqMZghoiIiKIagxkiIiKKagxmiIiIKKoxmCEiIqKopgv3BYSC3W5HdXU1EhMTIQhCuC+HiIiIfCCKIlpbW5GdnQ2Nxnv+5ZoIZqqrq5GbmxvuyyAiIqIAVFZWIicnx+vHr4lgJjExEYDjZiQlJYX5aoiIiMgXRqMRubm58jruzTURzEhbS0lJSQxmiIiIokxfJSIsACYiIqKoxmCGiIiIohqDGSIiIopqDGaIiIgoqjGYISIioqjGYIaIiIiiGoMZIiIiimoMZoiIiCiqMZghIiKiqMZghoiIiKIagxkiIiKKagxmiIiIKKoxmFFZu8mKP+06hwv17eG+FCIioqsSgxmVvX+kBqs2n8QrW0+H+1KIiIiuSgxmVGbstAAAyhuYmSEiIlIDgxmVWWwiAKC6pSvMV0JERHR1YjCjMovNDgCobzPBbLWH+WqIiIiuPgxmVCYFM6IIXDIyO0NERKQ0BjMqM9u6szE13GoiIiJSHIMZlbluLdW0dIbxSoiIiK5ODGZUZnHJzFQ3MzNDRESkNAYzKrNYRfm/mZkhIiJSHoMZlVlYM0NERKQqBjMqcy8AZmaGiIhIaQxmVOaWmWHNDBERkeIYzKhM6gAMAA3tZnRZbGG8GiIioqsPgxmVuWZmAKCWdTNERESKYjCjMlOPEQYsAiYiIlIWgxmV9czMsAiYiIhIWQxmVCYFMxn9DACYmSEiIlIagxmVSU3z8tPjAQDVzczMEBERKYnBjMqkzEx+miOYYWaGiIhIWQxmVCY1zctLZzBDRESkBlWDmbvuugt5eXmIjY1FVlYWHnroIVRXV7s9Ztu2bZgxYwYSExORlZWF5cuXw2q1uj3myJEjmD17NuLi4jBo0CD89Kc/hSiKiAZyZkYOZrjNREREpCRVg5k5c+Zg3bp1OHXqFNavX49z587h3nvvlT9++PBh3HHHHZg/fz5KS0tRXFyMTZs2YcWKFfJjjEYjbrvtNmRnZ2Pv3r347W9/i5dffhmvvPKKmpeuGKlpXl5aAgCgucOCTjMb5xERESlFp+YXf+qpp+T/zs/Px4oVK7Bw4UJYLBbo9XoUFxdj3LhxeO655wAAQ4cOxcqVK7Fo0SI8//zzSExMxJo1a9DV1YU333wTBoMBY8aMwenTp/HKK69g2bJlEARBzR8haGZnn5n0hBgkxGjRbrahuqUTQ/r3C/OVERERXR1CVjPT2NiINWvWYMaMGdDr9QAAk8mE2NhYt8fFxcWhq6sL+/fvBwB88cUXmD17NgwGg/yYefPmobq6GhcuXPD4vUwmE4xGo9tbuEg1MzE6DbJS4gBwRhMREZGSVA9mli9fjoSEBKSnp6OiogIbN26UPzZv3jzs3r0ba9euhc1mQ1VVFV588UUAQE1NDQCgtrYWAwYMcPua0v+vra31+D1XrlyJ5ORk+S03N1eNH61PoijKNTN6rQZZyY7ArZp1M0RERIrxO5h54YUXIAhCr2/79u2TH//000+jtLQUW7ZsgVarxeLFi+Xi3blz52L16tVYunQpDAYDioqKsGDBAgCAVquVv0bPrSTp871tMT3zzDNoaWmR3yorK/39MRVhs4uQ6pRjtBpkJzsyM5zPREREpBy/a2Yef/xx3H///b0+pqCgQP7vjIwMZGRkoKioCCNHjkRubi727NmD6dOnAwCWLVuGp556CjU1NUhNTcWFCxfwzDPPoLCwEAAwcODAKzIwdXV1AHBFxkZiMBjctqXCxXVitl4nICvFkZnhiSYiIiLl+B3MSMFJIKSMislkcnu/IAjIzs4GAKxduxa5ubmYOHEiAGD69Ol49tlnYTabERMTAwDYsmULsrOz3YKmSGR2mcukd8nMVLNmhoiISDGq1cyUlJTgd7/7HQ4ePIjy8nLs2LEDDzzwAIYMGSJnZQBg9erVOHLkCI4dO4af/exnWLVqFV577TV5m+mBBx6AwWDAI488gqNHj+Kdd97Bz3/+86g4yeQ6ZFKnETAwmZkZIiIipal2NDsuLg4bNmzA888/j/b2dmRlZWH+/PkoLi522wLavHkzXnrpJZhMJowfPx4bN27E7bffLn88OTkZW7duxWOPPYbJkycjNTUVy5Ytw7Jly9S6dMVIwUyMVuPIPknbTMzMEBERKUa1YGbs2LHYvn17n4/z5TFjx47FJ598osRlhZTUY0avdWSQspzbTK0mK1q7LEiM1Yft2oiIiK4WnM2kIotLjxkASDDokBTriB95oomIiEgZDGZUZLY6Cp712u7bnO1snFfNYIaIiEgRDGZU5NowTyIXATezCJiIiEgJDGZU1HObCeium2FmhoiISBkMZlRktrkXAANANjMzREREimIwoyKpA7DrNpM0bLLWyMwMERGREhjMqMhivbJmRsrMVDMzQ0REpAgGMyoyuzTNk0iZmZqWLnm8AxEREQWOwYyKPBUAD0xyZGY6zDYYO61huS4iIqKrCYMZFfXsAAwAcTFapMY7Ov9Wc0YTERFR0BjMqMhTATAAZCY6sjMNbeaQXxMREdHVhsGMiuSmeTr325zszMw0dTCYISIiChaDGRVZPBQAA5C3mZoZzBAREQWNwYyKPDXNA4DU+BgAQHOHJeTXREREdLVhMKMii4dBkwCQ4gxmmhjMEBERBY3BjIrMNhuAK4MZbjMREREph8GMiqTTTAZdz2BGyswwmCEiIgoWgxkVmT2MMwBcTzNxm4mIiChYDGZUJB/NvmKbSSoAZmaGiIgoWAxmVNTdZ6bnaSZnzUwnMzNERETBYjCjIqlmpmefGek0U0unBTY7h00SEREFg8GMisxetplSnJkZUQSMzM4QEREFhcGMiixeCoD1Wg0SDToAPNFEREQULAYzKvLWARjgiSYiIiKlMJhRkTybSXflbU6V62aYmSEiIgoGgxkVSeMMehYAA911M03tzMwQEREFg8GMirwVAAPsAkxERH1raDOhuKQCJqst3JcS0RjMqKi7z4ynYEaaz8TMDBERefbb7WexYsMR/GHHuXBfSkRjMKMiS68FwMzMEBFR705fagUAfHSsNsxXEtkYzKjIW9M8gF2AiYiob5VNHQCAk7WtuOj8b7oSgxkVeRs0CXA+ExER9c5qs6O6uUv+/9tP1oXxaiIbgxkV9VYAzNNMRETUm1pjl9vIm49PMJjxhsGMinzpM8PMDBEReVLZ2AkAiI/RAgD2nGtAm8kazkuKWAxmVCSNM/BcMyMVADMzQ0REV5JqZCblpyI/PR5mmx2fnbkc5quKTAxmVCQVAOt13scZdFps6LKwfwAREbmrbHJkZnJS43HLiAEAuNXkDYMZlYii2GvNTFKsDlqNI8hp4YkmIiLq4WKjIzOTkxqHW0dmAgB2nKxzq6MhBwYzKrG6/LJ5CmYEQUBKnDRsknUzRETk7qIzM5ObFo8phWlIjNWhod2Mg5XN4b2wCMRgRiVS8S/guWYG4IkmIiLyTuoxk5MaB71Wg9lF/QEA205cCudlRSQGMyqRhkwCnjsAAzzRREREnpmsNtQaHT1mclPjAQC3jnTUzWxj3cwVGMyoxGRzFPUKAuTamJ5SeKKJiIg8qGnugigCsXoNMvo51oqbhveHViPg1KVWVDayG7ArBjMqkU8yaTUQBG/BjDTSgJkZIiLq1r3FFC+vISnxMZiUnwqAW009MZhRidRjxuClXgbg5GwiIvJMLv5NjXN7v3SqaRtHG7hhMKMSeWK2h+6/EnmbqZ2ZGSIiNXRZbDhW3YJ3S6vwypZT+PxsfbgvySfSNlJuWrzb+29x1s3sKWuAycoeZRJduC/gatXdY8bzFhPALsBERGrZevwSVm0+gfP17XBty5K6pxwH/vs2r9v/kaK7YZ57ZmZwRgIMOg1MVjsutZiQlx7v6dOvOczMqMS1Zsab7m0mZmaIiJT018/KcO6yI5BJjtNjakEaBMHx4rG+LfKfc6VRBtJJJokgCMhKjgUAVLd0hvy6IhUzMyqRh0z2EsxI20zN7ABMRKSoqmbHQv/GI1Nw0/D+EAQBM3+xHRebOlHe0I7+iYYwX2HvpCGTOalXZl6ykuNwoaEDNQxmZMzMqEQqAO4tM5PCzAwRkeJsdhG1LY4eLcMHJspbSgXpCQCA8/XtYbs2X3RZbKhvMwEActPirvh4VoozM9PcFdLrimQMZlRikguA+66Zae6wQBQ5a4OISAn1bSZYbCK0GgGZLhmYggxHluNCQ2QHM9IWU6JBh2Tn2BtX2cmOAIeZmW4MZlTiT2bGahfRarKG5LqIiK520rHmgUmx0Lk8B0uZmQv1kd1wTtpiGpQa57FQWcrM1DAzI2MwoxKpALi3mplYvRZxei0AoJnzmYiIFFHtrJcZlOK+RVOYER3bTHLxb5rnk0pSZqa6hcGMhMGMSuQC4F76zADdJ5o4OZuISBlS8W+2M4MhKXAGMxca2iN6a9/bsWyJnJnhNpOMwYxKuvvM9H6Lk3miiYhIUdVyMOMeDOSmxkMjAB1mGy63msJxaT7xdixbkuXMzDR3WNBpZuM8gMGMaiw+NM0D2GuGiEhp8jZTj8xGjE4jH3WO5K0mqWbG2zZTUqwOCTGOEgX2mnFgMKMSXwqAAZcuwBxpQESkCKkAuGdmBnDfaopU3UMmPW8zCYKALOfPxiJgBwYzKvGlABjoPtHEkQZERMqQMjM5noKZdCkzE5knmlq7LPLwYW+ZGQDsAtwDgxmV+Foz091rhpkZIqJgtXZZYOxytLrI8hjMSMezIzMzI2WVUuP16Gfw3qRf7jXDzAwABjOqMVv7bpoHuHQBZgEwEYWYscuCp/51EJ+diY5J0r6QuuImx3kOBgojfJvpYpP3MQaueKLJHYMZlXTPZtL2+rgUTs4mojB5t7QK75RW4cX3j4f7UhTjrceMRKqZKW/oiMjj2ZWNUo8Zz9cvYa8ZdwxmVGLxYZwBwNNMRBQ+J2tb5f+tM14di2KVl2PZkpzUOGg1AjotNlwyRt7x7O7iXx8zM83MzAAMZlTjewGwlJlhMENEoXXKGcwAwCdXyVZTlZyZifX4cb1Wg1znKaFIPJ4tbTPlejnJJMmS5zNdHUFosBjMqMT3AmBnZobjDIgohERRxGmXYObTM5fDeDXK8dZjxlUkH8+WtplyejnJBHR3N24zWWHsUm/96LLYUHa5TbWvrxQGMyrxt89Mq8kqb00REamtpqXLbcDtZ2fqYbdHXg2Jv6p66TEjidQTTaIoytffV2YmPqZ7oraaJ5r++92juPlXu/D+4RrVvocSVA1m7rrrLuTl5SE2NhZZWVl46KGHUF1d7faYbdu2YcaMGUhMTERWVhaWL18Oq7X7D2znzp24++67kZWVhYSEBFx33XVYs2aNmpetCF87ACfF6SENRW3hiSYiChFpi6kwIwEJMVo0tJtxvMYY5qsKnrdRBq66e81EVjDT0mmRA8y+amYA9XvNiKKIbSfrAAArN5+AyRq5oxNUDWbmzJmDdevW4dSpU1i/fj3OnTuHe++9V/744cOHcccdd2D+/PkoLS1FcXExNm3ahBUrVsiP2b17N8aNG4f169fj8OHDWLJkCRYvXoz33ntPzUsPmtnHQZNajYCkWBYBE1FonbrkCGZGZydh+pAMAMAnUb7VZLXZUessZPbUME8SqdtM5Q2OLab+iQbE6ns/CQt0B2xqZWbK6tvR6OxOf7GpE2v2VKjyfZTgvSOPAp566in5v/Pz87FixQosXLgQFosFer0excXFGDduHJ577jkAwNChQ7Fy5UosWrQIzz//PBITE/Hss8+6fc0nnngCH330Ed555x3ceeedal5+UMxWR7q2r20mwFE309Jp4fFsIgoZKTMzfEAikuP1+PjEJXx6uh7/76ahYb6ywNUau2AXHRnxjH4Gr48rdDmebbeL0Gh6z6CHSlm9ozZlSP8Enx4vZWbU6jWz70IjAMCg08BkteO328/g3sk58gvwSBKympnGxkasWbMGM2bMgF7vuBEmkwmxse4V53Fxcejq6sL+/fu9fq2WlhakpaV5/bjJZILRaHR7C7XuPjN93+IUzmciimhWmz0ie5IEQw5mBiZi1rD+AIB95Y1od6mjiTZSw7ys5LheA5RBKXHQaQSYrHbURNCR9HN1jkzR4P79fHq8lJmpVikzs+9CEwDgkRkFGNw/AU0dFvx5V5kq3ytYqgczy5cvR0JCAtLT01FRUYGNGzfKH5s3bx52796NtWvXwmazoaqqCi+++CIAoKbGc7HRv//9b+zduxff+ta3vH7PlStXIjk5WX7Lzc1V9ofyQXefGd8yMwDkeRxEFDmsNjvu/N3nuPWVXXJn72hntdlx1nlCZfjARBSkxyM3LQ4Wm4gvzzeE+eoC11fDPIlOq0Ge87RQJBUBn7ssZWZ8C2ZUz8yUO4KZaYPT8aN5IwAAr39WFpE9ifwOZl544QUIgtDr2759++THP/300ygtLcWWLVug1WqxePFi+RXO3LlzsXr1aixduhQGgwFFRUVYsGABAEDroXPuzp078cgjj+Avf/kLRo8e7fUan3nmGbS0tMhvlZWV/v6YQevOzPSdvpTnM3UyM0MUab4834gTNUacu9yO05da+/6EKHChoQNmqx1xei1yU+MhCIKcnfnkdPT2m+mrYZ4rqW4mkoqAyy5LmRlft5nU6zVT32aS783EvFTMGz0AE/NS0GWx49fbzij+/YLldzDz+OOP48SJE72+jRkzRn58RkYGioqKcNttt6G4uBgffPAB9uzZI3982bJlaG5uRkVFBerr63H33XcDAAoLC92+765du3DnnXfilVdeweLFi3u9RoPBgKSkJLe3UDPbfK+ZSebkbKKItflod5b4WHVLGK9EOVJQVjSgn7wdc+Ow6C8CrvKhx4xEOp5dHiFFwDa7KAcPQ33eZnKeZmruVHwbVNpikmqqBEHAittHAgD+tbdSziJFCr8LgDMyMpCRkRHQN5Nutsnk3kJaEARkZ2cDANauXYvc3FxMnDhR/vjOnTvxla98Bb/4xS/wne98J6DvHWq+9pkBODmbKFLZ7SI+OnZJ/v/HqqP/6DLQPcagaECi/L7pQzKg1Qgou9yOi00dPh0NjjRSjxZv3X9dFWRIx7M7VL0mX11s6oDZZodBp/EpswQAA53bTCarHU0dFqQlxCh2PfvLHcW/kwpS5fdNLUzDrSMz8fGJOrz80Sn88cFJin2/YKlWM1NSUoLf/e53OHjwIMrLy7Fjxw488MADGDJkCKZPny4/bvXq1Thy5AiOHTuGn/3sZ1i1ahVee+01eZtp586dWLBgAZ544gl87WtfQ21tLWpra9HY2KjWpSvC4mMHYKC7ZqaJXYCJIsr+iiZcbu1+8XW06irJzLgU/0qS4/S4LjcFAPBplI428KXHjERunBchmRlpi6kwIwFaH09XGXRaZPRzBDDVCs9o2uvMzExxCWYA4KnbigAAH5+4FFF9Z1QLZuLi4rBhwwbccsstGD58OJYsWYIxY8Zg165dMBi6j8xt3rwZs2bNwuTJk/H+++9j48aNWLhwofzxN998Ex0dHVi5ciWysrLkt3vuuUetS1dEd5+Zvn8ppdNMjczMEEWUzUdqAQDjcpIBACdqWmG7CrrkSttMrsEMAMxybjVF42gDURR9LgAGuo9nVzR0RMS/qb/FvxI16mY6zTZ5S3VyvvvJ4VFZSUiJ18NiE91me4Wban1mxo4di+3bt/f5uL4e8+abb+LNN99U6KpCx59tJulVREVDZKQ7icixOH50zBHMfG/2ECxbdwidFhvO17djaKZ/C04k6bLY5GxEz2DmxqL++PXHZ/DZmXrY7KLPGYJI0NJpQbvZkSnwJTOTnRKHGK0GZpsd1c2dyO1jFpLapGDG1+JfSVZyLI5UtSh6ounQxWZYbCIGJBmQ06P+SBAEjB2UjE/P1OPwxRaMy0lR7PsGg7OZVCIVAPfVARhwFOEBjoZPvY00OF5txJ6yBlxuNV11PS+IIs3hiy2oau5EfIwWc0ZkYmSWY+GP9iLgs3VtsIuO7e3+PRrLjRuUjKRYHYxdVhy+2ByeCwyQVPybnhDjU/dcrUZAbppjoY6EraZzzm0mfzMzavSa2e88kj05Pw2CcGVAK2Uqj1yMnL8FVTsAX8v8qZlJjNUjOzkW1S1dOHOpFZMLrmwIWN7Qjjt/95mcDk2K1WFw/34YnZ2EH84drmjhFxEBm486sjJzhmciVq/F6OxkHKhoxrFqI+6+blCYry5wJ13qZXouVDqtBlMK0rDtZB0OVTZjQl6qpy8RkaTF3JeTTJLCjAScu9yOC/Xt8tH0cCkLeJtJ+V4ze52dfycXeP73HzsoBYAjgxMpmJlRiT8dgAGgyJnuPeWlj8XeC02w2UXEaDUQBMDYZcXBymas+bICb+8LfR8doquZKIr40Hkke/6YgQAcM4yA6M/MyPUyAxI9fnzMIMer7sNRVuxc1eTYps9O9j2YkQKHM3XhPWbc0mFBfZujZrLQ320mhecz2e2iW2bGk/G5jt+RM3Vt6DRHRhEwgxmV+JOZAbqPSJ655PmP6rjzSOg3p+XhxE/n48MfzMK9k3IcnxPmP0Siq83J2lZcaOiAQafBnBGZALoX+aNVxqje5pWKNosGeg5mpC2EaDu5Ve0sgPX1WDMAjHBuHZ6sCW8h6znnTKaBSbHoZ/BvwyTbmZmpUug00+m6VrR2WREfo5W3VnsamBSLjH4G2OxixExaZzCjAlEUYZGb5vlWQCcFM96qw6VXg6OykhCr12LEwCTMGe54ko205kVE0U7aYrqxqL+8uAwb0A86jYCWTkvAC4fZasfxaiNO1hpxtq4N5Q3tqGlRvuFZb6TnmBFegpmxzqDtbF0bOszRM6fJn4Z5khEDHdm2E7XhDVDPOV+QDsn0LysDdGdmLhm7FDmVJTXLm5CXAp2XF+OCIGC8M+iNlNoq1syoQApkAN9mMwHdRcBn6q4MZkSxO/odnZ0sv1/6xT9X1wZRFD0WahGR/6QtptudW0yAo6fHsAGJOFFjxLFqY0BN5R775wFsPX7pivfffV02fnP/hMAv2EctHRbUOufqDPOyzZSZFIsBSQZcMppwvNrosYYvEvnTME8ypL8jQG3tsqK6pcunI91qKHN2/h2c4f8puQGJBmgEwGoXUd9mwoAk339+T6RJ2d62mCRjc5Kx7WRdxBQBMzOjAqnHDOB7zczQzH4QBKC+zYyGNvcOyRebOtHaZYVeK7gdCS1IT5DrZxo4cZtIEecut+H0pTbotQJuGTnA7WNjpLqZALZgGtpM2HbCEchk9ItBcpwe8TGOUzfbT9aFJDMg1eQNSolDUqze6+Ok7MxhhRcqURRR29KFLcdq8cqWU3jkjRLMfXUXdp0Ovq+NPw3zJDE6jfycejKM2yVyZsbPehnAUbSdmdg91iBY0nBJb8W/Emk7MlKKgJmZUYHFZbKurzUz8TE65KbGo6KxA6cvtWG6y5FJqYV60YBEt6PesXotclLjUNnYiXN1bcjoccySiPy3+YgjKzNjSAaS49wX/NHZSXh7f2BjDbYcvwS76AgU3vv+TACOni+jnvsQrV1WXG4zyYuSWk65zGTqzdhBKfj4RJ2idTN2u4j7/7wHJReu7N7+r70VmF0U+Gkik9WGOmenZn+zKyMGJuJkbStO1rZeEbyGSnePmcD6F2WlxKLW2IWali4Ek9+rbenCxaZOaAT0eZJNOtFUVt+O1i4LEnsJjkOBmRkVSMW/Wo3gV9MpuQi4x1bTcZd6mZ6kanypRwERBc5uF/H2/osAgK+My7ri46OdGYtAgpkPjrifjgIcL0ikZm1lIfgbPt1H8a9kbI7juUbJE00HKppQcqERGsERQNw3OQcPTssDEPzPXuss/o3Va/xuUzHC+bx6IkyZGYvNjopGx0msIQE2Y5ROcAWbmfnyfAMAYGRWUp+FyP0TDchOjoUoRsbMMgYzKjDLJ5n8q2GRXi31LALurpfpLZhhETBRsL4oa0B5QwcSDTos8BDMjMxKgiA4GlzW99gO7k1LhwVfnHMsFK51OEBo/4b7Kv6VSCe3zl1uQ5tJmSLgLc5aoTvHZ+PDH9yIX947Ht+eNRgAcL6+HfYgilelepns5Di/awele3EyTK35Kxs7YLGJiNNrkRVgvYvUa+ZiU3DBzOdnHTO5ZgxJ9+nxYyOoCJjBjAq6TzL5d3ul1uI9j2dLUe8ol+JfidT6uozBzFXtgyM1uPv3n6O0oinclxIRKhs7cNsru/Dm5+cV/bprSyoAAHdPyEZ8zJWvTPsZdCh0Dij059Xo1hOXYLWLGDEw8YqthMEZUiG/upkZURRxsrZ7y7o3mYmxyHK+6j6uwKtu19EQ80Z3B3M5qfHQawWYrPagjhZLBbQFGf7XnIx0ZmbKLrehyxJYz5TKxg4seO1T/Ojfh+Qska9cB0xqAhwfIQUVUsAcCFEU8flZx+fPGJrh0+dIowyUrq0KBIMZFfjbME8yLLO7cZ5UDNjYbpYHiHk68381bjOJoohH/7YP9/zh84iayhpOr39ahkOVzVjy5l6cr796/q0D9U5pFc7UteEPO88pVjjb0GaSF9xFU/O8Pm5UAM3zNnvYYpJIWwtqZ2aqmjthdB4kkJ5reiM3z1PgVfepS60ob+hAjE7jVhuj1QjIdwaHwfxeS59bGEAwk5loQGq8HnbRcRw9EMV7K3Cs2oh1+y5izss78crW02j3MaMlD5gMYt7X7KL+0GoEnLrUisrGwGb8VTR2oKq5E3qtgKk+nmCTxxpEQE8iBjMqMPsxZNLV4P4J0AiOgWmXncVs0quigvR4jwVWUjBT2dQR8KuKSHO+vh0fn7iEAxXN2B3EK42rhclqw9Eqx+9BU4cF33qj5IoTb9ca6RVoXatJsf36DQeqYLGJGJeT7NYCoSdpkT9W5dv3be2y4NMzjvT97WOu3LoK1TaTdJ+GZib6NDNunNwkMPiF6qOjji2mWUMzkNCjFkPKTAWTXQ4mmBEEobvfTIB1M1JGY2BSLDotNry27Qxuenkn1jvrr3ojF/8GcO2SlPgYTMp3FOxKJ+b8Jf0ME3JTr/g38kY69Vbe0IHmjvCeqGUwowK5+6/Ov5RhrF4rp0mlUwfHa5zFvx7qZQDHEc/EWB1E0fELdTVwDWAC/cO8mhytMsJssyMlXo+c1DhcaOjAo3/fd9UEr/7qsthwwGW7bfvJuqC/piiKWLvXscV0/xTvWRnA/7EG20/WwWyzY3D/BI+niKTjuFXNnar+m0ovjDzV3nkyRqqHUCCY2XL8yi0mibTtVqZAZsbfidMSaaspkLqZlk6LnL3a8P9m4A/fnIi8tHhcbjXhh28fwp6y3l+QSdtMwWRmAODWkY4mqtsC/Hv4/JyzXmaob/UygCOIyk93FLCHOzvDYEYFgWZmAKDImf497aybOSY/AXl+pSgIQtiKgC02u5xBUtJu5x8VAGw/EZr+G5HsgLPvw5SCNLz5ralIjtOjtKIZPyg+qEjHz2hzsLIZJpf2BztOBR/MlJxvRNnldsTHaHHXddm9Plb6W7zQ0IHWLu9T7iUfOrsJ3z5moMfi1LSEGKTE6yGKwW219EU6SODpVKQn0qvu886jt4GqbOzAsWojNAJwi3PBddVd9xfYz262dp8GCqTpHOAy1qDW/8zMl2UNsIuOzEp2ShzuGJuFrctuxB1jHYHbOweqev18eZspwEBMIh0r31PW4Pe/l90uYrez+PcGH+tlJGr1JPIXgxkVSAXA/tbMAN1HJqUjlNKrqd6egORgJsQzmr7/z1JMW7lNHkqmBLtddCtiq27pwokwz00JN+n+TspPxdDMfvjzQ5MQo9Xgw2O1+OWHJ8N8daEnvdKV9vUPVjYHve1WvNcxrPWu8dl9HklNS4iR5+H0VRzbYbbKwZanLSYgdC9I5OcSHzMzGf2UOXornWKaXJCGdA+9sIYEeYihsqkDNruI+BgtBiQF1mtrpLzN1Or3iycpk+ya0TDotHhwWj4A4MNjtfIL3J4a281o6nAEHoFskbka0r8fCjMSYLGJ8ramr07UGtHUYUFCjBbX5ab49bnj5SLgZr8+T2kMZlTg75BJV1Ia+nRdKzrNNvnJrbfUsDzWIISZmc/O1OPDY7Ww2UW8ufuCYl/X9Y9KKhTcfvLa3WoSRRH7nVsqE51NrK4fnI7VXx8HAPjrZ+evue0mKdi9e0I2RmUlQRQRVAfZ5g4z3ncW6PZW+OtKOlnY1yK/69RldFnsyE2L6/VvWO0TTc0dZvm0kK/BDNB9SiaYlvWeTjG5krIp1S1dAc2COu9yGijQkS7DBvSDRnAEF5f9DIyl48wze2Q0ri9MR/9EA1o6LfjsrOffTymAG5QS5/H0nL9ucQ5F/djP7fndznqZqYVpfq9bSvyOKIHBjAqkPjO+FNn1NNxlevbJWiPsoqMupn+i91cc0pNBMHvO/rDbRazcfEL+/x8drUWTQuMUXP+opCe/j08Ev40QrS42deJyqwk6jSCfHAAcGYS0hBhY7aLX4aRXoy6LDaUVzQCA6YPTcbPzyTuYupl3SqtgttoxMivJ7R73ZswgR0DwRR/1EJvlLaasXhdatU80SVmZ3LTexxj0JG0hBFoP0dBmkmf9zB3lubtuakIMUuMd1xTINluZc+J0MJmNWL1W/nx/JmjXGbtwpq4NggBMG+xea6LVCFgw1pGN+8+hGo+f3935N7isjETaatp56rJfW9BSvYy/W0yAoyBeEBzBqBplB75iMKMCS4BN8wBHnwS9VkCbySpH16Oyk3t9IhzaY+Ck2jYeqsKxaiMSDToM6Z8As82Odw/2vi/sq90uf1TS/vqhi81h/SMJJ6nQdfSgZMTqtfL7BUFwKUQNf/fNUDlQ0QSzzY7MRAMKMxIwZ4Qje/fJ6cuw2jyn8nsjiqLcW+aBqbk+v7K/w7lIbT9Z57WvSJfFJgdZno5ku1J7m0luvJnlW7AmGevcQgg0mNl2og520ZFZljodeyIFEoEEM3Lxb5DbNCPkImDf/56kIGBMdjJS4q/sPCx1kd5y/JLHDKpc/BvgGIOeJhekIilWh8Z2s889qcxWO0rOOwLOGUP8D2b6GXTy9R+pavb785XCYEYFwWwz6bUaOdPybmk1gL4L9vLSEqDVCGg323DJqO6i32Wx4eWPTgMAvjdnCBZPLwAA/GtvZdCBlMXW/Uc1fUg6BiTFYuygZIiiMkWe0Uiul/EwJyWQfifRbo9zi2n6kHQIgoDrclORGq+HscuKA86MjT9O1LTi9KU2GHQa3D1hkM+fVzQgEVML0mCziyh2noLqafvJOrSZrMhKjsV1zqDAmyEuRbDBdML1xt96GYlrEbAxgCJgaYtp7qjegzn5RFMARcDS5wQ610gy0lmv6E+NXneTOc8ngCbmpSI7ORZtJit2nnLfarLbRbn+S6nMjF6rwU3Dpa0m354zD1Y2o8NsQ3pCTJ+dob0ZFwFFwAxmVGCxBl4ADDj2bwHIe9x9HaWM0WmQ73zVo3bdzN92X0BVcyeykmOx5IZCLLxuEGJ0GpysbQ36aN7hi81oN9uQGq+XC/KkbYRr9Yi2FMxMzE+54mOjfazbuJrsKXMEu1JKX6sRXGqr/A94pSB51rAMv7ZfAOCbzrlCxSWVV2SF7HYRr207AwD42sScPju75qY5OuF2WmyoNfrXQdYXx3w4SOBJWkKMPLjR334z7SYrPnXWk8wb0/sAx2A6mZcF0WPGlb+9ZkTR5QSQl4yGRiPIYzH+c7ja7WPr9lXi0MUWxOm1uM3LFlwgpIy2r8+ZUs3P9CHpAXcgvn1sFp64eShuDGJYaLAYzKjAHERmBuium5H48moqFGMNmtrN+N2OswCAH84djli9FsnxennWjHQiJFDSqxzXP6pbnXvAn56pV6QbsCiKePPz81i1+ST+sPMs3tpTjk2HqlFyvjHijoC3m6xy3wupIZYrKcg9WWu8Jo5od5ptKK10BHfTXeoT5jgD3h0BBDPSE770Nfwxf8xApCfEoNbYdcWr4PeP1OBkbSsSY3Xy/KHe6LUauROuLy9ITtYa8dS/DvrUsbbLYsNZ6SDBIP+CGaC7y6u/wcyu05dhttqRnx5/xXNaT4HW/bV2dTcYDWSUgSvpePa5y21eTx+5utDQgeqWLsRoNZjSS8fcr4xzHPXfdqJOLnC+3GrCzz9w1B3+cG4RspL9m/Tdm5uKMqHVCDhT14YKH3qP7Q6iXkZy26gBWDZ3uHxIIRwYzKhA7jMTQAEwAAxz+cOPj9GiIL3vP9JQjDX43Y6zaO2yYmRWEr7qkpL/xuRcAMB7B6vRaQ484JD+qFz3bUdnJyEz0YAOs01+VR6ML8414IX3juNPu87hlx+ewk/ePYon1pbivv/9Am/tKQ/66yvp0MVm2OwispNjPT7ZFaYnID5Giy6L/ZqYzXWgogkWm4is5Fi5URfgaOWuERyNJv2Z79PYbkZpZTMAYM5w/4MZg06L+6Y4fvfXfNn9u2O12fHqx46t2G/PGozkeN8yPt0nmvr+t/zDjnN4p7QK3/77vj4HQZ6+1AqbXURqvB4DAxhkOCaALYQ2kxWvbnXcg3mjPffXceW6zebPi4oL9Y7FOqNfDJLj/Mus9TQoJQ6JBh0sNlEuKu6NlNGYmJ+CuBit18eNy0lGXlo8Oi02bHMGvT/7z3EYu6wYMygJj8woCOq6e0qO12NKgSOo6OtUU7vJKhfUe8suRQsGMyoIpgAY6B44CTgmump9SP2pXUBY1dyJv39xAQDwzO0j3K5p2uB05KXFo9VkxQdHPFft96XTbMOB8mYA7hNbNRrB77Rpb6TumKOzk3DvpBzMHTVAfuX56sdngmoOprQD8haT51c7Go0gdy69FraapCPZ0wanuy2OKfEx8itCf7Izu07XQRQdf2PZKYG9Mn5gah4EwZE5lApR3z1YjbLL7UiN1+NbNxT4/LW6TzT1/YLkoDMIO1/fjh+/c6TXAOC4S+PNQI4uS38f+8ubfAo07HYRP1x3EGfq2jAgyYBHZxX2+Tl56fHQCI4gyJ9ifynoCLRZnitBELqb5/lQN/N5H1tMrl9XKgR+71A1dp2+jE2HqqERgJVfHQddgBn83kgZ7W19tLUoudAIq11Ebloc8tK9F2hHAwYzKgh00KQkLy0eBmdWp7cZMa6kXjOBdtHsy+6z9bDYRFyXm3LFvqhGI+C+yTkAHIXAgdhX3gizzY6s5Ngr9r5vGeH8w1SgG/BOZ43EY3OG4uWvj8efF0/Ghu/NwJD+CWhsN+N/d5UF9fWVJBW09pa69be1fjSTiiWnD76y2DKQrabtJx0FmTcHsMUkyU2Lx03Ov4d/flkOs9WO32xzZCSWzh7icZ6aN76+IGlsN8sdb7UaARsPVuPtfd5nAB0LsPhXMjk/DQadBjUtXXJn8t78cdc5fHTsEvRaAX98cBIyE/vOBhl0WuSkOhZTf7aaXCdOK0F6cXCijxNNdrsoH8v3ZcL0neMdW007T1/Gj985AgB4ZEah3KNFadIR7c/PNmDYjz/A+P/Zghkrt+G2V3bhgb/swQ/XHcLLH53C35w9wqI9KwMwmFGFWeoAHOA2k1YjYKjzVZqvc1SkVyZVzZ0BNZ7qi/Tk6e0J8d5JudAIjkg/kC2P3T1Oqbi6YWgGDDoNqpo75ZlVgahs7MC5y+3QagTMHNb9x6vTarB8/ggAwOuflXk9ahtKdrsoH8v2VC8jUeN4tiiK2H7yEqr92LIJxq+2nMLzG4/2WqfQYbbikLPDaM9+HkB3QPL5uXqfmghabXbscga2wQQzAPDN6x2dXt/efxH/2FOOysZOZPQzyCf9fCVttfQVzBxyZmUGZyTgh3OLAADPbTqK017+NvwdY9BTXIwW053Z0r5OFe48VYeXt5wCAPz07jF+1VAEMtZAHjCp0GkgqQi4r8zM8Rojmjss6GfQYbwPAcmIgYmONhZWOy42dSI7OVb+t1NDYUaC/KLTYhPR0mlBdYujJ87ucw1Yf+AifrfjrHzCypeALNIxmFFBMEezJU/PG46vTczpc06MJDUhBmkJjj4HamRnpCGW+V56RQxMjpWPBK7r5VWiN72dCoiL0crFaduCaKAnZWUm5adecXLltlEDMDk/FV0WO37trHcIp7L6djR3WBCr1/T6itr1RJNSBcz/2FOOJW/uw/xff4JPzwTeWdcX+8sb8dvtZ/G3L8rxo38f8noseX+5o15mUEocctOu3BIaMTARWcmx6LJ0H+/vzYGKZhi7rEiJ12NCkEWLc0ZkYlBKHJo7LHJR5+NzhvRaR+GJdLT4ktHUax2MVOdzXW4Klt44BLOGZaDLYsdjaw5c8ULGbhfl0zm+vjDyRKop6i3zVd7QjifWlkIUHZ2Ufe2mLJGLgP14MaRUjxmJrzOapC2maYPTfNomEgRBzs4AwP/cPcbnydSB+tu3puDQc3PxxTM34+Nls7HxsRuw5tHr8eo3xuPpecPx0LR83DoyE1+dMMhrQ8NowmBGBZYgBk1KbhqeiV/dN96vFte+vrILRLkzM5Pfy77qfc5C4J5HEPvS0mmRj3V769cgHb+VuokGYofzVYinYk9BEPDMHY7szLp9lTgTRAZICVK9zLiclF5/j4YN6AedRkBLp8Wv4ldv2k1W+UixscuKR97YK9dKqeHPn3Rv6717sFoOBnryVi8jEQRB3n7y5XdEOsZ9U1F/n2rSeqPVCFg01fG7LxVsL7rev4UcAJLj9Mhwzi7qbUGXMjPX5aVAoxHw6jeuQ/9EA87UteGFTcfcHnuhoR0dZhsMOk1QWzHS38y+8iaP/WbMVju++4/9MHZZMSEvBS/cNcrv7yFnZnzcZhJFUb5PSvVpGT4gERrBEVBe6uWI/OfSPCY/tmfum5zr+N2YmqfoUWxvBEFAcrweWclxGJrZD+NzU3DD0Ax8dUIOHpszFD9bOAavPzwFr37jOreGnNGKwYwKumtmgnuS9Ff3KxvlMzMVDY6vmZfm/UlDCkQuNnWiucP38QauU2e9HVGUiqLPBhiodVls8mmpm4Z77oUwKT8N80cPhF0EfhHmAY4Hesxj8sag08qn35TYavq/z86jvs2MvLR43DNxEGx2Ec9tPIafvHtE/r1Wyvn6dnkI4fdvHgoAeP2z8/jzJ+fcHrf3QiP+c9hRWD5tsPcjsJOcJzj2+TD4VMowBHIk25P7puRC5wyKvn/LMBh0gS0Ofb0gEUVR3m6TBvxl9DPgN/dfB0FwZEU3unTjlraYRmQlBVVompcejyH9E2Czi/jMwxDDzUcdR9HTEmLwpwcnBfTz+9te4nKrCe1mGzQCeu0u7I8Eg07OhH7pJcNnstqw1/kxf44zZ6fEYfczt2DlPWODv1C6AoMZFUg1M8FkZgKh1sBJY5dFnuzaW8V7UqweOamOYMSfLpquXX+9kWqILjZ1BnT8+8vzjeiy2DEwKbbXLpdPzx8OrUbAxyfq8GUfc3fU5Dopuy9K1c00tZvlTMkP5xbhV18fjxW3j4AgAG/tqcA3/vcL/GrLKfzzywrsOFUnH/kN1OuflkEUHTUrP5w7HM/c7siM/fyDk1i//yJ2nKrD1/+0G1//0xeoaOxAcpwes70EooCjUBVwnPTpbbTBxaYOnLrUCo3QnfELVmZiLFbeMxbfnT0Y907KCfjryCeavAycLG/oQHOHBTFajVysCjgyBN+/eRgA4NkNR3DBmd0ItFmeJ1J2xlNzwn984Tia/vD0AgwI4Pg30F0AXdnU6VOfFymDk5sWH3Dw6Mn1hY7nIW9//6UVzei02JDRzyAPBqbwYzCjgmD7zARKrV4zUuOljH4x6NfHPq/0BHvcxy6aro/tbchfekIMUuL1EMXAgjXplfhNw/v3PvCvfz/c7+wd8vPNJ8PSSK+lw4Izzl4jE/NS+ny8FMwcD/JE0x93nUOrydFH6M5x2RAEAUtnD8GfH5qMhBgtDlQ047fbz+LZd47gW2/sxdxXP8F3/7EvoO/V0GbCv/c7aqu+c+Ng+X8fnek4xvvDtw/hW2/sxd4LTYjRarBoai42PX5DrydjhmX2Q1KsDh1mW6/BtPS7MCk/1eM8nUB9fXIunrl9ZFAvYvo60SQdyR6VnXTFAYMnbh6KqQVpaDfb8PjaAzBZbQGPMfBEymLtPHXZrbbpeLUR+8qboNMIuN+53RaIzEQDEmK0sNlF+cBBb5Q+ySS5vtARFO/xEszI9X1DPW95UngwmFGBEgXAgZCfCOvacLGp7ycDX0nFv3k+pHKlV4D+tASXHjuyl1ePgiBgaBC9dHaddtTL3ORDc7Qnbx2G+BgtDlU244MjtX5/r2BJJ0YGZyQgvZ/3aekSJcYa1LR04k3nMc0fzRvu1tb8tlED8J8nZuHpecPxzevzcPOITDm7te1kHRoDmJj+9y/KYbLaMS4nWV48BEHAs3eMxEJn0Xt8jBbfnlWIT5fPwcp7xskdcr3RaAS5J8++cu91M9sV3mJSUl/bTAddin970mk1+M2i65Aar8fRKiN+sfmU/DsRTPGvZHJBKhJitKhvM7n9rr3lbBg4b/TAgLMygOPfv9CPrabzCkzL9mRqYRoEwfGi0FPPG6le5mo4znw1YTCjgnDVzOSnx2NiXgrMNjueLD4Y0BRhT8ob251fv+8njZF+BjOXjCY0dVigERzD+3ojzazypYW7q/P17Thf3w69VsANXgqMXWUmxsrZgl9+dNKnlLc3J2qM2HDgIsou+z7RfN0+R68eX0+yjXSewKhp6QoosACA33x8BmarHVML0jzWFBVmJOCxOUPx0lfH4v8emYIPf3AjRmYlQRQdzef80Wm24R/ObsvfuXGw26tbjUbAr+67DmsevR6fL78ZP14wyq8FcnJ+73UznWab3AYg2CPZapBekFyo7/C4hddbMAMAWclxePnr4wEA//f5edS3mSAICHiAoCuDrvtUoRRwG7sseLfUUaPz4LT8oL+HP2MN5JNMCk2clqTEx8hHtHuejGvtssj/Bt4OK1B4MJhRgRzMhHibSRAE/Ob+CUg06LC/vAm//viMIl+3vN7/zMyZS20+FYxKQc/g/v36rKiXnuj9DWakI9mT89N8bmL27VmDkdHPgPKGDvzTpVW9P0rON+Krf/gcy9Ydws2/2oUpL23D997ajzc+P++103BlYwd2n2uAIMDn2ovEWD0KnLVMgTTPO3e5TQ6gfjR/uM+p85tHSAMe/Tu+/e8DF9HYbkZOahzmj75ymrJWI+CGoRlITfB/C2iSs25m/wXP3Wq/KKuHyWpHdnJsn/OCwmFQShwMOg3MNvsV2VXXbSNvwQzgaJi25IburruDMxL8OhXZG7k5ofNv6p0DVegw2zAss1+vxdm+8qcIWJ6WrXBmBujeavryvPtWU8n5RtjsIvLT4+UmfxQZGMyoIFwFwICjGG7l1xzV8r/feVbe3w1Gd2am7z/enNQ49DPoYLbZfdoOkjpt9rbFJJGKgP0PZpxHskf4XuyZYNDhqdscBZWvbT/r8Thqbw5UNOFbb5Sgy+JYOGO0GtS3mbD5aC3+573jeOSNvR4X27eddSQzh2b49WQZzFbTK1tPwy4Ct47MxOReBub1JBWE7jpV53MW0GYX8ddPHUXG/9/MQsVbuY/PTYZWI6DW2IVqD80PpT5Fc0ZkRmS9g0YjyNsmZ3p02z1Z0wqzzY6UeH2ff4vLbx+Osc6ZSr52EfeFlLU7WNmMhjaTnGF7aHq+IvdTyrL0dSLTYrPLdTVKbzMB3afmetbNSMNw/TmSTaHBYEYFSvSZCcZXxmXj/im5EEXgB/86iIY232edeCIVAPsSzDjmBTle8fqy1SQVakqf0xspmLnQ0O7z4tlptsltx32pl3H1jcm5GCyPOTjX9yc4Ha1qwcP/V4J2sw3TB6dj+3/dhMMvzMW6707H0/OGIz5Gi/3lTfjomHs9js0u4t/ODMnXJ/tXSDkqwBNNF5s65HlaP5w73K/PnZCXipR4PYxdVnn0Ql+2Hr+ECw2Ok0n3+fkz+iI+RifXh/TsN9NlseG9Q44eSPM8ZIQihVQI33MKvbS9MT4npc/AwaDT4s+LJ+FbNxTgiVuGKXZtWclxGDEwEaIIrP7oFM7WtSE+Rus2eDYYUpalr22mi02dsNpFxOm1AQ3P7MtU54mm05fa3LZuuydMc4sp0jCYUUG4CoBdPX/naAzN7Ie6VhP+6+1DAZ/KMVltqHE2j+qtx4yr7rqZvo9n+1L8K8lOjkOcXguLTZSb+PVlT1kDzFY7BqXEYVimf3vrOq0GK6QxB5+eR01L303pTtYa8eBfv0RrlxVTClLx10cmI1avRaxei6mFaXhszlD8f84TO7/86JRbUPb52XpUt3QhKVbnd0fOQGc0rS2pgCg6hnv68m/gSqsR5KPNfbW5l0j1FYum5qnWAVU6zr6/R93MB0dqYOyyYlBKHGZGcPv2784e4mwPcAl7XQKyQ33Uy/SUlRwnPw8oSdpqkoKtr04Y5NcMqt5IWZbGdnOv9V9S8W9BRoJbsbpS0hJi5G3IEudWU32bCSdrHc9pnuaDUXgxmFFBd81M+NLYcTFa/O6BCYjRabDj1OUrXuX5qrKxE6LoOFmS0c+3GgZfi4C7LDZ5b9yXPhgajSD30umZgvdGWmT7OpLtzW2jBmBKQSpMVjte3ep9zIHdLuLDozV48PUv0dxhwfjcFPzfI1M81ip858bBSI3Xo+xyO9Yf6B79INWtLJwwyO+OnNJWwvn6drT30grfldlqlweDPhRg8ebNfgx4NFvt+My57Xn7GPUyI1K/mX0X3IOZtSUVAID7p+SqsgAqZUj/fnLW6hcu7QH6Kv4NlZ4dtB+aHnzhryTBoJNfdLy1x3utmpr1MpLr5a0mR0ApFY6Pykry6ZQhhRaDGRWYwrzNJBkxMAn/5Rxm9sbn5wPKzlQ0Sp1/430OBuReM33MCzp9qRV20fEqKDPRtycHf49nS23we0769pVjzMFIAI56lr/tvoCTtUb5pInZase6fZW49dVdWPrWAdS3mTEqKwl//9ZUr69WE2P1eGyOo+Ptq1vPoMtiQ3OHGVuOObrhBrL90j/RgMxEA0Sx77kykg+P1aK+zYzMRANuDbC9+o3D+kMjACdrW/scp7CvvBFtJisy+sXI9RxqmOzsBHyy1ijPODpb14q9F5qg1Qh+b+GFww9uHYZYvQb7ypvw8Yk6tHRY5K2X8WEOZibmpSAx1hGkTy1Ik0/+KEXaFvvfXec8Ho0GurehlBpj4InUPE+qm/n8DLeYIhmDGRVEwjaT5P6peYjVa3D6UpvcIt8f5X7Uy0ik+SYN7WavT0aA6xZTos+BktS635ci4NYuizz+wJ/pvT1NzEvFHWMHQhSB5zcdw/xff4rx/7MFD77+JWav3oEf/fswyi63IylWh+/fPBTF352G5Pje0+4PTstHdnIsao1d+PsXF7DxYDXMNjtGZiUF3BNE+rxSH+tX3nJ2bV00NS/g39XUhBh5UGNf2RmpEHt2UaaqmZEBSbHISY2DXQRKnb/za0scGag5wzMxMFn5GgulDUiKlU8k/fLDkzhQ6fg58tLi5YGy4aLTanCXc2ii1MJASQvGZmF8TjLazTZ5TpgrURRxzDnLTY3iX8lU54mmU5da0dxhxufOepmrYcL01Sj8q+1VyBLG00w9JcXq8ZVxjice6QndH93BjO9PGnExWvlJprdOwHLxrx+v7Pw5nn3kYgtE0XHctb+PmR9vfnnveDxxyzBMH5yO+Bgt2kxWfHa2HjUtXeifaMAzt4/A5ytuxg/nDr9iIrcnsXotnrrNkTX7/Y5zckr9G5NzAj4VIo2D+O32s6jso6boVG0rSi40Oock+j8U0ZWvW03d85CUGSHQG7nfzIUmdFls2ODcznvg+sjPyki+O3sIkuP0OFPXhpXOAZzh3mKSPH/naHz6ozkBZ/R6o9EIWHG7Ixv6z5KKK7Kwr207i0MXW6DTCD6N+whU/0QDhmb2gygC6w9U4WJTJ3QaAVP9OPFHoRP+1fYqJGVmDCHuM+ONtFj953A1Wjr9O2IsHX/0pceMK1/GGrgOwfOVVMx47nKbW0t1T0pdpgsHq59Bh2W3FWHtd6bh8PNz8f4TM/HiwjF49Rvj8emP5uC7s4f4XQR5z8QcDMvsh5ZOx/iCGK0Gd18X+KmQR2YUYnxuClo6LXjsn4529t5IwdNtIwcEnamQaig+P1ePLovn71nZ2IEzdW3QagTMGqp+MDPJueBIp8aaOizISo7F7KLIa5TnTXKcHo87tyNPO2vEIiWYidFpFBvu6Mn0Iem4ZUQmbHYRqz88Jb//vUPVePVjR+3azxaO8etFViCkfjN/3HkWADAhL0W1wnUKTmSstleZSNpmAhx73EUD+qHLYscml4m6vihv8L3HjCvpqLC3E03uYwx8b16Wnx4PnUZAh7n7lJU38ukP53Rhpei0GozOTsaD0/Lx1Qk5fhfrSrQaAU/P6z4OfdvoAQE1ipPE6DT4/QMTkBKvx+GLLXjp/RMeH9dmsuIdBbu2jsxKxMCkWHRZ7F7n2ex0jpOYlJfa5xacEqTMTGlFE9Z86Sj8vW9yLrQRXPjryUPTHduRknDXy4TS8ttHQCM4arv2lzfiYGUz/uvtQwCAR2cWBp1R9MX1zlNL9W2Ok1XsLxO5ImO1vcrIgyZDPM7AG0Ho3kpY82WFz4XAdruIyiZHUWe+j8eyJX2daKpq7kRrlxU6jeDX0VG9VoMC5xZWb1tNoih2n/5QIDOjlttGDcC0wY5ZMIGeKHKVkxqPV79xHQDH/KNNzr4qrt4trUKbyYrBGQmY0cukcl8JgiBvHXnbatopDfoMwRYT4BiNkWjQod1sQ8n5RmgE4L4p0bPFJHHdjtRrBUVmLEWLogGJcjH885uO4dG/7YPJasctIzLlony1TSt031K6gfUyEYvBjAoiqWZG8tUJgxCj0+BkbSsOXfStF0mtsQtmqx06jYDsFP+2IqSj1mWX2zxuPUgZm6GZ/WDQ+ZfZkE40nbnkvY9NrbELda0maDUCxijYAVVpgiDgrw9PwcfLZmOaQr0r5gzPxGNzhgAAVqw/jLN13fdJFEV5i+mB6/MUK8SVtpq2n6q7Iljustjk4smex3rVotUIbkHs7KL+GJQSF5LvrbR7Jubg8TlD8dLCsQFnAaPVU7cVIVavwdEqI+rbTBgxMBG/WTQhZBm2zKRY+fh3fIw2Yrb56EqRs9peRcI1m6k3KfExWDA2CwBQ7Oy30Rep+HdQapzfbeczEw1IS4iBXXQUm/bkT7O8nlzrZrw56DzRM3xAIuJiInsBSDDo5MJmpTx1axGmD05Hh9mGu373OW5YtR1zXt6J2179BCdrWxGr1+Drk5TLVNwwNAMxWg0qGzuv+HfZU9aALosdA5NiFRl46Cup3wyAkGxJqEWrEfBf84ZHZWYpWAOSYvHtWY4TUxn9YvD6w5PRL8Q1K1K/mamFaRH1nE7u+C+jMLtdhNUeeZkZwNEsDAA2HaqW+2/0xrXHjL8EQZCzM562mgKpl5H4Mj374MVmANdWjYErnVaD3yy6Drlpcegw21DV3Inz9e3yPfvaxBxFa1cSDDpMc25Z/fyDk24Tn11nY4VyHpJ0umtgUmxETsgm3zx+81D8ZMFIFH9neliGOy65oRBTClLx/24aGvLvTb5jWbbCzC7t6SOlZkYytTANg/snoOxyOzYdrMYD1/f+ajWQHjOuRmYl4rOz9X0EM/5nZnw5ni1lZiZco8EMAGQmxuLjZbNxrq4dFpsdZpvdMTdMCK7vjjdPzx2OL8sasP1kHX754Uk8c8dIiKKI7VK9TIi2mCRTC9Pw2qIJKBrQT/GBlhQ6Bp0Wj85Svp+Nr4YNSMTbS2eE7fuTb/gXrjCLWzATWbdXEAQsmuIIYNb6sNUkzT/yt/hX4u14drvJKn/tQIMZQQCaOiweh2ja7CKOOJtqXauZGYlBp8Wo7CSMz03BlII0zBiagRlDMlSpvRibk4zVXx8PAPjfT8rw7/0Xcb6+HRWNHdBrhbDMQ7prfLbiHWqJKPJE1mp7FZCKfwEgJsKCGQD42qQc6LUCjlS19FpAC/g3LdsTKVA5WdPqVhR6srYVouhoSpURwIyTuBitXMzpKTtztq4NHWYbEmK0ig/Zo97dNT5b7o3y7IYj+I2zg+v1hensz0FEqom81TbKSZkZnUaIyGF2aQkx8ivkj47V9vrY7h4zgWVmhvTvhxitBq0mKy42dc/tkWYHBZKVkUhBylkPRcAHna3fx+YkR11fkavBstuKMHfUAJhtdmw86DgaftPw0BzJJqJrE4MZhZkjZMhkb+aNdkws/rCXYKa5wwxjl6NIOJACYMBxmksKOt7ef1G+N3K9TBAnW7qPZ3sKZhxbTNflqtfqnLzTaAS8+o3r3E4uzWEBLhGpKHJX3CjV3f03cjMCt44aAI0AHK0y4mKT5xk+UvFvZqIhqKPNU5wTjF/bdgY3/nIHXv+0DIedfW6UyMx4Op4tN8vLjdz+Mle7BIMOrz88GblpcbhhaLrcq4OISA3cxFaYVDMTyf0IMvoZMLkgDSXnG7Hl2CUsmVl4xWPk4t8A62Ukz9wxEjmp8Xj9szLUGrvwokuL/WCCGW/HszvMVpx21gIxMxNeOanx2PVfcyJyu5WIri6Ru+JGqWjYZgL63moqr5d6zAT3ijpWr8W3bxyMT340B6vuGYsCZ3CUGq/H4P6Bf+2h/R1bGDUtXW49c45WGWGzixiQZAh6gCIFj4EMEYVCZK+4UcgcYUMmvZk7agAAYN+FRo/Hm5XKzEgMOi3un5qHbT+8CX9fMhVrvzMtqHuUHK/HgCTHSaifvncMVud9l4dLXuNHsomIriWRveJGoWiomQGA3LR4jM5Ogl0EPj5x6YqPB3ss2xutRsCNRf0V6f2xwjlVd92+i1j61gF0WWxyvcy13l+GiOhawmBGYd1zmSJ7HhAAzHduNX10zD2YOVrVglLn8WalZwYp6asTcvCHb05CjE6Dj09cwkN//RL7yx3XfV1OSngvjoiIQobBjMLkYCbCMzMAMG+MI5j57Ew9WrssAIBOsw1PFpfCYhMxd9QAjM6O7O6p88cMxD+WTEVirA57LzSh1tgFQXD0mCEiomsDgxmFma2ROWTSk2GZ/VCYkQCzzS4PA3zx/eM4d7kdmYkGrPrauJAOBgzU9YPTse6705GZ6KihGdq/HxJjlRuiSEREkS3yV9woY4mSAmDAMatpnrzVVIutxy9hzZeOmU2/um880hJiwnl5fhmZlYT135uBO8dn4+l5w8N9OUREFEKqrrh33XUX8vLyEBsbi6ysLDz00EOorq52e8y2bdswY8YMJCYmIisrC8uXL4fVavX49c6ePYvExESkpKSoedlBkYOZCO4z42reaMepph0n67B8/WEAwLdnFWLWsOhrP5+bFo/fLpqAuc4AjYiIrg2qrrhz5szBunXrcOrUKaxfvx7nzp3DvffeK3/88OHDuOOOOzB//nyUlpaiuLgYmzZtwooVK674WhaLBYsWLcKsWbPUvOSgSX1moqFmBgDG56RgQJIB7WYbGtvNGJmVhP9iZoOIiKKIqsHMU089hWnTpiE/Px8zZszAihUrsGfPHlgsjmLT4uJijBs3Ds899xyGDh2K2bNnY+XKlfj973+P1lb3ic4/+clPMGLECNx3331qXnLQommbCXA0NZs7ypHJMOg0eO3+62CIgpNYREREkpCtuI2NjVizZg1mzJgBvd5RnGkymRAb696lNS4uDl1dXdi/f7/8vu3bt+Ptt9/G73//+1BdbsDMtugpAJYsmVmIiXkpePnr4zFsQODDH4mIiMJB9RV3+fLlSEhIQHp6OioqKrBx40b5Y/PmzcPu3buxdu1a2Gw2VFVV4cUXXwQA1NTUAAAaGhrwyCOP4M0330RSkm/HhE0mE4xGo9tbqERbZgYACjMSsOH/3YA7x2eH+1KIiIj85veK+8ILL0AQhF7f9u3bJz/+6aefRmlpKbZs2QKtVovFixdDFB3Zi7lz52L16tVYunQpDAYDioqKsGDBAgCAVuvY6vj2t7+NBx54ADfeeKPP17hy5UokJyfLb7m5uf7+mAGzSDUzUVIATEREFO0EUYosfFRfX4/6+vpeH1NQUHDF9hEAXLx4Ebm5udi9ezemT58uv18URdTU1CA1NRUXLlzAqFGjUFJSgilTpiAlJQVtbW1uj7Xb7dBqtfjzn/+MJUuWXPF9TCYTTKbueUNGoxG5ubloaWnxObsTqFe2nMJr28/i4en5+J+7x6j6vYiIiK5mRqMRycnJfa7fOn+/cEZGBjIyMgK6KClucg00AEe/k+xsxxbH2rVrkZubi4kTJwIAvvjiC9hsNvmxGzduxC9+8Qvs3r0bgwYN8vh9DAYDDAZDQNcYrGismSEiIopmfgczviopKUFJSQlmzpyJ1NRUlJWV4bnnnsOQIUPcsjKrV6/G/PnzodFosGHDBqxatQrr1q2Tt5lGjhzp9nX37dsHjUaDMWMiM+sRbX1miIiIop1qwUxcXBw2bNiA559/Hu3t7cjKysL8+fNRXFzsljXZvHkzXnrpJZhMJowfPx4bN27E7bffrtZlqU7qM8PMDBERUWioFsyMHTsW27dv7/NxvjzG1SOPPIJHHnkkwKtSXzQNmiQiIroaMH2gMHMUHs0mIiKKZlxxFWZhATAREVFIccVVGPvMEBERhRZXXIV118zw1hIREYUCV1yFyTUzOhYAExERhQKDGYVF42wmIiKiaMYVV2HsM0NERBRaXHEVJp1mYs0MERFRaHDFVRi3mYiIiEKLK67CupvmsQCYiIgoFBjMKEw+ms0+M0RERCHBFVdhFis7ABMREYUSV1yFMTNDREQUWlxxFcZBk0RERKHFFVdhUp8ZnYYFwERERKHAYEZBoijKmRmDnreWiIgoFLjiKshiEyE66n9h0GnDezFERETXCAYzCjJZbfJ/G1gATEREFBJccRVkctbLAAxmiIiIQoUrroKkYCZGp4EgsACYiIgoFBjMKMhkcWwzMStDREQUOlx1FSRlZlj8S0REFDoMZhTUHczwthIREYUKV10FydtM7DFDREQUMlx1FcRtJiIiotBjMKMgbjMRERGFHlddBUlN8xjMEBERhQ5XXQWZLNJcJm4zERERhQqDGQVxm4mIiCj0uOoqiNtMREREocdVV0E8zURERBR6DGYU1F0zw9tKREQUKlx1FcRtJiIiotDjqqsgbjMRERGFHoMZBTEzQ0REFHpcdRUk1czEss8MERFRyDCYURD7zBAREYUeV10FydtMPM1EREQUMlx1FcQCYCIiotBjMKMguc8Mt5mIiIhChquugniaiYiIKPS46ipI3mbiaSYiIqKQYTCjIJ5mIiIiCj2uugriNhMREVHocdVVUHcBMLeZiIiIQoXBjIK6a2Z4W4mIiEKFq66CuM1EREQUelx1FSKKIpvmERERhQGDGYVYbCJE0fHf3GYiIiIKHa66CpG2mABuMxEREYUSV12FSFtMABCj5W0lIiIKFa66CnFtmCcIQpivhoiI6NrBYEYhJgtPMhEREYUDV16FcC4TERFReDCYUQjnMhEREYUHV16FcJuJiIgoPLjyKoQN84iIiMKDwYxCOJeJiIgoPLjyKoRzmYiIiMKDK69CTBZuMxEREYUDgxmF8DQTERFReHDlVYi8zcQ+M0RERCGlajBz1113IS8vD7GxscjKysJDDz2E6upqt8ds27YNM2bMQGJiIrKysrB8+XJYrVa3x4iiiJdffhlFRUUwGAzIzc3Fz3/+czUv3W/MzBAREYWHqivvnDlzsG7dOpw6dQrr16/HuXPncO+998ofP3z4MO644w7Mnz8fpaWlKC4uxqZNm7BixQq3r/Pkk0/i9ddfx8svv4yTJ0/ivffew9SpU9W8dL9118wwmCEiIgolQRRFMVTfbNOmTVi4cCFMJhP0ej2effZZbN26FXv37pUf8+6772LRokWoq6tDYmIiTpw4gXHjxuHo0aMYPnx4QN/XaDQiOTkZLS0tSEpKUurHcfPLD0/iDzvPYckNhXjuzlGqfA8iIqJria/rd8jSCI2NjVizZg1mzJgBvV4PADCZTIiNjXV7XFxcHLq6urB//34AwHvvvYfBgwfjP//5DwoLC1FQUIBHH30UjY2NXr+XyWSC0Wh0e1Mb+8wQERGFh+or7/Lly5GQkID09HRUVFRg48aN8sfmzZuH3bt3Y+3atbDZbKiqqsKLL74IAKipqQEAlJWVoby8HG+//Tb+/ve/480338T+/fvdtqt6WrlyJZKTk+W33NxcdX9IsM8MERFRuPi98r7wwgsQBKHXt3379smPf/rpp1FaWootW7ZAq9Vi8eLFkHa25s6di9WrV2Pp0qUwGAwoKirCggULAABareNUkN1uh8lkwt///nfMmjULN910E/76179ix44dOHXqlMdrfOaZZ9DS0iK/VVZW+n1j/MU+M0REROGh8/cTHn/8cdx///29PqagoED+74yMDGRkZKCoqAgjR45Ebm4u9uzZg+nTpwMAli1bhqeeego1NTVITU3FhQsX8Mwzz6CwsBAAkJWVBZ1Oh6KiIvlrjhw5EgBQUVHhsY7GYDDAYDD4+6MFhaeZiIiIwsPvYEYKTgIhZWRMJpPb+wVBQHZ2NgBg7dq1yM3NxcSJEwEAN9xwA6xWK86dO4chQ4YAAE6fPg0AyM/PD+g61NDdZ4bBDBERUSj5Hcz4qqSkBCUlJZg5cyZSU1NRVlaG5557DkOGDJGzMgCwevVqzJ8/HxqNBhs2bMCqVauwbt06eZvp1ltvxcSJE7FkyRL8+te/ht1ux2OPPYbbbrvNLVsTbpyaTUREFB6qpRHi4uKwYcMG3HLLLRg+fDiWLFmCMWPGYNeuXW5bQJs3b8asWbMwefJkvP/++9i4cSMWLlzYfYEaDd577z1kZGTgxhtvxIIFCzBy5EgUFxerdekBYZ8ZIiKi8FAtMzN27Fhs3769z8f58pjs7GysX79eictSDU8zERERhQdXXoV095nhNhMREVEoMZhRCE8zERERhQdXXoVwm4mIiCg8uPIqhE3ziIiIwoPBjEI4m4mIiCg8uPIqhNtMRERE4cGVVwGiKLJpHhERUZgwmFGAxSbCOamB20xEREQhxpVXAdIWE8BtJiIiolDjyqsAaYsJAGK0vKVEREShxJVXAa4N8wRBCPPVEBERXVsYzCjAZOFJJiIionDh6qsAzmUiIiIKHwYzCuBcJiIiovDh6qsAbjMRERGFD1dfBbBhHhERUfgwmFEA5zIRERGFD1dfBXAuExERUfhw9VWAycJtJiIionBhMKMAnmYiIiIKH66+CpC3mdhnhoiIKOQYzCiAmRkiIqLw4eqrgO6aGd5OIiKiUOPqq4Du00zcZiIiIgo1BjMKYJ8ZIiKi8OHqqwD2mSEiIgofrr4KYJ8ZIiKi8GEwowCeZiIiIgofrr4K6O4zw9tJREQUalx9FcCp2UREROHDYEYB7DNDREQUPlx9FcDTTEREROHD1VcB3X1muM1EREQUagxmFMDTTEREROHD1VcB3GYiIiIKH66+CmDTPCIiovBhMKMAzmYiIiIKH66+CuA2ExERUfhw9Q2SKIpsmkdERBRGDGaCZLGJEEXHf3ObiYiIKPS4+gZJ2mICuM1EREQUDlx9gyRtMQFAjJa3k4iIKNS4+gbJtWGeIAhhvhoiIqJrD4OZIJksPMlEREQUTlyBg8S5TEREROHFYCZInMtEREQUXlyBg8RtJiIiovDiChwkNswjIiIKLwYzQeJcJiIiovDiChwkzmUiIiIKL67AQTJZuM1EREQUTgxmgsTTTEREROHFFThI8jYT+8wQERGFBYOZIDEzQ0REFF5cgYPUXTPDW0lERBQOXIGD1H2aidtMRERE4cBgJkjsM0NERBReXIGDxD4zRERE4cUVOEjsM0NERBReDGaCxNNMRERE4cUVOEjdfWZ4K4mIiMKBK3CQODWbiIgovFQNZu666y7k5eUhNjYWWVlZeOihh1BdXe32mG3btmHGjBlITExEVlYWli9fDqvV6vaYjz76CNOmTUNiYiL69++Pr33tazh//ryal+4z9pkhIiIKL1VX4Dlz5mDdunU4deoU1q9fj3PnzuHee++VP3748GHccccdmD9/PkpLS1FcXIxNmzZhxYoV8mPKyspw99134+abb8bBgwfx0Ucfob6+Hvfcc4+al+4znmYiIiIKL0EURTFU32zTpk1YuHAhTCYT9Ho9nn32WWzduhV79+6VH/Puu+9i0aJFqKurQ2JiIv79739j0aJFMJlM0GgcAcN7772Hu+++W/46fTEajUhOTkZLSwuSkpIU/ZkWvPYpjlUb8bclUzG7qL+iX5uIiOha5uv6HbJ0QmNjI9asWYMZM2bIAYjJZEJsbKzb4+Li4tDV1YX9+/cDACZPngytVos33ngDNpsNLS0t+Mc//oG5c+d6DWRMJhOMRqPbm1p4momIiCi8VF+Bly9fjoSEBKSnp6OiogIbN26UPzZv3jzs3r0ba9euhc1mQ1VVFV588UUAQE1NDQCgoKAAW7ZswbPPPguDwYCUlBRcvHgRxcXFXr/nypUrkZycLL/l5uaq9vNxm4mIiCi8/F6BX3jhBQiC0Ovbvn375Mc//fTTKC0txZYtW6DVarF48WJIO1tz587F6tWrsXTpUhgMBhQVFWHBggUAAK3WcTqotrYWjz76KB5++GHs3bsXu3btQkxMDO6991542yF75pln0NLSIr9VVlb6fWN8xaZ5RERE4eV3zUx9fT3q6+t7fUxBQcEV20cAcPHiReTm5mL37t2YPn26/H5RFFFTU4PU1FRcuHABo0aNQklJCaZMmYL//u//xubNm90CJOnrfPHFF5g2bVqf16xmzcz4/9mClk4Ltv1wNob076fo1yYiIrqW+bp+6/z9whkZGcjIyAjooqS4yWQyub1fEARkZ2cDANauXYvc3FxMnDgRANDR0SFnaSTS/7fb7QFdh5K6LNxmIiIiCie/gxlflZSUoKSkBDNnzkRqairKysrw3HPPYciQIW5ZmdWrV2P+/PnQaDTYsGEDVq1ahXXr1skBy4IFC/Dqq6/ipz/9KRYtWoTW1lY8++yzyM/Px4QJE9S6fJ+IosimeURERGGmWjohLi4OGzZswC233ILhw4djyZIlGDNmDHbt2gWDwSA/bvPmzZg1axYmT56M999/Hxs3bsTChQvlj99888345z//iXfffRcTJkzA/PnzYTAY8OGHHyIuLk6ty/eJ2dadGeI4AyIiovAIaZ+ZcFGrZsbYZcG4F7YAAE69OJ/ZGSIiIgVFXJ+Zq5F0kgkAYrS8lUREROHAFTgIrj1mBEEI89UQERFdmxjMBIHdf4mIiMKPq3AQ5IZ5etbKEBERhQuDmSBwlAEREVH4cRUOAreZiIiIwo+rcBDYMI+IiCj8GMwEwSSNMmDDPCIiorDhKhwEbjMRERGFH1fhIHCbiYiIKPwYzASBp5mIiIjCj6twENhnhoiIKPwYzASBNTNEREThx1U4CNxmIiIiCj+uwkFgATAREVH46cJ9AdFs+uB0aARgUn5quC+FiIjomsVgJgg3FvXHjUX9w30ZRERE1zRuMxEREVFUYzBDREREUY3BDBEREUU1BjNEREQU1RjMEBERUVRjMENERERRjcEMERERRTUGM0RERBTVGMwQERFRVGMwQ0RERFGNwQwRERFFNQYzREREFNUYzBAREVFUuyamZouiCAAwGo1hvhIiIiLylbRuS+u4N9dEMNPa2goAyM3NDfOVEBERkb9aW1uRnJzs9eOC2Fe4cxWw2+2orq5GYmIiBEFQ9GsbjUbk5uaisrISSUlJin5tcsd7HTq816HDex06vNeho9S9FkURra2tyM7OhkbjvTLmmsjMaDQa5OTkqPo9kpKS+McRIrzXocN7HTq816HDex06Stzr3jIyEhYAExERUVRjMENERERRjcFMkAwGA55//nkYDIZwX8pVj/c6dHivQ4f3OnR4r0Mn1Pf6migAJiIioqsXMzNEREQU1RjMEBERUVRjMENERERRjcEMERERRTUGM0H4wx/+gMLCQsTGxmLSpEn49NNPw31JUW/lypWYMmUKEhMTkZmZiYULF+LUqVNujxFFES+88AKys7MRFxeHm266CceOHQvTFV89Vq5cCUEQ8IMf/EB+H++1cqqqqvDggw8iPT0d8fHxuO6667B//37547zXyrBarfjJT36CwsJCxMXFYfDgwfjpT38Ku90uP4b3OjCffPIJ7rzzTmRnZ0MQBLz77rtuH/flvppMJnz/+99HRkYGEhIScNddd+HixYvBX5xIASkuLhb1er34l7/8RTx+/Lj45JNPigkJCWJ5eXm4Ly2qzZs3T3zjjTfEo0ePigcPHhQXLFgg5uXliW1tbfJjVq1aJSYmJorr168Xjxw5In7jG98Qs7KyRKPRGMYrj24lJSViQUGBOG7cOPHJJ5+U3897rYzGxkYxPz9ffOSRR8Qvv/xSPH/+vPjxxx+LZ8+elR/De62MF198UUxPTxf/85//iOfPnxfffvttsV+/fuKvf/1r+TG814H54IMPxB//+Mfi+vXrRQDiO++84/ZxX+7r0qVLxUGDBolbt24VDxw4IM6ZM0ccP368aLVag7o2BjMBmjp1qrh06VK3940YMUJcsWJFmK7o6lRXVycCEHft2iWKoija7XZx4MCB4qpVq+THdHV1icnJyeKf/vSncF1mVGttbRWHDRsmbt26VZw9e7YczPBeK2f58uXizJkzvX6c91o5CxYsEJcsWeL2vnvuuUd88MEHRVHkvVZKz2DGl/va3Nws6vV6sbi4WH5MVVWVqNFoxA8//DCo6+E2UwDMZjP279+PuXPnur1/7ty52L17d5iu6urU0tICAEhLSwMAnD9/HrW1tW733mAwYPbs2bz3AXrsscewYMEC3HrrrW7v571WzqZNmzB58mR8/etfR2ZmJiZMmIC//OUv8sd5r5Uzc+ZMbNu2DadPnwYAHDp0CJ999hnuuOMOALzXavHlvu7fvx8Wi8XtMdnZ2RgzZkzQ9/6aGDSptPr6ethsNgwYMMDt/QMGDEBtbW2YrurqI4oili1bhpkzZ2LMmDEAIN9fT/e+vLw85NcY7YqLi3HgwAHs3bv3io/xXiunrKwMf/zjH7Fs2TI8++yzKCkpwRNPPAGDwYDFixfzXito+fLlaGlpwYgRI6DVamGz2fDSSy9h0aJFAPh7rRZf7mttbS1iYmKQmpp6xWOCXTsZzARBEAS3/y+K4hXvo8A9/vjjOHz4MD777LMrPsZ7H7zKyko8+eST2LJlC2JjY70+jvc6eHa7HZMnT8bPf/5zAMCECRNw7Ngx/PGPf8TixYvlx/FeB+9f//oX3nrrLfzzn//E6NGjcfDgQfzgBz9AdnY2Hn74YflxvNfqCOS+KnHvuc0UgIyMDGi12isiybq6uiuiUgrM97//fWzatAk7duxATk6O/P6BAwcCAO+9Avbv34+6ujpMmjQJOp0OOp0Ou3btwmuvvQadTiffT97r4GVlZWHUqFFu7xs5ciQqKioA8PdaSU8//TRWrFiB+++/H2PHjsVDDz2Ep556CitXrgTAe60WX+7rwIEDYTab0dTU5PUxgWIwE4CYmBhMmjQJW7dudXv/1q1bMWPGjDBd1dVBFEU8/vjj2LBhA7Zv347CwkK3jxcWFmLgwIFu995sNmPXrl2893665ZZbcOTIERw8eFB+mzx5Mr75zW/i4MGDGDx4MO+1Qm644YYrWgycPn0a+fn5APh7raSOjg5oNO5Lm1arlY9m816rw5f7OmnSJOj1erfH1NTU4OjRo8Hf+6DKh69h0tHsv/71r+Lx48fFH/zgB2JCQoJ44cKFcF9aVPve974nJicnizt37hRramrkt46ODvkxq1atEpOTk8UNGzaIR44cERctWsRjlQpxPc0kirzXSikpKRF1Op340ksviWfOnBHXrFkjxsfHi2+99Zb8GN5rZTz88MPioEGD5KPZGzZsEDMyMsQf/ehH8mN4rwPT2toqlpaWiqWlpSIA8ZVXXhFLS0vlliS+3NelS5eKOTk54scffyweOHBAvPnmm3k0O9x+//vfi/n5+WJMTIw4ceJE+fgwBQ6Ax7c33nhDfozdbheff/55ceDAgaLBYBBvvPFG8ciRI+G76KtIz2CG91o57733njhmzBjRYDCII0aMEP/85z+7fZz3WhlGo1F88sknxby8PDE2NlYcPHiw+OMf/1g0mUzyY3ivA7Njxw6Pz88PP/ywKIq+3dfOzk7x8ccfF9PS0sS4uDjxK1/5ilhRURH0tQmiKIrB5XaIiIiIwoc1M0RERBTVGMwQERFRVGMwQ0RERFGNwQwRERFFNQYzREREFNUYzBAREVFUYzBDREREUY3BDBEREUU1BjNEREQU1RjMEBERUVRjMENERERRjcEMERERRbX/H7m2YFcHV+SwAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "if MATGL_PRESENT:\n", " plt.plot(result_dict['energy_pot']);" @@ -5279,21 +3944,10 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 39, "id": "759b4aa7-927e-404d-a637-828298ab672f", "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "if MATGL_PRESENT:\n", " plt.plot(result_dict['positions'][:,:,0]);" @@ -5301,7 +3955,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 40, "id": "e1e34c2a-1664-41d4-82e5-57e8d7a628b5", "metadata": {}, "outputs": [], @@ -5312,7 +3966,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 41, "id": "a9d193fd-5ad7-42dd-bde6-ee2449c5a129", "metadata": {}, "outputs": [], @@ -5346,7 +4000,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 42, "id": "889829b1-080b-4255-983b-62f669cf041f", "metadata": {}, "outputs": [], @@ -5356,7 +4010,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 43, "id": "2bd4f113-9610-4daf-9567-0a85c4ad2d65", "metadata": {}, "outputs": [], @@ -5366,7 +4020,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 44, "id": "dcf9026b-f620-4d55-bb9a-e14cdb38bda9", "metadata": {}, "outputs": [], @@ -5376,7 +4030,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 45, "id": "080d9a25-8252-411b-b274-b75212ea793e", "metadata": {}, "outputs": [], @@ -5386,7 +4040,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 46, "id": "839cfcf8-14e0-4772-844b-770f7a129e0e", "metadata": {}, "outputs": [], @@ -5396,7 +4050,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 47, "id": "edfdec4c-a490-4a81-af73-94a333697d50", "metadata": {}, "outputs": [], @@ -5406,7 +4060,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 48, "id": "6a9ff00f-5030-40e6-9296-50e4025f7332", "metadata": {}, "outputs": [ @@ -5510,7 +4164,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 49, "id": "1f58da83-93f8-4df3-9b13-fe3f7756de88", "metadata": {}, "outputs": [], @@ -5523,7 +4177,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 50, "id": "7d836f6c-545c-4082-89b1-6a892c189884", "metadata": {}, "outputs": [], @@ -5543,7 +4197,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 51, "id": "123ddac3-bed1-474f-bf49-a7f20ad1c8e1", "metadata": {}, "outputs": [], @@ -5554,7 +4208,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 52, "id": "b6e76f6c-45b1-4982-8b14-ce0af3a75d2c", "metadata": {}, "outputs": [ @@ -5564,7 +4218,7 @@ "InputPhonopyGenerateSupercells(distance=1, is_plusminus='auto', is_diagonal=True, is_trigonal=False, number_of_snapshots=None, random_seed=None, temperature=None, cutoff_frequency=None, max_distance=10)" ] }, - "execution_count": 49, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -5575,7 +4229,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 53, "id": "8ade9cdc-23cd-45af-af6f-54a7504dc758", "metadata": {}, "outputs": [ @@ -5585,7 +4239,7 @@ "1" ] }, - "execution_count": 50, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -5596,7 +4250,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 54, "id": "cded0af4-4735-4e22-b1ee-cf5503a9f06d", "metadata": {}, "outputs": [ @@ -5623,7 +4277,9 @@ "----------\n", "distance : float, optional\n", " Displacement distance. Unit is the same as that used for crystal\n", - " structure. Default is 0.01.\n", + " structure. Default is 0.01. For random direction and distance\n", + " displacements generation, this value is used when `max_distance` is\n", + " unspecified.\n", "is_plusminus : 'auto', True, or False, optional\n", " For each atom, displacement of one direction (False), both\n", " direction, i.e., one directiona and its opposite direction (True),\n", @@ -5641,7 +4297,7 @@ " 'distance' parameter, i.e., all atoms in supercell are displaced\n", " with the same displacement distance in direct space. Default is\n", " None.\n", - "random_seed : 32bit unsigned int or None, optional\n", + "random_seed : int or None, optional\n", " Random seed for random displacements generation. Default is None.\n", "temperature : float or None, optional\n", " With given temperature, random displacements at temperature is\n", @@ -5658,7 +4314,17 @@ " In random displacements generation from canonical ensemble of\n", " harmonic phonons, displacements larger than max distance are\n", " renormalized to the max distance, i.e., a disptalcement d is shorten\n", - " by d -> d / |d| * max_distance if |d| > max_distance." + " by d -> d / |d| * max_distance if |d| > max_distance. In random\n", + " direction and distance displacements generation, this value is is\n", + " specified.\n", + "is_random_distance : bool, optional\n", + " Random direction displacements are generated also with random\n", + " amplitudes. The maximum value is defined by `distance` and minimum\n", + " value is given by `min_distance`. Default is False.\n", + "min_distance : float or None, optional\n", + " In random direction displacements generation with random distance\n", + " (`is_random_distance=True`), the minimum distance is given by this\n", + " value." ] }, "metadata": {}, @@ -5671,7 +4337,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 55, "id": "63d3eb9a-ee79-43a3-bb36-1e8460bf07e6", "metadata": {}, "outputs": [], @@ -5681,7 +4347,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 56, "id": "d7671f4b-1f57-4f07-9be7-1ff0955b8295", "metadata": {}, "outputs": [ @@ -5691,7 +4357,7 @@ "{'distance': 1}" ] }, - "execution_count": 53, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -5702,7 +4368,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 57, "id": "888b113d-a7a4-41ad-acfc-efbc252eab9e", "metadata": {}, "outputs": [], @@ -5714,7 +4380,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 58, "id": "b144573b-0f23-4f0f-b900-c1a10e27b1a7", "metadata": {}, "outputs": [ @@ -5732,7 +4398,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 59, "id": "c842e70f-7b4a-426a-9b9c-1e9614d3d171", "metadata": {}, "outputs": [ @@ -5759,7 +4425,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 60, "id": "0e4c64e2-10c5-47dd-a0ca-57f3d2b198ab", "metadata": {}, "outputs": [], @@ -5769,7 +4435,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 61, "id": "db423749-2af5-43e7-af45-48dd356e6717", "metadata": {}, "outputs": [ @@ -5782,7 +4448,7 @@ " [2.025, 2.025, 0. ]])" ] }, - "execution_count": 58, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -5794,7 +4460,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 62, "id": "5478171f-cd4e-4a18-ad9d-db6e74f861d2", "metadata": {}, "outputs": [], @@ -5805,7 +4471,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 63, "id": "c557b3b1-9eec-48b3-a06e-f56c1a394afa", "metadata": {}, "outputs": [ @@ -5841,7 +4507,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 64, "id": "429d927d-e694-4da1-9f66-2f878bd481be", "metadata": {}, "outputs": [ @@ -5885,7 +4551,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 65, "id": "39fa01fa-9ae8-4437-98d8-6b04a8a826b1", "metadata": {}, "outputs": [], @@ -5899,7 +4565,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 66, "id": "84e1a9ad-d324-4a75-88a5-f8b45ed7a0d7", "metadata": {}, "outputs": [], @@ -5913,7 +4579,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 67, "id": "a20e229c-0bf1-4d39-bd71-2929c12718f3", "metadata": {}, "outputs": [], @@ -5927,7 +4593,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 68, "id": "4a4f1794-05e8-4dd7-ad26-55b51f261699", "metadata": {}, "outputs": [ @@ -5937,7 +4603,7 @@ "" ] }, - "execution_count": 65, + "execution_count": 68, "metadata": {}, "output_type": "execute_result" } @@ -5948,7 +4614,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 69, "id": "fb62cfa1-3320-4d74-9e57-0ec10a55f796", "metadata": {}, "outputs": [], @@ -5970,7 +4636,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 70, "id": "50330980-ea4d-4f40-88ee-dd752863201a", "metadata": {}, "outputs": [ @@ -5980,7 +4646,7 @@ "(0.8414709848078965, array([1., 1., 1.]))" ] }, - "execution_count": 67, + "execution_count": 70, "metadata": {}, "output_type": "execute_result" } @@ -5992,7 +4658,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 71, "id": "8b242761-5c56-4f9d-a08d-1d335b15e46d", "metadata": {}, "outputs": [], @@ -6028,7 +4694,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 72, "id": "6558e0f8-b2a3-488e-988f-66ac0f5fded8", "metadata": {}, "outputs": [ @@ -6044,7 +4710,7 @@ "" ] }, - "execution_count": 69, + "execution_count": 72, "metadata": {}, "output_type": "execute_result" } @@ -6057,7 +4723,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 73, "id": "c5b312fd-6dd2-45a5-8c6e-ef4d9a6a9b97", "metadata": {}, "outputs": [], @@ -6093,34 +4759,80 @@ " return out_dict " ] }, + { + "cell_type": "markdown", + "id": "b7620240-f976-4cb5-9e95-d953d14c6bec", + "metadata": {}, + "source": [ + "### Parallel pooling" + ] + }, { "cell_type": "code", - "execution_count": 71, - "id": "cef22996-86fd-4ed8-88e3-253a5a4ff063", + "execution_count": 74, + "id": "5e0b5714-18de-4ea6-be1f-ab1f44f9e6f5", + "metadata": {}, + "outputs": [], + "source": [ + "@Workflow.wrap.as_function_node(\"sleep_time\", \"a_out\", \"b_out\")\n", + "def Sleep(time=1, a=None, b=10):\n", + " from time import sleep\n", + " \n", + " sleep(time) \n", + " return time, a, b" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "id": "1c37d595-1060-4969-8fc9-c78548bc1963", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "max_workers: 1\n" + "@Workflow.wrap.as_function_node(\"sleep_time\", \"a_out\", \"b_out\")\n", + "def Sleep(time=1, a=None, b=10):\n", + " from time import sleep\n", + " \n", + " sleep(time) \n", + " return time, a, b\n", + "\n" ] } ], "source": [ - "# %%time\n", - "df = wf.iter(\n", - " cell_size=list(range(1,2)), # Or 1,4, but that takes longer\n", - " element=['Al'], \n", - " vacancy_index=[None, 0], \n", - " displacement=[0.01, 0.1]\n", - ") #, Cu, Pd, Ag, Pt and Au])\n" + "import inspect\n", + "\n", + "print(inspect.getsource(Sleep.node_function))" ] }, { "cell_type": "code", - "execution_count": 72, - "id": "15426796-970d-467a-95b3-816f1067add0", + "execution_count": 76, + "id": "95d87f0c-3776-46d9-ba64-c4029be57192", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 262 ms, sys: 291 ms, total: 554 ms\n", + "Wall time: 5.65 s\n" + ] + } + ], + "source": [ + "%%time\n", + "with Workflow.create.Executor(max_cores=4) as exe:\n", + " df = Sleep().iter(a=[1,2,3,4,5])" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "id": "177feedd-115c-4ce4-b790-0e39b5916152", "metadata": {}, "outputs": [ { @@ -6144,65 +4856,62 @@ " \n", "
\n", " \n", - " cell_size\n", - " element\n", - " vacancy_index\n", - " displacement\n", - " data__dataframe\n", + " a\n", + " sleep_time\n", + " a_out\n", + " b_out\n", "
\n", "
\n", " \n", "
\n", " 0\n", " 1\n", - " Al\n", - " NaN\n", - " 0.01\n", - " G_Reuss G_VRH G_Voigt K...\n", + " 1\n", + " 1\n", + " 10\n", "
\n", "
\n", " 1\n", + " 2\n", " 1\n", - " Al\n", - " NaN\n", - " 0.10\n", - " G_Reuss G_VRH G_Voigt K...\n", + " 2\n", + " 10\n", "
\n", "
\n", " 2\n", + " 3\n", " 1\n", - " Al\n", - " 0.0\n", - " 0.01\n", - " G_Reuss G_VRH G_Voigt K...\n", + " 3\n", + " 10\n", "
\n", "
\n", " 3\n", + " 4\n", " 1\n", - " Al\n", - " 0.0\n", - " 0.10\n", - " G_Reuss G_VRH G_Voigt K...\n", + " 4\n", + " 10\n", + "
\n", + "
\n", + " 4\n", + " 5\n", + " 1\n", + " 5\n", + " 10\n", "
\n", "
\n", "\n", "" ], "text/plain": [ - " cell_size element vacancy_index displacement \\\n", - "0 1 Al NaN 0.01 \n", - "1 1 Al NaN 0.10 \n", - "2 1 Al 0.0 0.01 \n", - "3 1 Al 0.0 0.10 \n", - "\n", - " data__dataframe \n", - "0 G_Reuss G_VRH G_Voigt K... \n", - "1 G_Reuss G_VRH G_Voigt K... \n", - "2 G_Reuss G_VRH G_Voigt K... \n", - "3 G_Reuss G_VRH G_Voigt K... " + " a sleep_time a_out b_out\n", + "0 1 1 1 10\n", + "1 2 1 2 10\n", + "2 3 1 3 10\n", + "3 4 1 4 10\n", + "4 5 1 5 10" ] }, - "execution_count": 72, + "execution_count": 77, "metadata": {}, "output_type": "execute_result" } @@ -6213,297 +4922,19 @@ }, { "cell_type": "code", - "execution_count": 73, - "id": "89abfc53-4a70-4640-a34e-2825692ba8a6", - "metadata": {}, - "outputs": [], - "source": [ - "# df.energy_displaced\n", - "# Not a column" - ] - }, - { - "cell_type": "markdown", - "id": "b7620240-f976-4cb5-9e95-d953d14c6bec", - "metadata": {}, - "source": [ - "### Parallel pooling" - ] - }, - { - "cell_type": "code", - "execution_count": 74, - "id": "5e0b5714-18de-4ea6-be1f-ab1f44f9e6f5", - "metadata": {}, - "outputs": [], - "source": [ - "@function_node('out')\n", - "def sleep(time=1, a=None, b=10):\n", - " from time import sleep\n", - " \n", - " sleep(time) \n", - " return dict(times=time, a2=a, b2=b)" - ] - }, - { - "cell_type": "code", - "execution_count": 75, - "id": "1c37d595-1060-4969-8fc9-c78548bc1963", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "@function_node('out')\n", - "def sleep(time=1, a=None, b=10):\n", - " from time import sleep\n", - " \n", - " sleep(time) \n", - " return dict(times=time, a2=a, b2=b)\n", - "\n" - ] - } - ], - "source": [ - "import inspect\n", - "\n", - "print(inspect.getsource(sleep.node_function))" - ] - }, - { - "cell_type": "code", - "execution_count": 76, - "id": "95d87f0c-3776-46d9-ba64-c4029be57192", + "execution_count": 78, + "id": "f62e3bcd-a671-4bac-a6d1-c9c2e32575ef", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "max_workers: 5\n" - ] - }, - { - "ename": "FileNotFoundError", - "evalue": "[Errno 2] No such file or directory: 'sleep'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m:1\u001b[0m\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/node.py:855\u001b[0m, in \u001b[0;36mNode.iter\u001b[0;34m(self, max_workers, executor, **kwargs)\u001b[0m\n\u001b[1;32m 852\u001b[0m futures\u001b[38;5;241m.\u001b[39mappend(future)\n\u001b[1;32m 854\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m future \u001b[38;5;129;01min\u001b[39;00m as_completed(futures):\n\u001b[0;32m--> 855\u001b[0m out\u001b[38;5;241m.\u001b[39mappend(\u001b[43mfuture\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresult\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 856\u001b[0m out_index\u001b[38;5;241m.\u001b[39mappend(future_index_map[future])\n\u001b[1;32m 858\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(out) \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m:\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/concurrent/futures/_base.py:449\u001b[0m, in \u001b[0;36mFuture.result\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 447\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m CancelledError()\n\u001b[1;32m 448\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_state \u001b[38;5;241m==\u001b[39m FINISHED:\n\u001b[0;32m--> 449\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__get_result\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 451\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_condition\u001b[38;5;241m.\u001b[39mwait(timeout)\n\u001b[1;32m 453\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_state \u001b[38;5;129;01min\u001b[39;00m [CANCELLED, CANCELLED_AND_NOTIFIED]:\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/concurrent/futures/_base.py:401\u001b[0m, in \u001b[0;36mFuture.__get_result\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 399\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_exception:\n\u001b[1;32m 400\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 401\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_exception\n\u001b[1;32m 402\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 403\u001b[0m \u001b[38;5;66;03m# Break a reference cycle with the exception in self._exception\u001b[39;00m\n\u001b[1;32m 404\u001b[0m \u001b[38;5;28mself\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/concurrent/futures/thread.py:58\u001b[0m, in \u001b[0;36m_WorkItem.run\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 55\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[1;32m 57\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 58\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 59\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[1;32m 60\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfuture\u001b[38;5;241m.\u001b[39mset_exception(exc)\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/node.py:921\u001b[0m, in \u001b[0;36mfunc\u001b[0;34m(node, **kwargs)\u001b[0m\n\u001b[1;32m 919\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mfunc\u001b[39m(node, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[1;32m 920\u001b[0m \u001b[38;5;66;03m# print(\"func (node): \", node, kwargs)\u001b[39;00m\n\u001b[0;32m--> 921\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mnode\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mrun()\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/snippets/has_post.py:16\u001b[0m, in \u001b[0;36mHasPost.__call__\u001b[0;34m(cls, *args, **kwargs)\u001b[0m\n\u001b[1;32m 14\u001b[0m instance \u001b[38;5;241m=\u001b[39m \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39m\u001b[38;5;21m__call__\u001b[39m(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m post \u001b[38;5;241m:=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m(\u001b[38;5;28mcls\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m__post__\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mFalse\u001b[39;00m):\n\u001b[0;32m---> 16\u001b[0m \u001b[43mpost\u001b[49m\u001b[43m(\u001b[49m\u001b[43minstance\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 17\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m instance\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/node.py:350\u001b[0m, in \u001b[0;36mNode.__post__\u001b[0;34m(self, overwrite_save, run_after_init, *args, **kwargs)\u001b[0m\n\u001b[1;32m 348\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n\u001b[1;32m 349\u001b[0m \u001b[38;5;66;03m# Else neither loading nor running now -- no action required!\u001b[39;00m\n\u001b[0;32m--> 350\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgraph_root\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtidy_working_directory\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/working.py:33\u001b[0m, in \u001b[0;36mHasWorkingDirectory.tidy_working_directory\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 30\u001b[0m \u001b[38;5;124;03mIf the working directory is completely empty, deletes it.\u001b[39;00m\n\u001b[1;32m 31\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 32\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mworking_directory\u001b[38;5;241m.\u001b[39mis_empty():\n\u001b[0;32m---> 33\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mworking_directory\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdelete\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 34\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_working_directory \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/snippets/files.py:89\u001b[0m, in \u001b[0;36mDirectoryObject.delete\u001b[0;34m(self, only_if_empty)\u001b[0m\n\u001b[1;32m 87\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdelete\u001b[39m(\u001b[38;5;28mself\u001b[39m, only_if_empty: \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m):\n\u001b[1;32m 88\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mis_empty() \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m only_if_empty:\n\u001b[0;32m---> 89\u001b[0m \u001b[43mdelete_files_and_directories_recursively\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpath\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/snippets/files.py:9\u001b[0m, in \u001b[0;36mdelete_files_and_directories_recursively\u001b[0;34m(path)\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m path\u001b[38;5;241m.\u001b[39mexists():\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[0;32m----> 9\u001b[0m \u001b[43m\u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mitem\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mpath\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrglob\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m*\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 10\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mitem\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mis_file\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 11\u001b[0m \u001b[43m \u001b[49m\u001b[43mitem\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43munlink\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/pathlib.py:968\u001b[0m, in \u001b[0;36mPath.rglob\u001b[0;34m(self, pattern)\u001b[0m\n\u001b[1;32m 966\u001b[0m pattern_parts\u001b[38;5;241m.\u001b[39mappend(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 967\u001b[0m selector \u001b[38;5;241m=\u001b[39m _make_selector((\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m**\u001b[39m\u001b[38;5;124m\"\u001b[39m,) \u001b[38;5;241m+\u001b[39m \u001b[38;5;28mtuple\u001b[39m(pattern_parts), \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_flavour)\n\u001b[0;32m--> 968\u001b[0m \u001b[43m\u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mp\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mselector\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mselect_from\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 969\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01myield\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mp\u001b[49m\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/pathlib.py:408\u001b[0m, in \u001b[0;36m_RecursiveWildcardSelector._select_from\u001b[0;34m(self, parent_path, is_dir, exists, scandir)\u001b[0m\n\u001b[1;32m 406\u001b[0m successor_select \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msuccessor\u001b[38;5;241m.\u001b[39m_select_from\n\u001b[1;32m 407\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m starting_point \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_iterate_directories(parent_path, is_dir, scandir):\n\u001b[0;32m--> 408\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mp\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43msuccessor_select\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstarting_point\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mis_dir\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexists\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscandir\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 409\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mp\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43myielded\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 410\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01myield\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mp\u001b[49m\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/pathlib.py:355\u001b[0m, in \u001b[0;36m_WildcardSelector._select_from\u001b[0;34m(self, parent_path, is_dir, exists, scandir)\u001b[0m\n\u001b[1;32m 353\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_select_from\u001b[39m(\u001b[38;5;28mself\u001b[39m, parent_path, is_dir, exists, scandir):\n\u001b[1;32m 354\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 355\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[43mscandir\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparent_path\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m scandir_it:\n\u001b[1;32m 356\u001b[0m entries \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(scandir_it)\n\u001b[1;32m 357\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m entry \u001b[38;5;129;01min\u001b[39;00m entries:\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/pathlib.py:938\u001b[0m, in \u001b[0;36mPath._scandir\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 934\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_scandir\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m 935\u001b[0m \u001b[38;5;66;03m# bpo-24132: a future version of pathlib will support subclassing of\u001b[39;00m\n\u001b[1;32m 936\u001b[0m \u001b[38;5;66;03m# pathlib.Path to customize how the filesystem is accessed. This\u001b[39;00m\n\u001b[1;32m 937\u001b[0m \u001b[38;5;66;03m# includes scandir(), which is used to implement glob().\u001b[39;00m\n\u001b[0;32m--> 938\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m os\u001b[38;5;241m.\u001b[39mscandir(\u001b[38;5;28mself\u001b[39m)\n", - "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'sleep'" + "CPU times: user 11 µs, sys: 1 µs, total: 12 µs\n", + "Wall time: 14.1 µs\n" ] } ], - "source": [ - "%%time\n", - "sleep().iter(a=[1,2,3,4,5], max_workers=5, executor=1)" - ] - }, - { - "cell_type": "code", - "execution_count": 77, - "id": "9556bca8-a7aa-4df6-a1b3-13f6c6bfae38", - "metadata": {}, - "outputs": [], - "source": [ - "def sort_list_by_first_element(input_list):\n", - " sorted_list = sorted(input_list, key=lambda x: x[0])\n", - " return sorted_list" - ] - }, - { - "cell_type": "code", - "execution_count": 78, - "id": "78a1c63a-0805-4e98-b2ab-0353bf753b72", - "metadata": {}, - "outputs": [], - "source": [ - "def func(node, **kwargs):\n", - " return node(**kwargs).run()" - ] - }, - { - "cell_type": "markdown", - "id": "f87e4f17-6010-4070-b25c-139a5e997fd5", - "metadata": {}, - "source": [ - "create list of dictionaries" - ] - }, - { - "cell_type": "code", - "execution_count": 79, - "id": "2aa23533-8b09-46fa-8e9b-0fdc0b75d812", - "metadata": {}, - "outputs": [], - "source": [ - "def to_list_of_kwargs(**kwargs):\n", - " keys = list(kwargs.keys())\n", - " lists = list(kwargs.values())\n", - "\n", - " # Get the number of dimensions\n", - " num_dimensions = len(keys)\n", - "\n", - " # Get the length of each list\n", - " lengths = [len(lst) for lst in lists]\n", - "\n", - " # Initialize indices\n", - " indices = [0] * num_dimensions\n", - "\n", - " kwargs_list = []\n", - "\n", - " # Perform multidimensional for loop\n", - " count = 0\n", - " while indices[0] < lengths[0]:\n", - " # Access the current elements using indices\n", - " current_elements = [lists[i][indices[i]] for i in range(num_dimensions)]\n", - "\n", - " # Add current_elements as a dictionary\n", - " current_elements_kwarg = dict(zip(keys, current_elements))\n", - " kwargs_list.append(current_elements_kwarg)\n", - "\n", - " # Update indices for the next iteration\n", - " indices[num_dimensions - 1] += 1\n", - "\n", - " # Update indices and carry-over if needed\n", - " for i in range(num_dimensions - 1, 0, -1):\n", - " if indices[i] == lengths[i]:\n", - " indices[i] = 0\n", - " indices[i - 1] += 1\n", - " \n", - " return kwargs_list " - ] - }, - { - "cell_type": "code", - "execution_count": 80, - "id": "4fcc2d23-bdbb-4764-bbdb-ac941ed398d1", - "metadata": {}, - "outputs": [], - "source": [ - "def iter(node, max_workers=5, **kwargs):\n", - " from concurrent.futures import ThreadPoolExecutor, as_completed\n", - " import pandas as pd\n", - " \n", - " futures = []\n", - " future_index_map = {}\n", - " out = []\n", - " out_index = []\n", - "\n", - " refs = to_list_of_kwargs(**kwargs)\n", - " df_refs = pd.DataFrame(refs)\n", - " \n", - " with ThreadPoolExecutor(max_workers=max_workers) as executor: \n", - " for i, ref in enumerate(refs): \n", - " future = executor.submit(func, node, **ref)\n", - " future_index_map[future] = i\n", - " futures.append(future)\n", - " \n", - " for future in as_completed(futures):\n", - " out.append(future.result())\n", - " out_index.append(future_index_map[future])\n", - " \n", - " df_out = pd.DataFrame(out, index=out_index).sort_index()\n", - " return pd.concat([df_refs, df_out], axis=1)\n", - " " - ] - }, - { - "cell_type": "code", - "execution_count": 81, - "id": "eb9a7e6f-2171-4d4b-9804-58d09bdd8206", - "metadata": {}, - "outputs": [ - { - "ename": "FileNotFoundError", - "evalue": "[Errno 2] No such file or directory: 'sleep'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[81], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;43miter\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43msleep\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_workers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m10\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43ma\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;241;43m4\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mb\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", - "Cell \u001b[0;32mIn[80], line 20\u001b[0m, in \u001b[0;36miter\u001b[0;34m(node, max_workers, **kwargs)\u001b[0m\n\u001b[1;32m 17\u001b[0m futures\u001b[38;5;241m.\u001b[39mappend(future)\n\u001b[1;32m 19\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m future \u001b[38;5;129;01min\u001b[39;00m as_completed(futures):\n\u001b[0;32m---> 20\u001b[0m out\u001b[38;5;241m.\u001b[39mappend(\u001b[43mfuture\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresult\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m)\n\u001b[1;32m 21\u001b[0m out_index\u001b[38;5;241m.\u001b[39mappend(future_index_map[future])\n\u001b[1;32m 23\u001b[0m df_out \u001b[38;5;241m=\u001b[39m pd\u001b[38;5;241m.\u001b[39mDataFrame(out, index\u001b[38;5;241m=\u001b[39mout_index)\u001b[38;5;241m.\u001b[39msort_index()\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/concurrent/futures/_base.py:449\u001b[0m, in \u001b[0;36mFuture.result\u001b[0;34m(self, timeout)\u001b[0m\n\u001b[1;32m 447\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m CancelledError()\n\u001b[1;32m 448\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_state \u001b[38;5;241m==\u001b[39m FINISHED:\n\u001b[0;32m--> 449\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__get_result\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 451\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_condition\u001b[38;5;241m.\u001b[39mwait(timeout)\n\u001b[1;32m 453\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_state \u001b[38;5;129;01min\u001b[39;00m [CANCELLED, CANCELLED_AND_NOTIFIED]:\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/concurrent/futures/_base.py:401\u001b[0m, in \u001b[0;36mFuture.__get_result\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 399\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_exception:\n\u001b[1;32m 400\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 401\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_exception\n\u001b[1;32m 402\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m 403\u001b[0m \u001b[38;5;66;03m# Break a reference cycle with the exception in self._exception\u001b[39;00m\n\u001b[1;32m 404\u001b[0m \u001b[38;5;28mself\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/concurrent/futures/thread.py:58\u001b[0m, in \u001b[0;36m_WorkItem.run\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 55\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[1;32m 57\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m---> 58\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 59\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[1;32m 60\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mfuture\u001b[38;5;241m.\u001b[39mset_exception(exc)\n", - "Cell \u001b[0;32mIn[78], line 2\u001b[0m, in \u001b[0;36mfunc\u001b[0;34m(node, **kwargs)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mfunc\u001b[39m(node, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs):\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mnode\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241m.\u001b[39mrun()\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/snippets/has_post.py:16\u001b[0m, in \u001b[0;36mHasPost.__call__\u001b[0;34m(cls, *args, **kwargs)\u001b[0m\n\u001b[1;32m 14\u001b[0m instance \u001b[38;5;241m=\u001b[39m \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39m\u001b[38;5;21m__call__\u001b[39m(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[1;32m 15\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m post \u001b[38;5;241m:=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m(\u001b[38;5;28mcls\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m__post__\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mFalse\u001b[39;00m):\n\u001b[0;32m---> 16\u001b[0m \u001b[43mpost\u001b[49m\u001b[43m(\u001b[49m\u001b[43minstance\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 17\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m instance\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/node.py:350\u001b[0m, in \u001b[0;36mNode.__post__\u001b[0;34m(self, overwrite_save, run_after_init, *args, **kwargs)\u001b[0m\n\u001b[1;32m 348\u001b[0m \u001b[38;5;28;01mpass\u001b[39;00m\n\u001b[1;32m 349\u001b[0m \u001b[38;5;66;03m# Else neither loading nor running now -- no action required!\u001b[39;00m\n\u001b[0;32m--> 350\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgraph_root\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtidy_working_directory\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/working.py:33\u001b[0m, in \u001b[0;36mHasWorkingDirectory.tidy_working_directory\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 29\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 30\u001b[0m \u001b[38;5;124;03mIf the working directory is completely empty, deletes it.\u001b[39;00m\n\u001b[1;32m 31\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[1;32m 32\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mworking_directory\u001b[38;5;241m.\u001b[39mis_empty():\n\u001b[0;32m---> 33\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mworking_directory\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdelete\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 34\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_working_directory \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/snippets/files.py:89\u001b[0m, in \u001b[0;36mDirectoryObject.delete\u001b[0;34m(self, only_if_empty)\u001b[0m\n\u001b[1;32m 87\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdelete\u001b[39m(\u001b[38;5;28mself\u001b[39m, only_if_empty: \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m):\n\u001b[1;32m 88\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mis_empty() \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m only_if_empty:\n\u001b[0;32m---> 89\u001b[0m \u001b[43mdelete_files_and_directories_recursively\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpath\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/python_projects/git_libs/pyiron_workflow/pyiron_workflow/snippets/files.py:9\u001b[0m, in \u001b[0;36mdelete_files_and_directories_recursively\u001b[0;34m(path)\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m path\u001b[38;5;241m.\u001b[39mexists():\n\u001b[1;32m 8\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[0;32m----> 9\u001b[0m \u001b[43m\u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mitem\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mpath\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrglob\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m*\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 10\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mitem\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mis_file\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 11\u001b[0m \u001b[43m \u001b[49m\u001b[43mitem\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43munlink\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/pathlib.py:968\u001b[0m, in \u001b[0;36mPath.rglob\u001b[0;34m(self, pattern)\u001b[0m\n\u001b[1;32m 966\u001b[0m pattern_parts\u001b[38;5;241m.\u001b[39mappend(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 967\u001b[0m selector \u001b[38;5;241m=\u001b[39m _make_selector((\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m**\u001b[39m\u001b[38;5;124m\"\u001b[39m,) \u001b[38;5;241m+\u001b[39m \u001b[38;5;28mtuple\u001b[39m(pattern_parts), \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_flavour)\n\u001b[0;32m--> 968\u001b[0m \u001b[43m\u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mp\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mselector\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mselect_from\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 969\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01myield\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mp\u001b[49m\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/pathlib.py:408\u001b[0m, in \u001b[0;36m_RecursiveWildcardSelector._select_from\u001b[0;34m(self, parent_path, is_dir, exists, scandir)\u001b[0m\n\u001b[1;32m 406\u001b[0m successor_select \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msuccessor\u001b[38;5;241m.\u001b[39m_select_from\n\u001b[1;32m 407\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m starting_point \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_iterate_directories(parent_path, is_dir, scandir):\n\u001b[0;32m--> 408\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mp\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43msuccessor_select\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstarting_point\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mis_dir\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexists\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mscandir\u001b[49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 409\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mp\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43myielded\u001b[49m\u001b[43m:\u001b[49m\n\u001b[1;32m 410\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01myield\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mp\u001b[49m\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/pathlib.py:355\u001b[0m, in \u001b[0;36m_WildcardSelector._select_from\u001b[0;34m(self, parent_path, is_dir, exists, scandir)\u001b[0m\n\u001b[1;32m 353\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_select_from\u001b[39m(\u001b[38;5;28mself\u001b[39m, parent_path, is_dir, exists, scandir):\n\u001b[1;32m 354\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 355\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[43mscandir\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparent_path\u001b[49m\u001b[43m)\u001b[49m \u001b[38;5;28;01mas\u001b[39;00m scandir_it:\n\u001b[1;32m 356\u001b[0m entries \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlist\u001b[39m(scandir_it)\n\u001b[1;32m 357\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m entry \u001b[38;5;129;01min\u001b[39;00m entries:\n", - "File \u001b[0;32m~/mambaforge/envs/intel11/lib/python3.11/pathlib.py:938\u001b[0m, in \u001b[0;36mPath._scandir\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 934\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_scandir\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m 935\u001b[0m \u001b[38;5;66;03m# bpo-24132: a future version of pathlib will support subclassing of\u001b[39;00m\n\u001b[1;32m 936\u001b[0m \u001b[38;5;66;03m# pathlib.Path to customize how the filesystem is accessed. This\u001b[39;00m\n\u001b[1;32m 937\u001b[0m \u001b[38;5;66;03m# includes scandir(), which is used to implement glob().\u001b[39;00m\n\u001b[0;32m--> 938\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m os\u001b[38;5;241m.\u001b[39mscandir(\u001b[38;5;28mself\u001b[39m)\n", - "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'sleep'" - ] - } - ], - "source": [ - "iter(sleep, max_workers=10, a=[1,2,3,4], b=[1,3])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cfdce70a-08d5-4528-9c9a-c6f516b8e0df", - "metadata": {}, - "outputs": [], - "source": [ - "type(sleep())()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b228a8bd-f7e8-4fe1-aece-ee7dc56365e8", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f63a9ab5-b265-4f2e-bbfd-28ea12d72346", - "metadata": {}, - "outputs": [], - "source": [ - "pd.DataFrame(dict(a=[1,2,3,4]), index=[2,1,4,3]).sort_index()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "93883fd6-8847-48be-9b10-64fef0588e1f", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f62e3bcd-a671-4bac-a6d1-c9c2e32575ef", - "metadata": {}, - "outputs": [], "source": [ "%%time\n", "from pyiron_workflow.node_library.atomistic.engine.lammps import Code" @@ -6511,7 +4942,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 79, "id": "0a93e2cd-7636-4a33-a793-41d774fc639a", "metadata": {}, "outputs": [], @@ -6524,7 +4955,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 80, "id": "14ec45e1-7161-44fb-aac2-99c26a91b02e", "metadata": {}, "outputs": [], @@ -6535,107 +4966,240 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 81, "id": "4f2acd98-05de-479c-b5ff-5d2a0a6a5515", "metadata": {}, "outputs": [], "source": [ - "@Workflow.wrap_as.macro_node('energy_pot')\n", - "def energy_at_volume(wf, element='Al', cell_size=2, strain=0):\n", + "@Workflow.wrap.as_macro_node('energy_pot')\n", + "def EnergyAtVolume(wf, element='Al', cell_size=2, strain=0):\n", "\n", - " wf.structure = wf.create.atomistic.structure.build.cubic_bulk_cell(\n", + " wf.structure = wf.create.atomistic.structure.build.CubicBulkCell(\n", " element=element, \n", - " cubic=True, \n", " cell_size=cell_size\n", " )\n", - " wf.apply_strain = wf.create.atomistic.structure.transform.apply_strain(\n", - " structure=wf.structure.outputs.structure, \n", + " wf.apply_strain = wf.create.atomistic.structure.transform.ApplyStrain(\n", + " structure=wf.structure, \n", " strain=strain\n", " )\n", - " wf.engine = wf.create.atomistic.engine.lammps.Code(\n", + "# # atomistic.engine.lammps.Code takes a calculator\n", + "# # atomistic.calculator.generic.Static takes an engine\n", + "# # So we can't pass them to each other without trouble:\n", + "# wf.engine = wf.create.atomistic.engine.lammps.Code(\n", + "# structure=wf.apply_strain\n", + "# )\n", + "# wf.calc = wf.create.atomistic.calculator.generic.Static(\n", + "# structure=wf.apply_strain, \n", + "# engine=wf.engine\n", + "# )\n", + " \n", + "# return wf.calc.outputs.generic.energy_pot\n", + "\n", + " # Instead we can just use the lammps node directly,\n", + " # whose `calculator` is anyhow defaulting to InputCalcStatic()\n", + " wf.calc = wf.create.atomistic.engine.lammps.Code(\n", " structure=wf.apply_strain\n", - " ) # TODO: find a way to avoid structure=wf.structure !\n", - " wf.calc = wf.create.atomistic.calculator.generic.static(\n", - " structure=wf.apply_strain, \n", - " engine=wf.engine\n", " )\n", - " \n", " return wf.calc.outputs.generic.energy_pot" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 82, "id": "1d9470a3-de9d-4303-b0cd-1da4fd22eeaf", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n" + ] + } + ], + "source": [ + "df = EnergyAtVolume(element='Fe').iter(strain=np.linspace(-0.2, 0.2, 11).tolist())" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "id": "36422515-57fc-4061-bf8e-6466324b6023", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "df = energy_at_volume(element='Fe').iter(strain=np.linspace(-0.2, 0.2, 11))\n", "df.plot(x='strain', ylabel='Energy (eV)', title='Energy-Volume Curve');" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 84, "id": "26833a88-1564-4c36-950e-483ea7227b85", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:72: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " data = pd.read_csv(StringIO(\"\\n\".join(lines[9:])), names=data_head, delim_whitespace=True)\n", + "/Users/huber/anaconda3/envs/pyiron_311/lib/python3.11/site-packages/pymatgen/io/lammps/outputs.py:183: FutureWarning: The 'delim_whitespace' keyword in pd.read_csv is deprecated and will be removed in a future version. Use ``sep='\\s+'`` instead\n", + " df = pd.read_csv(StringIO(\"\".join(lines)), delim_whitespace=True)\n" + ] + }, + { + "data": { + "text/plain": [ + "{'engine__generic': OutputCalcStatic(energy_pot=-3.35999999707241, force=array([[5.55111512e-17, 1.52655666e-16, 1.52655666e-16]]), stress=None, structure=None, atomic_energies=None)}" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "wf = Workflow('test')\n", "wf.register('pyiron_workflow.node_library.atomistic', domain='atomistic')\n", - "wf.structure = wf.create.atomistic.structure.build.bulk('Al')\n", + "wf.structure = wf.create.atomistic.structure.build.Bulk('Al')\n", "wf.engine = wf.create.atomistic.engine.lammps.Code(structure=wf.structure) # TODO: find a way to avoid structure=wf.structure !\n", - "wf.calc = wf.create.atomistic.calculator.generic.static(structure=wf.structure, engine=wf.engine)\n", + "# wf.calc = wf.create.atomistic.calculator.generic.static(structure=wf.structure, engine=wf.engine)\n", "\n", "wf.run()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 85, "id": "461090fc-2cf5-48d3-b63a-79f090f78823", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "-3.35999999707241" + ] + }, + "execution_count": 85, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "wf.calc.outputs.generic.value.energy_pot" + "# wf.calc.outputs.generic.value.energy_pot\n", + "wf.engine.outputs.generic.value.energy_pot" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 86, "id": "d015fbd6-4585-47e9-96d4-37d2a10d0885", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "OutputCalcStatic(energy_pot=-3.35999999707241, force=array([[5.55111512e-17, 1.52655666e-16, 1.52655666e-16]]), stress=None, structure=None, atomic_energies=None)" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "wf.engine.outputs.generic.value" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 87, "id": "910840a9-e72b-4a8e-a095-3016e2b15ac7", "metadata": {}, "outputs": [], "source": [ - "wf.calc" + "# wf.calc" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 88, "id": "499186dc-f0aa-4065-ab5b-cac6d636a110", "metadata": {}, "outputs": [], "source": [ - "wf.calc.outputs.generic.value.energy_pot" + "# wf.calc.outputs.generic.value.energy_pot" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 89, "id": "6773c7de-07fd-4d6b-a7c7-ad4aa5ceb0f4", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 20 µs, sys: 1 µs, total: 21 µs\n", + "Wall time: 28.8 µs\n" + ] + } + ], "source": [ "%%time\n", "from pyiron_workflow.node_library.atomistic.calculator.data import InputCalcMinimize, InputCalcStatic" @@ -6643,10 +5207,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 90, "id": "a4543371-f2fe-44b2-97ce-89a9e178d5d3", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 5.67 ms, sys: 4.59 ms, total: 10.3 ms\n", + "Wall time: 9.81 ms\n" + ] + } + ], "source": [ "%%time\n", "from pyiron_atomistics.lammps.base import LammpsControl" @@ -6654,17 +5227,29 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 91, "id": "eda647f1-9b9d-4f8a-99d8-5bc104e53bba", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(InputCalcMinimize(e_tol=0.0, f_tol=0.0001, max_iter=1000000, pressure=None, n_print=100, style='cg'),\n", + " InputCalcStatic())" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "InputCalcMinimize(), InputCalcStatic()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 92, "id": "38698067-1f74-4fa2-a8a5-983bcd324d5d", "metadata": {}, "outputs": [], @@ -6674,7 +5259,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 93, "id": "8530dde2-be62-4b61-a9bb-8115df653336", "metadata": {}, "outputs": [], @@ -6707,7 +5292,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.2" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/notebooks/pyiron_like_workflows.ipynb b/notebooks/pyiron_like_workflows.ipynb index ad591717..913e3d32 100644 --- a/notebooks/pyiron_like_workflows.ipynb +++ b/notebooks/pyiron_like_workflows.ipynb @@ -40,8 +40,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 1 µs, sys: 1 µs, total: 2 µs\n", - "Wall time: 5.25 µs\n" + "CPU times: user 2 µs, sys: 0 ns, total: 2 µs\n", + "Wall time: 7.15 µs\n" ] } ], @@ -78,7 +78,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "02a18a9f7aba4c2798f9eda7b8a32670", + "model_id": "788d283c43d54566a3671102ed091890", "version_major": 2, "version_minor": 0 }, @@ -117,11 +117,11 @@ ], "source": [ "wf = Workflow('Lammps')\n", - "wf.structure = wf.create.atomistic.structure.build.bulk('Al', cubic=True)\n", - "wf.repeat = wf.create.atomistic.structure.transform.repeat(structure=wf.structure, repeat_scalar=1)\n", + "wf.structure = wf.create.atomistic.structure.build.Bulk('Al', cubic=True)\n", + "wf.repeat = wf.create.atomistic.structure.transform.Repeat(structure=wf.structure, repeat_scalar=1)\n", "\n", "wf.lammps = wf.create.atomistic_codes.Lammps(structure=wf.repeat, label='lammps')\n", - "wf.lammps.ListPotentials()[:5]" + "wf.lammps.list_potentials()[:5]" ] }, { @@ -147,11 +147,24 @@ ], "source": [ "wf = Workflow('Lammps')\n", - "wf.structure = wf.create.atomistic.structure.build.bulk('Al', cubic=True)\n", - "wf.repeat = wf.create.atomistic.structure.transform.repeat(structure=wf.structure, repeat_scalar=1)\n", + "wf.structure = wf.create.atomistic.structure.build.Bulk('Al', cubic=True)\n", + "wf.repeat = wf.create.atomistic.structure.transform.Repeat(structure=wf.structure, repeat_scalar=1)\n", "\n", - "wf.lammps = wf.create.atomistic_codes.Lammps(structure=wf.repeat, label='lammps')\n", - "wf.lammps.ListPotentials()[:5]" + "wf.lammps = wf.create.atomistic_codes.Lammps(\n", + " structure=wf.repeat, \n", + " label='lammps',\n", + " parent=wf # This is very sneaky and ugly\n", + " # In the Lammps macro definition, we leverage a child node's `.working_directory`\n", + " # Currently, this looks up the parent chain and uses the semantic path to create a file path.\n", + " # But once set, it is fixed.\n", + " # For a function this doesn't matter because it won't get called until run time, \n", + " # but for a macro the defining function gets run at initialization\n", + " # By creating _then_ parenting the Lammps macro, this gets set without the parent workflow\n", + " # in the path!\n", + " # We can get around this by specifying the parent right in the initialization,\n", + " # but this is clearly a bug/bad architecture in the `Node.working_directory` code\n", + ")\n", + "wf.lammps.list_potentials()[:5]" ] }, { @@ -169,12 +182,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "clusterlammps\n", - "\n", - "lammps: Lammps\n", + "\n", + "lammps: Lammps\n", "\n", "clusterlammpsInputs\n", "\n", @@ -187,26 +200,26 @@ "Inputs\n", "\n", "\n", - "clusterlammpsOutputs\n", + "clusterlammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterlammpsstructure\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "structure: UserInput\n", + "\n", + "structure: UserInput\n", "\n", "\n", "clusterlammpsstructureInputs\n", @@ -220,345 +233,312 @@ "Inputs\n", "\n", "\n", - "clusterlammpsstructureOutputs\n", + "clusterlammpsstructureOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterlammpspotential\n", + "clusterlammpspotential_object\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "potential: UserInput\n", + "\n", + "potential_object: Potential\n", "\n", "\n", - "clusterlammpspotentialInputs\n", + "clusterlammpspotential_objectInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterlammpspotentialOutputs\n", + "clusterlammpspotential_objectOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterlammpsPotential\n", + "clusterlammpslist_potentials\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Potential: Potential\n", + "\n", + "list_potentials: ListPotentials\n", "\n", - "\n", - "clusterlammpsPotentialOutputs\n", + "\n", + "clusterlammpslist_potentialsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterlammpsPotentialInputs\n", + "\n", + "clusterlammpslist_potentialsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", + "\n", "clusterlammpscalc\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "calc: CalcStatic\n", + "\n", + "calc: CalcStatic\n", "\n", - "\n", + "\n", "clusterlammpscalcInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterlammpscalcOutputs\n", + "\n", + "clusterlammpscalcOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterlammpsInitLammps\n", + "\n", + "clusterlammpsinit_lammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "InitLammps: InitLammps\n", + "\n", + "init_lammps: InitLammps\n", "\n", - "\n", - "clusterlammpsInitLammpsInputs\n", + "\n", + "clusterlammpsinit_lammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterlammpsInitLammpsOutputs\n", + "\n", + "clusterlammpsinit_lammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterlammpsShell\n", + "\n", + "clusterlammpsshell\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Shell: Shell\n", + "\n", + "shell: Shell\n", "\n", - "\n", - "clusterlammpsShellInputs\n", + "\n", + "clusterlammpsshellInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterlammpsShellOutputs\n", + "\n", + "clusterlammpsshellOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterlammpsCollect\n", + "\n", + "clusterlammpsparse_log_file\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Collect: Collect\n", + "\n", + "parse_log_file: ParseLogFile\n", "\n", - "\n", - "clusterlammpsCollectInputs\n", + "\n", + "clusterlammpsparse_log_fileInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterlammpsCollectOutputs\n", + "\n", + "clusterlammpsparse_log_fileOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterlammpsParseLogFile\n", + "clusterlammpsparse_dump_file\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "ParseLogFile: ParseLogFile\n", + "\n", + "parse_dump_file: ParseDumpFile\n", "\n", "\n", - "clusterlammpsParseLogFileInputs\n", + "clusterlammpsparse_dump_fileInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterlammpsParseLogFileOutputs\n", + "clusterlammpsparse_dump_fileOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterlammpsParseDumpFile\n", + "clusterlammpscalc__calculator_GetAttr_mode\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "ParseDumpFile: ParseDumpFile\n", + "\n", + "calc__calculator_GetAttr_mode: GetAttr\n", "\n", "\n", - "clusterlammpsParseDumpFileInputs\n", + "clusterlammpscalc__calculator_GetAttr_modeInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterlammpsParseDumpFileOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterlammpsListPotentials\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "ListPotentials: ListPotentials\n", - "\n", - "\n", - "clusterlammpsListPotentialsInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterlammpsListPotentialsOutputs\n", + "clusterlammpscalc__calculator_GetAttr_modeOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterlammpscalc__calculator_GetAttr_mode\n", + "clusterlammpscollect\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "calc__calculator_GetAttr_mode: GetAttr\n", + "\n", + "collect: Collect\n", "\n", "\n", - "clusterlammpscalc__calculator_GetAttr_modeInputs\n", + "clusterlammpscollectInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterlammpscalc__calculator_GetAttr_modeOutputs\n", + "clusterlammpscollectOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "\n", @@ -566,13 +546,13 @@ "\n", "run\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsOutputsran\n", - "\n", - "ran\n", + "clusterlammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterlammpsInputsaccumulate_and_run\n", @@ -592,7 +572,7 @@ "user_input\n", "\n", "\n", - "\n", + "\n", "clusterlammpsInputsstructure->clusterlammpsstructureInputsuser_input\n", "\n", "\n", @@ -604,24 +584,24 @@ "\n", "potential\n", "\n", - "\n", - "\n", - "clusterlammpspotentialInputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterlammpspotential_objectInputsname\n", + "\n", + "name\n", "\n", - "\n", - "\n", - "clusterlammpsInputspotential->clusterlammpspotentialInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpsInputspotential->clusterlammpspotential_objectInputsname\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsOutputsgeneric\n", - "\n", - "generic\n", + "clusterlammpsOutputsWithInjectiongeneric\n", + "\n", + "generic\n", "\n", "\n", "\n", @@ -629,613 +609,556 @@ "\n", "run\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsstructureOutputsran\n", - "\n", - "ran\n", + "clusterlammpsstructureOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterlammpsstructureInputsaccumulate_and_run\n", "\n", "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterlammpsPotentialInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterlammpspotential_objectInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterlammpsstructureOutputsWithInjectionran->clusterlammpspotential_objectInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", + "\n", + "clusterlammpslist_potentialsInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", "\n", - "clusterlammpsstructureOutputsran->clusterlammpsPotentialInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clusterlammpsstructureOutputsWithInjectionran->clusterlammpslist_potentialsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsListPotentialsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterlammpsinit_lammpsInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsstructureOutputsran->clusterlammpsListPotentialsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clusterlammpsstructureOutputsWithInjectionran->clusterlammpsinit_lammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsInitLammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterlammpsstructureOutputsWithInjectionuser_input\n", + "\n", + "user_input\n", "\n", - "\n", - "\n", - "clusterlammpsstructureOutputsran->clusterlammpsInitLammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpspotential_objectInputsstructure\n", + "\n", + "structure\n", "\n", - "\n", - "\n", - "clusterlammpsstructureOutputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterlammpsstructureOutputsWithInjectionuser_input->clusterlammpspotential_objectInputsstructure\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsPotentialInputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "clusterlammpslist_potentialsInputsstructure\n", + "\n", + "structure\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsstructureOutputsuser_input->clusterlammpsPotentialInputsstructure\n", - "\n", - "\n", - "\n", + "clusterlammpsstructureOutputsWithInjectionuser_input->clusterlammpslist_potentialsInputsstructure\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsListPotentialsInputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "clusterlammpsinit_lammpsInputsstructure\n", + "\n", + "structure\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsstructureOutputsuser_input->clusterlammpsListPotentialsInputsstructure\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterlammpsInitLammpsInputsstructure\n", - "\n", - "structure\n", + "clusterlammpsstructureOutputsWithInjectionuser_input->clusterlammpsinit_lammpsInputsstructure\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsstructureOutputsuser_input->clusterlammpsInitLammpsInputsstructure\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", "\n", - "clusterlammpspotentialInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterlammpspotentialOutputsran\n", - "\n", - "ran\n", + "clusterlammpspotential_objectInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "\n", - "clusterlammpspotentialInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterlammpspotentialOutputsran->clusterlammpsPotentialInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpspotential_objectOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterlammpspotentialOutputsuser_input\n", - "\n", - "user_input\n", + "clusterlammpspotential_objectInputsindex\n", + "\n", + "index\n", "\n", - "\n", - "\n", - "clusterlammpsPotentialInputsname\n", - "\n", - "name\n", + "\n", + "\n", + "clusterlammpspotential_objectOutputsWithInjectionran->clusterlammpsinit_lammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpspotentialOutputsuser_input->clusterlammpsPotentialInputsname\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpspotential_objectOutputsWithInjectionpotential\n", + "\n", + "potential\n", "\n", - "\n", - "\n", - "clusterlammpsPotentialInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterlammpsinit_lammpsInputspotential\n", + "\n", + "potential\n", "\n", - "\n", - "\n", - "clusterlammpsPotentialOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterlammpspotential_objectOutputsWithInjectionpotential->clusterlammpsinit_lammpsInputspotential\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "clusterlammpsPotentialInputsindex\n", - "\n", - "index\n", + "\n", + "\n", + "clusterlammpslist_potentialsInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterlammpsPotentialOutputsran->clusterlammpsInitLammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpslist_potentialsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterlammpsPotentialOutputspotential\n", - "\n", - "potential\n", - "\n", - "\n", - "\n", - "clusterlammpsInitLammpsInputspotential\n", - "\n", - "potential\n", - "\n", - "\n", - "\n", - "clusterlammpsPotentialOutputspotential->clusterlammpsInitLammpsInputspotential\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterlammpsListPotentialsInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterlammpsListPotentialsOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterlammpsListPotentialsOutputspotentials\n", - "\n", - "potentials\n", + "clusterlammpslist_potentialsOutputsWithInjectionpotentials\n", + "\n", + "potentials\n", "\n", "\n", - "\n", + "\n", "clusterlammpscalcInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterlammpscalcOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterlammpscalcOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "clusterlammpscalcInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", "\n", - "\n", + "\n", "clusterlammpscalcInputscalculator_input\n", - "\n", - "calculator_input: Union\n", + "\n", + "calculator_input: Union\n", "\n", - "\n", - "\n", - "clusterlammpscalcOutputsran->clusterlammpsInitLammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpscalcOutputsWithInjectionran->clusterlammpsinit_lammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clusterlammpscalc__calculator_GetAttr_modeInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterlammpscalcOutputsran->clusterlammpscalc__calculator_GetAttr_modeInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpscalcOutputsWithInjectionran->clusterlammpscalc__calculator_GetAttr_modeInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpscalcOutputscalculator\n", - "\n", - "calculator\n", + "\n", + "\n", + "clusterlammpscalcOutputsWithInjectioncalculator\n", + "\n", + "calculator\n", "\n", - "\n", - "\n", - "clusterlammpsInitLammpsInputscalculator\n", - "\n", - "calculator\n", + "\n", + "\n", + "clusterlammpsinit_lammpsInputscalculator\n", + "\n", + "calculator\n", "\n", - "\n", - "\n", - "clusterlammpscalcOutputscalculator->clusterlammpsInitLammpsInputscalculator\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpscalcOutputsWithInjectioncalculator->clusterlammpsinit_lammpsInputscalculator\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clusterlammpscalc__calculator_GetAttr_modeInputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", - "\n", - "clusterlammpscalcOutputscalculator->clusterlammpscalc__calculator_GetAttr_modeInputsobj\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpscalcOutputsWithInjectioncalculator->clusterlammpscalc__calculator_GetAttr_modeInputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsInitLammpsInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterlammpsinit_lammpsInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterlammpsInitLammpsOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterlammpsinit_lammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "\n", - "clusterlammpsInitLammpsInputsworking_directory\n", - "\n", - "working_directory\n", + "\n", + "\n", + "\n", + "clusterlammpsinit_lammpsInputsworking_directory\n", + "\n", + "working_directory\n", "\n", - "\n", - "\n", - "clusterlammpsShellInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterlammpsshellInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterlammpsInitLammpsOutputsran->clusterlammpsShellInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpsinit_lammpsOutputsWithInjectionran->clusterlammpsshellInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", + "\n", + "clusterlammpsinit_lammpsOutputsWithInjectionpath\n", + "\n", + "path\n", + "\n", + "\n", + "\n", + "clusterlammpsshellInputsworking_directory\n", + "\n", + "working_directory: str\n", + "\n", + "\n", + "\n", + "clusterlammpsinit_lammpsOutputsWithInjectionpath->clusterlammpsshellInputsworking_directory\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterlammpsshellInputsrun\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clusterlammpsshellOutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clusterlammpsshellInputscommand\n", + "\n", + "command: str\n", + "\n", + "\n", + "\n", + "clusterlammpsshellInputsenvironment\n", + "\n", + "environment: Optional\n", + "\n", + "\n", "\n", - "clusterlammpsInitLammpsOutputspath\n", - "\n", - "path\n", + "clusterlammpsshellInputsarguments\n", + "\n", + "arguments: Optional\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsShellInputsworking_directory\n", - "\n", - "working_directory: str\n", + "clusterlammpsparse_log_fileInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterlammpsInitLammpsOutputspath->clusterlammpsShellInputsworking_directory\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpsshellOutputsWithInjectionran->clusterlammpsparse_log_fileInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsInitLammpsOutputsbla\n", - "\n", - "bla\n", + "\n", + "\n", + "clusterlammpsparse_dump_fileInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterlammpsShellInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterlammpsshellOutputsWithInjectionran->clusterlammpsparse_dump_fileInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsShellOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterlammpsshellOutputsWithInjectionoutput\n", + "\n", + "output\n", "\n", - "\n", - "\n", + "\n", "\n", - "clusterlammpsShellInputscommand\n", - "\n", - "command: str\n", + "clusterlammpsshellOutputsWithInjectiondump\n", + "\n", + "dump\n", "\n", - "\n", - "\n", - "clusterlammpsShellInputsenvironment\n", - "\n", - "environment: Optional\n", + "\n", + "\n", + "clusterlammpsparse_dump_fileInputsdump_file\n", + "\n", + "dump_file\n", "\n", - "\n", - "\n", - "clusterlammpsShellInputsarguments\n", - "\n", - "arguments: Optional\n", + "\n", + "\n", + "clusterlammpsshellOutputsWithInjectiondump->clusterlammpsparse_dump_fileInputsdump_file\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsParseLogFileInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterlammpsshellOutputsWithInjectionlog\n", + "\n", + "log\n", "\n", - "\n", - "\n", - "clusterlammpsShellOutputsran->clusterlammpsParseLogFileInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpsparse_log_fileInputslog_file\n", + "\n", + "log_file\n", "\n", - "\n", - "\n", - "clusterlammpsParseDumpFileInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterlammpsshellOutputsWithInjectionlog->clusterlammpsparse_log_fileInputslog_file\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsShellOutputsran->clusterlammpsParseDumpFileInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpsparse_log_fileInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsShellOutputsoutput\n", - "\n", - "output\n", + "clusterlammpsparse_log_fileOutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clusterlammpscollectInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", + "\n", + "clusterlammpsparse_log_fileOutputsWithInjectionran->clusterlammpscollectInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", + "\n", + "\n", "\n", - "clusterlammpsShellOutputsdump\n", - "\n", - "dump\n", + "clusterlammpsparse_log_fileOutputsWithInjectionlog\n", + "\n", + "log\n", "\n", - "\n", - "\n", - "clusterlammpsParseDumpFileInputsdump_file\n", - "\n", - "dump_file\n", + "\n", + "\n", + "clusterlammpscollectInputsout_log\n", + "\n", + "out_log\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsShellOutputsdump->clusterlammpsParseDumpFileInputsdump_file\n", - "\n", - "\n", - "\n", + "clusterlammpsparse_log_fileOutputsWithInjectionlog->clusterlammpscollectInputsout_log\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsShellOutputslog\n", - "\n", - "log\n", + "clusterlammpsparse_dump_fileInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsParseLogFileInputslog_file\n", - "\n", - "log_file\n", + "clusterlammpsparse_dump_fileOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterlammpsShellOutputslog->clusterlammpsParseLogFileInputslog_file\n", - "\n", - "\n", - "\n", + "clusterlammpsparse_dump_fileOutputsWithInjectionran->clusterlammpscollectInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpsParseLogFileInputsrun\n", - "\n", - "run\n", - "\n", - "\n", + "\n", "\n", - "clusterlammpsParseLogFileOutputsran\n", - "\n", - "ran\n", + "clusterlammpsparse_dump_fileOutputsWithInjectiondump\n", + "\n", + "dump\n", "\n", - "\n", - "\n", - "\n", - "clusterlammpsCollectInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterlammpscollectInputsout_dump\n", + "\n", + "out_dump\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsParseLogFileOutputsran->clusterlammpsCollectInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clusterlammpsparse_dump_fileOutputsWithInjectiondump->clusterlammpscollectInputsout_dump\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsParseLogFileOutputslog\n", - "\n", - "log\n", - "\n", - "\n", - "\n", - "clusterlammpsCollectInputsout_log\n", - "\n", - "out_log\n", - "\n", - "\n", - "\n", - "clusterlammpsParseLogFileOutputslog->clusterlammpsCollectInputsout_log\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterlammpsParseDumpFileInputsrun\n", - "\n", - "run\n", + "clusterlammpscalc__calculator_GetAttr_modeInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterlammpsParseDumpFileOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterlammpsParseDumpFileOutputsran->clusterlammpsCollectInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterlammpsParseDumpFileOutputsdump\n", - "\n", - "dump\n", + "clusterlammpscalc__calculator_GetAttr_modeOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterlammpsCollectInputsout_dump\n", - "\n", - "out_dump\n", + "\n", + "\n", + "\n", + "clusterlammpscalc__calculator_GetAttr_modeInputsname\n", + "\n", + "name\n", "\n", - "\n", - "\n", - "clusterlammpsParseDumpFileOutputsdump->clusterlammpsCollectInputsout_dump\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpscalc__calculator_GetAttr_modeOutputsWithInjectionran->clusterlammpscollectInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpscalc__calculator_GetAttr_modeInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterlammpscalc__calculator_GetAttr_modeOutputsWithInjectiongetattr\n", + "\n", + "getattr\n", "\n", - "\n", + "\n", "\n", - "clusterlammpscalc__calculator_GetAttr_modeOutputsran\n", - "\n", - "ran\n", + "clusterlammpscollectInputscalc_mode\n", + "\n", + "calc_mode\n", "\n", - "\n", - "\n", - "\n", - "clusterlammpscalc__calculator_GetAttr_modeInputsname\n", - "\n", - "name\n", + "\n", + "\n", + "clusterlammpscalc__calculator_GetAttr_modeOutputsWithInjectiongetattr->clusterlammpscollectInputscalc_mode\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterlammpscalc__calculator_GetAttr_modeOutputsran->clusterlammpsCollectInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpscollectInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterlammpscalc__calculator_GetAttr_modeOutputsgetattr\n", - "\n", - "getattr\n", + "clusterlammpscollectOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "clusterlammpsCollectInputscalc_mode\n", - "\n", - "calc_mode\n", - "\n", - "\n", - "\n", - "clusterlammpscalc__calculator_GetAttr_modeOutputsgetattr->clusterlammpsCollectInputscalc_mode\n", - "\n", - "\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "clusterlammpsCollectInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterlammpsCollectOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterlammpsCollectInputsbla\n", - "\n", - "bla\n", + "clusterlammpscollectOutputsWithInjectiongeneric\n", + "\n", + "generic\n", "\n", - "\n", - "\n", - "clusterlammpsCollectOutputsgeneric\n", - "\n", - "generic\n", - "\n", - "\n", - "\n", - "clusterlammpsCollectOutputsgeneric->clusterlammpsOutputsgeneric\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterlammpscollectOutputsWithInjectiongeneric->clusterlammpsOutputsWithInjectiongeneric\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, "execution_count": 7, @@ -1244,7 +1167,7 @@ } ], "source": [ - "wf.lammps.draw(depth=2)" + "wf.lammps.draw(depth=2, size=(10, 10))" ] }, { @@ -1257,7 +1180,6 @@ "name": "stdout", "output_type": "stream", "text": [ - "Collect: static \n", "Potential energy: -13.4399999882896\n" ] } @@ -1280,14 +1202,6 @@ "fully_connected": false, "inputs": { "channels": { - "bla": { - "connected": false, - "connections": [], - "label": "bla", - "ready": true, - "type_hint": "None", - "value": "''" - }, "calc_mode": { "connected": true, "connections": [ @@ -1301,17 +1215,17 @@ "out_dump": { "connected": true, "connections": [ - "ParseDumpFile.dump" + "parse_dump_file.dump" ], "label": "out_dump", "ready": true, "type_hint": "None", - "value": "[]" + "value": "[]" }, "out_log": { "connected": true, "connections": [ - "ParseLogFile.log" + "parse_log_file.log" ], "label": "out_log", "ready": true, @@ -1320,11 +1234,11 @@ } }, "connected": true, - "fully_connected": false, + "fully_connected": true, "label": "Inputs", "ready": true }, - "label": "Collect", + "label": "collect", "outputs": { "channels": { "generic": { @@ -1338,7 +1252,7 @@ }, "connected": false, "fully_connected": false, - "label": "Outputs", + "label": "OutputsWithInjection", "ready": true }, "ready": true, @@ -1349,9 +1263,9 @@ "callback": "run", "connected": true, "connections": [ - "ParseLogFile.ran", "calc__calculator_GetAttr_mode.ran", - "ParseDumpFile.ran" + "parse_log_file.ran", + "parse_dump_file.ran" ], "label": "accumulate_and_run" }, @@ -1381,7 +1295,7 @@ } }, "text/plain": [ - "" + "" ] }, "execution_count": 9, @@ -1390,7 +1304,7 @@ } ], "source": [ - "wf.lammps.Collect" + "wf.lammps.collect" ] }, { @@ -1408,1472 +1322,1382 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "clusterLammps\n", - "\n", - "Lammps: Workflow\n", + "\n", + "Lammps: Workflow\n", "\n", "clusterLammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", "clusterLammpsOutputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Outputs\n", "\n", "\n", "clusterLammpsstructure\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "structure: bulk\n", + "\n", + "structure: Bulk\n", "\n", "\n", "clusterLammpsstructureInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterLammpsstructureOutputs\n", + "clusterLammpsstructureOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterLammpsrepeat\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "repeat: repeat\n", + "\n", + "repeat: Repeat\n", "\n", "\n", "clusterLammpsrepeatInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterLammpsrepeatOutputs\n", + "clusterLammpsrepeatOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clusterLammpslammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "lammps: Lammps\n", - "\n", - "\n", - "clusterLammpscalclammps\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "calc: CalcStatic\n", + "\n", + "lammps: Lammps\n", "\n", - "\n", - "clusterLammpscalclammpsInputs\n", + "\n", + "clusterLammpslammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterLammpscalclammpsOutputs\n", + "\n", + "clusterLammpslammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterLammpsShelllammps\n", + "\n", + "clusterLammpsstructurelammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Shell: Shell\n", + "\n", + "structure: UserInput\n", "\n", - "\n", - "clusterLammpsShelllammpsInputs\n", + "\n", + "clusterLammpsstructurelammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterLammpsShelllammpsOutputs\n", + "\n", + "clusterLammpsstructurelammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterLammpsInitLammpslammps\n", + "\n", + "clusterLammpspotential_objectlammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "InitLammps: InitLammps\n", + "\n", + "potential_object: Potential\n", "\n", - "\n", - "clusterLammpsInitLammpslammpsInputs\n", + "\n", + "clusterLammpspotential_objectlammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterLammpsInitLammpslammpsOutputs\n", + "\n", + "clusterLammpspotential_objectlammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterLammpsParseLogFilelammps\n", + "\n", + "clusterLammpslist_potentialslammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "ParseLogFile: ParseLogFile\n", + "\n", + "list_potentials: ListPotentials\n", "\n", - "\n", - "clusterLammpsParseLogFilelammpsInputs\n", + "\n", + "clusterLammpslist_potentialslammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterLammpsParseLogFilelammpsOutputs\n", + "\n", + "clusterLammpslist_potentialslammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterLammpsParseDumpFilelammps\n", + "\n", + "clusterLammpscalclammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "ParseDumpFile: ParseDumpFile\n", + "\n", + "calc: CalcStatic\n", "\n", - "\n", - "clusterLammpsParseDumpFilelammpsInputs\n", + "\n", + "clusterLammpscalclammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterLammpsParseDumpFilelammpsOutputs\n", + "\n", + "clusterLammpscalclammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterLammpsCollectlammps\n", + "\n", + "clusterLammpsinit_lammpslammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Collect: Collect\n", - "\n", - "\n", - "clusterLammpsCollectlammpsInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterLammpsCollectlammpsOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", + "\n", + "init_lammps: InitLammps\n", "\n", - "\n", - "clusterLammpslammpsInputs\n", + "\n", + "clusterLammpsinit_lammpslammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterLammpslammpsOutputs\n", + "\n", + "clusterLammpsinit_lammpslammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterLammpsstructurelammps\n", + "\n", + "clusterLammpsshelllammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "structure: UserInput\n", + "\n", + "shell: Shell\n", "\n", - "\n", - "clusterLammpsstructurelammpsInputs\n", + "\n", + "clusterLammpsshelllammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterLammpsstructurelammpsOutputs\n", + "\n", + "clusterLammpsshelllammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterLammpspotentiallammps\n", + "\n", + "clusterLammpsparse_log_filelammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "potential: UserInput\n", + "\n", + "parse_log_file: ParseLogFile\n", "\n", - "\n", - "clusterLammpspotentiallammpsInputs\n", + "\n", + "clusterLammpsparse_log_filelammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterLammpspotentiallammpsOutputs\n", + "\n", + "clusterLammpsparse_log_filelammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterLammpsPotentiallammps\n", + "\n", + "clusterLammpsparse_dump_filelammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Potential: Potential\n", + "\n", + "parse_dump_file: ParseDumpFile\n", "\n", - "\n", - "clusterLammpsPotentiallammpsInputs\n", + "\n", + "clusterLammpsparse_dump_filelammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterLammpsPotentiallammpsOutputs\n", + "\n", + "clusterLammpsparse_dump_filelammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clusterLammpsListPotentialslammps\n", + "\n", + "clusterLammpscalc__calculator_GetAttr_modelammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "ListPotentials: ListPotentials\n", + "\n", + "calc__calculator_GetAttr_mode: GetAttr\n", "\n", - "\n", - "clusterLammpsListPotentialslammpsInputs\n", + "\n", + "clusterLammpscalc__calculator_GetAttr_modelammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterLammpsListPotentialslammpsOutputs\n", + "\n", + "clusterLammpscalc__calculator_GetAttr_modelammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterLammpscalc__calculator_GetAttr_modelammps\n", + "clusterLammpscollectlammps\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "calc__calculator_GetAttr_mode: GetAttr\n", + "\n", + "collect: Collect\n", "\n", "\n", - "clusterLammpscalc__calculator_GetAttr_modelammpsInputs\n", + "clusterLammpscollectlammpsInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterLammpscalc__calculator_GetAttr_modelammpsOutputs\n", + "clusterLammpscollectlammpsOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "\n", "clusterLammpsInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", "\n", "\n", "clusterLammpsOutputsran\n", - "\n", - "ran\n", + "\n", + "ran\n", "\n", "\n", "\n", "\n", "clusterLammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", "\n", "\n", "clusterLammpsInputsstructure__name\n", - "\n", - "structure__name\n", + "\n", + "structure__name\n", "\n", "\n", "\n", "clusterLammpsstructureInputsname\n", - "\n", - "name\n", + "\n", + "name\n", "\n", "\n", - "\n", + "\n", "clusterLammpsInputsstructure__name->clusterLammpsstructureInputsname\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsInputsstructure__crystalstructure\n", - "\n", - "structure__crystalstructure\n", + "\n", + "structure__crystalstructure\n", "\n", "\n", "\n", "clusterLammpsstructureInputscrystalstructure\n", - "\n", - "crystalstructure\n", + "\n", + "crystalstructure\n", "\n", "\n", - "\n", + "\n", "clusterLammpsInputsstructure__crystalstructure->clusterLammpsstructureInputscrystalstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsInputsstructure__a\n", - "\n", - "structure__a\n", + "\n", + "structure__a\n", "\n", "\n", "\n", "clusterLammpsstructureInputsa\n", - "\n", - "a\n", + "\n", + "a\n", "\n", "\n", - "\n", + "\n", "clusterLammpsInputsstructure__a->clusterLammpsstructureInputsa\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsInputsstructure__c\n", - "\n", - "structure__c\n", + "\n", + "structure__c\n", "\n", "\n", "\n", "clusterLammpsstructureInputsc\n", - "\n", - "c\n", + "\n", + "c\n", "\n", "\n", - "\n", + "\n", "clusterLammpsInputsstructure__c->clusterLammpsstructureInputsc\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsInputsstructure__covera\n", - "\n", - "structure__covera\n", + "\n", + "structure__covera\n", "\n", "\n", "\n", "clusterLammpsstructureInputscovera\n", - "\n", - "covera\n", + "\n", + "covera\n", "\n", "\n", - "\n", + "\n", "clusterLammpsInputsstructure__covera->clusterLammpsstructureInputscovera\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsInputsstructure__u\n", - "\n", - "structure__u\n", + "\n", + "structure__u\n", "\n", "\n", "\n", "clusterLammpsstructureInputsu\n", - "\n", - "u\n", + "\n", + "u\n", "\n", "\n", - "\n", + "\n", "clusterLammpsInputsstructure__u->clusterLammpsstructureInputsu\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsInputsstructure__orthorhombic\n", - "\n", - "structure__orthorhombic\n", + "\n", + "structure__orthorhombic\n", "\n", "\n", "\n", "clusterLammpsstructureInputsorthorhombic\n", - "\n", - "orthorhombic\n", + "\n", + "orthorhombic\n", "\n", "\n", - "\n", + "\n", "clusterLammpsInputsstructure__orthorhombic->clusterLammpsstructureInputsorthorhombic\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsInputsstructure__cubic\n", - "\n", - "structure__cubic\n", + "\n", + "structure__cubic\n", "\n", "\n", "\n", "clusterLammpsstructureInputscubic\n", - "\n", - "cubic\n", + "\n", + "cubic\n", "\n", "\n", - "\n", + "\n", "clusterLammpsInputsstructure__cubic->clusterLammpsstructureInputscubic\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsInputsrepeat__repeat_scalar\n", - "\n", - "repeat__repeat_scalar: int\n", + "\n", + "repeat__repeat_scalar: int\n", "\n", "\n", "\n", "clusterLammpsrepeatInputsrepeat_scalar\n", - "\n", - "repeat_scalar: int\n", + "\n", + "repeat_scalar: int\n", "\n", "\n", - "\n", + "\n", "clusterLammpsInputsrepeat__repeat_scalar->clusterLammpsrepeatInputsrepeat_scalar\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsInputslammps__potential\n", - "\n", - "lammps__potential\n", + "\n", + "lammps__potential\n", "\n", "\n", "\n", "clusterLammpslammpsInputspotential\n", - "\n", - "potential\n", + "\n", + "potential\n", "\n", "\n", - "\n", + "\n", "clusterLammpsInputslammps__potential->clusterLammpslammpsInputspotential\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsOutputslammps__generic\n", - "\n", - "lammps__generic\n", + "\n", + "lammps__generic\n", "\n", "\n", "\n", "clusterLammpsstructureInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsstructureOutputsran\n", - "\n", - "ran\n", + "clusterLammpsstructureOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterLammpsstructureInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", "\n", "\n", "clusterLammpsrepeatInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterLammpsstructureOutputsran->clusterLammpsrepeatInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpsstructureOutputsWithInjectionran->clusterLammpsrepeatInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsstructureOutputsstructure\n", - "\n", - "structure\n", + "clusterLammpsstructureOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", "\n", "\n", "clusterLammpsrepeatInputsstructure\n", - "\n", - "structure: Atoms\n", + "\n", + "structure: Atoms\n", "\n", - "\n", - "\n", - "clusterLammpsstructureOutputsstructure->clusterLammpsrepeatInputsstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpsstructureOutputsWithInjectionstructure->clusterLammpsrepeatInputsstructure\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsrepeatInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsrepeatOutputsran\n", - "\n", - "ran\n", + "clusterLammpsrepeatOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterLammpslammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterLammpsrepeatOutputsran->clusterLammpslammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpsrepeatOutputsWithInjectionran->clusterLammpslammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsrepeatOutputsstructure\n", - "\n", - "structure: Atoms\n", + "clusterLammpsrepeatOutputsWithInjectionstructure\n", + "\n", + "structure: Atoms\n", "\n", "\n", "\n", "clusterLammpslammpsInputsstructure\n", - "\n", - "structure\n", + "\n", + "structure\n", "\n", - "\n", - "\n", - "clusterLammpsrepeatOutputsstructure->clusterLammpslammpsInputsstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpsrepeatOutputsWithInjectionstructure->clusterLammpslammpsInputsstructure\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpslammpsInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterLammpslammpsOutputsran\n", - "\n", - "ran\n", + "clusterLammpslammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterLammpsstructurelammpsInputsuser_input\n", - "\n", - "user_input\n", + "\n", + "user_input\n", "\n", "\n", - "\n", + "\n", "clusterLammpslammpsInputsstructure->clusterLammpsstructurelammpsInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpspotentiallammpsInputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterLammpspotential_objectlammpsInputsname\n", + "\n", + "name\n", "\n", - "\n", - "\n", - "clusterLammpslammpsInputspotential->clusterLammpspotentiallammpsInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpslammpsInputspotential->clusterLammpspotential_objectlammpsInputsname\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterLammpslammpsOutputsgeneric\n", - "\n", - "generic\n", + "clusterLammpslammpsOutputsWithInjectiongeneric\n", + "\n", + "generic\n", "\n", - "\n", - "\n", - "clusterLammpslammpsOutputsgeneric->clusterLammpsOutputslammps__generic\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpslammpsOutputsWithInjectiongeneric->clusterLammpsOutputslammps__generic\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterLammpsstructurelammpsInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsstructurelammpsOutputsran\n", - "\n", - "ran\n", + "clusterLammpsstructurelammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterLammpsstructurelammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterLammpsPotentiallammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterLammpspotential_objectlammpsInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterLammpsstructurelammpsOutputsWithInjectionran->clusterLammpspotential_objectlammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterLammpslist_potentialslammpsInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsstructurelammpsOutputsran->clusterLammpsPotentiallammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clusterLammpsstructurelammpsOutputsWithInjectionran->clusterLammpslist_potentialslammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpsListPotentialslammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterLammpsinit_lammpslammpsInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsstructurelammpsOutputsran->clusterLammpsListPotentialslammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clusterLammpsstructurelammpsOutputsWithInjectionran->clusterLammpsinit_lammpslammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpsInitLammpslammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterLammpsstructurelammpsOutputsWithInjectionuser_input\n", + "\n", + "user_input\n", "\n", - "\n", - "\n", - "clusterLammpsstructurelammpsOutputsran->clusterLammpsInitLammpslammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpspotential_objectlammpsInputsstructure\n", + "\n", + "structure\n", "\n", - "\n", - "\n", - "clusterLammpsstructurelammpsOutputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterLammpsstructurelammpsOutputsWithInjectionuser_input->clusterLammpspotential_objectlammpsInputsstructure\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpsPotentiallammpsInputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "clusterLammpslist_potentialslammpsInputsstructure\n", + "\n", + "structure\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsstructurelammpsOutputsuser_input->clusterLammpsPotentiallammpsInputsstructure\n", - "\n", - "\n", - "\n", + "clusterLammpsstructurelammpsOutputsWithInjectionuser_input->clusterLammpslist_potentialslammpsInputsstructure\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpsListPotentialslammpsInputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "clusterLammpsinit_lammpslammpsInputsstructure\n", + "\n", + "structure\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsstructurelammpsOutputsuser_input->clusterLammpsListPotentialslammpsInputsstructure\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterLammpsInitLammpslammpsInputsstructure\n", - "\n", - "structure\n", - "\n", - "\n", - "\n", - "clusterLammpsstructurelammpsOutputsuser_input->clusterLammpsInitLammpslammpsInputsstructure\n", - "\n", - "\n", - "\n", + "clusterLammpsstructurelammpsOutputsWithInjectionuser_input->clusterLammpsinit_lammpslammpsInputsstructure\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterLammpspotentiallammpsInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterLammpspotentiallammpsOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterLammpspotentiallammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "clusterLammpspotential_objectlammpsInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterLammpspotentiallammpsOutputsran->clusterLammpsPotentiallammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpspotential_objectlammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterLammpspotentiallammpsOutputsuser_input\n", - "\n", - "user_input\n", + "clusterLammpspotential_objectlammpsInputsindex\n", + "\n", + "index\n", "\n", - "\n", - "\n", - "clusterLammpsPotentiallammpsInputsname\n", - "\n", - "name\n", + "\n", + "\n", + "clusterLammpspotential_objectlammpsOutputsWithInjectionran->clusterLammpsinit_lammpslammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpspotentiallammpsOutputsuser_input->clusterLammpsPotentiallammpsInputsname\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpspotential_objectlammpsOutputsWithInjectionpotential\n", + "\n", + "potential\n", "\n", - "\n", - "\n", - "clusterLammpsPotentiallammpsInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterLammpsinit_lammpslammpsInputspotential\n", + "\n", + "potential\n", "\n", - "\n", - "\n", - "clusterLammpsPotentiallammpsOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterLammpspotential_objectlammpsOutputsWithInjectionpotential->clusterLammpsinit_lammpslammpsInputspotential\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "\n", - "clusterLammpsPotentiallammpsInputsindex\n", - "\n", - "index\n", + "\n", + "\n", + "clusterLammpslist_potentialslammpsInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterLammpsPotentiallammpsOutputsran->clusterLammpsInitLammpslammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpslist_potentialslammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterLammpsPotentiallammpsOutputspotential\n", - "\n", - "potential\n", - "\n", - "\n", - "\n", - "clusterLammpsInitLammpslammpsInputspotential\n", - "\n", - "potential\n", - "\n", - "\n", - "\n", - "clusterLammpsPotentiallammpsOutputspotential->clusterLammpsInitLammpslammpsInputspotential\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterLammpsListPotentialslammpsInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterLammpsListPotentialslammpsOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterLammpsListPotentialslammpsOutputspotentials\n", - "\n", - "potentials\n", + "clusterLammpslist_potentialslammpsOutputsWithInjectionpotentials\n", + "\n", + "potentials\n", "\n", "\n", - "\n", + "\n", "clusterLammpscalclammpsInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterLammpscalclammpsOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterLammpscalclammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "clusterLammpscalclammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", "\n", - "\n", + "\n", "clusterLammpscalclammpsInputscalculator_input\n", - "\n", - "calculator_input: Union\n", + "\n", + "calculator_input: Union\n", "\n", - "\n", - "\n", - "clusterLammpscalclammpsOutputsran->clusterLammpsInitLammpslammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpscalclammpsOutputsWithInjectionran->clusterLammpsinit_lammpslammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clusterLammpscalc__calculator_GetAttr_modelammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterLammpscalclammpsOutputsran->clusterLammpscalc__calculator_GetAttr_modelammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpscalclammpsOutputsWithInjectionran->clusterLammpscalc__calculator_GetAttr_modelammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpscalclammpsOutputscalculator\n", - "\n", - "calculator\n", + "\n", + "\n", + "clusterLammpscalclammpsOutputsWithInjectioncalculator\n", + "\n", + "calculator\n", "\n", - "\n", - "\n", - "clusterLammpsInitLammpslammpsInputscalculator\n", - "\n", - "calculator\n", + "\n", + "\n", + "clusterLammpsinit_lammpslammpsInputscalculator\n", + "\n", + "calculator\n", "\n", - "\n", - "\n", - "clusterLammpscalclammpsOutputscalculator->clusterLammpsInitLammpslammpsInputscalculator\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpscalclammpsOutputsWithInjectioncalculator->clusterLammpsinit_lammpslammpsInputscalculator\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clusterLammpscalc__calculator_GetAttr_modelammpsInputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", - "\n", - "clusterLammpscalclammpsOutputscalculator->clusterLammpscalc__calculator_GetAttr_modelammpsInputsobj\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpscalclammpsOutputsWithInjectioncalculator->clusterLammpscalc__calculator_GetAttr_modelammpsInputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpsInitLammpslammpsInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterLammpsinit_lammpslammpsInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterLammpsInitLammpslammpsOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterLammpsinit_lammpslammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "\n", - "clusterLammpsInitLammpslammpsInputsworking_directory\n", - "\n", - "working_directory\n", + "\n", + "\n", + "\n", + "clusterLammpsinit_lammpslammpsInputsworking_directory\n", + "\n", + "working_directory\n", "\n", - "\n", - "\n", - "clusterLammpsShelllammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterLammpsshelllammpsInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterLammpsInitLammpslammpsOutputsran->clusterLammpsShelllammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpsinit_lammpslammpsOutputsWithInjectionran->clusterLammpsshelllammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterLammpsinit_lammpslammpsOutputsWithInjectionpath\n", + "\n", + "path\n", + "\n", + "\n", + "\n", + "clusterLammpsshelllammpsInputsworking_directory\n", + "\n", + "working_directory: str\n", + "\n", + "\n", + "\n", + "clusterLammpsinit_lammpslammpsOutputsWithInjectionpath->clusterLammpsshelllammpsInputsworking_directory\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterLammpsshelllammpsInputsrun\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clusterLammpsshelllammpsOutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clusterLammpsshelllammpsInputscommand\n", + "\n", + "command: str\n", + "\n", + "\n", + "\n", + "clusterLammpsshelllammpsInputsenvironment\n", + "\n", + "environment: Optional\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsInitLammpslammpsOutputspath\n", - "\n", - "path\n", + "clusterLammpsshelllammpsInputsarguments\n", + "\n", + "arguments: Optional\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsShelllammpsInputsworking_directory\n", - "\n", - "working_directory: str\n", + "clusterLammpsparse_log_filelammpsInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterLammpsInitLammpslammpsOutputspath->clusterLammpsShelllammpsInputsworking_directory\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpsshelllammpsOutputsWithInjectionran->clusterLammpsparse_log_filelammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpsInitLammpslammpsOutputsbla\n", - "\n", - "bla\n", + "\n", + "\n", + "clusterLammpsparse_dump_filelammpsInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterLammpsShelllammpsInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterLammpsshelllammpsOutputsWithInjectionran->clusterLammpsparse_dump_filelammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpsShelllammpsOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterLammpsshelllammpsOutputsWithInjectionoutput\n", + "\n", + "output\n", "\n", - "\n", - "\n", + "\n", "\n", - "clusterLammpsShelllammpsInputscommand\n", - "\n", - "command: str\n", + "clusterLammpsshelllammpsOutputsWithInjectiondump\n", + "\n", + "dump\n", "\n", - "\n", - "\n", - "clusterLammpsShelllammpsInputsenvironment\n", - "\n", - "environment: Optional\n", + "\n", + "\n", + "clusterLammpsparse_dump_filelammpsInputsdump_file\n", + "\n", + "dump_file\n", "\n", - "\n", - "\n", - "clusterLammpsShelllammpsInputsarguments\n", - "\n", - "arguments: Optional\n", + "\n", + "\n", + "clusterLammpsshelllammpsOutputsWithInjectiondump->clusterLammpsparse_dump_filelammpsInputsdump_file\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpsParseLogFilelammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterLammpsshelllammpsOutputsWithInjectionlog\n", + "\n", + "log\n", "\n", - "\n", - "\n", - "clusterLammpsShelllammpsOutputsran->clusterLammpsParseLogFilelammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpsparse_log_filelammpsInputslog_file\n", + "\n", + "log_file\n", "\n", - "\n", - "\n", - "clusterLammpsParseDumpFilelammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterLammpsshelllammpsOutputsWithInjectionlog->clusterLammpsparse_log_filelammpsInputslog_file\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpsShelllammpsOutputsran->clusterLammpsParseDumpFilelammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpsparse_log_filelammpsInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsShelllammpsOutputsoutput\n", - "\n", - "output\n", + "clusterLammpsparse_log_filelammpsOutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clusterLammpscollectlammpsInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterLammpsparse_log_filelammpsOutputsWithInjectionran->clusterLammpscollectlammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsShelllammpsOutputsdump\n", - "\n", - "dump\n", + "clusterLammpsparse_log_filelammpsOutputsWithInjectionlog\n", + "\n", + "log\n", "\n", - "\n", - "\n", - "clusterLammpsParseDumpFilelammpsInputsdump_file\n", - "\n", - "dump_file\n", + "\n", + "\n", + "clusterLammpscollectlammpsInputsout_log\n", + "\n", + "out_log\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsShelllammpsOutputsdump->clusterLammpsParseDumpFilelammpsInputsdump_file\n", - "\n", - "\n", - "\n", + "clusterLammpsparse_log_filelammpsOutputsWithInjectionlog->clusterLammpscollectlammpsInputsout_log\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsShelllammpsOutputslog\n", - "\n", - "log\n", + "clusterLammpsparse_dump_filelammpsInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsParseLogFilelammpsInputslog_file\n", - "\n", - "log_file\n", + "clusterLammpsparse_dump_filelammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", + "\n", "\n", - "clusterLammpsShelllammpsOutputslog->clusterLammpsParseLogFilelammpsInputslog_file\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterLammpsParseLogFilelammpsInputsrun\n", - "\n", - "run\n", + "clusterLammpsparse_dump_filelammpsOutputsWithInjectionran->clusterLammpscollectlammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsParseLogFilelammpsOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterLammpsCollectlammpsInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterLammpsParseLogFilelammpsOutputsran->clusterLammpsCollectlammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "clusterLammpsparse_dump_filelammpsOutputsWithInjectiondump\n", + "\n", + "dump\n", "\n", - "\n", - "\n", - "clusterLammpsParseLogFilelammpsOutputslog\n", - "\n", - "log\n", - "\n", - "\n", - "\n", - "clusterLammpsCollectlammpsInputsout_log\n", - "\n", - "out_log\n", + "\n", + "\n", + "clusterLammpscollectlammpsInputsout_dump\n", + "\n", + "out_dump\n", "\n", - "\n", - "\n", - "clusterLammpsParseLogFilelammpsOutputslog->clusterLammpsCollectlammpsInputsout_log\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpsparse_dump_filelammpsOutputsWithInjectiondump->clusterLammpscollectlammpsInputsout_dump\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clusterLammpsParseDumpFilelammpsInputsrun\n", - "\n", - "run\n", + "\n", + "\n", + "clusterLammpscalc__calculator_GetAttr_modelammpsInputsrun\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsParseDumpFilelammpsOutputsran\n", - "\n", - "ran\n", + "clusterLammpscalc__calculator_GetAttr_modelammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "\n", - "clusterLammpsParseDumpFilelammpsOutputsran->clusterLammpsCollectlammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "clusterLammpscalc__calculator_GetAttr_modelammpsInputsname\n", + "\n", + "name\n", + "\n", + "\n", + "\n", + "clusterLammpscalc__calculator_GetAttr_modelammpsOutputsWithInjectionran->clusterLammpscollectlammpsInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterLammpsParseDumpFilelammpsOutputsdump\n", - "\n", - "dump\n", + "clusterLammpscalc__calculator_GetAttr_modelammpsOutputsWithInjectiongetattr\n", + "\n", + "getattr\n", "\n", - "\n", - "\n", - "clusterLammpsCollectlammpsInputsout_dump\n", - "\n", - "out_dump\n", + "\n", + "\n", + "clusterLammpscollectlammpsInputscalc_mode\n", + "\n", + "calc_mode\n", "\n", - "\n", - "\n", - "clusterLammpsParseDumpFilelammpsOutputsdump->clusterLammpsCollectlammpsInputsout_dump\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpscalc__calculator_GetAttr_modelammpsOutputsWithInjectiongetattr->clusterLammpscollectlammpsInputscalc_mode\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterLammpscalc__calculator_GetAttr_modelammpsInputsrun\n", - "\n", - "run\n", + "clusterLammpscollectlammpsInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterLammpscalc__calculator_GetAttr_modelammpsOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterLammpscollectlammpsOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "\n", - "clusterLammpscalc__calculator_GetAttr_modelammpsInputsname\n", - "\n", - "name\n", + "\n", + "\n", + "\n", + "clusterLammpscollectlammpsOutputsWithInjectiongeneric\n", + "\n", + "generic\n", "\n", - "\n", - "\n", - "clusterLammpscalc__calculator_GetAttr_modelammpsOutputsran->clusterLammpsCollectlammpsInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterLammpscalc__calculator_GetAttr_modelammpsOutputsgetattr\n", - "\n", - "getattr\n", - "\n", - "\n", - "\n", - "clusterLammpsCollectlammpsInputscalc_mode\n", - "\n", - "calc_mode\n", - "\n", - "\n", - "\n", - "clusterLammpscalc__calculator_GetAttr_modelammpsOutputsgetattr->clusterLammpsCollectlammpsInputscalc_mode\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterLammpsCollectlammpsInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterLammpsCollectlammpsOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterLammpsCollectlammpsInputsbla\n", - "\n", - "bla\n", - "\n", - "\n", - "\n", - "clusterLammpsCollectlammpsOutputsgeneric\n", - "\n", - "generic\n", - "\n", - "\n", - "\n", - "clusterLammpsCollectlammpsOutputsgeneric->clusterLammpslammpsOutputsgeneric\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterLammpscollectlammpsOutputsWithInjectiongeneric->clusterLammpslammpsOutputsWithInjectiongeneric\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, "execution_count": 10, @@ -2882,7 +2706,7 @@ } ], "source": [ - "wf.draw(depth=2)" + "wf.draw(depth=2, size=(10, 10))" ] }, { @@ -2904,18 +2728,15 @@ "output_type": "stream", "text": [ "T= 300\n", - "Collect: md \n", "T= 600\n", - "Collect: md \n", "T= 900\n", - "Collect: md \n", - "CPU times: user 476 ms, sys: 277 ms, total: 753 ms\n", - "Wall time: 4.84 s\n" + "CPU times: user 1.05 s, sys: 375 ms, total: 1.42 s\n", + "Wall time: 7.98 s\n" ] }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjMAAAGdCAYAAADnrPLBAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAADUO0lEQVR4nOydeXgUZfa2n+olnYVsJCGEkAXCFpbIqgQQBAERFFBRcUERR8cZGR3cUccF59OfCDPooDIqgxuKAiKCCMgWBNlX2beQBEISkpA96fRS3x9v3urqpDvppao7gXNfV650uitdb9LdVU+d85xzBFEURRAEQRAEQbRQNP5eAEEQBEEQhDeQmCEIgiAIokVDYoYgCIIgiBYNiRmCIAiCIFo0JGYIgiAIgmjRkJghCIIgCKJFQ2KGIAiCIIgWDYkZgiAIgiBaNDp/L8AXWK1W5ObmIjQ0FIIg+Hs5BEEQBEG4gCiKKC8vR7t27aDROI+/XBNiJjc3FwkJCf5eBkEQBEEQHpCTk4P27ds7ffyaEDOhoaEA2D8jLCzMz6shCIIgCMIVysrKkJCQIJ3HnXFNiBmeWgoLCyMxQxAEQRAtjKYsImQAJgiCIAiiRUNihiAIgiCIFg2JGYIgCIIgWjTXhGeGIAiCuHYRRRFmsxkWi8XfSyHqodVqodPpvG6bQmKGIAiCuGqpra3FpUuXUFVV5e+lEE4IDg5GXFwcAgICPH4OEjMEQRDEVYnVakVmZia0Wi3atWuHgIAAapzajBBFEbW1tbh8+TIyMzPRuXPnRhvjNQaJGYIgCOKqpLa2FlarFQkJCQgODvb3cggHBAUFQa/XIysrC7W1tQgMDPToecgATBAEQVzVeHq1T/gGJV4feoUJgiAIgmjRkJghCIIgCKJFQ2KGIAiCIIgWDYkZgiAIgmgmCILQ6NfUqVPdfs6TJ09i+PDhiI2NRWBgIDp27IhXX30VJpPJbruMjAz069dP2mbBggUNnmv58uXo3r07DAYDunfvjhUrVtg9PnXqVEycONHuvmXLliEwMBCzZ892e+2uQtVMBEH4nFJjKZadWoZxHcehbUhbfy+HIJoNly5dkm5/9913eO2113Dy5EnpvqCgILefU6/X46GHHkLfvn0RERGBQ4cO4bHHHoPVasXbb78NAMjMzMTYsWPx2GOP4euvv8b27dvx17/+FTExMbjrrrsAADt27MC9996Lt956C3fccQdWrFiBe+65B9u2bcMNN9zgcN+fffYZnnzySXz44Yf405/+5PbaXYXEDEEQPmflmZWYt38e8irz8MrAV/y9HOIaQRRFVJv80wU4SK91qcdN27Y2cR8eHg5BEOzu84SOHTuiY8eO0s9JSUnYsmULfvvtN+m+BQsWIDExEfPmzQMApKamYu/evZgzZ44kZubNm4dRo0Zh5syZAICZM2ciIyMD8+bNw7fffttgv7Nnz8Zrr72Gb775RnoOtSAxQxCEz7livAIAuFx92c8rIa4lqk0WdH9tnV/2fWzWLQgOUO6U26NHD2RlZTl9PCkpCUePHnX42JkzZ7B27Vrceeed0n07duzA6NGj7ba75ZZbsHDhQphMJuj1euzYsQMzZsxosA0XQHJeeuklfPjhh1i9ejVGjhzpxl/mGSRmCILwOVUm1lq+xFji34UQRAtlzZo1DTwvcvR6fYP7Bg0ahP3798NoNOLxxx/HrFmzpMfy8vIQGxtrt31sbCzMZjMKCwsRFxfndJu8vDy7+3755ResXLkSGzduxIgRIzz589yGxAxBED6n2lwNgHlnCMJXBOm1ODbrFr/tW0mSkpLc/p3vvvsO5eXlOHToEJ5//nnMmTMHL7zwgvR4/TSYKIoN7ne0Tf370tLSUFhYiNdeew0DBgxAaGio22t1FxIzBEH4HC5myoxlfl4JcS0hCIKiqR5/4kmaKSEhAQDQvXt3WCwWPP7443j22Weh1WrRtm3bBhGWgoIC6HQ6REVFAYDTbepHa+Lj47F8+XIMHz4cY8aMwdq1a1UXNFfHq0oQRIuiyszSTKW1FJkhCE/wJM0kRxRFmEwmKfqSnp6OVatW2W2zfv169O/fX3qu9PR0/Prrr3a+mfXr12PQoEENnj8xMREZGRkYPnw4Ro8ejXXr1iEsLMzlv89dSMwQBOFzeGTGaDGixlyDQJ1nw+UI4lrFnTTT4sWLodfr0atXLxgMBuzbtw8zZ87EvffeC52OyYAnnngC8+fPxzPPPIPHHnsMO3bswMKFC+2qlJ5++mkMHToU7777LiZMmICVK1diw4YN2LZtm8P9tm/fHlu2bLETNOHh4d794U6gpnkEQfgcbgAGyDdDEGqj0+nw7rvv4vrrr0daWhreeOMNPPnkk/jss8+kbTp06IA1a9Zgy5Yt6N27N9566y188MEHdiXVgwYNwpIlS7Bo0SKkpaXh888/x3fffee0xwzAUk4ZGRkoKSnBqFGjUFJSosrfKIg8xnQVU1ZWhvDwcJSWlqoa5iIIwjUm/DgB50rPAQCW3b4MXVt39fOKiKuRmpoaZGZmokOHDggMpOhfc6Wx18nV8zdFZgiC8DncMwMAZbVkAiYIwjtIzBAE4XO4ZwagNBNBEN5DYoYgCJ9DnhmCIJSExAxBED7FZDXBZLWVlFJ5NkEQ3kJihiAInyJPMQEUmSEIwntIzBAE4VOqTSRmCIJQFhIzBEH4FHklE0BihiAI7yExQxCET2mQZiLPDEEQXkJihiAInyKvZAIoMkMQhPeQmCEIwqfwyIxGYIcfEjMEYUMQhEa/pk6d6tHziqKIOXPmoEuXLjAYDEhISMDbb79tt01GRgb69euHwMBAdOzYEQsWLGjwPMuXL0f37t1hMBjQvXt3rFixwu7xqVOnYuLEiXb3LVu2DIGBgZg9e7ZHa3cFVcXM+PHjkZiYiMDAQMTFxWHKlCnIzc11uG1RURHat28PQRAazG5w5UUgCKJlwMVMdFA0AOoATBByLl26JH3NmzcPYWFhdve9//77Hj3v008/jc8++wxz5szBiRMnsGrVKlx//fXS45mZmRg7dixuvPFGHDhwAC+//DKeeuopLF++XNpmx44duPfeezFlyhQcOnQIU6ZMwT333INdu3Y53e9nn32GBx54APPnz8cLL7zg0dpdQdWp2cOHD8fLL7+MuLg4XLx4Ec899xwmTZqE33//vcG2jz76KNLS0nDx4sUGjz399NNYv3495syZg169eqG0tBSFhYVqLp0gCJXgBuC4kDgUVBWg2lwNo8UIg9bg55URhP9p27atdDs8PByCINjd5wnHjx/Hxx9/jCNHjqBrV8dz0BYsWIDExETMmzcPAJCamoq9e/dizpw50rDJefPmYdSoUZg5cyYAYObMmcjIyMC8efPspmtzZs+ejddeew3ffPON3cBKNVBVzMyYMUO6nZSUhJdeegkTJ06EyWSCXq+XHvv4449RUlKC1157Db/88ovdc7jyIhAE0XLgkZk2wW2gFbSwiBaUGcsQExzj55URVz2iCNTzbPkMfTAgCIo9XY8ePZCVleX08aSkJBw9ehQAsGrVKnTs2BGrV6/GmDFjIIoiRo4cidmzZ6N169YAWNRl9OjRds9xyy23YOHChdI5e8eOHXbndb4NF0ByXnrpJXz44YdYvXo1Ro4c6eVf2zSqihk5xcXFWLx4MQYNGmQnZI4dO4ZZs2Zh165dOHfuXIPfc+VFqI/RaITRaJR+LiujMDZBNBe4AThYF4ywgDBcMV5BibGExAyhPqYq4O12/tn3y7lAQIhiT7dmzRqYTCanj8vPs+fOnUNWVhaWLl2KL7/8EhaLBTNmzMCkSZOwadMmAEBeXh5iY2PtniM2NhZmsxmFhYWIi4tzuk1eXp7dfb/88gtWrlyJjRs3YsSIEd7+qS6huph58cUXMX/+fFRVVWHgwIFYvXq19JjRaMR9992H9957D4mJiQ7FjCsvQn3eeecdvPnmm6r9TQRBeA6PzATpghBuCMcV4xUyAROEmyQlJbm8rdVqhdFoxJdffokuXboAABYuXIh+/frh5MmTUtZDqBc5EkWxwf2Otql/X1paGgoLC/Haa69hwIABCA0Ndf0P8xC3xcwbb7zRpFDYs2cP+vfvDwB4/vnn8eijjyIrKwtvvvkmHnroIaxevRqCIGDmzJlITU3Fgw8+6PS5XH0R5MycORPPPPOM9HNZWRkSEhLc/VMJglAB7pkJ1gcjzBAGgHrNED5CH8wiJP7at4K4k2aKi4uDTqeTzqEA88QAQHZ2Nrp27Yq2bds2iLAUFBRAp9MhKioKAJxuUz9aEx8fj+XLl2P48OEYM2YM1q5dq7qgcVvMTJ8+HZMnT250m+TkZOl2dHQ0oqOj0aVLF6SmpiIhIQE7d+5Eeno6Nm3ahD/++APLli0DYFOB0dHReOWVV/Dmm2+69CLUx2AwwGAgMyFBNEfsIjMB4QCAMiOlggkfIAiKpnr8iTtppsGDB8NsNuPs2bNISUkBAJw6dQqALcKTnp6OVatW2T3H+vXr0b9/f+m50tPT8euvv9r5ZtavX49BgwY12H9iYiIyMjIwfPhwjB49GuvWrUNYWJiHf23TuC1muDjxBC5WuJ9l+fLlqK62dQPds2cPpk2bht9++036h7vyIhAE0XKQe2bCDUzMUJqJINzDnfPfyJEj0bdvX0ybNg3z5s2D1WrFk08+iVGjRkmBgieeeALz58/HM888g8ceeww7duzAwoUL7aqUnn76aQwdOhTvvvsuJkyYgJUrV2LDhg3Ytm2bw/22b98eW7ZssRM04eHh3v3hTlCtz8zu3bsxf/58HDx4EFlZWdi8eTPuv/9+pKSkID09HQCQkpKCnj17Sl8dOnQAwCIvbdq0AWD/Ihw4cAD79u3Dn//8Z7sXgSCIloMUmdEH2cQMpZkIQjU0Gg1WrVqF6OhoDB06FOPGjUNqaiqWLFkibdOhQwesWbMGW7ZsQe/evfHWW2/hgw8+sCupHjRoEJYsWYJFixYhLS0Nn3/+Ob777jvccMMNTvcdHx+PjIwMlJSUYNSoUQ36yCmFagbgoKAg/PDDD3j99ddRWVmJuLg4jBkzBkuWLHErBcRfhL/97W8YOnQoQkJCcOutt2Lu3LlqLZ0gCBWRPDMUmSGIRpk6darHHX/r065dO7sGeI4YNmwY9u/f3+g2kyZNwqRJk5w+/vnnnze4Ly4uDidOnHBpnZ6impjp1auX02ojZ9x0001SKkqOKy8CQRAtA0eemRJjiR9XRBBES4dmMxEE4VO4Z4aXZgNkACYIwjt81jSPIAgCsEVmgvXBsIgWAOSZIQjCO0jMEAThU+RpJg1ocjZBEN5DYoYgCJ8iRWZ0wQjUBgIgMUMQhHeQmCEIwmdYRatdZEanYYegKnMVTBYT9Fp9Y79OEAThEDIAEwThM2rMNdLtYH0wQgNCIYDNdSHfDEEQnkJihiAIn8F7zAgQEKgNhEbQ2OYzUaqJIAgPITFDEITPqDbZUkx80i7vNUNihiAITyExQxCEz+CRmSBdkHQfdQEmCMJbSMwQBOEz5D1mOFKaiTwzBAFBEBr98nS8wffff4/evXsjODgYSUlJeO+99xpsk5GRgX79+iEwMBAdO3bEggULGmyzfPlydO/eHQaDAd27d8eKFSvsHp86dSomTpxod9+yZcsQGBiI2bNne7R2VyAxQxCEz3AYmaE0E0FIXLp0SfqaN28ewsLC7O57//333X7OX375BQ888ACeeOIJHDlyBB999BH+9a9/Yf78+dI2mZmZGDt2LG688UYcOHAAL7/8Mp566im7UUI7duzAvffeiylTpuDQoUOYMmUK7rnnHuzatcvpvj/77DM88MADmD9/Pl544QW31+4qVJpNEITPkHtmOBGGCAAkZggCANq2bSvdDg8PhyAIdvd5wldffYWJEyfiiSeeAAB07NgRL774It599108+eSTEAQBCxYsQGJiIubNmwcASE1Nxd69ezFnzhxpcva8efMwatQozJw5EwAwc+ZMZGRkYN68efj2228b7Hf27Nl47bXX8M0339hN31YDEjMEQfgM+cRsDnlmCF8hiqKU6vQ1ctO7EvTo0QNZWVlOH09KSsLRo0cBAEajEcHBwXaPBwUF4cKFC8jKykJycjJ27NiB0aNH221zyy23YOHChTCZTNDr9dixYwdmzJjRYBsugOS89NJL+PDDD7F69WqMHDnSw7/SdUjMEIQfOF50HDnlORidPLrpja8i5A3zOJKYIc8MoTLV5mrc8M0Nftn3rvt32XnFvGXNmjUwmUxOH9frbQ0ob7nlFsyYMQNTp07F8OHDcebMGUmAXLp0CcnJycjLy0NsbKzdc8TGxsJsNqOwsBBxcXFOt8nLy7O775dffsHKlSuxceNGjBgxwsu/1DVIzBCEH3jxtxeRWZqJVZGrkBye7O/l+AyHBuAA6jNDEO6SlJTk8raPPfYYzp49i9tuuw0mkwlhYWF4+umn8cYbb0Cr1Urb1Y8ciaLY4H5H29S/Ly0tDYWFhXjttdcwYMAAhIaGurxWTyExQxB+IL8yHwCQW5l7TYmZKhOVZhP+I0gXhF33Ozerqr1vJXEnzSQIAt599128/fbbyMvLQ0xMDDZu3AgASE5OBsC8OvUjLAUFBdDpdIiKimp0m/rRmvj4eCxfvhzDhw/HmDFjsHbtWtUFDYkZgvAxFqtF8o5caydw+ZBJDhczZbVlflkTce0gCIKiqR5/4k6aiaPVahEfHw8A+Pbbb5Geno42bdoAANLT07Fq1Sq77devX4/+/ftLz5Weno5ff/3Vzjezfv16DBo0qMG+EhMTkZGRgeHDh2P06NFYt24dwsLC3P9DXYTEDEH4mEpzpXT7Ss0VP67E90il2XoqzSYIb3AnzVRYWIhly5bhpptuQk1NDRYtWoSlS5ciIyND2uaJJ57A/Pnz8cwzz+Cxxx7Djh07sHDhQrsqpaeffhpDhw7Fu+++iwkTJmDlypXYsGEDtm3b5nC/7du3x5YtW+wETXh4uOd/dCNQnxmC8DEVtRXS7WvtBO4oMsNLsytMFTBZnV9pEgThOV988QX69++PwYMH4+jRo9iyZQuuv/566fEOHTpgzZo12LJlC3r37o233noLH3zwgV1J9aBBg7BkyRIsWrQIaWlp+Pzzz/Hdd9/hhhucm6rj4+ORkZGBkpISjBo1CiUlJar8fRSZIQgfU2GyiZkrxmsrMuOomik0wJZLLzOWISooyufrIojmyNSpUz3u+CsnOjoaO3bsaHK7YcOGYf/+/Y1uM2nSJEyaNMnp459//nmD++Li4nDixIkm9+8NFJkhCB8jj8yUGEv8txA/wA3Act+CVqOVBA2VZxME4QkkZq4BTFYTrKLV38sg6pBHZkpqSvy3ED/gKDID2HwzZUYyARME4T4kZq5yai21uH3F7Zi2bpq/l0LUcU1HZhzMZgKoPJsgCO8gz8xVTm5FLi5WXMTFiouoNlcr3uuAcB+7yMw1JmYcGYAB6gJMEIR3UGTmKqfSZCsDzqvMa2RLwldcy2LGUdM8gCIzBEF4B4mZqxz5iZPETPNAnmaqNlfDaDH6cTW+xdE4A4B6zRDqwtvyE80TJV4fEjN+4EDBAby35z3pKlVNSMw0P8pry+1+9pcJuNpcjVJjKQqrC5FfmY/cilxV+7yIotikZ+Zai1QR6sI711ZVqX+sJTyHvz6Ouha7CnlmfMy50nP4y4a/oNJUidSoVNzW8TZV92eXZqoiMdMckL8mADuBx4bEOtlaHb469hXe2/MeRNhfEXWJ7IJlty9rMDhOCWqttVJVnTPPDFUzEUqi1WoRERGBgoICAEBwcLAq723CM0RRRFVVFQoKChAREWE39NJdSMz4kCpTFWZsniGdzIqqi1TfpzylwYcbEv6l3GQfmfFH47yMCxl2QkYn6GAWzTh15RSKa4pVaVwnj0Q69cyQAZhQmLZt2wKAJGiI5kdERIT0OnkKiRkfIYoiXv/9dZwrPSfd54u5PGQAdg2T1YTy2nK0Dmyt+r7kAhPwT2qluKYYALBg5AIMajcIgiBg5NKRyK/Kx8WKi6qIGe6XMWgN0Grsr8CceWYuV11GiD7kqhkOSPgeQRAQFxeHNm3aNDqYkfAPer3eq4gMh8SMj/j6+NdYe34tdIIOg+IHYeuFrT45iZFnxjH78vdh+8XtOFd6DudKzyGnLAdm0YxXb3gV93a7V9V9c4EZqA1EjaXGL54ZHhWMDoqWwu7xreKRX5WPC+UXkBaTpvg+nVUyAY6rmVaeWYnXf38dAdoAjEgcgXEdxiG9XTp0GjpsEe6j1WoVOWkSzRMyAPuAffn7MHfvXADA8wOex9D4oQB8H5m5VHmpWbj6TRYT3tvzHnZf2u2X/ZcaS/GndX/Cp398io3ZG5FZmgmzaAYA7M5Tf03cANw+tD0A30dmLFaLtE95BIav52LFRVX266zHDNAwzfTD6R/wj+3/gEW0oNpcjZ/P/Yy/bvwrbl56M2bvme0T8zzAIkOF1YU+2RdBtAREUcQXR7/A9ovb/b0UO0jMqExJTQmey3gOFtGCsR3G4r5u9yEiMII95uPITJW5qoFfwx9sztmML499idd/f90v4upS5SWYRTNC9CF4YcALWDByAf4x8B8AfBO94q+Jv8TMFeMVWEUrBAiINERK97dvxdZzoeKCKvt1VskE2MRMeW05lpxYwt4bEHFft/vw9divcV+3+xBpiERxTTG+OvYV1mSuUWWNcowWI+786U7cs+oeWKwW1fdHEC2Bffn7MGfvHPx141+xMWujv5cjQWJGZXZc2oHC6kK0b9Uer6e/DkGwnUB8YfysrLWvnGkOqabM0kwA7KSZXZ7t8/1frroMgJ28p3SfgsHxg9EzuicAILcyV9V9i6JoEzOt/CNmeIopMjDSzrsiRWbK1Y3MOBIzYQFh0u3/t+v/AQAeTH0QM6+fietirsPLN7yMjfdsxK0dbgXAOlurTW5FLkqMJbhcfRlFNeqb9QmiJXC48DAAwCpa8fzW57Hz0k4/r4hBYkZl+ImzV3QvycTIIzO+aBAmj8wAzUPMyAXMtovbfL5/njaIDoqW7osLiZMeq7XUqrZvo8UIs5WltKTIjI89M/zEXN/sHN8qHoB6kRlnDfMAQKfRoZW+lfTzw90fxgsDXrAro9Vr9EgJTwEAn6R+8qts1X8FVVQJQ7Qsai21qkS+jxQeAQBEGCJgsprw1KancPjyYcX34y4kZlSmsIYddOXeBB6ZKTGWqD7NmntmAjQBAJqHmMkqy5Ju+yPv6kjMRBgipIiBmiXsXFwKENAupB0A30dmeCVT/YolLmbyKvMkwaUkjRmAAZu4m9ZzGp7t/6zDfiAxwTEAgMvVlxVfX33k7wO5sCGI5k5hdSGGfz8cL/72ouLPfbTwKADg7SFvY2DcQFSbq/GXDX/B6SunFd+XO6gqZsaPH4/ExEQEBgYiLi4OU6ZMQW6u4/BwUVER2rdvD0EQUFJSYvfYunXrMHDgQISGhiImJgZ33XUXMjMz1Vy6YsirRjgRhggALExXvxus0vCTZ4fwDgCah5jJLrNFZvbk7fF5O39HYkYQBLQNYX0O1Ew18bLsVvpWiAy0iVpfwt+TUYH2YiYmOAYBmgBYRIsq75PGDMAAMHfYXCwYuQB/7/t3p43N+GtGkRmCcM7+/P0oqy1TvMiiuKZYOj72btMb7w9/H2kxaSirLcOff/0zcspzFN2fO6gqZoYPH47vv/8eJ0+exPLly3H27FlMmjTJ4baPPvoo0tIaloOeO3cOEyZMwIgRI3Dw4EGsW7cOhYWFuPPOO9VcumI4OnHqtXqE6EMAqF/RxCMzKREsPO/vK8yy2jLJK9Q6sDVqLDXYl7fPp2vgV/X8Kp/DU02XKi+ptm8uLkMCQvwnZurSTPUjMxpBg3atWLRIjVSTZADWO47MJIYlYnD84EY7tMYEsdfMJ2JGFpnh6WKCaAnwfmYlxhJFU008KpMclozQgFAE64Px0c0foVNEJ1yuvowPD36o2L7cRVUxM2PGDAwcOBBJSUkYNGgQXnrpJezcubNB46KPP/4YJSUleO655xo8x/79+2GxWPDPf/4TKSkp6Nu3L5577jkcOnSoRTRAciRmAFt0Ru0TGY8EdIroBMD/kRkelYkJisFNCTcBALbl+tY3I0Um6p3MJTFTob6YaaVvJb0HKk2VMFl8917mf7+jBoFqmoCbisy4AhegxTXFqlcYycd/+PsigCDc4VwJEzMW0aJoBeuRIuaX4QUTAKtE/GTUJ5jUZRJeT39dsX25i888M8XFxVi8eDEGDRpkN0zq2LFjmDVrFr788ktoNA2X079/f2i1WixatAgWiwWlpaX46quvMHr0aKdDqYxGI8rKyuy+/IUzMSNVNKkYmam11KLWysysPDLjbzHD/TKJYYkY3G4wAN+bgKXITJAfIjN14jI0IBShAaHQCOw978vojBSZCWzY5Zf7ZtToNdOUZ8YVIg2R0AgaWEWr5P1RC3lkhtJMREvibOlZ6baSBQY8MiMXMwC7yHg9/XWvPtveorqYefHFFxESEoKoqChkZ2dj5cqV0mNGoxH33Xcf3nvvPSQmJjr8/eTkZKxfvx4vv/wyDAYDIiIicOHCBSxZssTpPt955x2Eh4dLXwkJCYr/Xa7grDkZAJ/0mpE3zJOLGX82zuORmaSwJAxsNxBaQYvM0kzVGrU5wpnA5CkWNcUM90iF6EOgETRSdMaX85mKqx0bgAFZr5ly5dNMjVUzuYpWo5VEmNomYPLMEC0Ri9WC86XnpZ+VOseIoihVMvWI6qHIcyqJ22LmjTfegCAIjX7t3btX2v7555/HgQMHsH79emi1Wjz00EPSyXTmzJlITU3Fgw8+6HR/eXl5+NOf/oSHH34Ye/bsQUZGBgICAjBp0iSnJ+WZM2eitLRU+srJ8Y8piTcn0wgau+ZkgH1Fk1rwlEaQLgjtQtpBgIBaa63qV7SNkVVeF5kJTURYQBiui7kOgO+qmipNldJJtX5khhuA1RQzXGCG6kMBOG7jrzbO0myAul2AG2ua5w6+MAHXmGvsPpvkmSFaChcrLkoReUC5c0x+VT6KaoqgFbTo2rqrIs+pJG4POZk+fTomT57c6DbJycnS7ejoaERHR6NLly5ITU1FQkICdu7cifT0dGzatAl//PEHli1bBgCSOImOjsYrr7yCN998Ex9++CHCwsIwe/Zs6Tm//vprJCQkYNeuXRg4cGCD/RsMBhgMBnf/NMXhB9tIQ2SDwXo8MqPmFTk/cYboQ6DX6hEVFIXC6kLkVeWpMkjQFeSRGQAYHD8Y+wv2Y9vFbbin6z2q75+/JkG6oAYRArlnRhTFRo2onsLz160CWE+VSEMkMpHpk9EWAPuMSaXZjaSZ1DAAK+GZAVhI+3jxcVUFBo/E8Gni5aZyVJmqaOClguzP348vjn6BVwa+gjbBbfy9nKsG+TBjQDkrA4/KdIro5Nd0kjPcFjNcnHgCFytGIyvFXb58Oaqrq6XH9+zZg2nTpuG3335DSgpLi1RVVTUYDsZ/tlrV7dHiLc7SGYDMAKxiwzR5GTAAtA1uy8RMZZ7fwoRyzwzAxMx/DvwHuy7tgsligl7r2AelFPwEWD8qAwCxIbFS9Kqopsjh6+Yt9V8THpnxlWemrLZMmkPlSMzwyExxTbHiJ28lPDOA7bVTM83EU0ztQ9ujoKoAVeYqFFQVIDk8WbV9Xmu8u+ddHCs6hl4xvfCnXn/y93KuGs6WnLX7WaljCxcz9f0yzQXVPDO7d+/G/PnzcfDgQWRlZWHz5s24//77kZKSgvT0dABASkoKevbsKX116MB6oaSmpqJNG6bUx40bhz179mDWrFk4ffo09u/fj0ceeQRJSUno06ePWstXBFfEjK8iM4AtjeIvE3BJTQnKapkZOyGU+ZhSW6eidWBrVJmrcPDyQdXXwJsYOnpN9Bq9VC2j1v+IvyZSZMbH5dk8xRQaEOpQOIYGhEqjBZRONTU2zsAdfJFm4q9/bHCsFDUg34xynC89j2NFxwCo26TyasVkMTm1WfDIjAAWWVbq2HK0iJl/e0Q3P78MoKKYCQoKwg8//ICbb74ZXbt2xbRp09CzZ09kZGS4lQIaMWIEvvnmG/z444/o06cPxowZA4PBgLVr1yIoqPmFuuTwg62jlI50ElMzMmOqF5mpEzP+Onhwv0yb4DbSCU0jaKSqpt8u/qb6GgqrnIsZQP2KJm4A9ldkprFKJg6PzihtAuaeGW+jPVJkRsU0E4/MxIbIxEw1iRml+CXzF+k2iUT3OFhwEH2/7otFRxc5fJyXZXeJ7AJAmTSTKIqSmOkZdY1FZnr16oVNmzahqKgINTU1yMzMxMcff4z4+Hinv3PTTTdBFEVERETY3T958mTs378fFRUVKCgowMqVK9GtWze1lq4Yjrr/cnzRZ6a5RWbq+2U4g+OZmPGFCbixaBkAacSAWoMMJYEp88wAvpvP1Jj5l6NWebZikZlg9SMzXPBTZEZ5RFG0m3rui9EUVxO8lcWPZ35s8JgoilJkpl9sPwDKnGOyy7NRXluOAE0AOkV28vr51IBmM6lIYydOX1Yz8RNnbEgsAPtmYL5E8suE2pfhD2o3CAIEnLpySvUThrPuv5y2rdQVfPWjZb5qnshxKTLDy7MVNgFzz4zXBmAfembahrQlMaMwx4qP4XzZeelnakjoHvwiI7M0s4Ggz6/KR5W5CjpBh7QY1lFficgM98t0a90Neo26vkZPITGjIo16ZmSTs9XqZMrNplJkJrh5RmYiAyMlU9mWnC2qrsHZXCIOj8yolWaqbwD2uZhxITKjVhdgpSIz8pEGavVMIs+MevxyjqWY+rbpC4C9J9Xu5nw1IY8a783ba/cYN/8mhiVK5x0l2j5I/WWaqV8GIDGjKvwq2JGY4V4JEaJkilUayWxazzNTUFXgl4OH1GMmrGGDxFuSbwEArDi9QtU1NBWZ4Z4Z1dJMtfbRMl80T5TDy7IdjTLgqFGebbKaYLKykQ3eema4EDNZTar153HkmaEIgvdYRSt+Oc/EzIPdH4RW0MIiWqRjJdE08s/lnrw9do/xFFNKRIqiRSaSX6aZVjIBJGZUpTEDsF6jlxqnqVXRJA01rIvMxATFSAcPX+epRVG0RWZCkxo8fnvK7dBpdDhSdAQni0+qto6mPDNq+4r4a8Jfe1+U6MtxKzJTcVGxyAePygDeR2YCtAHSxYAa7+Nai62xpDwyQ43zvGdf/j4UVBUgVB+KYe2HSe9Dinq5Rq2l1u59uCffXszwyEyH8A5SkUmpsRRW0fM2JmarGceLjgNovuZfgMSMahgtRqlyxVlKQ7oqV+lEVj8yo9VopQNz/ZP1ieIT2HphqyrrAFhEoMJUAQECEsIajpdoHdgawxOGAwB+OP2DKmswW81S/tipAbhupMEV4xXJ46EUFqtFqugJCWACk4uZclO5FLlQE1c8M3EhcRAgoNpcrVi36GoTEzM6QadIzl1N3ww/sQZoAhBhiEBsMPOaXa667NVJgYBk/B2ZNBIB2gDpf0tRL9e4VHkJIkQEaAIgQGjgm5EiM+G2yIxFtEjnIk84V3oONZYahOhDmnWfJRIzKsGvgPUavdS3oz5qm4ClyEzdiROQRR5kJuAqUxUeXfcopm+cjpxydUY/ZJdnS/s3aB2X5t/Z+U4AwOpzq2G0GBVfQ3FNMUSIDsdLcEIDQiXxp7RRmr8egC0yExYQJvWD8MVIA1ciMwHaAEn0KpVqko8yUKKzspq9ZuQpJkEQEBUUBQECzKLZr6NAWjomiwnrz68HAIztOBYArgo/kiiKmPnbTPxt499UswxwuPk3MSxRGinAfTOiKEqRmY4RHRGgDZCi8t6cY/hwye5R3aXBuM2R5ruyFo48neHs4K22X6Ky1j4yA9hMwPJeM6vPrUZZbRlE2FJBSlO/868j0uPS0TakLcpqy7Axa6Pia+BX8VGBUQ3GS8iRBF+FsmKGR8oMWoPUsE6r0SLMwMSu2mJGFEWXIjOA8iZgyfyrV6Y3lJq9ZuRl2QC7IKF0iPf8nvs7ymrLEB0UjQGxAwCoJ2ZMFhOOFx1XXVwA7EJt9bnV2HJhC/7y618kX5wacDHTrlU79I/tD8DmmymqKUJZbRkECEgOSwagTIFBcx4uKYfEjEo05c0AZF2AVZrLU98zAzT0hIiiiG9PfCs9rtbBujG/DEer0eKOTncAUCfV1FjfHzk81ZRbqawJWD4xWw6PEqk9n6nKXCVFvBozAAPK95pRqiybo2avGXlkhuOLRn1XOz9n/gwAGJM8RrqYUEvM/Gvfv3DP6nsw+NvBuG3FbXhx64v48uiXDeYWKcH+/P3S7cOFh/GXDX+RLlyUhhcmxLeKx4C2TBBy30xmaSYAdiESqAsEoIwnj3dqbs6VTACJGdVozPzLUbsst75nBpD1mqkTM3vz9+JMyRnpcbV60LgSmQGAiZ0mQoCAXXm7FE958RNRU2JGrS7Akvk3INTufl91AeZiztGQzfpIXYAVSjMpVZbNUdMzI4mZYJuYIW+Hd1SZqqS2C2M7jJXuV+P/WmWqwooztqrIrLIsrMlcg/f2voe7Vt6F709+r2hJ/4GCAwCAmxJuQmhAKA5ePoi/bvir4p47wBYpjW8Vj36x/ex8M1KKKbyjtL0SA41zKnIaPG9zhMSMSrgSBeBuc7UjM3ZppnqRGR6V4aZM1SIz5Y57zNSnXat2SG/HZncpXabNBaazsmwO/x9dqlBWzDgSl4BvGigCrpl/ObxxnlJpJrlnRgl8mWYC1IkgmK3mZtVfRRRFfHP8Gxy+fFjx5864kIFqczXat2pvV96rxv/116xfUWmqREJoAjLuzcCCkQswvfd09I/tD7Noxls738KbO95EraVWkf1xMXN3l7vx6ehPEaoPxf6C/fjrxr8qHqHhkdL4VvEIN4SjW2vWCX9v3l47vwzH2w7jlaZKKf3Ne3A1V0jMqERjPWY4akZmLFaLdDXszACcV5mHTdmbAAD3dr0XgDpiRhRFlyMzAHBHZ5ZqWnlmJcxWs2LrkHtmGkOtxnn15zJxfNVrxhXzL0e1yIxCnhlfGYA5Sp90zVYz7vrpLkz+eXKzqZDakrMF7+x+BzO2zFB8TXwW060dbrXzEPILCyWPOzwqM7HTRLQObI3B8YPx5+v+jP/d8j/8ve/fIUDA8tPL8ci6R6T9iqKIouoi7M3bKxleXaGougjny85DgIDrYq5Dj6ge+O+o/6KVvhX25e/DyKUjMWvHLPxx+Q9FokFyzwwA9G9r883wNJNdZMbLcwxPa4UFhEm9sZorJGZUQvLMBDYSmVHxirzSbLsicGQALqwuxOLji2ERLRjQdgAGtRsEQJ0hlIXVhag2V0MjaJDQqmFZdn1GJIxAhCECBdUF+D33d8XWwU/mTUVm4lqpk2aqPzGb46teM011P5bDPTN5lXmKCEqlPTP8NVQjzcSjlvyzAigvZrLLsnGu9BxOFJ9oNhVSWy5sAcD+RiWjM+W15dI8oTEdxtg9xqNflaZKRaIYWWVZ2Je/DxpBg/Ep4+0eEwQBj/Z6FB+P/BihAaE4fPkw7ll1Dx5Y8wCGLBmCm76/CY+sewSTf57scq+rgwUHAQCdIjtJ6eJeMb3w31H/RUJoAipMFVh6ainuX3M/7vzpTnx74luPhWK1uVq6SOafT26k3pO/B2dLWWQmJTxF+h2lxAzfX3OGxIxKuGQAVvGKnFcy6TV6BGgDpPtbB7ZGgIb9zFNM93W7T7oKVSMyw6MycSFxUhVPYwRoA3B7yu0AgOWnliu2Dn7ic9Uzk1+Zr2gawGlkRsFOnY0hpZlciMxEB0UjQBMAi2hRpIGgWp6ZanO1oqF8k9UkfXYdRmYUmpydWZYp3W4OPhyraEVGTob0869Zvyr23JuyN8FkNSElPAWdIzrbPRaiD5EM8Uoce1aeWQmAzXvjUej6DI4fjCXjlqBTRCcU1RTh8OXDUhUQT7dzkdIU+wuY+ZePZuCkxaRh9R2rsXD0QtzW8TYYtAacKTmDt3e97fJz14envVvpW0ntPvrG9m3Qb6ZDeAfpd7y1MtSPBDVnSMyohCsGYDWrWBz5ZQB2dcIP0kaLEbHBsRieMFy6QrpivKJ4jxdX/TJyeFXT1gtb7brHeoPkmQlqPDLDOyWbRbOiaYz6gz85XMyoXZrNIzNNVTIBgEbQID5UubEG3DOjVGQmWB8sPZcnvhmTxYR3d7+Lzdmb7e4vrCqECBE6jc7u/6R0ZIanBAB1oqHucqzomN1IgQ1ZGxQzyfLxBbd0uMVhmwql/rcWq0USM/z44YzEsEQsHrsY/2/I/8OcYXOw7PZl2P3AbjzU/SEAwMkrrkVmuF+mT5s+DR7TCBpcH3c93rnxHWy6ZxN6x/QGACmC4i78cxjfKl76P8p9MwCLdMmPL95GZnh0ml/gNWdIzKgAz78CjYsZHpYsqy1T1BsC2FIa9cuAAdhdsdzb9V7oNDqEBYRJzeyUjs44m5bdGJ0iOiE6KBpm0SyVBnqDKIouCUyAlYhzcadkqqn+kEmOEhUHrsDTGa5EZgBZebYCJmClPTOAd6mmDdkb8PXxr/Ha76/ZdV6WVzLJG4TxE26psRQ15hpvlg0AOF96vsE+/QmvNLox/kYE6YKQW5mryOfuSs0V7MrdBYCVZDtCKTGzPXc7CqoLEGGIwE0JNzW5fbA+GONTxuOW5FvQtXVXBOoC0SWyCwDg1JVTTf5+lalKavPvSMzICQsIk4zPnvby4imf+lES7psB2EwmOVJkxsNji9xw3NwhMaMClaZK1FjYAa8xfwIXM4DyV+XOogCAzQug1+ilrruCIEgncKXFDC+xdicyIwjMUAcAhy4f8noNFaYKKeLUVJoJUMc346w022eRGTeqmQBbRZMikRmFPTOAdyZg3misxFiCXZd2Sffz1gTySiaAnYwCtax3hxIVVOfLzku3m0Nkho8yuSX5FtwYfyMAZVJNG7I3wCyakdo61S79IUep8uwfz/wIALit4212qXV34F11T1051aS35UjhEZhFM2KDY12KXCSEMr+gp2LGmbDgvhmgYfk0P8d4emxxJqCaIyRmVIAfXEP0IY328+AREUB534yjhnkcXrp3a4db7a7S1Wpg5U4lkxxJzBR4L2b41XsrfSuXfBtq9JrhkRl/Nc1zp5oJsIlPeR8iT1HaMwN4V54tnzbMK20Ax2XZABPXSvlmRFG0SzOp1dvJVfIq83C8+DgECBgSPwSjkkcBYGLG21TT2sy1ABoaf+UocdwprinG5hyWMuTVkJ6QFJaEAE0Aqs3VTUYk5X4ZV0Z08M8TT7u7izMxw30zgH1ZNmBfZOKJ8Zgf/ygyc43iivmXw8OASosZR6MMOPd3ux9vDX4Lr9zwit39/KCi9JUiN67xK31XkUdmvD2outr9l8PFDL8yUQJn0TJ+9VReW65IuvGzPz7DjM0zGqRD3I3MdI/qDgA4Vuh9uoGLmaaa9bmDp5GZy1WX7SIjm7I3SVE7R2XZHKXKiK8Yr9i12fd3ZIZHZdJi0hAVFIWh8UNh0BqQXZ7tUrrFGZerLkui8ZbkW5xup4SY+fnczzBbzegR1UNKFXmCTqOTUjVN+WYkv0xs4ykmDk+z55TneCQsnImZcEM4rm97PbSCVvLlcHjU1ypa3R42WWWqklLTPFLdnCExowKFNXXeDBdOGmqV5TYWmQnWB2Nip4kNTixqdOM0WUwoN9VND3cxIsDpHtUdOkGHopoir9vqu9r9l8M/vEpU8nCkNJPecQdgEaLXs2QKqwsx/8B8bMjeIPUQAoAac43ko3L1dejWuhs0ggYF1QVen8CVbpoHeO6Z2ZvPBvN1jeyK2OBYVJgqpNJhZ5EZQLnIpTwqA/jfM5NxgVUxDWs/DAA7PvBWDRuyN3j8vOuz1kOEiLSYtEav7L39v4qiKI0/acr46wqu+GbMVrNUlVS/kskZca3ioBN0MFqMHv2tjaV85t40F8vHL0fnSPtqMb1WL13Quhv55VGZUH2o02HJzQkSMyrgThRASjEobP501m22MdQoz+bKXifoGnhFmiJQFyg59b31zbhaycRRI83kbDaTTmP733gboVtzbg0sIisnl5+IeFRGr9G7/J4I1gdLOXhvzaBSZEZBz4ynIw34lOHr466XTKk8HdJYZEYpsc/Nv3wYYH5lvqLt9d2h2lwteYaGJQyT7h+VVJdqOu+5b2bd+XUAgFuTb210O2//r6dLTuNMyRkYtAbc2rHxfbkC98001mvm9JXTqDJXoZW+FTpFdHLpeXUanVQhyFPvrlJpqpSODY6EYbghvIH5V/4Y4P6xpSWVZQMkZlTBHTGj1lweKTIT0DAy4wwpzaTglSIXMxGBER6Nj7+ujTImYFcrmTiSmFFwpAEXmI5Enbdtxzmrzq2Sbm+7uE1KNRVX2yqZXMnvc/ikXD4511O4AVjJyIyUZqpyL83EB/MNiB2AWzuwk1/GhQxUmaqkSJwvIjPXt70eAFBrrVW9+7Mzdl3aBaPFiLiQOLseMMMShkGn0eFs6VmcK3F/OOOliks4UHAAAgSMTh7d6Lb8/1pUXeRRX6ecMlZg0CWyiyIRBFciM9wvc12b66Shma7AU03u+ma4sAg3hLvdidfT5qz82NcSUkwAiRlV8MQzo7T505PIjBoGYC5mXOlt4gjum/G2I6mrc5k4XMyUm8rdzjU7QhRFp6XZgDKjLU4Wn8SJ4hPQaXSICYpBtbla6qDsrl+Gw8tJjxa53uLdEaoagN2IzBRWFyKzNBMCBPSN7YvuUd2REJqAanM1NmZvtDXMa0TMeFvNxP06XSK7SJ8Lf6Wa5CkmucgNCwjDwLiBADyrauJRmX6x/aT/mzOiAqOgFbSwiBa7Xjeu4s7x1hW6RrLIzIWKC9Jntj7cL+NqiokjmYDdrGiSD5h0F6n1g5vnmIuVLacsGyAxowrufLjUms/krHKmMfgB/HLVZcVmsyglZk4Wn/Sqv4er3X85wfpgKWrmafWBnBpLDcwiM/c6urJSohv0qrMsKnNT+5skw+XG7I0A3K9k4vDIzLGiY16lQtQwAHNhWlZb5nKjR55i6ta6G8IN4RAEQUo18fEeWkHr8H2iVOSSi5nk8GRbikVlE3ClqRIfHvwQ2y9ul15HURSxNYeZf+UpJs7oJBZR8cQ3wwUQj3w1hlajld6XnghF/tl2NYXcFBGBEdJr7aiSTxRFHMh33iyvMTwtz86t9HysgKeRGcmj08wHTHJIzKiAOykN1aqZPIjMRAdFQyNoYBbNis2L8VbMxIXEISYoBmbR7FV0wJOrt7ToNADAztydHu+Xw18PjaBx6BvxVtSarWb8nPkzAGB8ynjcnHgzANYMzWQ1eRyZ6dK6C3SCDsU1xV75h9SIzIQFhEmjOVytaOLVNfJGY/yEy99f0UHRDlMH8siMp8LOZDHhQjnr29MhvIPkzVE7MvO/I//DgkML8MSGJ/Do+kdx6PIhHC8+joLqAgTpgjCg7YAGvzM8YTi0ghYnik9IqRxXqDJVSf/Loe2HuvQ7bYI8F4pKR2YAW6rJkW/mYsVFFFQXQKfR2U0AdwVPy7P5e8YTMcMvytz1ZbakuUwAiRlVcOcqWO1qJnfEjE6jk052Sh1cvRUzgiAgLYaJCm98M+6WZgPAje1Z87DfLv7m8X45cvOvI8+Kt++DnZd2orC6EJGGSAyJH4I+bfqgdWBrlNWWYW/eXo8jMwatQaqQ8FRMWkWrKgZgQRCk19PVK3q5X4bTObKznYnT2UwffsL1xuOSU54Di2hBsC4YMUExUmRGyao5R/x2wfYe3pO3Bw+ueRAzNs8AAKTHpUvdv+VEBEagfywTfdtzt7u8ryOFR2ARLWgb0tbp/7I+3qS4pc92sHJihqeaHPlmeIqpe1R3t8W5p+XZ3ggLfsHsbuM8vk/yzFyjWEWrdBXc2MRsjrftpp0hjTNwwwAM2FJNBZXK+Ga8FTOA983zTBaT9P91JxQ9JH4IADZ0zlvfTGN+GcD7yMxPZ34CwKIMeq0eWo0WwxOGA2CpJv6e9OR16BHNUk1HCz0TM1xYA8pGZgD3es3U98vIkbfad+SXAViZK///8ZOu2WrGp4c/xSvbXoHJYnL4e3K4+Tc5PBmCIEgnezUjM5erLuN4MWu7v+S2Jbiz853QCBopddFY6/9OkUzkuSO2+EUH/9y6gjdiRkohu3C8dRUpMuOg18zOSyxS665fBvC8PNubyiJpkK0bnpkac02DCd3NHRIzClNiLJFKY1sHNX3ikKqZmkFkBlC+ool/gBQRMx42z+MfSp2gsxsh0RQJoQlIDkuGRbRgR+4Ot/crp7HxEoB385nKa8uxKYf1lBmfMl66f2TSSACwM7a6m2YCZBVNRe5XNJmtZvxj2z8AMCEZqAt0+zkaQ4rMuGACru+XkSPvUOuoLJsjP+nmV+bj0XWP4oMDH+Cnsz/ZdRV2Bp+WzVv7q9HbqT68h07PqJ7oEdUDbw56EyvGr8CtybdiULtBUhm2I9z5/3IOXj4IAA0auDWGN+k2d839rsDLs09fOW0XQSmvLZf8QK7Mf6qPTqND+1DWPNSd8mweJXG38SjgmZWBC90QfUiL6DEDkJhRHP7BijRESuPkG4Obs8pN5XYD77yFdwB2xwAMKF/RpERkxtvmeXIPk7vl4UqlmpoSl/x9cKniktuC7desX2G0GJESniJ17QWAG9regFb6ViisLpSult1NMwG2iqZjhe6ZgEVRxJs73sSmnE0I0ATg3aHvelSe3xhS4zwX0kxcbPSL7dfgsaSwJOl/19icHR7ZW5O5Bnevulsq0QVcG/tQv8eMLwzA/L07pP0Q6b6OER0xe9hs/HfUfxst9XW3y7JVtErvtd5teru8Rk+PO1bR6lEKuSn4WIMqc5XdWIOfzv6EanM1OkV08igyA8hMwC76ZkqNpVLjUU9SPp5EZnhZdrtW7dxq5eBPSMwojLvehLCAMGmuhlKDBkVR9Dgyo7QhkYsZfnXgCd42z/PGIMhTTdsubvOqmqepNFOv6F7Qa/Q4eeWkVE7tKivPrAQA3J5yu92BR6/VSwZMPibBk8hMSkQKAjQBKDeVu3wAFkURc/fOxY9nfoRG0OC9Ye85NJl6izsnW97519k6Xr3hVUxImWAX3aoPP+muPrcaV4xX0K11N9ze8XYAwNnSs02uoUFkRvZ58/T9tSdvD/628W+SSVSO2WqWDOx8gKQ7uFv+fr7sPEqNpQjUBkrRDVfwVMyUGkulKkFP3tvOkI814L4ZURSx9ORSAMDdXe72+CTvbnk2j8pEBUZ5lKb1ZJCtlNZqIZVMAIkZxXH3xKnVaBVPNVWbqyGCHRjdjcwoHfbmYsbbA403zfO8Kd3sH9sfQbogFFYX4kTxCbd/n9NUmik2JBb3dbsPAPDvff922Rx45soZ7C/YDwECxnUc1+BxnmrieBKZ0Wv0kph01Tez8MhCfHHsCwDAG+lvYETiCLf36wqunmwLqwtxrvQcBAgOIzMA0CumF/455J+NpiLlhtZ7u96Lr8d+jaEJTDA2FZkRRbFBZIafxKvN1dLVt7ssPr4YWy5swQf7P2jw2KHLh1BuKkeEIUJKF7qDu40Jua+tR3QPlyLTHE/FDH/dIwwR0Gtd358r1PfN7Mvfh7OlZxGkC8LtKbd7/Lzulmc7m8nkKpIBuLbU5aaELWlaNofEjMJ4EvKUwoAKmYD5iVMjaNxW8kqmmapMVVIViyv+ocaQ+2aa2ufbu97GB/s/wOHLh2EVrW53/5UToA3ADXE3ALB5DxrDZDHhvtX34c+//tnuSrupyAwAPNbrMYTqQ3Hyykn8fO5np9sVVBVg8fHFmLp2Ku786U4AwA1xNzisHBncbrBUqaIVtG55huRIJmAXKppWnV2F9/e/DwB4rv9zXk0xbgqeZuKfO2dI85had/X4fwAA4zqOw8jEkZgzbA5eHfgqDFoDUsLZFfy5knONRleKa4pRVlsGAYJ0dR6kC5LW42mqib+/12etb9CxmlcxDY4f7FanWg7//14xXnHJ4OyJXwawXURVmCqkbtGuoEZZNodHlnhk5vuT3wMAxnYY6/ZoFjnulmd7K2b4+8udYZMtrSwbIDGjOJ58uJRunCcfMuluKFSqZlJAzHBxFqAJ8Lokl4uZU8WnJIHkiG9OfINvT3yLT//4FA+seQAjvh8hVfp4ahDk4XlXfDNnSs7gSNER/J77O86W2NIOTUVmAGYCfrTXowCA/xz4T4NGcCU1JXhy45O4eenN+L/d/4d9+fvYIL/oNMy8YabD5wzWB2Nwu8EA2BWap54VV8calNeWY/ae2QCAaT2n4eEeD3u0P1dx1aDKzb+81NhTEkIT8O/h/7abAp0clgydoEOFqaLRiCZvlteuVTs7I7S30VAu5CyiBV8f/9ruMS7AebrUXSIMEdAJOrYfF7rz8siMO5VMADtW8SiyO/8HNfwyHHmvmaLqIvyazYy/93S9x6vnTQxzrzzb2xlJeo1eGm7r6gUzNwBTZOYaxp2J2RxP2007g5t/3fXLALbITKWp0mkrb1fh84BaB7X22kQmb57nbOihyWLCt8e/BcA6c7bSt0JRTREuVDAvgacdQvmJ4NDlQ03mnc+V2ubY7Lhkq4ByNjG7Pg+kPoDY4FhcqryEJSeWSPdfqriEh9Y+hK0XWMfW62Kuw/P9n8e6u9Zh8bjF0kBIR/BUU2PG1qbgYuZ48fFGQ9UL/1iIEmMJOoR3wN/6/M3j/bkKf78W1xQ7TdNaRSu2X2R9UtTw7ei1eukE1ViqqX6KiSOVZ3sYmZE3uFx+erl09Z1fmY+TV05CgCAJWnfRCBopotmUL6nUWCr5hnha2B08iQq729nbHeRjDRYfXwyz1Yxe0b3sTPaeEBfiXnm2FJkJ9TxKwqMzrvpmWlr3X4DEjOJ4ktLwtN20M+SRGXcJ1gdLJ1xvozP8KsCbSiaOIAjS1R5vWlWftefXoqC6ADFBMVg4eiG23rsVn4z6BA+kPoBbkm+xu5p2h3at2iElPAVW0dpkibZczMiNvM4mZtcnUBeIJ3s/CQD45PAnKDWW4vSV03jwlweRWZqJ2OBYLLt9Gb4e+zUe6vGQS1dOYzuMxYx+M/Di9S82ua0zOoR3QJAuCNXmainCUJ+8yjwpMjCj7wzoNDqP9+cqUYFRSG2dCqtoxdJTSx1us/XCVlyouIDQgFBp3pDScLOoPBpXH95jhpt/Od5EZqrN1agys7RMfKt4VJoqsfzUcgC2Rne9ont5ZcCXfElNVIzx+WlJYUkefeYdiZlaSy3WnV/nVKhKZdkKjTKQIx9r8MVR5v/yNioDuF+eLaV8QjwXM+7MADRajJJIpMjMNYxHnhmlIzMejDKQo1RFE/9fKCFmACC9XToA4KtjXzW4whBFUTrg3J96P/RaPfRaPdLbpeOl61/CnGFzvPJKuFqiLZ8wvC9/H2ottQAan5hdn/Ep49EpohPKasvwj+3/wMNrH0ZBVQE6hnfE12O/dqtKBGAm82k9p7kd+q//HKmtUwE4TzXx1Fi/2H4e9eDwBEEQMKX7FAAsxcj/33L4++LuLncrOhtKDu8g3Ghkhs9kqheZ8UbM8M+YQWvA42mPAwC+Pv41TFaT5JeRl2R7Au+s21Qqj/tlPH2f1U9xW0UrXvrtJTyX8Rz+c+A/Dn+HG5M98cO5Ak811VprERoQ6vEFUX1cLc+2WC2KRGbcsTJw31WQLkj6vZaAqmJm/PjxSExMRGBgIOLi4jBlyhTk5ubabSMIQoOvBQsW2G3zxx9/YNiwYQgKCkJ8fDxmzZrlVZmsWlSbq6U3njtXCjwyo1RpthSZcbP7L0epxnlK9JiRc0enO9AxvCOKa4obVG7sztuNk1dOIkgXhLu73K3I/uTIS7Qby3PLIzPV5mrJsOyKAZij1Wgxox9rNb85ZzPKa8txXcx1+PLWL11uD68GjZmATxaflAZdPtvvWZ/2phiTPAZtgtqgsLoQa8+vtXvsaNFR7M3fC52gk6rF1MCryEyI571m5J2dx3Uch6jAKORX5WPNuTVSmtOTkmw5rpa/c7+MO/1l5PBjJhczH+z/QGpQ56gTL2BL66sRmQFsqSYAmJAyQbEO1q6WZy87tQzV5mqEG8K9Svm402lePtSypfSYAVQWM8OHD8f333+PkydPYvny5Th79iwmTZrUYLtFixbh0qVL0tfDD9tMg2VlZRg1ahTatWuHPXv24D//+Q/mzJmDf/3rX2ou3SN+yfwF1eZqJIQmoGOEcw9DfZSuZvI2MqNURZPSYkav1ePVga8CAJaeWiqFtQHgy2NfAmAHHG8iMM7o26YvgnXBKK4pllrD18dkNUkHJ2405akmXnbbmAFYzo3xN+KGtqyKalj7Yfh09Keq/F3u0DOKNc9zJGb+te9fECFiTPIY9Irp5dN16bV63JfKhMqXR7+0u9D58ih7X4zpMEZVIcgjM2dLzjq80DJZTNKFTnJ4st1j3sxn4r60qMAoGLQGSbC9u+ddVJoq0TqwtdceD1fK381WM/4o/AOA+5VMHPlxZ8XpFVh4ZKH0WE6540GXPPWlhmcGsEVmAGVSTBxXyrNLjaX4z0EWkXqy95NelZ67M/tNmsnkhcfOH6gqZmbMmIGBAwciKSkJgwYNwksvvYSdO3fCZLIv8YuIiEDbtm2lr6Agm/pdvHgxampq8Pnnn6Nnz56488478fLLL+Nf//pXs4vOfHfyOwAsnO1O1YjSwybdiQI4Qikxo8Qog/oMaDsA41PGQ4SIt3a+BbPVjHMl57D1wlYIEPBg9wcV25ccnrIC7If2yckpy4FZNCNYF4yJnSYCgOSxcdeULQgCPhjxAT4Z9QnmDZ+n+EwjT+CRmRNFJ5BXmSd9/n6/+Dt+z/0dOo0OT/V5yi9ru7vL3QjSBeHklZNSp9+8yjysO78OAPBQ94dU3X9iWCJ0Gh2qzFUORUn9AZNyvEnrStPQ69Is93a9F4HaQMmjNbjdYK+7LrvSa+ZMyRlUmavQSt9KilK5Cxd1+wv2Y9aOWQCAB1PZ57m4pli6SJOjxpBJOTfE3YA2QW1we8fbG0TUvMGV8uz5B+aj1FiKzpGdvY42u5Nmaok9ZgAfemaKi4uxePFiDBo0CHq9vcKcPn06oqOjMWDAACxYsABWqy2Mv2PHDgwbNgwGg22q6y233ILc3FycP3/e4b6MRiPKysrsvtTmSOERHCs6hgBNgHQicxUeAiyoLsDRwqM4c+UMcspzPE47SUMmPTAAA8q1WFei+68jnun3DMICwnCi+ASWnFiCr45/BYDNSuEHCTXgqSZeGVMfXsnRMbyjJHyOFR1DSU2J25EZgJmx09ul+8RI6woJoQkIDQhFrbUWo5aNwk3f34S/bPgL/rnrnwCAyV0nIyEswS9rCzeES517eZRu8fHFsIgWXN/2eqRGpaq6f71GL3lhHPlm5Cmm+qF7eY8VdysI63ccjwiMwIROE6THudfLG1yJzBwsOAgASItJ81g8ySvTzKIZtybfihcGvCCl4etHZ2rMNdLnSq3ITFRQFDbesxH/b8j/U/R5myrPPnXlFL4/xfravDTgJa+PAe7MfpOnmVoSqouZF198ESEhIYiKikJ2djZWrlxp9/hbb72FpUuXYsOGDZg8eTKeffZZvP3229LjeXl5iI21H/zGf87LcxyWfeeddxAeHi59JSSof4DlUZnRyaPdPnlLYqaqAJN/now7froDY38YixuX3Ci1qncHT0cZcJTqAqx0mokTFRSFp/s+DQCYf3C+5NVQu6cJL+s9VnTModGUm387RnREm+A26BTRCSJEbM/dLvXG8fQ1aQ5oBA2e6/8cOkd2hlbQorimGNsubkNOeQ5C9aH4c9qf/bq+B1MfhAABGRcycLTwKJadWgZA/fcFpzHfDB9jUD/FBLCLDk8rCB1NQ5/SfQo0ggZ6jR6D2g1y6/kcIc2/akzMeNgsTw4XMwAzEb815C0IgiAJ5PopGe7hMWgNTbY88BalvSONlWeLooh3d78Lq2jFqKRRuD7ueq/3J1XMupFmuuojM2+88YZD0678a+/evdL2zz//PA4cOID169dDq9XioYcesksPvfrqq0hPT0fv3r3x7LPPYtasWXjvvffs9ln/jcR/39kbbObMmSgtLZW+cnIc51uVotRYirWZzHh4b9d73f79xNBE3N7xdiSFJSE2OBYRhggEaAIgQsSGrA1uP583pdmAcmkmKQSu4MwUzqQuk5AWk4ZKUyWMFiN6RPXwePCbqySGJiLSEIlaa63D0Qbc/MvD0Tw6I38N3YnMNEfu7Hwnfhj/A3bcvwNfj/0aL9/wMu7tei/eG/aedPXnL5LDkzEsYRgAYPqm6agwVaBDeAePG8a5C+8E7Cgyw++rX8nE4ammvCr3fDOOxoUkhSXhk1Gf4L+j/quIz4pHPYqri52a33lkxpP+MpyY4Bj0iu6FzpGd8f7w96XO1dxfUj8yI29Q2pKMqkDj5dkbsjdgd95uGLQGPNv/WUX2506aqSXOZQIAt2NX06dPx+TJkxvdJjk5WbodHR2N6OhodOnSBampqUhISMDOnTuRnp7u8HcHDhyIsrIy5OfnIzY2Fm3btm0QgSkoYCfZ+hEbjsFgsEtLqc1PZ39CjaUGXSK7eFSWKAgC3r7xbbv7DhQcwEO/POS0QVxjSAZgD0+c/MBaVFMEk8XkkfFMFEVVPDMcjaDBPwb+A/euvhdW0YqHezys+gFNEASkxaQh40IGDl0+hLSYNLvHuZjhJ7X0uHR8dewrqQtroDbQrXk1zZkgXRCui7nOq3JvNXio+0PYkrNFOtHxKIUvcBaZMVqM2JrDmh06mwsVGxyLMyVn3E7tOhtsy0dwKEFUUBQECDCLZpQYSxp8ngurC3Gx4iIECEiLTnPyLE2jETRYPHYxRIh2r5krYqYlkhCagPNl55Fdni29XjXmGszZMwcA8EjPRxRL9bgqZkwWk2SqbmmRGbfFDBcnnsAjKkaj0ek2Bw4cQGBgICIiIgAA6enpePnll1FbW4uAgAAAwPr169GuXTs70eQvRFGUZnbc2/VexU6oXSO7QiNoUFBdgIKqArsQbFPwvLunkZlIQyT0Gj1MVhMuV1/26E1dYaqAycqM3kp7ZjjdWnfDPwf/E+fLzmN00mhV9lGf62Kuk8TMFEyR7rdYLZIvgley9YvtB71GjxpLDQDPXw/CdfrH9kdq61QcLz6O1oGtpYnWvkCqaCo9C6tolU7Iv134DeWmcsQGxzoXMx6agNWMfnL0Gj0iAyNRXFOMy1WXG4gZXpLdKbKT15FHQRAgwP4YerWKmaSwJPx28Tdkl2WjylSF9VnrsfTkUuRW5qJtSFtM6zlNsX3xqGmpkQ2bdDanK68yDyJEBGoDVbkIVRPVLll2796N+fPn4+DBg8jKysLmzZtx//33IyUlRYrKrFq1Cp9++imOHDmCs2fP4rPPPsMrr7yCxx9/XIqs3H///TAYDJg6dSqOHDmCFStW4O2338YzzzzTLEKLu/N243zZeQTrgh1OLfaUYH2w1KLe3eiMt6XZgiB4nWri4e9gXbDdHBqluT3ldvytz988GqLnCTwaIy8LB5hpzmgxIkATIF1NBeuD0adNH2kbb4bTEa4hCAKm95kOnaDDE9c9oep7rz4JYQnQaXSoNlfjUqVt4OPqc6sBAGM7jnUaJfLUp6Z0Y0pnNDbS4FgxOz71ilanJD8x1GaWlaPmKANfwE3Aq86uwk3f34R/bP8HDhcehk7Q4dUbXlW0gpGnG0WIKKt1XhBzsdI2B6o5nF/dQTUxExQUhB9++AE333wzunbtimnTpqFnz57IyMiQhIper8dHH32E9PR0pKWl4f3338esWbMwd+5c6XnCw8Px66+/4sKFC+jfvz/++te/4plnnsEzzzyj1tLdght/b0+5XfErb94fwl0x461nBpD1vnAzh89RM8XkT3pF94JG0OBS5SU7ocejMknhSXaVB9w3A7Rs829LYmj7oTjw0AFVm+Q5Ql7RxFNNpcZSaZ7WuA7OL3Y8qSA0WUzSiUmtDricxiqa+Hufp1eVhntL8irz7Iz3ag6Z9AVJoazysqimCNXmaiSGJuKpPk/hl7t+kbxfSqHX6KWLqcYqmrJKmX8nrlXL6jEDeJBmcpVevXph06ZNjW4zZswYjBkzxqXn2rp1q1JLU4zLVZexOXszAKjSdbZHVA/8dPYnh03KGsPbyAwgMwFXehaZkaosgq4uMROsD0bniM44eeUkDl0+hFFJowDYTl71Bz6mt0vH+/vfB+B5R2ai5dApohPOlJzBmZIzGNp+KDZkbYDJakKniE6NjqFwlGYSRRGvbHsFBy8fxLfjvm1g5uXRT62gVb2hYmNdgOVVfGoQFRglzQW7UHFB+oy19DTTgLgBmNhpIrSCFuNTxqNPmz6qRkMiDZEory1nFU1O3i6/nP8FANAnpo/jDZoxNJvJC5afXg6zaEbvmN5uz8txBR6ZOVp41OUGgaIo2kqzvchfK5Vmam24usQMYJs9w70CQEPzLye1dapkvlO7fJTwP/VNwDzFdFvH2xr9PUdpphVnVmDVuVXIKc+RqoXk8AuGyMBI1U3OzoZNmq1mZJWzq/nGJrd7gyAIUqrpQvkF6X4eJVJrlIHa6DV6vDX4Lbwx6A30je2relqnKRPw+dLz2Je/DxpBY9erqKVAYsYLQgNCER0UrWibazldW3eFVtCiqKbIZVFRa62F2WoGoEyaydNeM7zN+tUWmQFkvplCm29GKsuOsO8SqhE00qTmll6WTTSNfOBkXmUe9uazNhVjO4xt9Pd4ZKbUWIpqczXyKvPw3h5bi4oLFRca/I6jsmy1cNZrJqc8B2arGUG6IFXHRTgyAbf0yIyv4SZgZ2LmhzM/AGDNQf05A85TSMx4wQOpD2D9pPUY06HpVJknBOmCpCs9V1NN8g6iwTrPJwTzgyvvBukuPC97tXlmAFtk5mjhUZgsJoiiaAu1O7g6vavLXWgd2Npn/U4I/8E/r5mlmVJUpn9s/yY9CKH6UOnzml+Zjzd2vCFFWAH7iATHWVm2GnDBwPfJ4SI+OSxZ1ehQ/VlGVtEqXTCRmHENHsHal7+vwWMmq0lq0Hpn5zt9ui6lIDHjJXqNXtXeIVKqyUUxw/0ywbpgryp8Okd0BgCcvnJaivS4Az/Q8M6TVxNJYUmIMERIzfMuV19GhakCGkHjsCnawLiByLg3A7ck3+L7xRI+JSE0AXqNHtXmanx97GsAcKnKURAE6QLik8OfYPvF7QjQBOD+bvcDcByZ8UVZNseZAbh+OwK14F2AeWSmxFgCs2iGAOGqjP6qARcpq86ualBUsjVnK4prihEVGIWh7Yf6Y3leQ2KmmdMjig33czky4+UoA05yeDJC9CGoNldLV1/uIHlmrsIDDW+eBwCHLh+S/BEJoQkI0Ab4c2mEn9FpdNLIgqKaIug1eskk3hQ8tbvqHBvP8WSfJ6UTS2ORGV9EP7mYKawutPPvNRaRVJL6aSbu3YkMjLxqGlGqTVpMGsZ2GAsRIt7b857d67j89HIAwIROE1rs/5PETDOHi5njRccbmIDzKvPwwM8P4MujX0r3SUMmvayc0QgaOwOyuziaGXM1wVNNhy8flsSe2gd0omXQKbyTdHto+6EuVxpxMQOwFgAPdX9I6ll0seJig8+/5JnxQZqJ76PaXG03vdpX730uZi5UXIDFavFpiu1q4u99/w6D1oC9+XuxKZtVG+dV5mF7Lhue21JTTACJmWZPl9ZdoBN0KK4pRl6lfc+XRUcW4XDhYfx737+lKyTumVGip0nPqJ4A2ERwd+F9ZnwRAvcH8siMr65OiZYB980ArqWYODzNxKtcdBoda14GAdXmakm8cHx5Qg/WB0sFBTzVJIqiLc2k8nu/bXBb6DQ6mK1m5Fflo7CGmX9baiWTv4hrFScNXp27by5qLbX48cyPsIpW9I/tj6SwJD+v0HNIzDRzDFoDOkWyKz15qqm8thw/nvkRAGAWzZi9dzYAZRrmcXpEs6jQkSL3xIxVtEoGYLVGGfgb3jwvtzIXu/N2A1DfN0C0DPjnNVQf6pb/YHjCcLQNaYuXb3hZEkQB2gCpTUJ934wvPTOAfaoJYJWOVeYq6ASd5GlRC61Gi/atWPO8nPIcKc1E5l/3ebTno4gJikFOeQ4WH1+MFadXAGjZURmAxEyLgKea5KatH8/8iCpzFdqFtINOo8P2i9ux9cJWRRrmcXpGs8jMqSun7DpvNkWpsVSarns1GoABJhZ5Ge75svMA1OuASrQshsYPxaQuk/Ba+mvS5GdX6BndE79O+hWTukyyu593wL1YftHufl96ZgCbcOBCgkckE8ISfOKz4P+H7PJsSVBRmsl9gvXB+FufvwEAPtj/AXIrcxGqD3XZ29VcITHTAqhf0WSxWrD4+GIAwKO9HsWU7mzg4Xt73pMiIkpEZtqFtEOkIRJmqxkni0+6/Hs8HB4WEObRxO2WQv2J0R3COzjZkriW0Gv1eD39dcVaNvCIhDwyI49++uqEXr+iyddeMfmMJi5mKM3kGRM6TUBq61SYRVapOq7jOJ/OMVMDEjMtAHlFkyiKyLiQgYsVFxEWEIbbU27H470eR1RgFM6Xncd3J9isKCUatAmC4FGqSapkukrNvxzumwGAuJA4BOs97+tDEM6ID7WZgDklxhJb9NNHqdzoYPteM74WM5IJuPwCNczzEo2gwfMDnpd+vqvLXX5cjTKQmGkBdI7sDJ1Gh1JjKXIrc6WozF1d7kKQLgitAlrh6b5PA7Dl0ZUaeslTTe6YgK8VMSOPzJD5l1ALKTIjK8/mgiLCEOGzUtr6kRneksBXEUl54zwSM94zoO0A/GPgPzDz+pno1rqbv5fjNSRmWgAB2gCpid2K0yuwO283tIIW93W1TQWe0GmClI4ClJvQzCua3CnPvlbETHJYslR2SykmQi3kEQmOP1ofSJ6ZOjHjq4Z5HHnjPL4GEjPecU/Xe3B/6v3+XoYikJhpIfB0z8IjCwEANyfebNciXSNo8NL1L0k/KxWZ4fs9V3rOrr8EZ8XpFVK/Ag4vy77axYwgCBgQOwCA7f9EEErDe83kVeXBZDUBsHXY9qUBls9nKqwqxJWaK5Jnp0OYb4R8+1btIUBAlblKOhaRZ4bgkJhpIXDfDB8t8GD3Bxts06dNH9zd5W5oBA16RfdSZL/RQdFoG9IWIsQGLbD35+/Ha7+/hme2PGPXA4dHZq7Wsmw5L9/wMv7vxv9rcpAgQXhKdFA0DFoDrKIVeRXsc+brsmzAPs3E/TK+9IoFaAOkXjwAEKgNVOyijWj5kJhpIchTSN2juqN3TG+H2/1j4D+w474dSI1KVWzfzlJNXx37CgBgES1YdmqZdP+1kmYC2NXquI7jVB2yR1zbCIIgRWdyKlg7f390wOUpnbLaMpwoPgHA914xXtHE1yMIgk/3TzRf6AjcQugc0Vky+j2Y+qDTD7EgCIpfKTmqaLpQfgGbcmzppWWnlsFkYSFwqf/FVTiXiSD8gdRrpq6iyR+embCAMARo2OyxPXl7APjeK8b9QwD5ZQh7SMy0EPRaPV4Y8ALu7XovxiQr07/CVRxVNC0+vhhW0Yob2t6AmKAYFNUUYUP2BgCw9b+4SkcZEISvqV/RJM1l8uFnTBAESUBwMePrrtdc1AE2Dw9BACRmWhSTu03GqwNf9XkjOp7iulhxEVdqrqCitgIrzrAW2FN7TsXdXe4GAHx74lsAMs/MVdr9lyB8jXzgJOCfNBNg6zVTVlsGwL9pJrpYIuSQmCGaJCwgDMlhyQBY474fTv+ASlMlOoZ3xOB2gzGpyyToBB0OFBzA0cKjKDWWAqA0E0EoBY9I8MiMv6bS168e8rWYoTQT4QwSM4RLcN/M4cuH8c2JbwCwiipBEBATHIObk24GAHx86GMArFQ8PCDcP4sliKsMScxUXIAoin4pzQbsBUSkIdLnFYtyMUNpJkIOiRnCJXhF0+Lji3Gx4iIiDBG4vePt0uP3dWMN/DIuZABgnUm1Gq3vF0oQVyHcM1NqLEV+VT5qrWzwq69TLfLIjD8aRbYKaCWlrykyQ8ghMUO4BDcB81z53V3uthtM1rdNX3SO7Cz9fC2UZROErwjWB0ufqUOXDwFgjTF9PRxQHg3xtfmXM67jOLQLaYe06LSmNyauGUjMEC7RtXVXaAUWadFpdFIkhiMIgt19JGYIQlm4CZiLGX98xuTREH/NI3vx+hex9q61iAiM8Mv+ieYJiRnCJYJ0QegU0QkAcGvyrQ7z1eM6jEOoPhQAiRmCUBqeajp8+TAA/1TzNAcxA4Ca5RENIDFDuMzDPR5Gr+heeOK6Jxw+HqwPxh2d7wAAJIYlOtyGIAjP4CZgPlbE1+ZfwN4zQ5PiieaEzt8LIFoOt6fcjttTbm90m7/3/Tt6RvfEkPghPloVQVwb8DQTHzbpr8jM0PZDoRE0aBvS1uf7JwhnkJghFEWv1ePWDrf6exkEcdUh734L+KePkyAI+PDmD32+X4JoCkozEQRBtAB4ZIZDHXAJwgaJGYIgiBZA25C2UkUh4B/PDEE0V0jMEARBtAB0Gh3iQuKknykyQxA2SMwQBEG0EOJDbakman9AEDZIzBAEQbQQeK8ZgNJMBCGHxAxBEEQLgVc0BWgC0Erfys+rIYjmA4kZgiCIFgKPzLQOak1dcAlCBokZgiCIFkKfNn3QSt8K6XHp/l4KQTQrVBUz48ePR2JiIgIDAxEXF4cpU6YgNzfXbhtBEBp8LViwQHp8y5YtmDBhAuLi4hASEoLevXtj8eLFai6bIAiiWRIbEout927Fm4Pe9PdSCKJZoaqYGT58OL7//nucPHkSy5cvx9mzZzFp0qQG2y1atAiXLl2Svh5++GHpsd9//x1paWlYvnw5Dh8+jGnTpuGhhx7CqlWr1Fw6QRBEs0Sv1VOKiSDqIYiiKPpqZz/99BMmTpwIo9EIvV7PFiAIWLFiBSZOnOjy84wbNw6xsbH43//+59L2ZWVlCA8PR2lpKcLCwjxZOkEQBEEQPsbV87fPPDPFxcVYvHgxBg0aJAkZzvTp0xEdHY0BAwZgwYIFsFqtjT5XaWkpWrd23mPBaDSirKzM7osgCIIgiKsT1cXMiy++iJCQEERFRSE7OxsrV660e/ytt97C0qVLsWHDBkyePBnPPvss3n77bafPt2zZMuzZswePPPKI023eeecdhIeHS18JCQmK/T0EQRAEQTQv3E4zvfHGG3jzzcbNZ3v27EH//v0BAIWFhSguLkZWVhbefPNNhIeHY/Xq1U5zvnPnzsWsWbNQWlra4LEtW7bgtttuw0cffYSHHnrI6f6NRiOMRqP0c1lZGRISEijNRBAEQRAtCFfTTG6LmcLCQhQWFja6TXJyMgIDAxvcf+HCBSQkJOD3339Herrj0sLt27djyJAhyMvLQ2xsrHR/RkYGbrvtNsydOxePP/64O0smzwxBEARBtEBcPX/r3H3i6OhoREdHe7QorpvkUZP6HDhwAIGBgYiIiJDu4xGZd999120hQxAEQRDE1Y3bYsZVdu/ejd27d2PIkCGIjIzEuXPn8NprryElJUWKyqxatQp5eXlIT09HUFAQNm/ejFdeeQWPP/44DAYDACZkxo0bh6effhp33XUX8vLyAAABAQGNmoAJgiAIgrg2UM0AHBQUhB9++AE333wzunbtimnTpqFnz57IyMiQhIper8dHH32E9PR0pKWl4f3338esWbMwd+5c6Xk+//xzVFVV4Z133kFcXJz0deedd6q1dIIgCIIgWhA+7TPjL8gzQxAEQRAtj2bXZ4YgCIIgCEINSMwQBEEQBNGiITFDEARBEESLhsQMQRAEQRAtGhIzBEEQBEG0aEjMEARBEATRoiExQxAEQRBEi4bEDEEQBEEQLRoSMwRBEARBtGhIzBAEQRDKIoqAqdrfqyCuIUjMEARBEMpRVQwsHA281xkovejv1RDXCCRmCIIgCGWoLAS+GA9c2A3UlgPnt/l7Re5RcRm4dNjfqyA8gMQMQRAE4T3l+cDntwH5f9juk99uCSydCnwyDCg47u+VEG5CYoYgCILwjrJc4PNxwOXjQGgckD6d3Z9/1L/rcgdTDZCzExCtQM5uf6+GcBMSMwRBEFc7l08BC24ETq5V/rkrLgOLxgJFp4HwBOCRNUD3ieyxvCPK708tLp8ArGZ2u/CUf9dCuA2JGYIgiKudQ98AeYeBre8p/9wHFwNXMoGIRCZkWncE2qQCEIDKAqCiQPl9qkGeLCV2+aT/1kF4BIkZgiCIqx1+cr64j5l0laToNPve+0EmaADA0Apo3YHdzm8h0RkSMy0aEjMEQRBXO5dP1N0QgTMblX3u4kz2nYsXTmxP9r2l+GbkYqY0G6it9N9aCLchMUMQBHE1Y6oBrpy3/Xx6vbLPL4mZjvb3czHTEnwzVqtMzAjsW+Fpvy3HZ9SUsr5AVwEkZgiCIK5mis6wCh3OmQ2A1aLMc5uqgfJcdru+mGnbgiIzJedZXxytAWg/gN13tZuArRbgv8OAjwcDxnJ/r8ZrSMwQBHF1cfkU8MlNwNlN/l5J84CnmOL7AYERQE0JcGGvMs/NIz6GcCAo0v6x2B62/ZtrldmfWvCoTGx39gVc/b6ZysvMuF2eq3zq0Q+QmCEI4uri4GIg9wDw+3/8vZLmAT8px/YAOt3MbiuVaio+x7637gAIgv1jEUmAIQywmmwm4eYKFzNtewHRXdntwqtczJTn2W6f+Nl/61AIEjMEQVxdXKnzcFzYx7wQ1zo8MhPTDeg8mt1WXMx0bPiYINiiM83dNyOJmTQgpgu7fbmZpJlEUZ3nrci33T69DrCY1NmPjyAxQ6jPmY3Ajg9b/IeFaCFwQ6qx9Oq/unYF7v2I7gqk3AxAYD1nyi55/9zOKpk4XMw09/JseWQmphu7XXzW/8csi5mNV/h4iPJrkUdmakqBrO3KPr+PITFDqIvVCix/FFj3MvDNPUBNmb9XRFzNiKLtBAtQW3qLiRmAASCmK9AqBojvy34+s8H7528sMgPIyrObsZipLALKLgKoiySFxQMBrVg3YP73+YuLe4FLh9iMK/46KoU8MgO0+FQTiRlCXYpOA9VX2O2zm1jb87Jc/66JuHqpKmJVKZwL17iYKT7HTsoBrYDw9uw+JVNNLosZBxVNV7JYR2J/93PJq5uS3bojYAhl6bHozuw+f5uA5a9RwTFln5tHZtr2Yt9P/KxeSssHkJgh1IVXTbROAUJi2BXGZyNbRrmmklRfadEHihaDPCoDADl7/LOO5gL3y0R3sRl0O49i389u9i51Ya4FSnPY7UgnaSY+1qAin81w4ogi8MNjwKZ/Ar/9y/M1KIE8xcRpLibgU3Ixo/Akbx6ZSZsM6ENYdOrSQWX34UNIzBDqcqHuZNJtLPCnDeygWnYR+N8YIOt3/67NVxxfBbybDOz5zN8rufrhkYKYVPa98KQtMngtwk2sMV1t98X1AYKjWQQre6fnz12aw/rX6IKA0LaOt3E21iB7B5Czi90+/J1/jdqOxExzMAGX5bKLP06+SpGZyCRblVsLTjWRmCHU5WJdZCa+PxCZDExbByQNBoxlwOpn/Lo0n3HyF/b9yA/+Xce1AK9kat/flvq4sM9/6/E3UiWTTMxoNLbojDepJnmKqX5ZthxHvplt/7bdLs1h4sZfyCuZOGpEZnYuAL66wz5C1Rinf2XfdUHsu9JpJj4AtFVboNtt7DaJGcItRJF1zrzaqa20XU3wrprBrYHJ3wAaHXD5OFB01n/r8xU8pZZ7wP/VEVc78uqa9tez29eyb4Z7PniFDkcSM796/txNVTJx6vtm8v5gIkrQAB2GsfsOL/F8Hd5gqrZVe9lFZur+X4WnlYsabfs38w2ue9m17bnQ7DuFfb9yXjl/kSgCFXWRmdBYoMtoQNAywVTf9Fx9BcjcCpRebNapchIz/mDtS8Db8cClw/5eibrkHgRECxAaB4TH2+4PimDRGQA4ucYfK/MdVovt6thcbT/MjlAeHpmJ7AAk1Anoa7WiyWqxNauTR2YAIGUEExOXjzf0GbmKvGFeY7StN6Np2zz2vccdwNDn2e2jK9kMKV9TcJwdo4Kj7VNlkcmANgAwVdl8Qd5QXWITD398D5zb0vj25lrbNtfdB4S0ASAqZ0iuvgJY6royt4pl3ZuTh7CfT8iOydk7gf/0B764Hfh3d2B2B+Dz24BfXmRVVs0IEjO+pvA0sPsT9gE6t1m9/Yh1b/xt84AvJwCb31ZvX86QUkz9Gj7WbRz7fuIqFzPFmYBZdpC+cI0bUtVGnvrgkZmL12jzvJIs9t7TBbJuvHKCIm1RkZ0fe/b8cuHYGPKxBoWngaN16dbBf2cXNWHtWU+gU794tg5v4JVMcWn2qTKtjhUtAMrMaKr/HKufaVy8Ze8AaiuYiInrXWekhnKpJm7+DYoEdAZ2u36q6eA3TMRUFbLtBC0TQed/A3YtAJY/psxaFILEjK/Z+p5t6JsaPQwKTjDV/EFv4MPrgQ2vM4Wf8S5rjORLeCUTTzHJ6Xor+56zk/V58DfF54DvHlQ+WlZQr2rrWo0S1MdUDexdpOzEXmM5mzcDsGhBm+6sSsNYZouOXUvwq/iozoBG2/DxIX9n3/d/YfNPuENTZdmciCQgIJSNNfjpKXb86zSSCQiNBki7m213+Hv31+Atjsy/HMkErEA0RJqP1Z95VIrPAtvnOd+ep5g6j2L/ozZ186KUqmji5t9WsmhUt7Hse/YO4OdngR//wqI3qeOBGUeBl3OBP28Fxs5h2xWfVW5gqQKQmPElhaeBP5baflZSzFSXAL+8BHw8iKnmK+dZmLTTSCA4im3j67SWJGb6N3wsIpEdQEQrcGqtb9fliC3vsqqj3z9Q9nm5Zyg8gX1vLv6N/KOsLLaxabnntwH/6g4cW6n8/nd8CKz+O/D9Q8rl4fnQw6DWQGA4u7rmDeKay//dlzgy/8rpMIydXM01wI757j231WL7fzclZuRjDbLrKhiHyMz/aZPZ99PrfX9h48j8y1HSBMwFUfv+wJh32O3f5jr3DMrFDKBeZCY01nZfeHsWBYJoq7wc+jxw9xdAQAigDwTirgP6TwM0eta/qFyBLtIKQWLGl/CoTEQi+7lIATFjtQL7vwT+0w/Y9TFLX3W5Fbh3MfBCJvDgcps/JfeA9/tzlbJcNo1V0NR9QBzQtS7VpLZv5rd/AR/0dX6FZTba1qB0+SM/+PR+AIAAlGQD5fmN/orqVF8Bvp7E3o9b33O+3YY3WBm9GlfMZ+tSrOd/A06sVuY5HUUKeFTwWkzvSWXZ3Rw/LgjA0OfY7T0L3YuSleWyq3aN3taMrzG4bwZg6b+kQbaf23RjJ0mr2ZaC8gVWq83H4zAyUydmlCjPlkZKdGFeoU4j2f/v52caivniTLa9oAU6Dmf3+SIyA9hSTVoDcOdnwIhXWWRIjkZre81LspVZjwKoKmbGjx+PxMREBAYGIi4uDlOmTEFurn33V0EQGnwtWLDA4fOdOXMGoaGhiIiIUHPZ6iCPytw2j30vu+BdVVNtFfC/W4Cf/sbymtFdgAd/AO5fAqTexno8AEC7Puy7L8UMj8q06WFbR314WPPsJs/+D2WXgF9fB1Y9zSJTjtizENj4JguJOou6nN3EUhEAO4iYa91fizO4mEm8wXZ15e8T68/PMaEJsFSPoxETOXts6yxUeOKxqdo+UrL+VWXMn46qaxLqfDPXYvO8piIzANBlDBDbi/kzdn/i+nNz4RiZ5DiFVR8emQGAITMalnKn3cu+H/JhVdOVTMBUyUqfozo1fDy6Ls1UeNL76KF82KcgAGPfY16mc1saXizwMROJA1mxBMAEH8AiIUqkZnlkplUb+/sHPgEMfQF4dL0t/ecIfkF+rYiZ4cOH4/vvv8fJkyexfPlynD17FpMmTWqw3aJFi3Dp0iXp6+GHH26wjclkwn333Ycbb7xRzSWrB4/KdLmVVRIYwtn9PFTrCcdWspNCQCtg9P8D/vK7rfmRHL+ImbqTR3sH5l9O2zSWfjFVAecyXH/uorMs9/5+Gss77/ucibr6H6yTvwBrnrP9fGQFYKxo+HxHf7TdtppsFSDeYqq2HfTb9JBFCfyY8vhjGXBkGbvqC41jIm7/Fw232/mh7XbxOWVz4zm72VVpq1i2hivngZ0fNdzOagWOr3b9M+LIkMr/5640z6utBLJ2NOvyU5fhBQBA42JGEIAb61I+Oz9uPO0ox1W/DCdpMHvPxV3HBFR9ek5iUdyLe9Vr15B3hPV6WfcK8P3DwJIH2P2x3R0LsujOAAT2vqks9Hy/tZVASV1FFH8tWne0RcVW/hXIeI8NlQQappgANmaBCwglojM8MlO/2aEhFBjxCtCud+O/f62JmRkzZmDgwIFISkrCoEGD8NJLL2Hnzp0wmex7bURERKBt27bSV1BQUIPnevXVV9GtWzfcc889ai5ZHQrP2KIyN73IDiD86tEb3ww3l/a+Hxg0HdDqHW/H35hXMn3XDfViXaOyeAd+GY4g2IzAJ11o1lRZCCybBszvz07AllogcRAQ2o5d+Xw2kpWDA6xR2rJpTED2eZBVJpgqgaMr7J9TnmIKrBOYSqWaLp9g+w9qza6A/B0lKMtlYW2AHUiH1/W72PGRfTSqJAc49hO7LWiZwCvJUm4d539j3zsMA0a+yW7/Ntd+im9tFbD0IeC7B1yvmnB0gg2Jdr153poXgEVjgANfO9+m8DTwxXjmJ2rOlF5g73eNrmnB0X0CMwnXlLBIpiu4WsnEiekK/HUn8NDKhmkLgHk3Ukaw24e/c+053eHA18B/hwJrX2T+oGM/srJ0oG6SuAP0QSzyBHhnIC88DUBkx4GQaNv9g54GetzJ0mub/wksupUdezK3ssc732L/PFKqSYHjkxSZiW18O2dIYkbB44KX+MwzU1xcjMWLF2PQoEHQ6+1PutOnT0d0dDQGDBiABQsWwFqvjHLTpk1YunQpPvzwQ7iC0WhEWVmZ3ZdfkUdleJQkqq7sz5urEK7QefrCGUGRtoMOP9mricVsiwI5qmSS07Uu1XRybdPls7sWAEeWs/9l59HAI2uBab+wMQmxPdkHdNFYYO//2IRuUxXLTd82jwkaoOGJiqeYQtuxAwvQsALJU7goiu3BhBsvFfZH8zyrFfjxr6yirV0fZuxLu5cdzMpz2f+Vs/u/zHvVYajtSlLJq+VMLmZuBHrdzQRvbQWwcRa7vzwP+HwsM2QDbF6MK/+v4vPse/2+J640zzPVsBMcAOxt5IS+6Z9AZgaw+9Om1+NPuGk1qpPzixyORstSPwAzZruS8nU3MgOw6qCgSOePcyPwvi9cOx64giiyqMfKJ9l7OvlGIH06MOb/gHu/Bv78m03UO8IVE7AoMn/Zr685jupxv0x975IuAJj0P+COTwBDGHt/LhjCDNlh7Rse1yUTsIqRGVe51iIzAPDiiy8iJCQEUVFRyM7OxsqV9pURb731FpYuXYoNGzZg8uTJePbZZ/H227aeKEVFRZg6dSo+//xzhIWFubTPd955B+Hh4dJXQkKCon+TW+QfZU2SABaV4fCDgFeRGS5muje9rS9TTZePMyFhCLPlnZ2RPISl3CoLbH1pnMErD0bNAh5YCiSls5/D44FHfmFmOVMlsHoG8xDFXcec+Fo9i14JWlYKLveA8BRT9wm2vL5SQzD5FRR/faI6AYER/mmet+cz1tdIF8gOnlo96y9xwxPs8d//ww7Exgpg35fsvoFP2rwESvlmaittUbvkG9lV+q3vsp8PLmZi89Ob2fs0qDWgD2YRuKb2b65lHjSgYbTAleZ557YwQQWwfTuKzlUW2npwlF1sfD31ubCPpUbLfFT94UqKSU7aPUB4Ivsc7v+q6e0l4eiGmGmKbmOBsHjWXO7be4GPBrK1mI2ePZ/VwiKRm//Jfh7yDPDwKuCW/wcM/AuQenvD/jL1cWVG0+UTrLvv9veBojMOHuevhYNjoSAA190L/GU7kDSECS6ApZjqr6tN3fFJ0ciMt2JGgYaCCuG2mHnjjTccmnblX3v32k5Kzz//PA4cOID169dDq9XioYcegihTr6+++irS09PRu3dvPPvss5g1axbee89WYfHYY4/h/vvvx9ChQ11e48yZM1FaWip95eT46R9eXQJ8N4VFErrdZhMUgK0hk6diprrEdkB1Vq0gRwkxU1nEzGlNeQq4X6ZdH8chZTlavS033NRcEP4hdtSELzCMCZzedRGYiETg/qU283FoW9t+DtQdrOUpph4TZW3XFUoz8fXG1okZjUb56pqC48CicbZohyOKM4Ff/8Fuj3rL/qDafxrzXBUcBc5sZILCWMpETOfRNjHj6CDtjMIzLG1UW9XwseydLG0VnsC6rAKsXJVfla98komSqE4s4sZLZuVzfRxRks0+Z/qQhqZGV5rn8SgQ5+Dihtsc/IatHWBpHHdY+xJLjf7wmHsRB6vVMw+PNC3bRTGj1QNDnma3d/+38W1F0fXuv+4QEAI8tgkY9BS7ECo8Cfw0HZjXi4kad/4PpmpW9r/3fwAE4Nb3gJGvNy5cHOFKZEb+3jnv4HMoN/86IyIRePgnYPQ/2fFtwJ8abiMvz/bG12WssAn3UC/TTKUXmk2vGbfFzPTp03H8+PFGv3r2tJXhRUdHo0uXLhg1ahSWLFmCNWvWYOdO55NaBw4ciLKyMuTnM+W4adMmzJkzBzqdDjqdDo8++ihKS0uh0+nwv//9z+FzGAwGhIWF2X35HKsVWPFnVkUTngDc/r79495GZvgHJCze5nhvDEnMHPRsfwDLN399V9N9R7g3oakUE4dXNTVWom0st4U0nUWitHpgwnwWpXk8o+EHtU/djJOD37K0hTzF1P5628Gi7ILz6ih34KKIX1EBMt+MQibgjNlA1jZg1VM2A2F9ts5hoevkGxseJIMigL51hvvt82zdYG94gomv6M7sZ3dM0WueZSmjrbMbPsa9Jsk32p9YRr7OhAh/7E8bWCpWaoXfRCRLfnJtcEUra5536WDD37WYbe+9gU+y74e/s09tiaK9Ubo8z/Wqt5IcW4rr/G/AHhdSVNVX2NX+v3uwFvKuDifkuBuZAYBe97BS66IzjacVKwpYBFTQ2E5qShHaFhj9FjDjCIvAhrZjUYSfprNjj6sicvPbrORfawDu/hy44XHP1sOjtRf2OhbnQD0x48BLJS/LbgyNFhj0Nybo5KXsnOjOLLpcU+pdfxceldGHMMOvJ4TGMT+W1WTvdfMjbouZ6OhodOvWrdGvwMBAh7/LIzJGo/Ow4YEDBxAYGCiVX+/YsQMHDx6UvmbNmoXQ0FAcPHgQd9xxh7vL9x1bZ7NmcFoDcO9X9sYvwCZmSi94VpYqpTCa8Mtw4q6r21+25858fgLmpYPO4OkiR83yHNFpJDuIFp5iV/WO4AfnVm3ZsEpnCALrYeFomy63ACExLJR++lf7FJNGw07sYXX9E7zNS1cV22axtJFdkSlZ0VRTajsJF5+zb8jIuXLeNsTv5tcdR8oG/oUdmM7/xoydgeEsLQfIIjMuemZqymwH9L3/a1g9Jpl/61UlhrUDpqxg3UUf/MHmrXA0cdkRkiE1ueFjWh177QHHXpfs34HqYrbPm19jLeQrL9sPYczazk7y+hD2mYboeqqJi/+AuhPHr687/38WnQXWPM+aFW54g/mZqq8AObtc2xdQr5LJhagtJzDMlrptbJo2/1+Ht7e1wleawHBg8NPA04eYqNEagLMbgQ8HMk9NU5GJzLrqyNv+xaKunhLXm3Uwrq1w3A/pynnbSASAvfflazPX2l5rd14LR+gMts+jN6kmyS/jYVQGaJa9ZlTzzOzevRvz58/HwYMHkZWVhc2bN+P+++9HSkoK0tPZB2bVqlX49NNPceTIEZw9exafffYZXnnlFTz++OMwGNiHJDU1FT179pS+4uPjodFo0LNnT0RGNmIm8ycn1wJb6ro83vZv+/QSJySahVIhOi49Lclhhk1nB72CusiMq2ImMMz2QfAkOlNbaXvTZjuPrKGm1HYgbaySyW5t4UByXWO/s5scb8N9LK7+vY7Q6m39LPYutE8xcXhKqKmTZ1Pw9UYk2V/9xPeDYs3zjq+qm/tUF4nY+l7D6My2f7NqiZQRNu9IfSISgJ532X7uN5WF/AHbe6bsomsTe89uYvsD2HtBnq4xlgMX97PbfKidnMQbgOsfY8ZIDm9mltfE69HUBOf06ez7H0sbXknyK+uu41iX07S6ikn52vfVRWV63cX+X4DrUQJeQXfzP5ip2lzNWsXLw/OmGlYy/J9+rN+LqYoJOR7Vc6eFQ1URq0yC4Lh/SmN0Hs2+n1rnfBupx4yCKSZn6AKYqHliG4ue1pazKOQ39zg3hZuNtqioo/eZO2g0NmF/8JuGj/PUePsBTHBV5Nv7u4rPMR9MQCgT7N7Cj3/epML5RZanfhkO72p+tYuZoKAg/PDDD7j55pvRtWtXTJs2DT179kRGRoYkVPR6PT766COkp6cjLS0N77//PmbNmoW5c+eqtSz1KToL/FAX0hzwGNDnAcfbNVWe/dtcdjDd8n+Of7++udQVvPHN8PJCgKUcnIW9L+5n20UkAa1iXH/+xLorQm4OrY87ZufG4KmmMxvsU0wcHlb21mTn7PUJDLPd5210hpew3vgMM8sWn2U9ZDglOcCBuhPysBcb/r6cQU+x7xodcL0sJB/cmj034Fp0hl/Rh9YduHd+ZDtpZ+9kB/aIJNfTE21SAQgsmtbY/KCmqmva9wMSBrKwuLw5HO9lAzBDKGCrfDu1lr3Pq4pt0ZW+U21XpK6ImStZLFIpaIDuE4EJH7ITW84uVjkEsIuLT4bVjRQQmaB46Cd2Au9SJy7cETO8XDa0LRNn7sDLgbO2O+7JBHhWyeQtMV2AaWtZPy1dIHufOYseFRxjr3NgRMMBm55wXZ2f69wWoLReNI4L4V5321LIct8M99pEd3bfr+MI6fjkReSYX0R5E5kBbP/bq13M9OrVC5s2bUJRURFqamqQmZmJjz/+GPHx8dI2Y8aMwYEDB1BeXo7Kykr88ccfePrpp6HT6Zw+79SpU1FSUqLWsj3n0iEWHv50BDNQJgwEbmliUnVjvpmsuhkmWdsdh1RdLcuW442YqT8KIMdJdIanolxNMXG4qddZRVN9M62ntOlm7+XpPt4+9cKvhL01AfPIjKP1ulJd0xSlF22m335TWa4dYB4aHp3ZPo8d1DsMZd1EG6NtT+C+JcADyxq2p3fVN2O12k4wt89jaZsr521Xr7x/Rv0UU2MEhNjaGDQWLXOl78mguujM3v/Zoky5+1kqJ6AV0PEmdl+bVKBdXxZh+uN71qHVYmSRkvi+7okZXu6dNJidPCISgTF1x4VN/wTWvgx8djPzwIW0Ae77jhnZOw5jJz+eNnNLzNSdXDzxs0R3Zvu01NpSNfVpKgqmFhotew155MzZ54dHntv1VkZARCbXjYQR7XvgVBTYotTdxtmiQHLfjCfpvsZQYkaTUpEZyQR8lYuZa4LKItZR8uMhrCHT7k9YeDe6C3DPF/bhckdIFU31rngrLtsUfdnFho2JKi6z0mMIrlcrAF6KmXpNo7J2ON7udF14uoPr1WcAbGKm6Izjxn6eiDdn8OgMwOakyOHiw9uKgcYiSVLfEy8qmv5YCkBkB9mIRJaekUdnynLZzC6AtSd3ha63AinDG97vqm8m9wDzmhjCWFqr/6Psfj7EkF+xJrv53uBXo85STVYri4AAjZ9gu45lYqf6ii1lwK+sO4+2j2LwiOqBxTbjb9+H2cmRh9dLXaiS5Ckm+fuszxS2P4uRdVq2mtlk4r/uBLrW647razEjCLZUk7PIhz8iM3Ka+vxwk7ezmXCecN197PvBb2zHhZNrAIjsuBre3l7M8G0aK8v2BH48uXyy8SoiUWTdvh0NF1YsMtO8es2QmPGGNc+yCp/8P9iE6h53MvPiX3e61ozIWWQmu55QOL/d/meuylt3AAKCXV9v2zQAArsSddevIU19rTuQ8Om3csrzbWkiRy3LGyO4tayxXz2xVVnI0gyAMlc4Pe9kJ4l2fexTTADrhKrRsxSUKycrR4iiTczIZ9JweGQo94Dnc6D4PBd+lWoItUVntr7HhmvyDsne+gZc7TXDp5+njGD+pOsfZ5+LnF0srXfpEHvcncgMwGYHAc4jM+W5TBho9DYDtyM0WmDgX9ltnv46XtfpOPU2+2173sU8EAVH2edNF2ibVeNqZKY4k73GgoaJFY4gALd/wBoWGsJZ3597vgRCoho+BxczJVmul3Tz3h+eVhrxVNPpXx0Len+LGZ7OubjfsW9GHplRiu4T2AynotO2YxwXwjw9Gd+fvWcqC2yflUKFIzORyWwd5urGBe7Oj4Dlj7Ly9PooHZkhMXMVcN39rEpo7Bzg2ZPA3YvYbCRXBq8Bsi7A9cRMVp14EbT2P3M89Y8YWtlKNR2VqDYGj8z0m1r3+4cb5tT5lVy7Pp51luTRmfpt57l4i0y2GVO9wRAKTN8H/GlTw+oeXYCthNLTVFNJNjMqavSODZhRnVgUxVzDhmC6GwHKO8JOstoA5sPg8OhM0Rlb+e+wF7wPtbvaa4ZH5XjlUGgsK/cFgJXTWR+Y1inuGyGl8mwnYoafXCMSWeVSY/S+nxnOi88B2/7FvmsDbNEITlCkvcDpPtFWYeWqmOEppuQbG/rHwuKAv+0DnjvJmqY5e43C2jMfk6XW9XJcbyIzABO/uiAWFa4vIDN/Y9Fnjc5x5ZgviOrsvPmkudZ2vFAyMhMYZhMtB79h5nY+T65b3f36QHvfjNViEzVNlWW7ikZrO4Y7SzXlHmQVcwBLv9ZvbKd4ZCZHmW7NXkJixhs6jwL+vJWdRBorF3aGVJ6dY9/lkosXbjyr37vA3bJsOZ6kmkw1Nk9Cp5EszC5aGoZ5+ZV5l1vdXxdg89nUNwFL4s1BlMNTtDrnDf2kVJOHnYD56xPT1XEreY2G9dIAWApm0z/de35eat1ljH2PIUOozRcCsKgT94F4g+SZOeNceJVdqou8CEAn2YC89LpICD8RexIl4uXZhScdd4N1x8NhaMUaBQKsFwlQN/jVQb+N3jLzfj/Z8FspzXShcSHqKMVkt5ZQNv+nMbQ62/5cTTVxMcN/z130gcyzA9inmixm1vwPsK948zWNNZ8sOMaEX2CE8mKLVzUdWc6iMlYTEynyFFJyXdTx/Db2OphrWLRGybXwi1hHF1vGCjaTziqLWNWvPlUqMhMaZ5vdxp/Tj5CY8QZvr3hDYpjxUF6eXV1iuwId8gwLUZdk2V8FeuMf8UTMFJ1hV9WBEay7KjeTyj8kphpbWTW/MncXuQlYfpLwRrx5QmMHC1dwpYy8z4OsKykA/DaHGXddwWphuXDAVmYu5/rHbdVHw15UyADZAYDAUm+VTqrY+EmvfX/7KERsD9sAQcB9LxXAIiGB4cxbUt+IDtiEtqtpj+sfZ5EFse5qkl9x16fjTWyac+8HbNV2gC2yZKp0Pri16CwTd4LWPsXkCe74ZkRRFpnxopJHKtGWiZn9X7BITWAEMPwVz59bCZw1n5T8Mtcp896X02Eoa1JaU2KLfNR/78h9M/y9Gt3Z9Wi9K/B2Bb9/YO/hAYBfXmC+ubB4ljkA7C0BZqPtPevpXCaOVsdGyQDNItVEYsafCEJD30zOLgAiC+1Hd7I1u+PVTXI/hidlynIx42p6Q96OWxBsB3b5h+T8b6w3Rmg725rdpW0vdpKpvGzvV8n3sZiRGrV5GZlp6vW54XHWvhwANv8/1hOmKc7/xqIcgRG28QxyDKHA1NWsMqnzSLeW7RR9oC2k7Mw3w/uS1J/0C9h6vACeRWYEoXHfjLt9T8LaMZECMLHhLJKo0QKTFgITP7I/MeqD2IUI4DzVxKMyHYc59sK4gztipqqYiSygYVWaO3Axc2E3e87qK7YI4vBXPItEKwmPzNQXM2r4ZTgare0Coqqu8Wi3el6r+H7MX1VZAJyo89S404XZFfpOYXOcaitYv6Llj7K01+GlrJ2HoAHu/MTWWV1+0cm7/2oDGh/66SpSebb/ZzSRmPE39cUMTzElDar7XtdMjqeaSi/Y/Bi8GsodYnuyA3hFvus5eEnM1H0ouZi5sNdmwDv5C/ve5RbPr4j0QTYhwVNNTZlp1YCnmYpOe2bQdWe9g/4GjKibm7ThDZb6aGxC9KG60tCedzrvvhrbg1UmKYnkm3EgZkw1bIgl4DgqlzKCVVSN/qfnV4ONDQH1pFR4yN9ZVLTHHZ6JjaZ8M7y7tLMUkzu4I2Z4mWwrD3rMyIlIYGJctLKI6+Z3WJfkmFRbms6f8OaTpdn2TRDVqGSSw1NNAPMz1W+Iqg+0Ca0/6ibRu1Nx6gqGUDbHacQ/2LH8yHI2bXt13eTzoc+zi4aEugh6wTEmSAGbX6ZVrDKRK8k3k9X4dj6AxIy/4WKGl73yCAwXMfxKloscfqKM6tR06bcjAoJtEQ5XU031B6XFdGORAVMVMwKLou3K3NuTqGQCrus3461484SweFZlYjXb5qq4itlo+x1XI2dDnwOG1XkRMt4FPh3e8LWpKWONFI/+wH52lGJSE7lvpj5Z22xROR4ClyMIwIhXbNVWnuBsRpMoS9G6U13TJhV47jS7gvWExsRM0VlW4ajRNbxy9wR3xIyUYvLQLyOHR2d2LWBT1wHg1v9r2mTtCwLDbAKXR2fMtTaxq0ZkBmCfA97ZPPU2x4KA+2bM1ey7UmXZcjRadtyYto5FR3jRQcJAWyuGVjHMLA3YxmFIfhkvzb+cZlTRRGLG3/CKpuJzrJEXP4nxyEziQAACO4mU5ynjH+Ef9KzfG91Mov7QOo1GlmrawU4wZRdYBYQnngg5km+mru09F2/RnT0Tb54gCJ53Aj78PRNBoe3cC/Pf9BJwx39Z6DfvD9Z8cf2r7DXPmM0mB2+cZRsYmXCDe+vyFqk824GY4UK2y2jlfQoc+YwmeXo0Zzfz8gha9z0iAcGeexka6zXDWysk3KBMOsYjMaPAAEgeZbuwhxn+u92mjKFcKerPObt8os78G67uqIWxs1nH38F/d/x4/VSqUmXZjkgYwDpF932Ivd/u+tRebCbJjtOAbC6Tl34ZDokZQkJKM51lB2armR0o+ZskKNJ2IM/6XZm2/rwHzL7Pmx466WxQmmQC3mE7maUMb7o6oyl4RdOlg6x6glcUeTvGwF08mdFktdh8L+l/de/ELgiseu3JPczPIVqB3/8DzO3K/DQ1Jewq645PgCk/qicanOGsPFselXO3t5A7tEllXoCqItsBWRSB9XVG1N73eZdWcZfGIjOSb8PBTDZP4GKmsqDp+VhKipn21zNhALCKHO7vai7UNwGraf6VE98PuOszVl7v7HFd3XtR0KofUQ4MA8b/B3h0fcPXnV908ianfCSIUpGZZjSficSMv+Fv9NILNt8Bj8pw+BDGrO3KRGa63cZyyrUVLHXRGMVnHQ9K42vM3mEb2KjEySyqM9uXqQq4fFzZzr/u4ElF07GV7P8VGAH0e8Sz/baKYabT+7+3NYCL6QbctRB4chfrSeKPMD9PM13JtPf05P3B8uVag/dRucbQB9lC5lxgHvuRRQ30wcDwV9XbtyMaEzO8OaBSvo2gCPaeAmydjp3hbcM8OVod65oMsJJ/X48vaAre8DL3ILvo4iJSLb+Mq8h9M607+i6i7AguZnIPAKZqW5pJ6chMqf97zZCY8Tet2gD6EHYlzru61hcz3D+TuVXmx/Di5C4IwM2vsdt7PmvciS43/8qvduJ6s6uPqiI23wbwvCRbjkYDxNdd0V7c59lATSVwN80kiqwJGwAM/AvrZ+INXW4Bpu8G/rQR+MsOoNckZcs73SW0HUsjWs22qzBRZKkwgLXhV7vviNw3YzbaymMHP+38KlktJDFT77Njtdh8PZ5W9TnC1VSTkpEZABjzDjD5G/+XYjsiKoW1IbAYgbzDtsiMWn4Zd+hQ16fH21ly3hKZzMzgVhM7nsoNwEoQFs+iT5ZaW5d2P0Fixt/Iy7N5dVFSvZwrFzeFp5hnQhfkfROmlBFsP5ZaZjp1hrNBaboAmxEO8LzrryP48+bsBi4rIN48ge+v7CKwaCywaBzw+W3AlxNtolPOmQ3sJKYPsZ867Q0BISzt5qy5ny/RaBqONTiynA0j1AUCI99Ufw1y38zuT1lEqFVb74zFnsLD6+V59hVvhaeY8TOglePuz57iipiR95gJV0jMBEWyIYr+FNLOEARbBCRru60/l78jMwC7oLnxWVulor8QBJtvJmuH8pEZrY4JGsDvqaZmcJQkECWrwghpYzMFS/dF24uJmK7eH1wEARhZd2V7cLHz/iE8MtPGgYlNPonZ066/juAm4OOr2FWXPsS7BmCeEBgu8yptZxU7539jqcAfHgN2fGi/PU/X9X/E/z041EIav3GGVVetq7tav/FZ36Qg+OuRsxvYWtdkcMSr/ulEGxzNUmsQ2WwoDk8xte2lrAh1RcxUX2EVLYAy1UwtAe6b2f8VO1YYwv03M0qOoRWLfvP0rD+RF2soHZkBmo0JuBnU2BF2H76kQY7Na0mDZcJCodBlwvVMhJz6hTXEuueLhts0NsI+KR2oG4TcYNqvN3AxYyxj39t080904sEfgJydLAUoiux7zi42HX3dy8w7MuTvzJidvYM1opI3iLvakMqzTwNb3mFXea07AoOe8s3+eZqJp3Zie9r3/fAlGg3rflp8jvlmuNhQ2i/DcUXM8JNJSBvvjfgtBS5meP+juDTfm+ObO1zM5Oy2NVRUKjIDMDGTBb/3miEx0xyQu925P6Y+yYOBvQvZbSVTLjf/g81UOvYjM9DJ880Wsy1i46iLZWI6i5i0iq2byK0QYXHMo8GveH2dYuKExrJpuXJ63sXy9Bn/B2x4nQka3sOh9wO+9274Ep42ObOJpd8ANmTVV1VEoXHsf19d1wBs9Fv+TX+Et7eJGY5kQlXQLwO4JmZKFTT/thTa9WVVbnw0RXPwyzQ3YnsAhjDbxaGgsXWwVoKI5lHRRGmm5kD9yIwj5D4aJc2wsT1YzwSA9TGRcyWTGcf0IbbKGjkBIcBTB4Fpa5W/Gmrfz3ZbyQGT3iIIwPCZLL0BAJv/CZz5lR0gBvsoQuEveDVRaTarcOs+gU2J9xWCYIvOdBplP/PJH9TvNWO1MiMqoJ6YKclyXjWitPm3JWBoZd9puzn4ZZobGq0tggUwIaPkRUAzSTORmGkOtEllyjky2blQCY0FUm5mhsf2/R1v4ynDZ7JupWc32oYYArJKpi7O0zwajTpXx/FyMeOnyExjDH0eGCUTfz3vah65ejWRe7n0IcAt7/h+DUOeYeXC4+b4ft/1kU/PBlhZfm0FM+hHK9z1Nbw9qxox19jm69RHye6/LYn2shO1Ur19rjbkg1KV9MsAMjHj3/lMlGZqDgS3Zl0cdYbGvSEPLGXflRYPrTsCQ2YAW98DfnqKmRdjujYcY+BL7MSMn8sbnTH4aSZCD3/XPEtXlSYogonpijzWsZhPzPUlKcPZV3Ogfq8ZyfzbU/leQFo9219JFks1OUpnXouRGYB1vt27sO6CsJn1wmkuyMWMkn4ZwL7XjCj6zbNEkZnmQmRS028yjVY9j8BNM1mbfFMl8P1DrNNo/TEGviS+PxNZCQNZL57mSv9HWJqtuTUUU4vxH7AGdQP/4u+V+J8GYuYg+650ionTlG9GEjM+rvzzN11uYYImfXrzaGPQHInvxwoUAOUjM2HxLM1urrF1GPYDFJkhGBot6zL73xtZRGb1M0CBHyMzAcHA3/ZTZUJzo8styjRHvBqQp5lEUf0OtJHJrK+PUzFzDRqAARYxfHS9v1fRvNEHshRczi7lxYxWzwRNaQ4T1KEKP7+LkIwlbITGApP+x1T24SVs8i/gn8gMQEKGaN7wNFttBevxckkl8y+nschMdQlgLK1blxsDTolrhz5TWBNENYzz0owm/5Vnk5gh7EkeYt+1Uhd47YWtCcIV9EGseR7AGisaS1koX61IZmNihqeYgqP900SQaP70nQK8kGmb9ackzaCiidJMREMG/52FI0+tZVUZzbGVOUE0B8LbA1WFwPHV7OfYHuoNFnRFzFxrKSbCPdSKdnccxtJNvEu3HyAxQzREowHuWABsfgfoquCYAoK42ghvz4y/p9ayn9VKMQE2MVORB9RWMV8Zh8QM4U963++/btx1kJghHBMUCYyd7e9VEETzhnsFakrYdzWbtgVFstlDxlLmTZD3X7oWu/8ShAzyzBAEQXhKfbOtmpEZQWAtHICGqSaKzBDXOCRmCIIgPEUuZjQ69Rs88n5GDcRMXRUJiRniGoXEDEEQhKeEy0YHtElVf+imMxMwRWaIaxwSMwRBEJ4ij8yomWLicDFTnGm7r6aUfQH24oogriFIzBAEQXhKSIytTbwvJjbzyeVnNwL7vmC3eeff4Cg2RZogrkFIzBAEQXiKRgNE13XITrhB/f0lDwF63Q1YzcCqp4B1rwBX6qI0FJUhrmGoNJsgCMIb7v4cKD4HxKWpvy9BAO78lEVotrwN7JgPHFrCHiO/DHENQ2KGIAjCG6I7sS9fIQjATS8C0Z2BH//COhADJGaIaxpKMxEEQbREet4JTF1jm4Isb6JHENcYFJkhCIJoqbTvBzyxDTi/Deh2m79XQxB+Q9XIzPjx45GYmIjAwEDExcVhypQpyM3NtdtGEIQGXwsWLLDbRhRFzJkzB126dIHBYEBCQgLefvttNZdOEATRMmjVhkVp1BpwSRAtAFUjM8OHD8fLL7+MuLg4XLx4Ec899xwmTZqE33//3W67RYsWYcyYMdLP4eHhdo8//fTTWL9+PebMmYNevXqhtLQUhYWFai6dIAiCIIgWgiCKouirnf3000+YOHEijEYj9Ho9W4AgYMWKFZg4caLD3zl+/DjS0tJw5MgRdO3a1aP9lpWVITw8HKWlpQgLC/N0+QRBEARB+BBXz98+MwAXFxdj8eLFGDRokCRkONOnT0d0dDQGDBiABQsWwGq1So+tWrUKHTt2xOrVq9GhQwckJyfjT3/6E4qLi53uy2g0oqyszO6LIAiCIIirE9XFzIsvvoiQkBBERUUhOzsbK1eutHv8rbfewtKlS7FhwwZMnjwZzz77rJ0f5ty5c8jKysLSpUvx5Zdf4vPPP8e+ffswadIkp/t85513EB4eLn0lJFAzKYIgCIK4WnE7zfTGG2/gzTffbHSbPXv2oH///gCAwsJCFBcXIysrC2+++SbCw8OxevVqCILg8Hfnzp2LWbNmobSUzRp5/PHH8emnn+LkyZPo0qULAGD//v3o168fTpw44TD1ZDQaYTQapZ/LysqQkJBAaSaCIAiCaEG4mmZy2wA8ffp0TJ48udFtkpOTpdvR0dGIjo5Gly5dkJqaioSEBOzcuRPp6ekOf3fgwIEoKytDfn4+YmNjERcXB51OJwkZAEhNZf0UsrOzHYoZg8EAg8Hg7p9GEARBEEQLxG0xw8WJJ/AgkDxqUp8DBw4gMDAQERERAIDBgwfDbDbj7NmzSElJAQCcOnUKAJCUlOTROgiCIAiCuHpQrTR79+7d2L17N4YMGYLIyEicO3cOr732GlJSUqSozKpVq5CXl4f09HQEBQVh8+bNeOWVV/D4449LkZWRI0eib9++mDZtGubNmwer1Yonn3wSo0aNsovWEARBEARxbaKaATgoKAg//PADbr75ZnTt2hXTpk1Dz549kZGRIQkVvV6Pjz76COnp6UhLS8P777+PWbNmYe7cubYFajRYtWoVoqOjMXToUIwbNw6pqalYsmSJWksnCIIgCKIF4dM+M/6C+swQBEEQRMuj2fWZIQiCIAiCUAMSMwRBEARBtGhIzBAEQRAE0aIhMUMQBEEQRIuGxAxBEARBEC0aEjMEQRAEQbRoSMwQBEEQBNGiITFDEARBEESLhsQMQRAEQRAtGhIzBEEQBEG0aEjMEARBEATRoiExQxAEQRBEi4bEDEEQBEEQLRoSMwRBEARBtGhIzBAEQRAE0aIhMUMQBEEQRIuGxAxBEARBEC0aEjMEQRAEQbRoSMwQBEEQBNGiITFDEARBEESLhsQMQRAEQRAtGhIzBEEQBEG0aEjMEARBEATRoiExQxAEQRBEi4bEDEEQBEEQLRoSMwRBEARBtGhIzBAEQRAE0aIhMUMQBEEQRIuGxAxBEARBEC0aEjMEQRAEQbRoSMwQBEEQBNGiITFDEARBEESLhsQMQRAEQRAtGhIzRIvmxwMX8c6a46iutfh7KQRBEISf0Pl7AQThKQXlNXhh2WHUWqzYn30FC6cOQFig3t/LIgiCIHyMqpGZ8ePHIzExEYGBgYiLi8OUKVOQm5trt40gCA2+FixYYLfNunXrMHDgQISGhiImJgZ33XUXMjMz1Vw60QL4emc2ai1WAMCe81dw3yc7UVhh9POqiObI1lOX8dGWMzDXvV8Igri6UFXMDB8+HN9//z1OnjyJ5cuX4+zZs5g0aVKD7RYtWoRLly5JXw8//LD02Llz5zBhwgSMGDECBw8exLp161BYWIg777xTzaUTzZwakwWLd2YBAJ4a0QnRrQJwNLcM9yzYgYsl1X5eHdGcEEURz3x/CLPXnsQXO7L8vRyCIFRA1TTTjBkzpNtJSUl46aWXMHHiRJhMJuj1tnRAREQE2rZt6/A59u/fD4vFgn/+85/QaJj2eu655zBhwoQGz0NcO/x0MBdFlbVoFx6Ip27ujIl94jFl4W6cK6zE3R//jq//dAM6xrTy9zKJZkBWUZUUsZv36ymMv64dYkINfl4VQRBK4jMDcHFxMRYvXoxBgwY1ECDTp09HdHQ0BgwYgAULFsBqtYWC+/fvD61Wi0WLFsFisaC0tBRfffUVRo8e7VTIGI1GlJWV2X0RVw+iKOJ/21ma8eFBydBpNegY0wpLn0hHSkwIcktr8NLyP/y8SqK5sC/rinS73GjGu2tP+HE1BEGogepi5sUXX0RISAiioqKQnZ2NlStX2j3+1ltvYenSpdiwYQMmT56MZ599Fm+//bb0eHJyMtavX4+XX34ZBoMBERERuHDhApYsWeJ0n++88w7Cw8Olr4SEBNX+PsL3/H62CCfyyhEcoMXk6xOl+9tFBOHzR64HAOzJKkZxZa2/lkg0I/ZlMzGT3jEKALBs3wXsz77S2K8QBNHCEERRFN35hTfeeANvvvlmo9vs2bMH/fv3BwAUFhaiuLgYWVlZePPNNxEeHo7Vq1dDEASHvzt37lzMmjULpaWlAIC8vDwMHToUEydOxH333Yfy8nK89tpr0Ol0+PXXXx0+j9FohNFoM4KWlZUhISEBpaWlCAsLc+fPJdykqMKIhdsyYdBp0TbcgLbhQWgbFoikqGAE6rWK7GPa53uw6UQBHk5PwpsTejZ4/Nb3f8PxS2WYd29vTOwTr8g+iZbLmHlbcSKvHAse7IeNx/OxdN8F9IoPx49PDoZW4/g4RBDXKmcKKnClqhYmsxW1FivMFhFtwgxIax/hl/WUlZUhPDy8yfO3256Z6dOnY/LkyY1uk5ycLN2Ojo5GdHQ0unTpgtTUVCQkJGDnzp1IT093+LsDBw5EWVkZ8vPzERsbiw8//BBhYWGYPXu2tM3XX3+NhIQE7Nq1CwMHDmzwHAaDAQYD5cT9wQcbTzs0WbYLD8R3f05HQutgr57/7OUKbDpRAEEApg7u4HCb4V1jcPxSGTafLCAxc41TVmPCyfxyAEDfpAj0S4rE2qN5+ONiKb7fm4P7ZJE9omUgiqLTi2FveeOno/j1WD4+eqAvrkuIUGUfzZmfD1/Ck9/sd/jYqulD0Kt9uI9X5Dpup5mio6PRrVu3Rr8CAwMd/i4PAsmjJvU5cOAAAgMDERERAQCoqqqCVmt/Rc9/lntrCP9jtYpYcyQPADAyNRY3dY1Bt7ahCAnQIre0Bq//dBRuBgIb8Pn28wCAm7u1QYfoEIfb3NS1DQBWjmuxerc/onG2nrqMm97bjNnN1IdyKKcEoggktg5Gm9BAxIQaMGNkFwDA7LUnUFJFqUhv2XmuCF1e/QWLtqvfLkMURUz/9gD6vfUrsouqFH3uCqMZ3+zKxsWSakxZuAtHLpYq+vwtAe5FbBNqQJfYVujRLgzRrQIAABmnCvy5tCZRzTOze/duzJ8/HwcPHkRWVhY2b96M+++/HykpKVJUZtWqVfj0009x5MgRnD17Fp999hleeeUVPP7441JkZdy4cdizZw9mzZqF06dPY//+/XjkkUeQlJSEPn36qLV8wgP2ZV/B5XIjQgN1+OiBvvj8keux9u9DsXL6YOi1AjadKMC6o/keP39JVS2W7bsAAJg2xHFUBgD6JkYgNFCHK1UmHLpQ4nCb/LIan3lqDmRfwYQPt2P7mUKf7K+61uKTfjuLd2Xhkc/34HxRFRZknEVOsbInFyXg5t9+SZHSfQ+lJ6FrbCiuVJnwr19P+WtpqrIvqxgHc0p8sq8vd5xHrdmKDzaeRo1J3U7c64/l4+fDl1BUWYsPNp1W9Lm3nb4s9a0qqzHjgc924WiueoLmTEE5/vL1PvzfLydQVmNSbT+ucjKvHPuyrkCnEbD6b0OwfsYw/PzUjZg+vBMAYPf55u0zU03MBAUF4YcffsDNN9+Mrl27Ytq0aejZsycyMjIkoaLX6/HRRx8hPT0daWlpeP/99zFr1izMnTtXep4RI0bgm2++wY8//og+ffpgzJgxMBgMWLt2LYKCgtRaPuEBa/64BAAYlRqLAJ3trdWpTSj+PDQFAPDmqqOoMJo9ev4f9l9EtcmC1LgwyczpCJ1Wg6FdYgAAW040vJrIK63ByH9l4Pb/bINJ5SZqJosVzy87jEM5Jfhk6zlV9wWwK9cHPtuJwf+3CfuyilXZh9Uq4v/9fAyvrDgCi1VEK4MOVtF2Vdec4GKmb2KEdJ9Oq8Eb43sAAL7emYXjl66uasefDuXiro934K6Pf8chlQVNjcmCzScuAwCuVJnw44GLqu7rnz8fk35eceAizhdWKvb8G46zY8XkAQnokxiB0moTHvxsF07kKfv+MFms+M/G0xj7/jb8ciQPCzLOYsScLfhuT7ZfI8nf7s4GwKLqbcJs2ZUBHVoDAPZnXWnWkW7VxEyvXr2wadMmFBUVoaamBpmZmfj4448RH2/zMIwZMwYHDhxAeXk5Kisr8ccff+Dpp5+GTmdv5Zk8eTL279+PiooKFBQUYOXKlejWrZtaS/eKjcfz8f2eHLfTKQeyr+B0XW6/JWK1ilhbl2K6tVdcg8enj+iExNbBuFRag3keXg3/frYIAHBHn3ZN5syH16Watpy63OCx/23PRHmNGRdLqrEnU50TPufrnVk4U1ABANidWYxas7ri6dilMuzPLoHRbMVT3x5EaZXnV3yiKOJYbhkOXyjBqfxyZBdVIbekGn9ZvA+f/saEyzOjumD+/SxC+v2eHJRWO95fpdGs+lV7fSxWEQezSwAAfWWRGQBIT4nCuF5xsIrMJ+Ft+rO58PuZQjz7/UEA7O9/dukhVf/vW09dRrXs+RdtP6/a/3LhtkzkFFcjNsyAQSlRsFhFzN98RpHntlpFbK678Bnfux2+mHY9rmsfjitVJjzw6S6cUujYfPhCCW7/zzbM/fUUai1W3Ng5Gh2jQ1BYUYsXl/+BCR9uw97z6h6THFFda8EP+1nU+74b7H1k3dqGIdSgQ4XR3KyFPw2aVIhKoxnPfn8Ij36xFy8sP4yPM866/LsXrlThnv/uwKQFO1DpYdTC3xy8UIJLpTUICdDixs7RDR4P1GsxawK7Gl70+3m3w7eiKErltP2TWze5/bC6yMzhC6W4XG5LuZRWmaTOwQALW6tFUYUR/5YJt2qTRfXQ/08HbeNCLpZU44Xlhzw+uXy/NwdjP/gN4+dvx+h/b8XQ9zZj0P9twrqj+QjQavD+5N546ubOGNYlBl1jQ1FZa8GSuqs7ORdLqnHTnC0Y+/5vPr2yO11QjnKjGSEBWnSNDW3w+Myx3RCo12BXZjHW/JHns3WpxbHcMvz5q30wWUSM7h6LmFADzhRUqJpKW3uU/d8m9WuP4AAtTuaXY0fdRYeSXCqtxvxNTLjMvDUVz9/SFQCLzmQVeR+dOXihBEWVtQgN1GFAcmuEBerx5bQb0DM+DEWVtZj2+R6PI8qchdsyMfHD7TiRV47IYD3en9wbX05jqfhXx6Ui1KDDkYtlmLRgB35V8bjkiJ//uISyGjPaRwbhxk72x2+tRkC/ZHYxsFvliz9vIDGjAEdzS3H7f7Zh+f4L4AGD99adlJR+U2w+eRkmi4jSapOUqnGVCqO5WVxV8qjMzamxTkuwb+raBuN6xcFiFfHKiiOwunFiO1dYieLKWhh0GvRs17SjPibUgF7xbLsMWXTmq53nUVlrQaCevfV/PZav2v/vX7+eQlmNGalxYRjbi3W4/v2ser4Zq1XET4eYmJk+vBP0WgHrjubjq52etfDn/qTIYD1ahwQgOEALQQDiI4LwzWM3YEJvFmUVBAGP3sg8TJ//ft4udWexipix5CAulxtxrrBS8ZA9AKw+nCu9/+TwFFPvxAjotP+/vfMOi+ra2vh7plOGXoYiTTqIUmygIvYekxijsV5vYkzUaEyx3mhMot6b8iXxJiYmxhR7oonRqMHeFWkKiFgA6SCCVKmzvz/OzJGBGZiBoXn373nmeZKZg3PYzJyz9lrvelfTS52juSEWhLPlz4/+utmtJ69nFVdi7vYolFXXob+rBb6cHohNz/UCAHx3PrVddvu19XKcUNx0p4b0wPNBjgCAHxQifX2y6egtPK6tR7CzOZ7pY49AJ3MM9bJmszOn2p6dOZnM/h7hntYQKj4rpoZC7PhnfziYGSCr+DH+fbT1Ives4kpsOpoMOQEm9bbHiWXheKaPAxiGgUjAw8uD3XD6naEY48deJ74+o93vRAjBg7JqxGc+ws2c1n+3lCWm6f2cwFNjV9BXsYG81glZI22hwUwbIITgp0vpeParS0gtrIDMRII9rwzA9H5OIAR4Y08cUh+Ut/jvnGtws90Xnan1+0cm5SH4g+OY/UNUh6fwG0II4YIw5U1bE/+a4AtjsQDxmY+wS80uXhMxCvFZb0czFT1Oc0R4sdmZ0ylsUFlVW4/tigvt2ol+kAh5yH70GLfy9F/eu5lTyl0g1k30xWAP9lwu3dX/rlVJVHoRckuqIJUIsGiYO1aM9QEAfHg4WedMWH5pFaIVwcCRJYMR+6+RuLl+DFI3jMOF5RFNsmPP9LGHlbEYuSVV+OvGk4B8y5m7iGpwAdR3We/+wwos2hWH13bGNOk+4cS/TubqfhQAsCC8JxzMDJBTUoVvdMimaqKoogZbztzDhM3nseVM2/+9liCE4N6Dcszdfg0FZdXwtDXG1tkhkAj5GO5jiynBjiAEePvX66is0W/W90rqQ5RW1cHKWIRgZ3PMDXMBAJy8la+XbImS6PQiHIzPAcMA70/y40rMS4Z7AAAO6CE7c1KhlxnhY6vyvJmhCP+ZEgAA+OXK/VZvRv576i5q6wlCe1riy+mBsDRuah1iZSzG+sl+EPAYxGU80vidzX70GP/88RoiPjkDr38dQ9+PTmDyVxcx7svzOBivu2apofD3hRBHtcf0c30SzHSFzbM6aDDTBt797QbW/pmEmno5RvjY4OiSwejvZon3J/khxNkcZVV1eOXnaJQ1o1SvqZPjUoMul2vpxbinRQCUkleGN/fGo7pOjvN3CvHG7rhOmwicmF2KrOLHMBDyEe5p0+yxMlMJ3hrFtsb+++gt5Gg5FDJaIWZVpju1Yag3ey7nbz9AXb0cv0Zn4mFFDRzMDPBCsCMGubMBhr5TuoQQvH8oCXICjA+wQ383S4T2ZAXLcZnFer+pKFFeyMb520Ei5GNemAuGe9ugpl6OxbvidCph/p2UB0JY4ayd6ROhvXKyfWPEAj7mDHQGwGYCCCGIyyjG/51gO04CFP4UUXre2R1VZGQIATYcSVa50MYqxb/Omj8zEiEfq8ezQV9bOrJuZD3CW/uuY8DGk/j3sVtIzC5tt+6eS3cL8Z9jtzBr21X0WX8cwz89i7sF5bAzleCnef1gavBkzMu/JvjCzlSC9IeV+M+xFL2ehzIbNtJXBj6PQU9rY4R7WoMQ4Gc9DfSslxOsO5QEAHgxpAf8HZ5kZQOdzBHuyWZnvmqDdiaruBK38srAY56UpxsS5m6FlxQ6kuX7b+gsBbj/sAK/KrKcymufJmykEozxZzeEO66o3+xtOJKMk7cKkFZYgZo6ORiGzZ4qz0/XjYuK8Feq3lYlwNEUIgEPheU1SNOj6Fqf0GCmDYz0Zbt21k30xXezQ2BuxPbjiwQ8fD0zCDITCe49qMCbe+M1llRi7hejoqYeVsYiLpPQUnamuKIGL/98DRU19fCzN4FIwEPkzXys+SOxU6LmI4nsTnyYtw0MRC27/M4e6IJAJzOUVddh5YEErc45WpGZCWnmxtSY3o5mMDcUorSqDtfSi7H1PNtNNH+IGwR8Hkb6ssGOvoOZo4l5uJpWBLGAh5VjWaG6k4UhHMwMUFtP2qXuXF1Xz+k+nuljD4ANPD5+oTdkJhKkFlaodIK0xJNMW1MxtyZmDnCGRMhDUk4pTiQXYMmeeNTLCSb1tsfqcWzAEJVWrNfP6NEG5aVL9x7iTAqb5XxYXo10hQ9JYDOZGQAY6y/DQDdLVNfJseFIss7nsPr3BEz670Xsj81CTZ0c/g6sN8fj2nqcv6PfsuL1zEd46fur+PrMPZy/U4iSx7UQCXjo52qBn+f1Uwk8AcDUQIhNz7OZhR8vpastx7WGejnhbBaUN18A+IciO7PvWmabNSbxmY8Ufi+lkEoEeFuhk2nIkhFsdmZ/bHarfWdOKeQAIc4W3DW8MSvHesPBzACZRY919lT64uQd1MsJwj2tEezcst5v5gB2U3AwPrtJy3Zidgn+upELhgG+mRmE8+9G4PaHYxG9ZiSGelmjqlaO+T/HaG070VD4+1J/zQaSYgEffRQOwF211ESDmTYwyk+Gc+9EYG6Ya5Pdqo1Ugm9nBUMk4OFEcoHGncO5O+zFd4iHNV7sy36Y9sdka2wZrq2XY+GuWGQWPUYPCwPs+Gd/fDmtD3gMsOdaJj6NbF7sl5hdguW/3UDoxpOYte0qvjuXipS8slbfYAghOKq48TW8qDUHn8fg4ykBEAl4OHv7Abdr0cTDclZvAaj6hWjzPsoW7fcOJiKz6DEsjESYGsLO6hrmbQuGARKyS5Bbol2GqDHbL6bBc81RuK86As81R+Hzr2N4Y3ccAODV8J5wNGcdjxmGQZg7m525pEYgmVvyGGM+P4fQjScx5vNzmPrtZbz8UzTWHkzUyoPibMoDlDyuha2JGP0btK1bGInw+bQ+AIB90VlapeMflFVzAZe2f1MAMDcSYUowm6ZeuDMWGUWVcDAzwIfP+qN3DzOI+DwUNggy2krOo8e4nvkIDAM8F8TqdzYcSUZdvRyxii4mT1tjlUyFOhiGwdpJvuDzGBxNzMMFHQKQ5NxS7LyaAYYBJvexx4HXQ3Fo0SBMCGADysgk/QqLLyiyuN4yKTY82wuHFw9C4rrR2PfqQHioETkDbLZB6XS8YEcM3tgdh4KyqjadR2xGMQrLWU+phjYJQzys4WZthLLqOuxv4Xutidv5ZXj1l2hM/uoiLt17CCGfwfuT/GClpjQT5GSOIYrsTGt9Z5QlpmE+mrPKUokQm55nNUg/Xb6PK6nsd5gQgqziSvx1IxdxauZ93S0o59rVl41sPiujpL+rBTxsjFFZU4/fY1XLRkox96Te9hjjb4ceFoYQ8nng8xh88WIgnC0Nkf3oMRbtitUqU68U/vawMMAg96aNGw3p68pee692UREwDWbaiMxUfVoOAHr3MMOHitlBW8+lqk1PnlXsJId4WmO4jw2sjEUoLK/mdpiN+eivZFy69xBGIj6+n90X5kYijPG3w0fPsl+0/56+i63n7qFAYQpXWlWLsirW/+G5ry9iwuYL2BudiZySKpy/U4iPjiRj9Ofn0H/DSaw/dFMnUS4AJOeWIf1hJcQCHiK8my8xNcTdRsp9uT84fLPZYEKpfXC3MYaZofqdkyaULdp3FO3Rc0NduOyRtVSMIMWu/UQrszPbL7KGYXVygpo6OR7X1qNOTuBsaYgF4W4qx4YpLhbq6u5fn76HW3llyCmpwq28MkSlFeFEcj5+unwfu6+2rC06qBD+TgywbzJvaICbJSeW1EYXEnkzD3IC9HY05YIxbZkX5gqGAWrq5eAxwBfT+sBEIoREyEfvHopSU5p+dEPKLENfZwusnegHM0Mh7hSUY190VgN/Ge2CX2+ZCWYpdsSr/0jQujz0rWI9x/Wyw+fTAhHkZA6GYTDKj9VenEjO12v5V/l7vRDSAy/1d4K/g6lWGrK1E33xjzAX8BjWh2bEp2ex62qGzt93JccaOH03fH8ej8E/Ql0AsJkgXf59Qgg2KK5Hfyflg8ewXVKn3hqK54LUazkAYKkiO/NbTJbOWc+K6jqu+2pEM8EMAAz2sMb0fuxG6K191/HajhgM2HgSg/59Ggt3xeLZry9xwbSSL07egZyw66TteASGYbjszI4r97mNZnR6EU7dKgCfx3Au1g0xNRRi66wQGIr4uHTvITYqBMtyOcHdgnL8FpOFDUeSseaPBLy17zoW7orFp5Fs6XFaX/XC34Z0dREwDWbamSnBjnC1YncqBxoZShWUVuFmbikYBhjsYQUhn8d9afdea1pq2nU1Az9eSgcA/N+LfeAle7ITm97PiWtX3HDkFvptOImgD44jYF0keq2LxNK98YjNeAQBj8HE3vb4YW4I1oz3QbinNSRCHgrKqvHDxTRu56ctRxUlpnBPaxiLdRv19fIgV/TuYYayqjqsaqbcpLyA99VBL6NkiKc112FmKOJjtkLXoWSkL3vTOZ6su1V3emEFMooqIeAxOP32UFxYHoFz70Tg9NtD8ffSITAUqa7HQIVuJimnVMVGv6CsCnsVpcWPpwTg53n98N+XArksR0ulirKqWi4Y0zSLavEw1sXzt5isFnVKRxM0+wW1hJu1MdeRsXiYh4pQWCkijErTj5Oo8oY6xl8GUwMhJwj97PhtXLjLbgaa08s05q1RnpCZSHD/YSW+ONnyLj+zqBKHFGLn1xRdUUr6uVjA3FCI4spaTkjdVuRywnUl6fpdkAj5WDvRD38sDIO/gwlKq+qw6vcETNt6RWcNCCFEZe0b81yQI6QSAdIKK3TSsqTkl2HruVQQwpb+/l46BJ+80LvFeW5BTuaY1pcNMlbsv6GTTun8nULU1MvhbGmIntbGLR6/apwP7E0lyH70GEcT85BfWg0+j4GnLfuzW8+lYta2KBSWVyMlrwyHb7CbDG2zMkqeDXKAgZCPOwXliEpjRbcf/80GHlNDHOGiYZSLl0yKT1/oDYBtBZ/6zWX0WR+JEZ+dxdu/XsfWc6nYcSUD+2Oz8NeNXOSWVEEs4GkU/jYk2NkcPAbILHqMvJK2ZfbaAxrMtDM8HsPt+H6+pGoodU5xk+rlYMqp25UlkNMpBSgoZT8whBB8fz4Vq/9IAAC8NdITo/yaXkReH9oTbwxz51poGyIzkWDZSE9cWjkMm6cHYpi3LV4e7Iaf5vVD/HujOJ3FMS3T4nX1csRlFHOtwLpoK5QI+Dx8MiUAIj4Pp1MeYH+seiV+NGdJ33K9uTEWRiL0UeyIpvdzapLZUQYzl+8VNivUVoeyRBjiYg5XKyM4mhvCydIQrlZGatvTbaQSeNgYgxCoeHFsu5CGmjo5gpzMMCXYEUM8rTEhwJ7L7ESlFzV7gY5Mykd1nRw9rY3gZ69+qmywswUGuFmgtp4060RcVFGDy4oU+lgdSkwN+fiF3tg7fwC3Y1aiz51dQVkVrt1XLYXN6O8MF0tDFJZXIzGbbVPVpSwplQg5L6St51JbbHX9/nwq6uUEgz2sVISpAPvZHq7ojPlbw3fqbkG5TmLKuw/KUVpVBwMhHz52mqcHN0eAoxn+eD0M703whZGIj6j0InyrozN1Uk4psh+xgv8hHk0Fs0ZiAVYqOuk+PX6bu6G3xOHrCgdxX1tsmRmssWymjpXjfGAjFSO1sAJfqglE80qqMPmrixj1f2fxR1w253d06ha7CRjmbaPV8EqpRIhvZgXjuSAHLB/jjb3zByBx3WhEvhmOLTOCYCTi43LqQ0zcfAGrf08AIWyHp6+G76UmTCRCTA5kr8k7rmbgwt1CXE0rgkjAw+JhHs3+7NhedlgYwQbXUelFKK2qg1jAQ18Xc8wNdcGS4R5YMdYb6yb64t/P98L+10I1Cn8b/+7Kz52+hfz6QOep2RTdmRLiiE8iU3CnoByX7z1EqKLcoGzJbqigd7cxRrCzOWLuF2N/bDbmD3HD+kNJ3CTqOQOdsUixy24MwzBYNsoLy0axGRq5nKBWzo5wZwMc9V9WiZCP54MccTA+B5FJefjgGf8mpQqA9bTZcYWtF19LK0KFwpdDJOA1W29uDg9bKZaO9MB/jqXg/UNJGOxhBdsGVtpVtfVIyGLV+bqIfxuybqIfDsbn4I3hTS8CPa2N4WZlhNTCCpy9/YDTOmhDwxKhtoS5W+FOQTku3ivE2F52KKmsxQ7F33ZhhLvK36intTFkJhLklVYhKq1I4/v8oehiUvpWaGLxMA9cSb2K3VEZeD2ip9oL2PGbeaiXE/jamcDZUv3uryWMxQIV3Y4S5c4uo6gSeSVVzZZoWyIyKR+EsKVcezNW9CoS8LBirDcW7GCn/poZCuGmYQeriVF+Moz1l+FoYh5WHriBA6+Hqf0uPCyv5rJpCxplZbh/y9cWv8VkITIpH+9N8FX529x/WIHxX56HiM/D2XcjYKFBeNoQZRDYp4cZ54XSGgR8HuYNcoWdqQSv7YzF9+dTMXOAk1Y3NOBJRmyol7VGwf9L/Z1w70E5tl1Iw7J912FvZtBsyY8QwgU9EwJ03xiZGgjxwWR/vPpLDL49l4rxAXbwU/hRpRdWYOa2q8gqZjOSS/fGY/OpO1g6whOnFKMYGrdkN0eAoxk+m9qnyfNje9nBw9YY83+JQeqDCuSWVIFhgKVqSkLaMHOAM3ZHZeJYYi5uK+wjZvZ35j7vzbFspBdkpgYghCDIyRxeMmmbPjNK+rpYICmnFNfSijCpt/bXyo6AZmY6ABOJkBMo/nQ5HQDbDXD+jvqb4VRFym9fdCZe/SWGC2TWjPfBugY+Cy3B4zEQC/gwEgta/JkBbpYwkQhQWF6j0WBr0a5YbDp6C2dSHqCiph4mEgFG+triq5eCYCJpXmTZHPMHuyHA0RRlVXVcDVdJYnYJaurlsDIWwdlSN/2Gkt49zPDeRF+NQlCu1KSDbqamTs5lMNTtTjWhbNFW+s38dJk18fOWSTGskeaIYRjOTVlT+e9BWTU3wFKZXWvuvQOdzFBdJ8e28+rnKCk7olryC2oNUomQ26G2dWfHjc5olD0a7Sfjgl6lfkVX3p/kB6lEgOtZJVxZtzE/XUpHVa0cAY6m3N+0MUM8rWEg5CP70WMkNcrybDiSjOo6Ocqq6/CLlm3MSq+l1pRb1THGX4bePcxQWVOvNpuhDkIIV1puSRy+apwPRvjYoKZOjvk/Rzfb9p6UU4r0h5WQCHk6BRYNGe0n40w53/3tBurq5biZwzrqZhU/houlIZYM94CpgRD3HlRg8e44VsQsFnBZw7bibiPFwYVhGK3QTE0N7gFPHTJMDfGzN0Wgkxlq6wlS8stgKOLj9Qj1gXNj+IqKwOyBLvB3MNVLIAOo+s10NWgw00HMGegCgL1hZhVXIiG7BMWVtZBKBAhsJAwbH2APQxEfaYUVOJGcz7Z6zwjCy4PdWnVx1gaRgIcRipv6UTXtm3cLynAm5QF4DLB6nA/+emMQ4t4bhe9mh3DBQGtpOPjvt5gsFZfY6AZTj9vrd1ee/+lbBVwXWUllLU7dytc46Tr6fhEqa+phZSyGrw4p//5uluAxrKPxvQfl2K4Yzvh6o6yMkkEeqlm8xhy6ngM5YXfrLWVSGIbhtDM7rtxHcaP2zZLKWu73bY1eRhu4UlMbOiKKG5TCxjQqtzIMg03PB2CYtw1eH6rdhb8xNiaSJ2WSyBRkFavehCuq67gNxoLwns1mPId4sn+/hl1Nl+4VqkyP//lyulY6D+67oKcbL8MwnHXA7qhMrQw+D9/Ixb0HFVoJ/vk8Bl9MC4SPnQkKy2vwz5+uaezMO6TIygzztoGRjtq7hqyb5AdTAyGSckqxfH8Cpm29jMLyavjameDXBaF4c6Qnzi+PwJsjPCFVvM8IX1utjTi1QSoR4puZwTixLBwbFC7MrWVm/ycav3lhrmo7ujoS5fc3Jb+sTXPf2gMazHQQHrZShPa0hJwAO69mcCWKQe5WTazWjcUCTFSUO8wNhdj1cv9WaVJ0Zaw/+x5/J+U16UJQmmCN8LHFK0Pc4Gdvqjb93lqCnMwxrpcMcgIV2/An/jL6uYCrI9DJHJZGIpRW1WHxrjiM+fwc+nwQiXk/RmPG91fV7kKUIxKGeFi12AXQEFMDIXop/BqW7buO4spaOFsaYpyGXa6yXfJWXlmTdlpCCPZcYzudng9SL/xtTISXDXztTFBRU4/tjbIOx5PzUScn8LKVaiWGbA39XJQi4NYHM8eT81EvJ/CWSdUKId1tjPHD3L5azfDSxLS+PdDPxQKVNfVYtCsOZxXGiwBrMlbyuBauVkYYrUa71hDl68oZYPVygg8Os142L/V3gqO5AR5W1GB/bPNtzAWlVcgoqgSPUZ0A3lYGuFlimLcN6uUEn0Q2b6pXWlWL9YdZr6LXh7prlY01EguwbU4IbKRi3M4vx9v7rjc5hhDCuUaP79W20oW1VIx/TfAFAOyPzUJpVR36uphj9/wBsJaygYCJRIglIzxwYfkwfPVSELeR0icMw8DdxrjN18jxAXZwtjSEvakErwxxa/kH2hlrqRiuVkYg5ImRaVeBBjMdyBxFy+KeqAwcT2Z3auocJwHg7dFeWDLcA38sDGvTRVkXBntYwUjER25JFa5nPeKeL6uq5TwjlL9De/DOaG+2MyjlAS7dLVQZLqmL86+u8HkMhis0P8eS8nArrwyEAFIJu3NTZ0t/7jabwdBFL6NEWZa4rhg6uSC8p9rZQQBgaSzmRL2Ns0Qx94txO78cBkI+ntHQxdQYhmE4zdX2i2nYE5WBwzdycCalgPsbj22HEpOSvq5PdnYNO7p04UmJqf0CfB6PwYbnekEs4CE+8xHm/BCFARtP4f1DSdh2gc2mzR/i1uLNapi3Dfg8BrfyynD/YQX2RWciObcUJhIB3h7lhX8OYmdafX8+rdk2ZmVWxktmAmkbSrrqeHeMFxiGLTGq80pR8lnkbTwoq4ablREWDNX+xmpvZoDv54RAwGMQeTO/ycy661klyCp+DEMRv0mptTU8H+TAXVeHelnj53n91ZaYTQ2FGB9g16IPUWciEfLx99IhOPFWeJc5T2WZs6uJgGkw04EM97aBg5kBiitruW4LTTdDa6kYb470bLUIszVIhHwuddywq+lAbDYqaurR09pIoz5AH7haGWGGwoVy49FbuPdAt+GSbeH1oe4Y5WuLf4S54JuZQYheMwIHF4aBYViH0JQG85sKSquQ3KClXlfCej75GVsTMaen0oRyrlPjFm3lbKuJve100iyN8ZPB3cYYZVV1WHEgAYt2xWHu9mtc6aY9s4BWxmK4WbOfaWXWTRfKqmo5U7v2DLoANsPz56JBmDXAGeaGQhSWV2P7xXTkllTBWirGs1oEkGaGIgxwYwO4/TFZ+ETRXrt0hCdn4GiiaGM+nqxZs9UaB2xt8ZaZcEMiNx29pdYi4UbWI/ys0Pt9MNkfYkHLTt8NCXA0wzxF4Lb+8E1U1z0pqx1WdEQO97HVykG8JRiGwdbZwdj1Sn98NztEL/9mZyIR8pvYPHQm/VzZe8DxpPxWb0jaAxrMdCACPo8zQwIADxtjrZTpHYlyt3ssMQ+EEBBCuIvYnFCXdtOtKFk83APGYgESsku4lLYuwyVbi4uVEbbODsHaiX4Y42+nuOk+8Uxp2M6sbKn3tzdVOzCuJUJczLnf55XBbi3eGDgR8J1C7kZTUlnLpeaV7q7awuMx+L+pffBckANG+Nigv6sFfO1M4GRhiKkhjvCwaZ8SkxKu1NSKnd2pWwWoqZfDzdqo3c8TYH07Ppjsj6jVI7BtTggm9raHjVSMVeO8NU6Hb8woX/YztPn0XTysqIGbtRFmKfyOjMQC7prwXTMt0sqUfkg7ZSjfHOkJkYCHq2lF3GBWJdyUe8KKzMNacIrVxOJh7rCWipFWWIEfLqQDYDsu/1I4iLemi0kTYgEfoT2t9CZ8pTxhqJc1TCQCpBZW4LmvLyG9i8xqon/pDmZa3x4QK25kmkpMnclQL2uIBTzcf1iJ5NwyXLr3EPceVMBIxNdqJ9pWrIzFeFVRG1aKXtuzxNQSryrabg/GZ3Nmc+pa6nVBIuTj3dFemNjbvtl5KEqCnc05Y8Pb+axI80BcFqrr5PCWSTkfHV3o5WiKz6b2wfdz+mLvqwNxZMlgnHs3Av+Z0rvdA9Yn5nm6BzN/xrO7+LH+snY/z4YIFb4xm6cHImr1CDwb2LLJmBKlwFyZ8Fgz3kflJjs31AUiPg/R94s5g8iGVNbUcd1Q7VVydjAz4Jx7F/wSi7f2Xed8dnZevY+E7BJIJQJuKGdrkEqEWDGGFRxvPnUH+aVViM0oRm5JFYzFgi55PaQ0xcpYjF8XhMLBzACphRV49uuLXaK7iQYzHYy5kQgvD3aFRMhr1qK7szASC7jS17HEXPykEIk+H+yo91q9Jv452BU20icZj/ZIrWtLnx5mGOBmgTo5wQ8X0pptqdeFlwe7YfP0QK3SxxIhn0vtnr/zAIQQbtLtjP5OHXpT1wfKjojE7BKdJohnPKzEKUXW4Pku+N3RhL2ZATc1fIinNTdiQ4mNiYQzSFOXnYnPeIR6OYG9qQQO7ZjJfT3CHQPdLFFTL8f+2CyM+/I8pm+9go8V07bfHeOttReNJp4NdECQE9sOvunoLRy+8cQoT9tMF6Xz8ZJJ8fvCUAQ4mqK4shYzvruKg/HqTU87ChrMdAJvj/JC8voxOrtCdhRK745fY7JwQlHHbzwGoD0xFAlU7L91cXFtD5TZmd1RGbh4txDFlbUwFgsQqMeukpYYoig1nb9TiNgMVvgrEfK0Fv52JRzNDWBnKkGdnCBOMRBSG365kg5C2IDArZ26rdqLFWO8MdrPFh9N9lcbfL4ymM1G/n0zr0naXt8t2ZowNRBi9/wBOPB6KCYE2IHPY3A59SHKquvQu4cZXtKxnKkOHo9ReGUBv8dl4zeF6FyfJSZKx2AjlWDv/IEY7WeLmno5luyJx9dntB9foW9oMNMJMAzTpXfTw31sIeQzyC2pgpwAYe6WcLdpnfFTa5kS7IjZA53xzmgvnYdL6puhntbwlklRUVOP5ftvAGDXpCPr8Uq/matpD7H9YjoAdqhkW8wKOwuGYbhS077oTI0T4htSWVPHzSubG9pxgbW+CHW3wrezQjTOGfKwZU0TCQH+dTBRRSCrDGY6KkMZ5GSO/74UhHPvRuDVIW4I7WmJT18I0JsVQ4CjGV5UjG0pr66DiUTAidwp3QsDER9bZgRjvkIaoOtgWn1CgxlKE0wNhAht0HEzW2H415EI+Dysf8YfCyPUj27oSBiGwauKOUm5igFrbSkxtQYvWymspWJU1cq51Px0LfQ2XRXl2IiD8Tl44ZvLzbrDAsAfcTkoraqDk4Uhhnq2vX23K7JspCckQh7O3ynEwp1xqKmTo15OEKsMZjpYO+ZgZoCV43yw65UBet/MvDPai7M+GO0na3eBP6X94PEYrBrng8OLB3XqiAP6CaKoRWln72BmgOF68H7o7kwIsFfRK+gywkAfMAyDwQ26SLxl0ibO0d2Jkb622DIjCCYSAeIzH2HcF+e5oaWNIYRw2q3ZA511MinsTvg7mGLbnL4QC3g4kZyPJXvicDOnFOXVdTAWC+At65pl6dZgaSzGv58PQC8HU25XT+neNB622tHQYIailueCHPHOaC98NSNIo6Hb/xJCPo8zOOtpbaSxXNCeDPZ8Esy81A2Fv40Z28sOR5YMRrCzOcqq6/DG7jgs/+2GSokFAK6kFiElvwwGQj5eUJQnnlbC3K3w7axgiPg8HE3Mw/xfogEAgU5menXc7gqM62WHQ4sH6TQdm0LRBL1LUdQi5POwMMK9VW2/TyuzBjpjxVhvfKpmYm5HMMidbZuXigV4pk/3E/6qw9HcEHvnD8DiYe5gGGBvdCZmbYtSmfui9Dl6Nsihy7igtidDvWzw9YwgCHgMV9Zsz3EeFMrTAEPU2T0+ZZSWlsLU1BQlJSUwMXl6UrWU/z0Ss0sgFvCeyt3sudsP8PrOWJRX18Hdxhg//qMveAyDwf85jXo5wd9Lh8BL9vT93po4lpiLhbviUC8n2P3KAAxsR/dtCqWrou39mwYzFAqly5CcW4p/bL+GvFJ2ZEBfF3McScjDADcL7Jk/sLNPr8O5fO8hUvJKO8R9m0Lpimh7/6ZlJgqF0mXwsTPB7wtD4S2T4kFZNY4ksDPC5rbjgNOuzMCelpgb5koDGQqlBWgwQ6FQuhR2pgbYt2AgwtzZsoqDmQFG+Nh28llRKJSuTNcZxUmhUCgKTCRCbJ/bD/uiMxHkZE476igUSrPQYIZCoXRJRALVKfMUCoWiCbrdoVAoFAqF0q2hwQyFQqFQKJRuDQ1mKBQKhUKhdGtoMEOhUCgUCqVbQ4MZCoVCoVAo3RoazFAoFAqFQunWtGswM2nSJDg5OUEikcDOzg6zZs1CTk5Ok+N+/PFHBAQEQCKRQCaTYdGiRSqvJyQkIDw8HAYGBnBwcMD69evxPzCFgUKhUCgUiha0q89MREQEVq1aBTs7O2RnZ+Ptt9/GlClTcOnSJe6Yzz77DJ9++ik+/vhj9O/fH1VVVUhNTeVeLy0txciRIxEREYFr167h9u3bmDt3LoyMjPDWW2+15+lTKBQKhULpBnTooMk///wTkydPRnV1NYRCIYqLi+Hg4IBDhw5h+PDhan9my5YtWLlyJfLz8yEWiwEAmzZtwubNm5GVlaXVzBI6aJJCoVAolO5Hlxs0WVRUhJ07dyI0NBRCoRAAcPz4ccjlcmRnZ8PHxweOjo6YOnUqMjMzuZ+7fPkywsPDuUAGAEaPHo2cnBykp6erfa/q6mqUlpaqPCgUCoVCoTydtHsws3z5chgZGcHS0hIZGRk4ePAg91pqairkcjk2bNiAzz//HL/99huKioowcuRI1NTUAADy8vJga6s6ZE75/3l5eWrfc+PGjTA1NeUePXr0aKffjkKhUCgUSmejczCzbt06MAzT7CM6Opo7/p133kFcXBwiIyPB5/Mxe/ZsTrwrl8tRW1uLL7/8EqNHj8aAAQOwe/du3LlzB6dPn+b+jcalJOXPayoxrVy5EiUlJdyjYaaHQqFQKBTK04XOAuBFixZh2rRpzR7j4uLC/beVlRWsrKzg6ekJHx8f9OjRA1euXMHAgQNhZ2cHAPD19eWOt7a2hpWVFTIyMgAAMpmsSQamoKAAAJpkbJSIxWKVshSFQqFQKJSnF52DGWVw0hqUGZXq6moAQFhYGAAgJSUFjo6OAFhtTWFhIZyd2Wm5AwcOxKpVq1BTUwORSAQAiIyMhL29vUrQpM37Uu0MhUKhUCjdB+V9u8VeJdJOXL16lWzevJnExcWR9PR0curUKTJo0CDSs2dPUlVVxR33zDPPED8/P3Lx4kWSkJBAJkyYQHx9fUlNTQ0hhJBHjx4RW1tbMn36dJKQkEAOHDhATExMyCeffKL1uWRmZhIA9EEf9EEf9EEf9NENH5mZmc3e59utNTshIQFLlizB9evXUVFRATs7O4wZMwZr1qyBg4MDd1xpaSnefPNNHDhwADweD+Hh4fjiiy9URLsJCQlYuHAhoqKiYG5ujgULFuC9997Tqi0bYLU5OTk5kEqlWv+MtpSWlqJHjx7IzMykbd8dAF3vjoWud8dC17tjoevdsbRmvQkhKCsrg729PXg8zTLfDvWZeRqhHjYdC13vjoWud8dC17tjoevdsbTnetPZTBQKhUKhULo1NJihUCgUCoXSraHBTBsRi8VYu3YtbQXvIOh6dyx0vTsWut4dC13vjqU915tqZigUCoVCoXRraGaGQqFQKBRKt4YGMxQKhUKhULo1NJihUCgUCoXSraHBDIVCoVAolG4NDWbawNdffw1XV1dIJBIEBwfj/PnznX1KTwUbN25E3759IZVKYWNjg8mTJyMlJUXlGEII1q1bB3t7exgYGGDo0KFISkrqpDN+eti4cSMYhsHSpUu55+ha65/s7GzMnDkTlpaWMDQ0RJ8+fRATE8O9Ttdcf9TV1WHNmjVwdXWFgYEB3NzcsH79esjlcu4Yut6t59y5c5g4cSLs7e3BMAz++OMPlde1Wdvq6mosXrwYVlZWMDIywqRJk5CVlaXbiWg/bYnSkD179hChUEi+++47cvPmTbJkyRJiZGRE7t+/39mn1u0ZPXo02b59O0lMTCTx8fFk/PjxxMnJiZSXl3PHbNq0iUilUrJ//36SkJBAXnzxRWJnZ0dKS0s78cy7N1FRUcTFxYUEBASQJUuWcM/TtdYvRUVFxNnZmcydO5dcvXqVpKWlkRMnTpC7d+9yx9A11x8ffvghsbS0JIcPHyZpaWnk119/JcbGxuTzzz/njqHr3XqOHDlCVq9eTfbv308AkN9//13ldW3WdsGCBcTBwYEcP36cxMbGkoiICNK7d29SV1en9XnQYKaV9OvXjyxYsEDlOW9vb7JixYpOOqOnl4KCAgKAnD17lhBCiFwuJzKZjGzatIk7pqqqipiampJvvvmms06zW1NWVkY8PDzI8ePHSXh4OBfM0LXWP8uXLyeDBg3S+Dpdc/0yfvx4Mm/ePJXnnnvuOTJz5kxCCF1vfdI4mNFmbR89ekSEQiHZs2cPd0x2djbh8Xjk2LFjWr83LTO1gpqaGsTExGDUqFEqz48aNQqXLl3qpLN6eikpKQEAWFhYAADS0tKQl5ensv5isRjh4eF0/VvJwoULMX78eIwYMULlebrW+ufPP/9ESEgIXnjhBdjY2CAwMBDfffcd9zpdc/0yaNAgnDx5Erdv3wYAXL9+HRcuXMC4ceMA0PVuT7RZ25iYGNTW1qocY29vD39/f53WX6C/0/7fobCwEPX19bC1tVV53tbWFnl5eZ10Vk8nhBAsW7YMgwYNgr+/PwBwa6xu/e/fv9/h59jd2bNnD2JjY3Ht2rUmr9G11j+pqanYsmULli1bhlWrViEqKgpvvPEGxGIxZs+eTddczyxfvhwlJSXw9vYGn89HfX09PvroI0yfPh0A/Yy3J9qsbV5eHkQiEczNzZsco8v9lAYzbYBhGJX/J4Q0eY7SNhYtWoQbN27gwoULTV6j6992MjMzsWTJEkRGRkIikWg8jq61/pDL5QgJCcGGDRsAAIGBgUhKSsKWLVswe/Zs7ji65vph79692LFjB3bt2gU/Pz/Ex8dj6dKlsLe3x5w5c7jj6Hq3H61ZW13Xn5aZWoGVlRX4fH6TqLGgoKBJBEppPYsXL8aff/6J06dPw9HRkXteJpMBAF1/PRATE4OCggIEBwdDIBBAIBDg7Nmz+PLLLyEQCLj1pGutP+zs7ODr66vynI+PDzIyMgDQz7e+eeedd7BixQpMmzYNvXr1wqxZs/Dmm29i48aNAOh6tyfarK1MJkNNTQ2Ki4s1HqMNNJhpBSKRCMHBwTh+/LjK88ePH0doaGgnndXTAyEEixYtwoEDB3Dq1Cm4urqqvO7q6gqZTKay/jU1NTh79ixdfx0ZPnw4EhISEB8fzz1CQkIwY8YMxMfHw83Nja61ngkLC2tiNXD79m04OzsDoJ9vfVNZWQkeT/VWx+fzudZsut7thzZrGxwcDKFQqHJMbm4uEhMTdVv/VsuW/8dRtmZv27aN3Lx5kyxdupQYGRmR9PT0zj61bs9rr71GTE1NyZkzZ0hubi73qKys5I7ZtGkTMTU1JQcOHCAJCQlk+vTptJVSTzTsZiKErrW+iYqKIgKBgHz00Ufkzp07ZOfOncTQ0JDs2LGDO4auuf6YM2cOcXBw4FqzDxw4QKysrMi7777LHUPXu/WUlZWRuLg4EhcXRwCQzz77jMTFxXE2Jdqs7YIFC4ijoyM5ceIEiY2NJcOGDaOt2R3JV199RZydnYlIJCJBQUFc6zClbQBQ+9i+fTt3jFwuJ2vXriUymYyIxWIyZMgQkpCQ0Hkn/RTROJiha61/Dh06RPz9/YlYLCbe3t5k69atKq/TNdcfpaWlZMmSJcTJyYlIJBLi5uZGVq9eTaqrq7lj6Hq3ntOnT6u9Xs+ZM4cQot3aPn78mCxatIhYWFgQAwMDMmHCBJKRkaHTeTCEENKmPBKFQqFQKBRKJ0I1MxQKhUKhULo1NJihUCgUCoXSraHBDIVCoVAolG4NDWYoFAqFQqF0a2gwQ6FQKBQKpVtDgxkKhUKhUCjdGhrMUCgUCoVC6dbQYIZCoVAoFEq3hgYzFAqFQqFQujU0mKFQKBQKhdKtocEMhUKhUCiUbg0NZigUCoVCoXRr/h85q8iRYbzyHwAAAABJRU5ErkJggg==", + "image/png": "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", "text/plain": [ "
" ] @@ -2928,10 +2749,18 @@ "%%time\n", "for T in [300, 600, 900]:\n", " wf = Workflow('Lammps')\n", - " wf.structure = wf.create.atomistic.structure.build.bulk('Al', cubic=True)\n", - " wf.repeat = wf.create.atomistic.structure.transform.repeat(structure=wf.structure, repeat_scalar=3)\n", + " wf.structure = wf.create.atomistic.structure.build.Bulk(\n", + " 'Al', \n", + " cubic=True\n", + " )\n", + " wf.repeat = wf.create.atomistic.structure.transform.Repeat(\n", + " structure=wf.structure, \n", + " repeat_scalar=3\n", + " )\n", " \n", - " wf.lammps = wf.create.atomistic_codes.Lammps(structure=wf.repeat, label='lammps')\n", + " wf.lammps = wf.create.atomistic_codes.Lammps(\n", + " structure=wf.repeat\n", + " )\n", " wf.lammps.calc_select.md(\n", " calculator_input = {\n", " \"temperature\": T, \n", @@ -2983,15 +2812,22 @@ "metadata": {}, "outputs": [], "source": [ - "@Workflow.wrap_as.macro_node('energy_pot')\n", + "@Workflow.wrap.as_macro_node('energy_pot')\n", "def energy_at_volume(wf, element='Al', cell_size=2, strain=0):\n", - " wf.bulk = wf.create.atomistic.structure.build.cubic_bulk_cell(element=element, cubic=True, cell_size=cell_size)\n", - " wf.apply_strain = wf.create.atomistic.structure.transform.apply_strain(structure=wf.bulk.outputs.structure, strain=strain)\n", + " wf.bulk = wf.create.atomistic.structure.build.CubicBulkCell(\n", + " element=element, \n", + " cell_size=cell_size\n", + " )\n", + " wf.apply_strain = wf.create.atomistic.structure.transform.ApplyStrain(\n", + " structure=wf.bulk.outputs.structure, \n", + " strain=strain\n", + " )\n", " \n", - " wf.lammps = wf.create.atomistic_codes.Lammps(structure=wf.apply_strain, label='lammps')\n", + " wf.lammps = wf.create.atomistic_codes.Lammps(\n", + " structure=wf.apply_strain\n", + " )\n", " \n", - " return wf.lammps.outputs.generic.energy_pot\n", - " #return wf.bulk" + " return wf.lammps.outputs.generic.energy_pot" ] }, { @@ -3000,13 +2836,6 @@ "id": "86cd8ba7-8bb6-49f8-a343-a6f498ff4717", "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collect: static \n" - ] - }, { "data": { "text/plain": [ @@ -3049,7 +2878,7 @@ ], "source": [ "wf = Workflow('Lammps')\n", - "wf.bulk = wf.create.atomistic.structure.build.cubic_bulk_cell(element='Al', cubic=True)\n", + "wf.bulk = wf.create.atomistic.structure.build.CubicBulkCell(element='Al')\n", "\n", "wf.run()" ] @@ -3069,618 +2898,384 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "clusterbulk\n", - "\n", - "bulk: cubic_bulk_cell\n", + "\n", + "bulk: CubicBulkCell\n", "\n", "clusterbulkInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterbulkOutputs\n", + "clusterbulkOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterbulkelement\n", + "clusterbulkbulk\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "element: UserInput\n", + "\n", + "bulk: Bulk\n", "\n", "\n", - "clusterbulkelementInputs\n", + "clusterbulkbulkInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterbulkelementOutputs\n", + "clusterbulkbulkOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterbulkcell_size\n", + "clusterbulkcell\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "cell_size: UserInput\n", + "\n", + "cell: Repeat\n", "\n", "\n", - "clusterbulkcell_sizeInputs\n", + "clusterbulkcellInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clusterbulkcell_sizeOutputs\n", + "clusterbulkcellOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clusterbulkvacancy_index\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "vacancy_index: UserInput\n", - "\n", - "\n", - "clusterbulkvacancy_indexInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulkvacancy_indexOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulkbulk\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "bulk: bulk\n", - "\n", - "\n", - "clusterbulkbulkInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulkbulkOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clusterbulkcell\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "cell: repeat\n", - "\n", - "\n", - "clusterbulkcellInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clusterbulkcellOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", "clusterbulkstructure\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "structure: create_vacancy\n", + "\n", + "structure: CreateVacancy\n", "\n", - "\n", + "\n", "clusterbulkstructureInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterbulkstructureOutputs\n", + "\n", + "clusterbulkstructureOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "\n", "clusterbulkInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clusterbulkOutputsran\n", - "\n", - "ran\n", + "clusterbulkOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clusterbulkInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", "\n", "\n", "clusterbulkInputselement\n", - "\n", - "element: str\n", + "\n", + "element: str\n", "\n", - "\n", + "\n", "\n", - "clusterbulkelementInputsuser_input\n", - "\n", - "user_input: str\n", + "clusterbulkbulkInputsname\n", + "\n", + "name\n", "\n", - "\n", - "\n", - "clusterbulkInputselement->clusterbulkelementInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterbulkInputselement->clusterbulkbulkInputsname\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterbulkInputscell_size\n", - "\n", - "cell_size: int\n", + "\n", + "cell_size: int\n", "\n", - "\n", - "\n", - "clusterbulkcell_sizeInputsuser_input\n", - "\n", - "user_input: int\n", + "\n", + "\n", + "clusterbulkcellInputsrepeat_scalar\n", + "\n", + "repeat_scalar: int\n", "\n", - "\n", - "\n", - "clusterbulkInputscell_size->clusterbulkcell_sizeInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterbulkInputscell_size->clusterbulkcellInputsrepeat_scalar\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterbulkInputsvacancy_index\n", - "\n", - "vacancy_index\n", + "\n", + "vacancy_index\n", "\n", - "\n", - "\n", - "clusterbulkvacancy_indexInputsuser_input\n", - "\n", - "user_input\n", + "\n", + "\n", + "clusterbulkstructureInputsindex\n", + "\n", + "index\n", "\n", - "\n", - "\n", - "clusterbulkInputsvacancy_index->clusterbulkvacancy_indexInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterbulkInputsvacancy_index->clusterbulkstructureInputsindex\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clusterbulkOutputsstructure\n", - "\n", - "structure\n", + "clusterbulkOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", - "\n", + "\n", "\n", - "clusterbulkelementInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulkelementOutputsran\n", - "\n", - "ran\n", + "clusterbulkbulkInputsrun\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "\n", - "clusterbulkelementInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "\n", + "clusterbulkbulkOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", + "\n", "\n", - "\n", + "\n", "clusterbulkbulkInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulkelementOutputsran->clusterbulkbulkInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulkelementOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulkbulkInputsname\n", - "\n", - "name\n", - "\n", - "\n", - "\n", - "clusterbulkelementOutputsuser_input->clusterbulkbulkInputsname\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulkcell_sizeInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulkcell_sizeOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulkcell_sizeInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulkcellInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulkcell_sizeOutputsran->clusterbulkcellInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulkcell_sizeOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulkcellInputsrepeat_scalar\n", - "\n", - "repeat_scalar: int\n", - "\n", - "\n", - "\n", - "clusterbulkcell_sizeOutputsuser_input->clusterbulkcellInputsrepeat_scalar\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulkvacancy_indexInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulkvacancy_indexOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulkvacancy_indexInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulkstructureInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clusterbulkvacancy_indexOutputsran->clusterbulkstructureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulkvacancy_indexOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clusterbulkstructureInputsindex\n", - "\n", - "index\n", - "\n", - "\n", - "\n", - "clusterbulkvacancy_indexOutputsuser_input->clusterbulkstructureInputsindex\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clusterbulkbulkInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clusterbulkbulkOutputsran\n", - "\n", - "ran\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", "\n", - "\n", + "\n", "clusterbulkbulkInputscrystalstructure\n", - "\n", - "crystalstructure\n", + "\n", + "crystalstructure\n", "\n", "\n", - "\n", + "\n", "clusterbulkbulkInputsa\n", - "\n", - "a\n", + "\n", + "a\n", "\n", "\n", - "\n", + "\n", "clusterbulkbulkInputsc\n", - "\n", - "c\n", + "\n", + "c\n", "\n", "\n", - "\n", + "\n", "clusterbulkbulkInputscovera\n", - "\n", - "covera\n", + "\n", + "covera\n", "\n", "\n", - "\n", + "\n", "clusterbulkbulkInputsu\n", - "\n", - "u\n", + "\n", + "u\n", "\n", "\n", - "\n", + "\n", "clusterbulkbulkInputsorthorhombic\n", - "\n", - "orthorhombic\n", + "\n", + "orthorhombic\n", "\n", "\n", - "\n", + "\n", "clusterbulkbulkInputscubic\n", - "\n", - "cubic\n", + "\n", + "cubic\n", "\n", - "\n", - "\n", - "clusterbulkbulkOutputsran->clusterbulkcellInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterbulkcellInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterbulkbulkOutputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "clusterbulkbulkOutputsWithInjectionran->clusterbulkcellInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterbulkbulkOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", "\n", - "\n", + "\n", "clusterbulkcellInputsstructure\n", - "\n", - "structure: Atoms\n", + "\n", + "structure: Atoms\n", "\n", - "\n", - "\n", - "clusterbulkbulkOutputsstructure->clusterbulkcellInputsstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterbulkbulkOutputsWithInjectionstructure->clusterbulkcellInputsstructure\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clusterbulkcellInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterbulkcellOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterbulkcellOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "\n", - "clusterbulkcellOutputsran->clusterbulkstructureInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "clusterbulkstructureInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clusterbulkcellOutputsstructure\n", - "\n", - "structure: Atoms\n", + "\n", + "\n", + "clusterbulkcellOutputsWithInjectionran->clusterbulkstructureInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterbulkcellOutputsWithInjectionstructure\n", + "\n", + "structure: Atoms\n", "\n", "\n", - "\n", + "\n", "clusterbulkstructureInputsstructure\n", - "\n", - "structure\n", + "\n", + "structure\n", "\n", - "\n", - "\n", - "clusterbulkcellOutputsstructure->clusterbulkstructureInputsstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterbulkcellOutputsWithInjectionstructure->clusterbulkstructureInputsstructure\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clusterbulkstructureInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clusterbulkstructureOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clusterbulkstructureOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", - "\n", - "clusterbulkstructureOutputsstructure\n", - "\n", - "structure\n", + "\n", + "\n", + "\n", + "clusterbulkstructureOutputsWithInjectionstructure\n", + "\n", + "structure\n", "\n", - "\n", - "\n", - "clusterbulkstructureOutputsstructure->clusterbulkOutputsstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clusterbulkstructureOutputsWithInjectionstructure->clusterbulkOutputsWithInjectionstructure\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, "execution_count": 17, @@ -3689,7 +3284,7 @@ } ], "source": [ - "wf.bulk.draw(size=(20,10))" + "wf.bulk.draw(size=(10,10))" ] }, { @@ -3698,27 +3293,9 @@ "id": "6d848d6d-2a6e-48e8-9cd2-7cac862a3d1a", "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "max_workers: 1\n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n" - ] - }, { "data": { - "image/png": "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ymA4IiIiIiqD4YiIiIioDIYjIqIq6NatGyZPnix1GUSkQ5wEkoiM2ujRo5GRkYE///yzWvaXlpYGhUIBKyuratkfEekfU6kLICLSB4WFhQ9dfLUse3v7GqiGiKTEy2pEZBT++OMPNGvWDObm5nBwcECvXr3wwQcfYPXq1di6dStkMhlkMhkOHTqEmJgYyGQybNy4Ed26dYOZmRnWrVuH1NRUvPLKK/D09ISFhQWaNWuG9evXa33Ovy+r+fj4YN68eRgzZgysrKxQt25d/PjjjzXceyKqTgxHRGTwEhIS8Morr2DMmDEIDw/HoUOHMGTIEMyaNQtDhw5F3759kZCQgISEBHTo0EHzvo8++giTJk1CeHg4goODkZeXh8DAQGzfvh1XrlzBW2+9hZEjR+L06dOP/PxFixYhKCgIoaGhmDBhAsaPH49r167puttEpCO8rEZEBi8hIQFFRUUYMmQIvL29AQDNmjUDAJibmyM/Px+urq7l3jd58mQMGTJEa9u0adM0//3uu+9i165d+P3339G2bduHfn7//v0xYcIEACWB6+uvv8ahQ4fg5+f31H0joprHcEREBq9Fixbo2bMnmjVrhuDgYPTp0wcvvvgi7OzsHvm+oKAgrefFxcVYsGABfvvtN9y5cwf5+fnIz8+HpaXlI/fTvHlzzX/LZDK4uroiOTn5yTtERJLiZTUiMngmJibYu3cvdu7cCX9/fyxevBiNGzdGdHT0I9/379CzaNEifP311/jwww9x4MABXLhwAcHBwSgoKHjkfv49kFsmk0GtVj9ZZ4hIcjxzRERGQSaToWPHjujYsSNmzpwJb29vbNmyBUqlEsXFxZXax9GjRzFo0CCMGDECAKBWq3Hjxg00adJEl6UTkZ5hOCIig3f69Gns378fffr0gbOzM06fPo27d++iSZMmyMvLw+7duxEREQEHBwfY2Ng8dD8NGzbEpk2bcOLECdjZ2eGrr75CYmIiwxFRLcNwREQGz9raGkeOHME333yDrKwseHt7Y9GiRejXrx+CgoJw6NAhBAUF4d69ezh48CB8fHwq3M///d//ITo6GsHBwbCwsMBbb72FwYMHIzMzs2Y7RESS4gzZRERERGVwQDYRERFRGQxHRERERGUwHBERERGVwXBEREREVAbDEREREVEZDEdEREREZTAcEREREZXBcERERERUBsMRERERURkMR0RERERlMBwRERERlcFwRERERFTG/wOC5q7f+IrFCQAAAABJRU5ErkJggg==", + "image/png": "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ymA4IiIiIiqD4YiIiIioDIYjIqIq6NatGyZPnix1GUSkQ5wEkoiM2ujRo5GRkYE///yzWvaXlpYGhUIBKyuratkfEekfU6kLICLSB4WFhQ9dfLUse3v7GqiGiKTEy2pEZBT++OMPNGvWDObm5nBwcECvXr3wwQcfYPXq1di6dStkMhlkMhkOHTqEmJgYyGQybNy4Ed26dYOZmRnWrVuH1NRUvPLKK/D09ISFhQWaNWuG9evXa33Ovy+r+fj4YN68eRgzZgysrKxQt25d/PjjjzXceyKqTgxHRGTwEhIS8Morr2DMmDEIDw/HoUOHMGTIEMyaNQtDhw5F3759kZCQgISEBHTo0EHzvo8++giTJk1CeHg4goODkZeXh8DAQGzfvh1XrlzBW2+9hZEjR+L06dOP/PxFixYhKCgIoaGhmDBhAsaPH49r167puttEpCO8rEZEBi8hIQFFRUUYMmQIvL29AQDNmjUDAJibmyM/Px+urq7l3jd58mQMGTJEa9u0adM0//3uu+9i165d+P3339G2bduHfn7//v0xYcIEACWB6+uvv8ahQ4fg5+f31H0joprHcEREBq9Fixbo2bMnmjVrhuDgYPTp0wcvvvgi7OzsHvm+oKAgrefFxcVYsGABfvvtN9y5cwf5+fnIz8+HpaXlI/fTvHlzzX/LZDK4uroiOTn5yTtERJLiZTUiMngmJibYu3cvdu7cCX9/fyxevBiNGzdGdHT0I9/379CzaNEifP311/jwww9x4MABXLhwAcHBwSgoKHjkfv49kFsmk0GtVj9ZZ4hIcjxzRERGQSaToWPHjujYsSNmzpwJb29vbNmyBUqlEsXFxZXax9GjRzFo0CCMGDECAKBWq3Hjxg00adJEl6UTkZ5hOCIig3f69Gns378fffr0gbOzM06fPo27d++iSZMmyMvLw+7duxEREQEHBwfY2Ng8dD8NGzbEpk2bcOLECdjZ2eGrr75CYmIiwxFRLcNwREQGz9raGkeOHME333yDrKwseHt7Y9GiRejXrx+CgoJw6NAhBAUF4d69ezh48CB8fHwq3M///d//ITo6GsHBwbCwsMBbb72FwYMHIzMzs2Y7RESS4gzZRERERGVwQDYRERFRGQxHRERERGUwHBERERGVwXBEREREVAbDEREREVEZDEdEREREZTAcEREREZXBcERERERUBsMRERERURkMR0RERERlMBwRERERlcFwRERERFTG/wOC5q7f+IrFCQAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -3728,13 +3305,13 @@ } ], "source": [ - "df = energy_at_volume().iter(strain=np.linspace(-0.2, 0.2, 11))\n", + "df = energy_at_volume().iter(strain=np.linspace(-0.2, 0.2, 11).tolist())\n", "df.plot(x='strain', ylabel='Energy (eV)', title='Energy-Volume Curve');" ] }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 19, "id": "2836f6d9-6c44-4215-90bf-450c7012cf67", "metadata": {}, "outputs": [ @@ -3838,7 +3415,7 @@ "10 0.20 -81.021492" ] }, - "execution_count": 40, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -3849,30 +3426,13 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "id": "3050e5e5-4c68-4195-a665-0f5d4b0b78b0", "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n", - "Collect: static \n" - ] - }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -3884,15 +3444,8 @@ "source": [ "import numpy as np\n", "\n", - "energy_pot = []\n", - "strain_lst = np.linspace(0.86, 1, 11) - 1\n", - "for strain in strain_lst:\n", - " wf = energy_at_volume(element='Al', strain=strain)\n", - " out = wf.run()\n", - "\n", - " energy_pot.append(out['energy_pot']) \n", - "\n", - "plt.plot(strain_lst, energy_pot);" + "df = energy_at_volume(element='Al').iter(strain=(np.linspace(0.86, 1, 11) - 1).tolist())\n", + "plt.plot(df[\"strain\"], df[\"energy_pot\"]);" ] }, { @@ -3907,7 +3460,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "id": "7fcc5262-713b-4755-a730-0b1f6fa6349c", "metadata": {}, "outputs": [], @@ -3917,7 +3470,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "id": "6fd3b054-d066-4b05-9cbf-f5fa88476a45", "metadata": {}, "outputs": [], @@ -3928,7 +3481,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "id": "347cdcc9-db7a-4d84-9578-a04cad573aac", "metadata": {}, "outputs": [], @@ -3938,7 +3491,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "id": "e3720e67-67d0-4f3a-a8ae-57dbd86c20b7", "metadata": {}, "outputs": [], @@ -3948,7 +3501,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "id": "2c3b4321-f8a9-4e7f-bd8e-63bae6abf8d5", "metadata": {}, "outputs": [], @@ -3966,7 +3519,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "id": "e9083ee4-df8a-4a57-b168-f932b52071c3", "metadata": {}, "outputs": [], @@ -3976,7 +3529,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "id": "17f03ced-1b20-4db1-b78a-70db0c4f9c1c", "metadata": {}, "outputs": [], @@ -3994,7 +3547,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "id": "7c476ec9-e35b-46fe-bca1-f7705aa205d2", "metadata": {}, "outputs": [], @@ -4004,7 +3557,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 29, "id": "f0b574b3-3c74-44fe-a471-650738f2af9c", "metadata": {}, "outputs": [], @@ -4015,7 +3568,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 30, "id": "bf304ec5-2fe4-46cc-8ec8-2fcdb82500b6", "metadata": {}, "outputs": [], @@ -4026,7 +3579,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 31, "id": "397d1b5e-9336-463a-94d5-a508b8a1b9e7", "metadata": {}, "outputs": [], @@ -4036,7 +3589,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "id": "fc47c328-a3c6-42da-856e-cdfe58bdb5f5", "metadata": {}, "outputs": [], @@ -4046,7 +3599,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 33, "id": "3edaaa9e-8b3f-4298-a9f2-456d7013276a", "metadata": {}, "outputs": [], @@ -4056,7 +3609,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 34, "id": "b1d5e21f-6cae-4d0c-8543-1d3c1790fc37", "metadata": {}, "outputs": [], @@ -4074,7 +3627,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 35, "id": "21e00942-bc9a-48f9-838d-e16aa4d67fb6", "metadata": {}, "outputs": [], @@ -4084,13 +3637,13 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "id": "afd76914-fa37-4ec2-b22d-adecc03f232f", "metadata": {}, "outputs": [], "source": [ "wf = Workflow('Murnaghan')\n", - "wf.bulk = wf.create.atomistics.task.Bulk('Al', cubic=True)\n", + "wf.bulk = wf.create.atomistics.task.Bulk('Al')\n", "wf.lammps = wf.create.atomistics.calculator.Lammps()\n", "wf.lammps_potential = wf.create.atomistics.calculator.LammpsPotential(structure=wf.bulk)\n", "wf.macro = wf.create.atomistics.macro.EnergyVolumeCurve(calculator=wf.lammps)" @@ -4098,7 +3651,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "id": "bc0aae29-1d88-4720-97c1-035071b200d7", "metadata": {}, "outputs": [ @@ -4108,7 +3661,7 @@ "Atoms(symbols='Al', pbc=True, cell=[[0.0, 2.025, 2.025], [2.025, 0.0, 2.025], [2.025, 2.025, 0.0]])" ] }, - "execution_count": 36, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -4120,7 +3673,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "id": "84d5b8d5-c39f-4102-9a22-4298611abb94", "metadata": {}, "outputs": [ @@ -4161,10 +3714,10 @@ "ready": false }, "text/plain": [ - "" + "" ] }, - "execution_count": 37, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -4175,7 +3728,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "id": "37c3d874-2be5-4b83-8a8b-19c26c6d5197", "metadata": {}, "outputs": [], @@ -4201,48 +3754,702 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 40, "id": "ac98a74f-c712-49bd-8622-dadf3579be07", "metadata": {}, - "outputs": [ - { - "ename": "AttributeError", - "evalue": "'Wrappers' object has no attribute 'single_value_node'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[39], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;129m@Workflow\u001b[39m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwrap_as\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msingle_value_node\u001b[49m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mstring\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 2\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mcreate_string\u001b[39m(my_string: \u001b[38;5;28mstr\u001b[39m\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m):\n\u001b[1;32m 3\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m my_string\n\u001b[1;32m 5\u001b[0m \u001b[38;5;129m@Workflow\u001b[39m\u001b[38;5;241m.\u001b[39mwrap_as\u001b[38;5;241m.\u001b[39msingle_value_node(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mstring\u001b[39m\u001b[38;5;124m'\u001b[39m)\n\u001b[1;32m 6\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mplus\u001b[39m(my_string_1: \u001b[38;5;28mstr\u001b[39m\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m, my_string_2: \u001b[38;5;28mstr\u001b[39m\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m, my_string_3: \u001b[38;5;28mstr\u001b[39m\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m, my_string_4: \u001b[38;5;28mstr\u001b[39m\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m'\u001b[39m):\n", - "\u001b[0;31mAttributeError\u001b[0m: 'Wrappers' object has no attribute 'single_value_node'" - ] - } - ], + "outputs": [], "source": [ - "@Workflow.wrap_as.single_value_node('string')\n", - "def create_string(my_string: str=''):\n", + "@Workflow.wrap.as_function_node('string')\n", + "def CreateString(my_string: str=''):\n", " return my_string\n", "\n", - "@Workflow.wrap_as.single_value_node('string')\n", - "def plus(my_string_1: str='', my_string_2: str='', my_string_3: str='', my_string_4: str=''):\n", + "@Workflow.wrap.as_function_node('string')\n", + "def Plus(my_string_1: str='', my_string_2: str='', my_string_3: str='', my_string_4: str=''):\n", " return my_string_1 + my_string_2 + my_string_3 + my_string_4" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "id": "d01decfc-8d46-47f2-b7d4-495e2d91db59", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertest\n", + "\n", + "test: Workflow\n", + "\n", + "clustertestInputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inputs\n", + "\n", + "\n", + "clustertestOutputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Outputs\n", + "\n", + "\n", + "clusterteststring1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "string1: CreateString\n", + "\n", + "\n", + "clusterteststring1Inputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inputs\n", + "\n", + "\n", + "clusterteststring1OutputsWithInjection\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "OutputsWithInjection\n", + "\n", + "\n", + "clusterteststring2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "string2: CreateString\n", + "\n", + "\n", + "clusterteststring2Inputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inputs\n", + "\n", + "\n", + "clusterteststring2OutputsWithInjection\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "OutputsWithInjection\n", + "\n", + "\n", + "clusterteststring3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "string3: CreateString\n", + "\n", + "\n", + "clusterteststring3Inputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inputs\n", + "\n", + "\n", + "clusterteststring3OutputsWithInjection\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "OutputsWithInjection\n", + "\n", + "\n", + "clustertestsum_1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "sum_1: Plus\n", + "\n", + "\n", + "clustertestsum_1Inputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inputs\n", + "\n", + "\n", + "clustertestsum_1OutputsWithInjection\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "OutputsWithInjection\n", + "\n", + "\n", + "clustertestsum_2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "sum_2: Plus\n", + "\n", + "\n", + "clustertestsum_2Inputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inputs\n", + "\n", + "\n", + "clustertestsum_2OutputsWithInjection\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "OutputsWithInjection\n", + "\n", + "\n", + "clustertestsum_3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "sum_3: Plus\n", + "\n", + "\n", + "clustertestsum_3Inputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inputs\n", + "\n", + "\n", + "clustertestsum_3OutputsWithInjection\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "OutputsWithInjection\n", + "\n", + "\n", + "\n", + "clustertestInputsrun\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clustertestOutputsran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clustertestInputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clustertestInputsstring1__my_string\n", + "\n", + "string1__my_string: str\n", + "\n", + "\n", + "\n", + "clusterteststring1Inputsmy_string\n", + "\n", + "my_string: str\n", + "\n", + "\n", + "\n", + "clustertestInputsstring1__my_string->clusterteststring1Inputsmy_string\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestInputsstring2__my_string\n", + "\n", + "string2__my_string: str\n", + "\n", + "\n", + "\n", + "clusterteststring2Inputsmy_string\n", + "\n", + "my_string: str\n", + "\n", + "\n", + "\n", + "clustertestInputsstring2__my_string->clusterteststring2Inputsmy_string\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestInputsstring3__my_string\n", + "\n", + "string3__my_string: str\n", + "\n", + "\n", + "\n", + "clusterteststring3Inputsmy_string\n", + "\n", + "my_string: str\n", + "\n", + "\n", + "\n", + "clustertestInputsstring3__my_string->clusterteststring3Inputsmy_string\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestInputssum_1__my_string_4\n", + "\n", + "sum_1__my_string_4: str\n", + "\n", + "\n", + "\n", + "clustertestsum_1Inputsmy_string_4\n", + "\n", + "my_string_4: str\n", + "\n", + "\n", + "\n", + "clustertestInputssum_1__my_string_4->clustertestsum_1Inputsmy_string_4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestInputssum_2__my_string_3\n", + "\n", + "sum_2__my_string_3: str\n", + "\n", + "\n", + "\n", + "clustertestsum_2Inputsmy_string_3\n", + "\n", + "my_string_3: str\n", + "\n", + "\n", + "\n", + "clustertestInputssum_2__my_string_3->clustertestsum_2Inputsmy_string_3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestInputssum_2__my_string_4\n", + "\n", + "sum_2__my_string_4: str\n", + "\n", + "\n", + "\n", + "clustertestsum_2Inputsmy_string_4\n", + "\n", + "my_string_4: str\n", + "\n", + "\n", + "\n", + "clustertestInputssum_2__my_string_4->clustertestsum_2Inputsmy_string_4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestInputssum_3__my_string_4\n", + "\n", + "sum_3__my_string_4: str\n", + "\n", + "\n", + "\n", + "clustertestsum_3Inputsmy_string_4\n", + "\n", + "my_string_4: str\n", + "\n", + "\n", + "\n", + "clustertestInputssum_3__my_string_4->clustertestsum_3Inputsmy_string_4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestOutputssum_3__string\n", + "\n", + "sum_3__string\n", + "\n", + "\n", + "\n", + "clusterteststring1Inputsrun\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clusterteststring1OutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clusterteststring1Inputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterteststring1OutputsWithInjectionstring\n", + "\n", + "string\n", + "\n", + "\n", + "\n", + "clustertestsum_1Inputsmy_string_1\n", + "\n", + "my_string_1: str\n", + "\n", + "\n", + "\n", + "clusterteststring1OutputsWithInjectionstring->clustertestsum_1Inputsmy_string_1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestsum_1Inputsmy_string_3\n", + "\n", + "my_string_3: str\n", + "\n", + "\n", + "\n", + "clusterteststring1OutputsWithInjectionstring->clustertestsum_1Inputsmy_string_3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestsum_3Inputsmy_string_2\n", + "\n", + "my_string_2: str\n", + "\n", + "\n", + "\n", + "clusterteststring1OutputsWithInjectionstring->clustertestsum_3Inputsmy_string_2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterteststring2Inputsrun\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clusterteststring2OutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clusterteststring2Inputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterteststring2OutputsWithInjectionstring\n", + "\n", + "string\n", + "\n", + "\n", + "\n", + "clustertestsum_1Inputsmy_string_2\n", + "\n", + "my_string_2: str\n", + "\n", + "\n", + "\n", + "clusterteststring2OutputsWithInjectionstring->clustertestsum_1Inputsmy_string_2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestsum_3Inputsmy_string_3\n", + "\n", + "my_string_3: str\n", + "\n", + "\n", + "\n", + "clusterteststring2OutputsWithInjectionstring->clustertestsum_3Inputsmy_string_3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clusterteststring3Inputsrun\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clusterteststring3OutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clusterteststring3Inputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clusterteststring3OutputsWithInjectionstring\n", + "\n", + "string\n", + "\n", + "\n", + "\n", + "clustertestsum_2Inputsmy_string_2\n", + "\n", + "my_string_2: str\n", + "\n", + "\n", + "\n", + "clusterteststring3OutputsWithInjectionstring->clustertestsum_2Inputsmy_string_2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestsum_1Inputsrun\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clustertestsum_1OutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clustertestsum_1Inputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clustertestsum_1OutputsWithInjectionstring\n", + "\n", + "string\n", + "\n", + "\n", + "\n", + "clustertestsum_2Inputsmy_string_1\n", + "\n", + "my_string_1: str\n", + "\n", + "\n", + "\n", + "clustertestsum_1OutputsWithInjectionstring->clustertestsum_2Inputsmy_string_1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestsum_2Inputsrun\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clustertestsum_2OutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clustertestsum_2Inputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clustertestsum_2OutputsWithInjectionstring\n", + "\n", + "string\n", + "\n", + "\n", + "\n", + "clustertestsum_3Inputsmy_string_1\n", + "\n", + "my_string_1: str\n", + "\n", + "\n", + "\n", + "clustertestsum_2OutputsWithInjectionstring->clustertestsum_3Inputsmy_string_1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "clustertestsum_3Inputsrun\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clustertestsum_3OutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clustertestsum_3Inputsaccumulate_and_run\n", + "\n", + "accumulate_and_run\n", + "\n", + "\n", + "\n", + "clustertestsum_3OutputsWithInjectionstring\n", + "\n", + "string\n", + "\n", + "\n", + "\n", + "clustertestsum_3OutputsWithInjectionstring->clustertestOutputssum_3__string\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "wf = Workflow('test')\n", "\n", - "wf.string1 = create_string('a')\n", - "wf.string2 = create_string('b')\n", - "wf.string3 = create_string('c')\n", + "wf.string1 = CreateString('a')\n", + "wf.string2 = CreateString('b')\n", + "wf.string3 = CreateString('c')\n", "\n", - "wf.sum_1 = plus(wf.string1, wf.string2, wf.string1)\n", - "wf.sum_2 = plus(wf.sum_1, wf.string3)\n", - "wf.sum_3 = plus(wf.sum_2, wf.string1, wf.string2)\n", + "wf.sum_1 = Plus(wf.string1, wf.string2, wf.string1)\n", + "wf.sum_2 = Plus(wf.sum_1, wf.string3)\n", + "wf.sum_3 = Plus(wf.sum_2, wf.string1, wf.string2)\n", "\n", "wf.draw()" ] @@ -4272,7 +4479,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.2" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/notebooks/quickstart.ipynb b/notebooks/quickstart.ipynb index b4757dca..be42dfd7 100644 --- a/notebooks/quickstart.ipynb +++ b/notebooks/quickstart.ipynb @@ -37,7 +37,7 @@ "metadata": {}, "outputs": [], "source": [ - "@Workflow.wrap_as.function_node()\n", + "@Workflow.wrap.as_function_node()\n", "def AddOne(x):\n", " y = x + 1\n", " return y\n", @@ -123,7 +123,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:171: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n", + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:176: UserWarning: The channel run was not connected to ran, andthus could not disconnect from it.\n", " warn(\n" ] }, @@ -212,6 +212,39 @@ "n3()" ] }, + { + "cell_type": "markdown", + "id": "23aa57a3-12a9-418c-ba7e-2aaa1a4ba2b0", + "metadata": {}, + "source": [ + "The names of input to nodes is pulled directly from the signature of the wrapped function. By default, we also scrape the names of the output labels this way. Sometimes you want to return something that looks \"ugly\" -- like `x + 1` in the example above. You can create a new local variable that looks \"pretty\" (`y = x + 1` above) and return that, or you can pass an output label to the decorator. Nodes also pull hints and defaults from the function they wrap. We can re-write our example above to leverage all of this:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e6d06a0c-a558-4bb0-b72e-83820a6f0186", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'inputs': {'x': (int, NOT_DATA)}, 'outputs': {'y': int}}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "@Workflow.wrap.as_function_node(\"y\")\n", + "def AddOne(x: int) -> int:\n", + " return x + 1\n", + "\n", + "AddOne.preview_io()" + ] + }, { "cell_type": "markdown", "id": "dfa3db51-31d7-43c8-820a-6e5f3525837e", @@ -223,12 +256,12 @@ "\n", "The `Workflow` class not only gives us access to the decorators for defining new nodes, but also lets us register modules of existing nodes and use them. Let's put together a workflow that uses both an existing node from a package, and another function node that has multiple return values. This function node will also exploit our ability to name outputs (in the decorator argument) and give type hints (in the function signature, as usual). \n", "\n", - "In addition to using output channels (or nodes, if they have only a single output) to make connections to input channels, we can perform many (but not all) other python operations on them to dynamically create new output nodes! Below see how we do math and indexing right on the output channels:" + "In addition to using output channels (or nodes, if they have only a single output) to make connections to input channels, we can perform many (but not all) other python operations on them to dynamically create new nodes! Below see how we do math and indexing right on the output channels:" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "id": "4c80aee3-a8e4-444c-9260-3078f8d617a4", "metadata": {}, "outputs": [], @@ -237,7 +270,7 @@ "\n", "wf = Workflow(\"my_workflow\")\n", "\n", - "@Workflow.wrap_as.function_node(\"arange\", \"length\")\n", + "@Workflow.wrap.as_function_node(\"arange\", \"length\")\n", "def Arange(n: int) -> tuple[np.ndarray, int]:\n", " \"\"\"\n", " Two outputs is silly overkill, but just to demonstrate how Function nodes work\n", @@ -263,7 +296,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "c1ef0cf9-131f-4abd-a1dd-d4f066fe1d32", "metadata": {}, "outputs": [ @@ -276,12 +309,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "clustermy_workflow\n", - "\n", - "my_workflow: Workflow\n", + "\n", + "my_workflow: Workflow\n", "\n", "clustermy_workflowInputs\n", "\n", @@ -293,214 +326,214 @@ "\n", "Inputs\n", "\n", - "\n", - "clustermy_workflowplot\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "plot: Scatter\n", - "\n", - "\n", - "clustermy_workflowplotInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clustermy_workflowplotOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", "\n", "clustermy_workflowOutputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Outputs\n", "\n", "\n", "clustermy_workflowarange\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "arange: Arange\n", "\n", "\n", "clustermy_workflowarangeInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "Inputs\n", "\n", "\n", - "clustermy_workflowarangeOutputs\n", + "clustermy_workflowarangeOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clustermy_workflowarange__length_Subtract_1\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "arange__length_Subtract_1: Subtract\n", + "\n", + "arange__length_Subtract_1: Subtract\n", "\n", - "\n", - "clustermy_workflowarange__length_Subtract_1Outputs\n", + "\n", + "clustermy_workflowarange__length_Subtract_1Inputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clustermy_workflowarange__length_Subtract_1Inputs\n", + "\n", + "clustermy_workflowarange__length_Subtract_1OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_None\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "arange__arange_Slice_None_arange__length_Subtract_1__sub_None: Slice\n", + "\n", + "arange__arange_Slice_None_arange__length_Subtract_1__sub_None: Slice\n", "\n", "\n", "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputs\n", + "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "arange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice: GetItem\n", + "\n", + "arange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice: GetItem\n", "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputs\n", + "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "arange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2: Power\n", + "\n", + "arange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2: Power\n", + "\n", + "\n", + "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inputs\n", "\n", "\n", - "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Outputs\n", + "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", - "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputs\n", + "\n", + "clustermy_workflowplot\n", "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "plot: Scatter\n", + "\n", + "\n", + "clustermy_workflowplotInputs\n", + "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", + "\n", + "\n", + "clustermy_workflowplotOutputsWithInjection\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "\n", @@ -511,8 +544,8 @@ "\n", "\n", "clustermy_workflowOutputsran\n", - "\n", - "ran\n", + "\n", + "ran\n", "\n", "\n", "\n", @@ -568,15 +601,15 @@ "\n", "\n", "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstart\n", - "\n", - "start\n", + "\n", + "start\n", "\n", "\n", "\n", "clustermy_workflowInputsarange__arange_Slice_None_arange__length_Subtract_1__sub_None__start->clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstart\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -587,15 +620,15 @@ "\n", "\n", "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstep\n", - "\n", - "step\n", + "\n", + "step\n", "\n", "\n", "\n", "clustermy_workflowInputsarange__arange_Slice_None_arange__length_Subtract_1__sub_None__step->clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstep\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -606,21 +639,21 @@ "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsother\n", - "\n", - "other\n", + "\n", + "other\n", "\n", "\n", "\n", "clustermy_workflowInputsarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2__other->clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsother\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clustermy_workflowOutputsplot__fig\n", - "\n", - "plot__fig\n", + "\n", + "plot__fig\n", "\n", "\n", "\n", @@ -628,41 +661,41 @@ "\n", "run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarangeOutputsran\n", + "clustermy_workflowarangeOutputsWithInjectionran\n", "\n", "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clustermy_workflowarangeInputsaccumulate_and_run\n", "\n", "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarangeOutputsarange\n", + "clustermy_workflowarangeOutputsWithInjectionarange\n", "\n", "arange: ndarray\n", "\n", "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarangeOutputsarange->clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsobj\n", - "\n", - "\n", - "\n", + "clustermy_workflowarangeOutputsWithInjectionarange->clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarangeOutputslength\n", + "clustermy_workflowarangeOutputsWithInjectionlength\n", "\n", "length: int\n", "\n", @@ -672,9 +705,9 @@ "\n", "obj\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarangeOutputslength->clustermy_workflowarange__length_Subtract_1Inputsobj\n", + "clustermy_workflowarangeOutputsWithInjectionlength->clustermy_workflowarange__length_Subtract_1Inputsobj\n", "\n", "\n", "\n", @@ -685,205 +718,205 @@ "\n", "run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__length_Subtract_1Outputsran\n", - "\n", - "ran\n", + "clustermy_workflowarange__length_Subtract_1OutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clustermy_workflowarange__length_Subtract_1Inputsaccumulate_and_run\n", "\n", "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__length_Subtract_1Outputssub\n", - "\n", - "sub\n", + "clustermy_workflowarange__length_Subtract_1OutputsWithInjectionsub\n", + "\n", + "sub\n", "\n", "\n", "\n", "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstop\n", - "\n", - "stop\n", + "\n", + "stop\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__length_Subtract_1Outputssub->clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstop\n", - "\n", - "\n", - "\n", + "clustermy_workflowarange__length_Subtract_1OutputsWithInjectionsub->clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstop\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsran\n", - "\n", - "ran\n", + "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsslice\n", - "\n", - "slice\n", + "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsWithInjectionslice\n", + "\n", + "slice\n", "\n", "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsitem\n", - "\n", - "item\n", + "\n", + "item\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsslice->clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsitem\n", - "\n", - "\n", - "\n", + "clustermy_workflowarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsWithInjectionslice->clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsitem\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsran\n", - "\n", - "ran\n", + "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsgetitem\n", - "\n", - "getitem\n", + "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjectiongetitem\n", + "\n", + "getitem\n", "\n", "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsgetitem->clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsobj\n", - "\n", - "\n", - "\n", + "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjectiongetitem->clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsobj\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clustermy_workflowplotInputsx\n", - "\n", - "x: Union\n", + "\n", + "x: Union\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsgetitem->clustermy_workflowplotInputsx\n", - "\n", - "\n", - "\n", + "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjectiongetitem->clustermy_workflowplotInputsx\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Outputsran\n", - "\n", - "ran\n", + "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2OutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Outputspow\n", - "\n", - "pow\n", + "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2OutputsWithInjectionpow\n", + "\n", + "pow\n", "\n", "\n", "\n", "clustermy_workflowplotInputsy\n", - "\n", - "y: Union\n", + "\n", + "y: Union\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Outputspow->clustermy_workflowplotInputsy\n", - "\n", - "\n", - "\n", + "clustermy_workflowarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2OutputsWithInjectionpow->clustermy_workflowplotInputsy\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clustermy_workflowplotInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowplotOutputsran\n", - "\n", - "ran\n", + "clustermy_workflowplotOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clustermy_workflowplotInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowplotOutputsfig\n", - "\n", - "fig\n", + "clustermy_workflowplotOutputsWithInjectionfig\n", + "\n", + "fig\n", "\n", - "\n", + "\n", "\n", - "clustermy_workflowplotOutputsfig->clustermy_workflowOutputsplot__fig\n", - "\n", - "\n", - "\n", + "clustermy_workflowplotOutputsWithInjectionfig->clustermy_workflowOutputsplot__fig\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -904,31 +937,23 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "id": "c499c0ed-7af5-491a-b340-2d2f4f48529c", "metadata": {}, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/io.py:404: UserWarning: The keyword 'arrays__x' was not found among input labels. If you are trying to update a class instance keyword, please use attribute assignment directly instead of calling this method\n", - " warnings.warn(\n" - ] - }, { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhYAAAGdCAYAAABO2DpVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAYmUlEQVR4nO3dbWyV5f3A8V9bpVXXngmuUEKn1Rm0NjoBiSi4bGwMNUTfLIvRxc0tGaQ+oC820G0EN9eRGecSJ8aHGRfiQzJkkziZZArMKEGgThkTt8kGmSWdw512GOoo9/8FoX86WugpV0859PNJzovevW7O5ZUr63f3Oec+ZVmWZQEAkED5cE8AADhxCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEjmpGI/4f79++O9996L6urqKCsrK/bTAwCDkGVZdHZ2xvjx46O8vP/rEkUPi/feey/q6+uL/bQAQAI7d+6MCRMm9Pv7oodFdXV1RByYWE1NTbGfHgAYhI6Ojqivr+/5O96foofFwZc/ampqhAUAlJijvY3BmzcBgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJFP0G2QBAOl1789iw/bd0d65N2qrq2Jqw+ioKC/+d3IJCwAocau2tMXilVujLb+351hdrioWzWmM2U11RZ2Ll0IAoISt2tIW85Zt7hUVERG78ntj3rLNsWpLW1HnIywAoER1789i8cqtkfXxu4PHFq/cGt37+xoxNIQFAJSoDdt3H3al4lBZRLTl98aG7buLNidhAQAlqr2z/6gYzLgUhAUAlKja6qqk41IQFgBQoqY2jI66XFX096HSsjjw6ZCpDaOLNidhAQAlqqK8LBbNaYyIOCwuDv68aE5jUe9nISwAoITNbqqLpTdMinG53i93jMtVxdIbJhX9PhZukAUAJW52U118oXGcO28CAGlUlJfFtHPGDPc0vBQCAKQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJBMQWGxb9+++M53vhMNDQ1xyimnxNlnnx1333137N+/f6jmBwCUkJMKGbxkyZJ46KGH4oknnogLLrggNm7cGF/72tcil8vFbbfdNlRzBABKREFh8dprr8U111wTV199dUREnHXWWfHUU0/Fxo0bh2RyAEBpKeilkOnTp8fvfve7eOeddyIi4g9/+EO88sorcdVVV/V7TldXV3R0dPR6AAAnpoKuWHz729+OfD4f5513XlRUVER3d3fcc889cd111/V7TktLSyxevPiYJwoAHP8KumLxzDPPxLJly+LJJ5+MzZs3xxNPPBH33ntvPPHEE/2es3Dhwsjn8z2PnTt3HvOkAYDjU1mWZdlAB9fX18eCBQuiubm559gPfvCDWLZsWbz99tsD+jc6Ojoil8tFPp+PmpqawmcMABTdQP9+F3TF4sMPP4zy8t6nVFRU+LgpABARBb7HYs6cOXHPPffEJz/5ybjggguitbU17rvvvrjpppuGan4AQAkp6KWQzs7O+O53vxsrVqyI9vb2GD9+fFx33XXxve99L0aNGjWgf8NLIQBQegb697ugsEhBWABA6RmS91gAAByJsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJFNwWPzjH/+IG264IcaMGROnnnpqfPrTn45NmzYNxdwAgBJzUiGDP/jgg7j88svjs5/9bLzwwgtRW1sbf/3rX+PjH//4EE0PACglBYXFkiVLor6+Ph5//PGeY2eddVbqOQEAJaqgl0Kee+65mDJlSnzpS1+K2trauPjii+ORRx454jldXV3R0dHR6wEAnJgKCot33303li5dGueee2789re/jblz58att94av/jFL/o9p6WlJXK5XM+jvr7+mCcNAByfyrIsywY6eNSoUTFlypR49dVXe47deuut8frr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", "text/plain": [ "
" ] @@ -938,7 +963,7 @@ } ], "source": [ - "out = wf(arrays__x=5)\n", + "out = wf(arange__n=5)\n", "out.plot__fig" ] }, @@ -952,7 +977,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "id": "04a19675-c98d-4255-8583-a567cda45e08", "metadata": {}, "outputs": [ @@ -981,42 +1006,9 @@ "\n", "There's just one last step: once we have a workflow we're happy with, we can package it as a \"macro\"! This lets us make more and more complex workflows by composing sub-graphs.\n", "\n", - "We don't yet have an automated tool for converting workflows into macros, but we can create them by decorating a function that takes a macro instance and builds its graph, so we can just copy-and-paste our workflow above into a decorated function! \n", + "We don't yet have an automated tool for converting workflows into macros, but we can create them by decorating a function that takes a macro instance and macro input, builds its graph, and returns the parts of it we want as macro output. We can do most of this by just copy-and-pasting our workflow above into a decorated function! \n", "\n", - "We can also give our macro prettier IO names. This can be done with \"maps\" (which are also available on the workflows):" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "f67312c0-7028-4569-8b3a-d9e2fe88df48", - "metadata": {}, - "outputs": [], - "source": [ - "@Workflow.wrap_as.macro_node()\n", - "def MySquarePlot(macro):\n", - " macro.arange = Arange()\n", - " macro.plot = macro.create.plotting.Scatter(\n", - " x=macro.arange.outputs.arange[:macro.arange.outputs.length -1],\n", - " y=macro.arange.outputs.arange[:macro.arange.outputs.length -1]**2\n", - " )\n", - " macro.inputs_map = {\"arange__n\": \"n\"}\n", - " macro.outputs_map = {\n", - " \"arange__arange\": \"x\",\n", - " \"arange__length\": \"n\",\n", - " \"plot__fig\": \"fig\"\n", - " }\n", - " # Note that we also forced regularly hidden IO to be exposed!\n", - " # We can also hide IO that's usually exposed by mapping to `None`\n", - " # but that would be a lot of typing in this case" - ] - }, - { - "cell_type": "markdown", - "id": "e260929f-2d13-486c-b547-f5d8e2f0a330", - "metadata": {}, - "source": [ - "Or we can use a more function-node-like defintion of our macro with args and/or kwargs, and return values and output labels. The \"maps\" above _always take precedence_ so you still have full control over your macro-level IO, but using this format switches us over to an \"whitelist\" paradigm that automatically turns off all the other IO, which can make it easier to keep things tidy:" + "Just like a function node, the IO of a macro is defined by the signature and return values of the function we're decorating. Just remember to include a `self`-like argument for the macro instance itself as the first argument, and (usually) to only return single-output nodes or output channels in the `return` statement:" ] }, { @@ -1026,14 +1018,14 @@ "metadata": {}, "outputs": [], "source": [ - "@Workflow.wrap_as.macro_node(\"x\", \"n\", \"fig\")\n", - "def MySquarePlot(macro, n: int):\n", - " macro.arange = Arange(n=n)\n", - " macro.plot = macro.create.plotting.Scatter(\n", - " x=macro.arange.outputs.arange[:macro.arange.outputs.length -1],\n", - " y=macro.arange.outputs.arange[:macro.arange.outputs.length -1]**2\n", + "@Workflow.wrap.as_macro_node(\"x\", \"n\", \"fig\")\n", + "def MySquarePlot(wf, n: int):\n", + " wf.arange = Arange(n=n)\n", + " wf.plot = wf.create.plotting.Scatter(\n", + " x=wf.arange.outputs.arange[:wf.arange.outputs.length -1],\n", + " y=wf.arange.outputs.arange[:wf.arange.outputs.length -1]**2\n", " )\n", - " return macro.arange.outputs.arange, macro.arange.outputs.length, macro.plot" + " return wf.arange.outputs.arange, wf.arange.outputs.length, wf.plot" ] }, { @@ -1042,12 +1034,20 @@ "id": "b43f7a86-4579-4476-89a9-9d7c5942c3fb", "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/huber/work/pyiron/pyiron_workflow/pyiron_workflow/channels.py:176: UserWarning: The channel user_input was not connected to n, andthus could not disconnect from it.\n", + " warn(\n" + ] + }, { "data": { "text/plain": [ "{'square_plot__n': 10,\n", - " 'square_plot__fig': ,\n", - " 'plus_one_square_plot__fig': }" + " 'square_plot__fig': ,\n", + " 'plus_one_square_plot__fig': }" ] }, "execution_count": 14, @@ -1100,12 +1100,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "clustersquare_plot\n", - "\n", - "square_plot: MySquarePlot\n", + "\n", + "square_plot: MySquarePlot\n", "\n", "clustersquare_plotInputs\n", "\n", @@ -1118,29 +1118,29 @@ "Inputs\n", "\n", "\n", - "clustersquare_plotOutputs\n", + "clustersquare_plotOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clustersquare_plotn\n", + "clustersquare_plotarange\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "n: UserInput\n", + "\n", + "arange: Arange\n", "\n", "\n", - "clustersquare_plotnInputs\n", + "clustersquare_plotarangeInputs\n", "\n", "\n", "\n", @@ -1151,213 +1151,180 @@ "Inputs\n", "\n", "\n", - "clustersquare_plotnOutputs\n", + "clustersquare_plotarangeOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clustersquare_plotarange\n", + "clustersquare_plotarange__length_Subtract_1\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "arange: Arange\n", + "\n", + "arange__length_Subtract_1: Subtract\n", "\n", "\n", - "clustersquare_plotarangeInputs\n", + "clustersquare_plotarange__length_Subtract_1Inputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clustersquare_plotarangeOutputs\n", + "clustersquare_plotarange__length_Subtract_1OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clustersquare_plotarange__length_Subtract_1\n", + "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_None\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "arange__length_Subtract_1: Subtract\n", + "\n", + "arange__arange_Slice_None_arange__length_Subtract_1__sub_None: Slice\n", "\n", "\n", - "clustersquare_plotarange__length_Subtract_1Inputs\n", + "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", "\n", - "clustersquare_plotarange__length_Subtract_1Outputs\n", + "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", - "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_None\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "arange__arange_Slice_None_arange__length_Subtract_1__sub_None: Slice\n", - "\n", - "\n", - "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", - "\n", - "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "arange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice: GetItem\n", + "\n", + "arange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice: GetItem\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputs\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "arange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2: Power\n", + "\n", + "arange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2: Power\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Outputs\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2OutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", - "\n", + "\n", "clustersquare_plotplot\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "plot: Scatter\n", + "\n", + "plot: Scatter\n", "\n", - "\n", + "\n", "clustersquare_plotplotInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clustersquare_plotplotOutputs\n", + "\n", + "clustersquare_plotplotOutputsWithInjection\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "OutputsWithInjection\n", "\n", "\n", "\n", @@ -1365,13 +1332,13 @@ "\n", "run\n", "\n", - "\n", + "\n", "\n", - "clustersquare_plotOutputsran\n", - "\n", - "ran\n", + "clustersquare_plotOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", "\n", "clustersquare_plotInputsaccumulate_and_run\n", @@ -1384,428 +1351,383 @@ "\n", "n: int\n", "\n", - "\n", + "\n", "\n", - "clustersquare_plotnInputsuser_input\n", - "\n", - "user_input: int\n", + "clustersquare_plotarangeInputsn\n", + "\n", + "n: int\n", "\n", - "\n", - "\n", - "clustersquare_plotInputsn->clustersquare_plotnInputsuser_input\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotInputsn->clustersquare_plotarangeInputsn\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "clustersquare_plotOutputsx\n", - "\n", - "x: ndarray\n", + "clustersquare_plotOutputsWithInjectionx\n", + "\n", + "x\n", "\n", - "\n", + "\n", "\n", - "clustersquare_plotOutputsn\n", - "\n", - "n: int\n", + "clustersquare_plotOutputsWithInjectionn\n", + "\n", + "n\n", "\n", - "\n", + "\n", "\n", - "clustersquare_plotOutputsfig\n", - "\n", - "fig\n", + "clustersquare_plotOutputsWithInjectionfig\n", + "\n", + "fig\n", "\n", - "\n", + "\n", "\n", - "clustersquare_plotnInputsrun\n", + "clustersquare_plotarangeInputsrun\n", "\n", "run\n", "\n", - "\n", + "\n", "\n", - "clustersquare_plotnOutputsran\n", - "\n", - "ran\n", + "clustersquare_plotarangeOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", - "clustersquare_plotnInputsaccumulate_and_run\n", + "clustersquare_plotarangeInputsaccumulate_and_run\n", "\n", "accumulate_and_run\n", "\n", - "\n", - "\n", - "clustersquare_plotarangeInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", - "\n", - "\n", - "\n", - "clustersquare_plotnOutputsran->clustersquare_plotarangeInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clustersquare_plotnOutputsuser_input\n", - "\n", - "user_input\n", - "\n", - "\n", - "\n", - "clustersquare_plotarangeInputsn\n", - "\n", - "n: int\n", - "\n", - "\n", - "\n", - "clustersquare_plotnOutputsuser_input->clustersquare_plotarangeInputsn\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "clustersquare_plotarangeInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clustersquare_plotarangeOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__length_Subtract_1Inputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clustersquare_plotarangeOutputsran->clustersquare_plotarange__length_Subtract_1Inputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarangeOutputsWithInjectionran->clustersquare_plotarange__length_Subtract_1Inputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clustersquare_plotarangeOutputsran->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarangeOutputsWithInjectionran->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clustersquare_plotarangeOutputsarange\n", - "\n", - "arange: ndarray\n", + "\n", + "\n", + "clustersquare_plotarangeOutputsWithInjectionarange\n", + "\n", + "arange: ndarray\n", "\n", - "\n", - "\n", - "clustersquare_plotarangeOutputsarange->clustersquare_plotOutputsx\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarangeOutputsWithInjectionarange->clustersquare_plotOutputsWithInjectionx\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", - "\n", - "clustersquare_plotarangeOutputsarange->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsobj\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarangeOutputsWithInjectionarange->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsobj\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clustersquare_plotarangeOutputslength\n", - "\n", - "length: int\n", + "\n", + "\n", + "clustersquare_plotarangeOutputsWithInjectionlength\n", + "\n", + "length: int\n", "\n", - "\n", - "\n", - "clustersquare_plotarangeOutputslength->clustersquare_plotOutputsn\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarangeOutputsWithInjectionlength->clustersquare_plotOutputsWithInjectionn\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__length_Subtract_1Inputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", - "\n", - "clustersquare_plotarangeOutputslength->clustersquare_plotarange__length_Subtract_1Inputsobj\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarangeOutputsWithInjectionlength->clustersquare_plotarange__length_Subtract_1Inputsobj\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__length_Subtract_1Inputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__length_Subtract_1Outputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clustersquare_plotarange__length_Subtract_1OutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__length_Subtract_1Inputsother\n", - "\n", - "other\n", + "\n", + "other\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__length_Subtract_1Outputsran->clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarange__length_Subtract_1OutputsWithInjectionran->clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__length_Subtract_1Outputssub\n", - "\n", - "sub\n", + "\n", + "\n", + "clustersquare_plotarange__length_Subtract_1OutputsWithInjectionsub\n", + "\n", + "sub\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstop\n", - "\n", - "stop\n", + "\n", + "stop\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__length_Subtract_1Outputssub->clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstop\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarange__length_Subtract_1OutputsWithInjectionsub->clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstop\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstart\n", - "\n", - "start\n", + "\n", + "start\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneInputsstep\n", - "\n", - "step\n", + "\n", + "step\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsran->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsWithInjectionran->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsslice\n", - "\n", - "slice\n", + "\n", + "\n", + "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsWithInjectionslice\n", + "\n", + "slice\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsitem\n", - "\n", - "item\n", + "\n", + "item\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsslice->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsitem\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarange__arange_Slice_None_arange__length_Subtract_1__sub_NoneOutputsWithInjectionslice->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsitem\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceInputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsran->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjectionran->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotplotInputsaccumulate_and_run\n", - "\n", - "accumulate_and_run\n", + "\n", + "accumulate_and_run\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsran->clustersquare_plotplotInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjectionran->clustersquare_plotplotInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsgetitem\n", - "\n", - "getitem\n", + "\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjectiongetitem\n", + "\n", + "getitem\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsobj\n", - "\n", - "obj\n", + "\n", + "obj\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsgetitem->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsobj\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjectiongetitem->clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsobj\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotplotInputsx\n", - "\n", - "x: Union\n", + "\n", + "x: Union\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsgetitem->clustersquare_plotplotInputsx\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__sliceOutputsWithInjectiongetitem->clustersquare_plotplotInputsx\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsrun\n", - "\n", - "run\n", + "\n", + "run\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Outputsran\n", - "\n", - "ran\n", + "\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2OutputsWithInjectionran\n", + "\n", + "ran\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Inputsother\n", - "\n", - "other\n", + "\n", + "other\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Outputsran->clustersquare_plotplotInputsaccumulate_and_run\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2OutputsWithInjectionran->clustersquare_plotplotInputsaccumulate_and_run\n", + "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Outputspow\n", - "\n", - "pow\n", + "\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2OutputsWithInjectionpow\n", + "\n", + "pow\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotplotInputsy\n", - "\n", - "y: Union\n", + "\n", + "y: Union\n", "\n", - "\n", - "\n", - "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2Outputspow->clustersquare_plotplotInputsy\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "clustersquare_plotarange__arange_GetItem_arange__arange_Slice_None_arange__length_Subtract_1__sub_None__slice__getitem_Power_2OutputsWithInjectionpow->clustersquare_plotplotInputsy\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "clustersquare_plotplotInputsrun\n", - "\n", - "run\n", - "\n", - "\n", - "\n", - "clustersquare_plotplotOutputsran\n", - "\n", - "ran\n", - "\n", - "\n", - "\n", - "\n", - "clustersquare_plotplotOutputsfig\n", - "\n", - "fig\n", - "\n", - "\n", - "\n", - "clustersquare_plotplotOutputsfig->clustersquare_plotOutputsfig\n", - "\n", - "\n", - "\n", + "\n", + "run\n", + "\n", + "\n", + "\n", + "clustersquare_plotplotOutputsWithInjectionran\n", + "\n", + "ran\n", + "\n", + "\n", + "\n", + "\n", + "clustersquare_plotplotOutputsWithInjectionfig\n", + "\n", + "fig\n", + "\n", + "\n", + "\n", + "clustersquare_plotplotOutputsWithInjectionfig->clustersquare_plotOutputsWithInjectionfig\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - "" + "" ] }, "execution_count": 15, diff --git a/pyiron_workflow/channels.py b/pyiron_workflow/channels.py index 5aa565c0..c7d97517 100644 --- a/pyiron_workflow/channels.py +++ b/pyiron_workflow/channels.py @@ -11,7 +11,6 @@ import typing from abc import ABC, abstractmethod import inspect -from warnings import warn from pyiron_workflow.has_interface_mixins import HasChannel, HasLabel, UsesState from pyiron_workflow.has_to_dict import HasToDict @@ -119,6 +118,9 @@ def connect(self, *others: Channel) -> None: channels, i.e. they are instances of each others :attr:`connection_partner_type`. + New connections get _prepended_ to the connection lists, so they appear first + when searching over connections. + Args: *others (Channel): The other channel objects to attempt to connect with. @@ -132,8 +134,10 @@ def connect(self, *others: Channel) -> None: if other in self.connections: continue elif self._valid_connection(other): - self.connections.append(other) - other.connections.append(self) + # Prepend new connections + # so that connection searches run newest to oldest + self.connections.insert(0, other) + other.connections.insert(0, self) else: if isinstance(other, self.connection_partner_type): raise ChannelConnectionError( @@ -167,11 +171,6 @@ def disconnect(self, *others: Channel) -> list[tuple[Channel, Channel]]: self.connections.remove(other) other.disconnect(self) destroyed_connections.append((self, other)) - else: - warn( - f"The channel {self.label} was not connected to {other.label}, and" - f"thus could not disconnect from it." - ) return destroyed_connections def disconnect_all(self) -> list[tuple[Channel, Channel]]: @@ -505,10 +504,10 @@ def connection_partner_type(self): def fetch(self) -> None: """ - Sets :attr:`value` to the first value among connections that is something other - than `NOT_DATA`; if no such value exists (e.g. because there are no connections - or because all the connected output channels have `NOT_DATA` as their value), - :attr:`value` remains unchanged. + Sets :attr:`value` to the first value among connections (i.e. the most recent) + that is something other than `NOT_DATA`; if no such value exists (e.g. because + there are no connections or because all the connected output channels have + `NOT_DATA` as their value), :attr:`value` remains unchanged. I.e., the connection with the highest priority for updating input data is the 0th connection; build graphs accordingly. @@ -588,14 +587,14 @@ def __init__( object. Must be a method on the owner. """ super().__init__(label=label, owner=owner) - if self._is_method_on_owner(callback) and self._takes_zero_arguments(callback): + if self._is_method_on_owner(callback) and self._all_args_arg_optional(callback): self._callback: str = callback.__name__ else: raise BadCallbackError( f"The channel {self.label} on {self.owner.label} got an unexpected " f"callback: {callback}. " f"Lives on owner: {self._is_method_on_owner(callback)}; " - f"take no args: {self._takes_zero_arguments(callback)} " + f"all args are optional: {self._all_args_arg_optional(callback)} " ) def _is_method_on_owner(self, callback): @@ -604,17 +603,22 @@ def _is_method_on_owner(self, callback): except AttributeError: return False - def _takes_zero_arguments(self, callback): - return callable(callback) and self._no_positional_args(callback) + def _all_args_arg_optional(self, callback): + return callable(callback) and not self._has_required_args(callback) @staticmethod - def _no_positional_args(func): - return all( - [ - parameter.default != inspect.Parameter.empty - or parameter.kind == inspect.Parameter.VAR_KEYWORD - for parameter in inspect.signature(func).parameters.values() - ] + def _has_required_args(func): + return any( + ( + param.kind + in ( + inspect.Parameter.POSITIONAL_ONLY, + inspect.Parameter.POSITIONAL_OR_KEYWORD, + inspect.Parameter.KEYWORD_ONLY, + ) + and param.default == inspect.Parameter.empty + ) + for param in inspect.signature(func).parameters.values() ) @property diff --git a/pyiron_workflow/composite.py b/pyiron_workflow/composite.py index 7b10866b..f099d405 100644 --- a/pyiron_workflow/composite.py +++ b/pyiron_workflow/composite.py @@ -5,26 +5,29 @@ from __future__ import annotations -from abc import ABC, abstractmethod -from functools import wraps +from abc import ABC +from time import sleep from typing import Literal, Optional, TYPE_CHECKING -from bidict import bidict - -from pyiron_workflow.create import Creator, Wrappers -from pyiron_workflow.io import Outputs, Inputs +from pyiron_workflow.create import HasCreator from pyiron_workflow.node import Node -from pyiron_workflow.node_package import NodePackage from pyiron_workflow.semantics import SemanticParent from pyiron_workflow.topology import set_run_connections_according_to_dag from pyiron_workflow.snippets.colors import SeabornColors from pyiron_workflow.snippets.dotdict import DotDict if TYPE_CHECKING: - from pyiron_workflow.channels import Channel, InputData, OutputData + from pyiron_workflow.channels import ( + Channel, + InputData, + OutputData, + InputSignal, + OutputSignal, + ) + from pyiron_workflow.create import Creator, Wrappers -class Composite(Node, SemanticParent, ABC): +class Composite(SemanticParent, HasCreator, Node, ABC): """ A base class for nodes that have internal graph structure -- i.e. they hold a collection of child nodes and their computation is to execute that graph. @@ -68,26 +71,21 @@ class Composite(Node, SemanticParent, ABC): - Force a child node's IO to _not_ appear Attributes: - inputs/outputs_map (bidict|None): Maps in the form - `{"node_label__channel_label": "some_better_name"}` that expose canonically - named channels of child nodes under a new name. This can be used both for re- - naming regular IO (i.e. unconnected child channels), as well as forcing the - exposure of irregular IO (i.e. child channels that are already internally - connected to some other child channel). Non-`None` values provided at input - can be in regular dictionary form, but get re-cast as a clean bidict to ensure - the bijective nature of the maps (i.e. there is a 1:1 connection between any - IO exposed at the :class:`Composite` level and the underlying channels). - children (bidict.bidict[pyiron_workflow.node.Node]): The owned nodes that - form the composite subgraph. strict_naming (bool): When true, repeated assignment of a new node to an existing node label will raise an error, otherwise the label gets appended with an index and the assignment proceeds. (Default is true: disallow assigning to existing labels.) create (Creator): A tool for adding new nodes to this subgraph. + provenance_by_completion (list[str]): The child nodes (by label) in the order + that they completed on the last :meth:`run` call. + provenance_by_execution (list[str]): The child nodes (by label) in the order + that they started executing on the last :meth:`run` call. + running_children (list[str]): The names of children who are currently running. + signal_queue (list[ starting_nodes (None | list[pyiron_workflow.node.Node]): A subset of the owned nodes to be used on running. Only necessary if the execution graph has been manually specified with `run` signals. (Default is an empty list.) - wrap_as (Wrappers): A tool for accessing node-creating decorators + wrap (Wrappers): A tool for accessing node-creating decorators Methods: add_child(node: Node): Add the node instance to this subgraph. @@ -100,68 +98,37 @@ class Composite(Node, SemanticParent, ABC): register(): A short-cut to registering a new node package with the node creator. """ - wrap_as = Wrappers() - create = Creator() - def __init__( self, - label: str, *args, + label: Optional[str] = None, parent: Optional[Composite] = None, overwrite_save: bool = False, run_after_init: bool = False, storage_backend: Optional[Literal["h5io", "tinybase"]] = None, save_after_run: bool = False, strict_naming: bool = True, - inputs_map: Optional[dict | bidict] = None, - outputs_map: Optional[dict | bidict] = None, **kwargs, ): + self.starting_nodes: list[Node] = [] + self.provenance_by_execution: list[str] = [] + self.provenance_by_completion: list[str] = [] + self.running_children: list[str] = [] + self.signal_queue: list[tuple] = [] + self._child_sleep_interval = 0.01 # How long to wait when the signal_queue is + # empty but the running_children list is not + super().__init__( + label, *args, - label=label, parent=parent, - save_after_run=save_after_run, + overwrite_save=overwrite_save, + run_after_init=run_after_init, storage_backend=storage_backend, + save_after_run=save_after_run, strict_naming=strict_naming, **kwargs, ) - self._inputs_map = None - self._outputs_map = None - self.inputs_map = inputs_map - self.outputs_map = outputs_map - self.starting_nodes: list[Node] = [] - - @property - def inputs_map(self) -> bidict | None: - self._deduplicate_nones(self._inputs_map) - return self._inputs_map - - @inputs_map.setter - def inputs_map(self, new_map: dict | bidict | None): - self._deduplicate_nones(new_map) - if new_map is not None: - new_map = bidict(new_map) - self._inputs_map = new_map - - @property - def outputs_map(self) -> bidict | None: - self._deduplicate_nones(self._outputs_map) - return self._outputs_map - - @outputs_map.setter - def outputs_map(self, new_map: dict | bidict | None): - self._deduplicate_nones(new_map) - if new_map is not None: - new_map = bidict(new_map) - self._outputs_map = new_map - - @staticmethod - def _deduplicate_nones(some_map: dict | bidict | None) -> dict | bidict | None: - if some_map is not None: - for k, v in some_map.items(): - if v is None: - some_map[k] = (None, f"{k} disabled") def activate_strict_hints(self): super().activate_strict_hints() @@ -179,19 +146,70 @@ def to_dict(self): "nodes": {n.label: n.to_dict() for n in self.children.values()}, } - @property def on_run(self): - return self.run_graph + # Reset provenance and run status trackers + self.provenance_by_execution = [] + self.provenance_by_completion = [] + self.running_children = [] + self.signal_queue = [] - @staticmethod - def run_graph(_composite: Composite): - for node in _composite.starting_nodes: + for node in self.starting_nodes: node.run() - return _composite + + while len(self.running_children) > 0 or len(self.signal_queue) > 0: + try: + ran_signal, receiver = self.signal_queue.pop(0) + receiver(ran_signal) + except IndexError: + # The signal queue is empty, but there is still someone running... + sleep(self._child_sleep_interval) + return self + + def register_child_starting(self, child: Node) -> None: + """ + To be called by children when they start their run cycle. + + Args: + child [Node]: The child that is finished and would like to fire its `ran` + signal. Should always be a child of `self`, but this is not explicitly + verified at runtime. + """ + self.provenance_by_execution.append(child.label) + self.running_children.append(child.label) + + def register_child_finished(self, child: Node) -> None: + """ + To be called by children when they are finished their run. + + Args: + child [Node]: The child that is finished and would like to fire its `ran` + signal. Should always be a child of `self`, but this is not explicitly + verified at runtime. + """ + try: + self.running_children.remove(child.label) + self.provenance_by_completion.append(child.label) + except ValueError as e: + raise KeyError( + f"No element {child.label} to remove while {self.running_children}, " + f"{self.provenance_by_execution}, {self.provenance_by_completion}" + ) from e + + def register_child_emitting_ran(self, child: Node) -> None: + """ + To be called by children when they want to emit their `ran` signal. + + Args: + child [Node]: The child that is finished and would like to fire its `ran` + signal. Should always be a child of `self`, but this is not explicitly + verified at runtime. + """ + for conn in child.signals.output.ran.connections: + self.signal_queue.append((child.signals.output.ran, conn)) @property - def run_args(self) -> dict: - return {"_composite": self} + def run_args(self) -> tuple[tuple, dict]: + return (), {} def process_run_result(self, run_output): if run_output is not self: @@ -203,7 +221,9 @@ def _parse_remotely_executed_self(self, other_self): for node in self: node._parent = None other_self.running = False # It's done now - self.__setstate__(other_self.__getstate__()) + state = other_self.__getstate__() + state.pop("executor") # Got overridden to None for __getstate__, so keep local + self.__setstate__(state) def disconnect_run(self) -> list[tuple[Channel, Channel]]: """ @@ -226,80 +246,6 @@ def set_run_signals_to_dag_execution(self): _, upstream_most_nodes = set_run_connections_according_to_dag(self.children) self.starting_nodes = upstream_most_nodes - def _build_io( - self, - i_or_o: Literal["inputs", "outputs"], - key_map: dict[str, str | None] | None, - ) -> Inputs | Outputs: - """ - Build an IO panel for exposing child node IO to the outside world at the level - of the composite node's IO. - - Args: - target [Literal["inputs", "outputs"]]: Whether this is I or O. - key_map [dict[str, str]|None]: A map between the default convention for - mapping child IO to composite IO (`"{node.label}__{channel.label}"`) and - whatever label you actually want to expose to the composite user. Also - allows non-standards channel exposure, i.e. exposing - internally-connected channels (which would not normally be exposed) by - providing a string-to-string map, or suppressing unconnected channels - (which normally would be exposed) by providing a string-None map. - - Returns: - (Inputs|Outputs): The populated panel. - """ - key_map = {} if key_map is None else key_map - io = Inputs() if i_or_o == "inputs" else Outputs() - for node in self.children.values(): - panel = getattr(node, i_or_o) - for channel in panel: - try: - io_panel_key = key_map[channel.scoped_label] - if not isinstance(io_panel_key, tuple): - # Tuples indicate that the channel has been deactivated - # This is a necessary misdirection to keep the bidict working, - # as we can't simply map _multiple_ keys to `None` - io[io_panel_key] = self._get_linking_channel( - channel, io_panel_key - ) - except KeyError: - if not channel.connected: - io[channel.scoped_label] = self._get_linking_channel( - channel, channel.scoped_label - ) - return io - - @abstractmethod - def _get_linking_channel( - self, - child_reference_channel: InputData | OutputData, - composite_io_key: str, - ) -> InputData | OutputData: - """ - Returns the channel that will be the link between the provided child channel, - and the composite's IO at the given key. - - The returned channel should be fully compatible with the provided child channel, - i.e. same type, same type hint... (For instance, the child channel itself is a - valid return, which would create a composite IO panel that works by reference.) - - Args: - child_reference_channel (InputData | OutputData): The child channel - composite_io_key (str): The key under which this channel will be stored on - the composite's IO. - - Returns: - (Channel): A channel with the same type, type hint, etc. as the reference - channel passed in. - """ - pass - - def _build_inputs(self) -> Inputs: - return self._build_io("inputs", self.inputs_map) - - def _build_outputs(self) -> Outputs: - return self._build_io("outputs", self.outputs_map) - def add_child( self, child: Node, @@ -386,70 +332,28 @@ def replace_child( # first guaranteed to be an unconnected orphan, there is not yet any permanent # damage is_starting_node = owned_node in self.starting_nodes + # In case the replaced node interfaces with the composite's IO, catch value + # links + inbound_links = [ + (sending_channel, replacement.inputs[sending_channel.value_receiver.label]) + for sending_channel in self.inputs + if sending_channel.value_receiver in owned_node.inputs + ] + outbound_links = [ + (replacement.outputs[sending_channel.label], sending_channel.value_receiver) + for sending_channel in owned_node.outputs + if sending_channel.value_receiver in self.outputs + ] self.remove_child(owned_node) replacement.label, owned_node.label = owned_node.label, replacement.label self.add_child(replacement) if is_starting_node: self.starting_nodes.append(replacement) - - # Finally, make sure the IO is constructible with this new node, which will - # catch things like incompatible IO maps - try: - # Make sure node-level IO is pointing to the new node and that macro-level - # IO gets safely reconstructed - self._rebuild_data_io() - except Exception as e: - # If IO can't be successfully rebuilt using this node, revert changes and - # raise the exception - self.replace_child(replacement, owned_node) # Guaranteed to work since - # replacement in the other direction was already a success - raise e + for sending_channel, receiving_channel in inbound_links + outbound_links: + sending_channel.value_receiver = receiving_channel return owned_node - def _rebuild_data_io(self): - """ - Try to rebuild the IO. - - If an error is encountered, revert back to the existing IO then raise it. - """ - old_inputs = self.inputs - old_outputs = self.outputs - connection_changes = [] # For reversion if there's an error - try: - self._inputs = self._build_inputs() - self._outputs = self._build_outputs() - for old, new in [(old_inputs, self.inputs), (old_outputs, self.outputs)]: - for old_channel in old: - if old_channel.connected: - # If the old channel was connected to stuff, we'd better still - # have a corresponding channel and be able to copy these, or we - # should fail hard. - # But, if it wasn't connected, we don't even care whether or not - # we still have a corresponding channel to copy to - new_channel = new[old_channel.label] - new_channel.copy_connections(old_channel) - swapped_conenctions = old_channel.disconnect_all() # Purge old - connection_changes.append( - (new_channel, old_channel, swapped_conenctions) - ) - except Exception as e: - for new_channel, old_channel, swapped_conenctions in connection_changes: - new_channel.disconnect(*swapped_conenctions) - old_channel.connect(*swapped_conenctions) - self._inputs = old_inputs - self._outputs = old_outputs - e.message = ( - f"Unable to rebuild IO for {self.label}; reverting to old IO." - f"{e.message}" - ) - raise e - - @classmethod - @wraps(Creator.register) - def register(cls, package_identifier: str, domain: Optional[str] = None) -> None: - cls.create.register(package_identifier=package_identifier, domain=domain) - def executor_shutdown(self, wait=True, *, cancel_futures=False): """ Invoke shutdown on the executor (if present), and recursively invoke shutdown @@ -500,28 +404,8 @@ def to_storage(self, storage): for label, node in self.children.items(): node.to_storage(storage.create_group(label)) - storage["inputs_map"] = self.inputs_map - storage["outputs_map"] = self.outputs_map - super().to_storage(storage) - def from_storage(self, storage): - from pyiron_contrib.tinybase.storage import GenericStorage - - self.inputs_map = ( - storage["inputs_map"].to_object() - if isinstance(storage["inputs_map"], GenericStorage) - else storage["inputs_map"] - ) - self.outputs_map = ( - storage["outputs_map"].to_object() - if isinstance(storage["outputs_map"], GenericStorage) - else storage["outputs_map"] - ) - self._rebuild_data_io() # To apply any map that was saved - - super().from_storage(storage) - def tidy_working_directory(self): for node in self: node.tidy_working_directory() @@ -573,15 +457,6 @@ def __getstate__(self): state["_child_data_connections"] = self._child_data_connections state["_child_signal_connections"] = self._child_signal_connections - # Transform the IO maps into a datatype that plays well with h5io - # (Bidict implements a custom reconstructor, which hurts us) - state["_inputs_map"] = ( - None if self._inputs_map is None else dict(self._inputs_map) - ) - state["_outputs_map"] = ( - None if self._outputs_map is None else dict(self._outputs_map) - ) - # Also remove the starting node instances del state["starting_nodes"] state["_starting_node_labels"] = self._starting_node_labels @@ -592,15 +467,6 @@ def __setstate__(self, state): # Purge child connection info from the state child_data_connections = state.pop("_child_data_connections") child_signal_connections = state.pop("_child_signal_connections") - - # Transform the IO maps back into the right class (bidict) - state["_inputs_map"] = ( - None if state["_inputs_map"] is None else bidict(state["_inputs_map"]) - ) - state["_outputs_map"] = ( - None if state["_outputs_map"] is None else bidict(state["_outputs_map"]) - ) - # Restore starting nodes state["starting_nodes"] = [ state[label] for label in state.pop("_starting_node_labels") diff --git a/pyiron_workflow/create.py b/pyiron_workflow/create.py index 38772f08..75e077f3 100644 --- a/pyiron_workflow/create.py +++ b/pyiron_workflow/create.py @@ -4,6 +4,9 @@ from __future__ import annotations +from abc import ABC +from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor +from functools import wraps, lru_cache from importlib import import_module import pkgutil from sys import version_info @@ -11,31 +14,19 @@ from typing import Optional, TYPE_CHECKING from bidict import bidict -from pyiron_workflow.snippets.singleton import Singleton - -# Import all the supported executors -from pympipool import Executor as PyMpiPoolExecutor, PyMPIExecutor - -try: - from pympipool import PySlurmExecutor -except ImportError: - PySlurmExecutor = None -try: - from pympipool import PyFluxExecutor -except ImportError: - PyFluxExecutor = None +from pympipool import Executor as PyMpiPoolExecutor from pyiron_workflow.executors import CloudpickleProcessPoolExecutor - -# Then choose one executor to be "standard" -Executor = PyMpiPoolExecutor - -from pyiron_workflow.function import Function, function_node +from pyiron_workflow.function import function_node, as_function_node from pyiron_workflow.snippets.dotdict import DotDict +from pyiron_workflow.snippets.singleton import Singleton if TYPE_CHECKING: from pyiron_workflow.node_package import NodePackage +# Specify the standard executor +Executor = PyMpiPoolExecutor + class Creator(metaclass=Singleton): """ @@ -56,16 +47,15 @@ def __init__(self): self._package_registry = bidict() self.Executor = Executor + # Standard lib + self.ProcessPoolExecutor = ProcessPoolExecutor + self.ThreadPoolExecutor = ThreadPoolExecutor + # Local cloudpickler self.CloudpickleProcessPoolExecutor = CloudpickleProcessPoolExecutor - self.PyMPIExecutor = PyMPIExecutor + # pympipool self.PyMpiPoolExecutor = PyMpiPoolExecutor - self.Function = Function - - # Avoid circular imports by delaying import for children of Composite - self._macro = None - self._workflow = None - self._meta = None + self.function_node = function_node if version_info[0] == 3 and version_info[1] >= 10: # These modules use syntactic sugar for type hinting that is only supported @@ -75,53 +65,64 @@ def __init__(self): self.register("pyiron_workflow.node_library.standard", "standard") @property - def PyFluxExecutor(self): - if PyFluxExecutor is None: - raise ImportError(f"{PyFluxExecutor.__name__} is not available") - return PyFluxExecutor + @lru_cache(maxsize=1) + def for_node(self): + from pyiron_workflow.for_loop import for_node - @property - def PySlurmExecutor(self): - if PySlurmExecutor is None: - raise ImportError(f"{PySlurmExecutor.__name__} is not available") - return PySlurmExecutor + return for_node @property - def Macro(self): - if self._macro is None: - from pyiron_workflow.macro import Macro + @lru_cache(maxsize=1) + def macro_node(self): + from pyiron_workflow.macro import macro_node - self._macro = Macro - return self._macro + return macro_node @property + @lru_cache(maxsize=1) def Workflow(self): - if self._workflow is None: - from pyiron_workflow.workflow import Workflow + from pyiron_workflow.workflow import Workflow - self._workflow = Workflow - return self._workflow + return Workflow @property + @lru_cache(maxsize=1) def meta(self): - if self._meta is None: - from pyiron_workflow.meta import ( - for_loop, - input_to_list, - list_to_output, - while_loop, - ) - from pyiron_workflow.snippets.dotdict import DotDict - - self._meta = DotDict( - { - for_loop.__name__: for_loop, - input_to_list.__name__: input_to_list, - list_to_output.__name__: list_to_output, - while_loop.__name__: while_loop, - } - ) - return self._meta + from pyiron_workflow.transform import inputs_to_list, list_to_outputs + from pyiron_workflow.loops import while_loop + from pyiron_workflow.snippets.dotdict import DotDict + + return DotDict( + { + inputs_to_list.__name__: inputs_to_list, + list_to_outputs.__name__: list_to_outputs, + while_loop.__name__: while_loop, + } + ) + + @property + @lru_cache(maxsize=1) + def transformer(self): + from pyiron_workflow.transform import ( + dataclass_node, + inputs_to_dataframe, + inputs_to_dict, + inputs_to_list, + list_to_outputs, + ) + + return DotDict( + { + f.__name__: f + for f in [ + dataclass_node, + inputs_to_dataframe, + inputs_to_dict, + inputs_to_list, + list_to_outputs, + ] + } + ) def __getattr__(self, item): try: @@ -315,16 +316,33 @@ class Wrappers(metaclass=Singleton): A container class giving access to the decorators that transform functions to nodes. """ - def __init__(self): - self.function_node = function_node + as_function_node = staticmethod(as_function_node) + + @property + @lru_cache(maxsize=1) + def as_macro_node(self): + from pyiron_workflow.macro import as_macro_node - # Avoid circular imports by delaying import when wrapping children of Composite - self._macro_node = None + return as_macro_node @property - def macro_node(self): - if self._macro_node is None: - from pyiron_workflow.macro import macro_node + @lru_cache(maxsize=1) + def as_dataclass_node(self): + from pyiron_workflow.transform import as_dataclass_node + + return as_dataclass_node + + +class HasCreator(ABC): + """ + A mixin class for creator (including both class-like and decorator) and + registration methods. + """ + + create = Creator() + wrap = Wrappers() - self._macro_node = macro_node - return self._macro_node + @classmethod + @wraps(Creator.register) + def register(cls, package_identifier: str, domain: Optional[str] = None) -> None: + cls.create.register(package_identifier=package_identifier, domain=domain) diff --git a/pyiron_workflow/executors/cloudpickleprocesspool.py b/pyiron_workflow/executors/cloudpickleprocesspool.py index aa779d26..1d566b48 100644 --- a/pyiron_workflow/executors/cloudpickleprocesspool.py +++ b/pyiron_workflow/executors/cloudpickleprocesspool.py @@ -109,8 +109,11 @@ class CloudpickleProcessPoolExecutor(ProcessPoolExecutor): >>> print(fs.done()) True + >>> import time + >>> time.sleep(1) # Debugging doctest on github CI for python3.10 >>> print(instance.result.result) This was an arg + """ def submit(self, fn, /, *args, **kwargs): diff --git a/pyiron_workflow/for_loop.py b/pyiron_workflow/for_loop.py new file mode 100644 index 00000000..5b4815b1 --- /dev/null +++ b/pyiron_workflow/for_loop.py @@ -0,0 +1,485 @@ +from __future__ import annotations + +from abc import ABC +from concurrent.futures import Executor +from functools import lru_cache +import itertools +import math +from typing import Any, ClassVar, Literal, Optional + +from pandas import DataFrame + +from pyiron_workflow.channels import NOT_DATA +from pyiron_workflow.composite import Composite +from pyiron_workflow.io_preview import StaticNode +from pyiron_workflow.snippets.factory import classfactory +from pyiron_workflow.transform import inputs_to_dict, inputs_to_dataframe, InputsToDict + + +def dictionary_to_index_maps( + data: dict, + nested_keys: Optional[list[str] | tuple[str, ...]] = None, + zipped_keys: Optional[list[str] | tuple[str, ...]] = None, +): + """ + Given a dictionary where some data is iterable, and list(s) of keys over + which to make a nested and/or zipped loop, return dictionaries mapping + these keys to all the indices of the data they hold. Zipped loops are + nested outside the nesting loops. + + Args: + data (dict): The dictionary of data, some of which must me iterable. + nested_keys (tuple[str, ...] | None): The keys whose data to make a + nested for-loop over. + zipped_keys (tuple[str, ...] | None): The keys whose data to make a + zipped for-loop over. + + Returns: + (tuple[dict[..., int], ...]): A tuple of dictionaries where each item + maps the dictionary key to an index for that key's value. + + Raises: + (KeyError): If any of the provided keys are not keys of the provided + dictionary. + (TypeError): If any of the data held in a provided key does cannot be + operated on with `len`. + (ValueError): If neither set of keys to iterate on is provided, or if + all values being iterated over have a length of zero. + """ + + try: + nested_data_lengths = ( + [] + if (nested_keys is None or len(nested_keys) == 0) + else list(len(data[key]) for key in nested_keys) + ) + except TypeError as e: + raise TypeError( + f"Could not parse nested lengths -- Does one of the keys {nested_keys} " + f"have non-iterable data?" + ) from e + n_nest = math.prod(nested_data_lengths) if len(nested_data_lengths) > 0 else 0 + + try: + n_zip = ( + 0 + if (zipped_keys is None or len(zipped_keys) == 0) + else min(len(data[key]) for key in zipped_keys) + ) + except TypeError as e: + raise TypeError( + f"Could not parse zipped lengths -- Does one of the keys {zipped_keys} " + f"have non-iterable data?" + ) from e + + def nested_generator(): + return itertools.product(*[range(n) for n in nested_data_lengths]) + + def nested_index_map(nested_indices): + return { + nested_keys[i_key]: nested_index + for i_key, nested_index in enumerate(nested_indices) + } + + def zipped_generator(): + return range(n_zip) + + def zipped_index_map(zipped_index): + return {key: zipped_index for key in zipped_keys} + + def merge(d1, d2): + d1.update(d2) + return d1 + + if n_nest > 0 and n_zip > 0: + key_index_maps = tuple( + merge(nested_index_map(nested_indices), zipped_index_map(zipped_index)) + for nested_indices, zipped_index in itertools.product( + nested_generator(), zipped_generator() + ) + ) + elif n_nest > 0: + key_index_maps = tuple( + nested_index_map(nested_indices) for nested_indices in nested_generator() + ) + elif n_zip > 0: + key_index_maps = tuple( + zipped_index_map(zipped_index) for zipped_index in zipped_generator() + ) + else: + if nested_keys is None and zipped_keys is None: + raise ValueError( + "At least one of `nested_keys` or `zipped_keys` must be specified." + ) + else: + raise ValueError( + "Received keys to iterate over, but all values had length 0." + ) + + return key_index_maps + + +class UnmappedConflictError(ValueError): + """ + When a for-node gets a body whose output label conflicts with looped a input + label and no map was provided to avoid this. + """ + + +class MapsToNonexistentOutputError(ValueError): + """ + When a for-node tries to map body node output channels that don't exist. + """ + + +class For(Composite, StaticNode, ABC): + """ + Specifies fixed fields of some other node class to iterate over, but allows the + length of looped input to vary by dynamically destroying and recreating (most of) + its subgraph at run-time. + + Collects looped output and collates them with looped input values in a dataframe. + + The :attr:`body_node_executor` gets applied to each body node instance on each + run. + """ + + _body_node_class: ClassVar[type[StaticNode]] + _iter_on: ClassVar[tuple[str, ...]] = () + _zip_on: ClassVar[tuple[str, ...]] = () + + def __init_subclass__(cls, output_column_map=None, **kwargs): + super().__init_subclass__(**kwargs) + + unmapped_conflicts = ( + set(cls._body_node_class.preview_inputs().keys()) + .intersection(cls._iter_on + cls._zip_on) + .intersection(cls._body_node_class.preview_outputs().keys()) + .difference(() if output_column_map is None else output_column_map.keys()) + ) + if len(unmapped_conflicts) > 0: + raise UnmappedConflictError( + f"The body node {cls._body_node_class.__name__} has channel labels " + f"{unmapped_conflicts} that appear as both (looped) input _and_ output " + f"for {cls.__name__}. All such channels require a map to produce new, " + f"unique column names for the output." + ) + + maps_to_nonexistent_output = set( + {} if output_column_map is None else output_column_map.keys() + ).difference(cls._body_node_class.preview_outputs().keys()) + if len(maps_to_nonexistent_output) > 0: + raise MapsToNonexistentOutputError( + f"{cls.__name__} tried to map body node output(s) " + f"{maps_to_nonexistent_output} to new column names, but " + f"{cls._body_node_class.__name__} has no such outputs." + ) + + cls._output_column_map = output_column_map + + @classmethod + @property + @lru_cache(maxsize=1) + def output_column_map(cls) -> dict[str, str]: + """ + How to transform body node output labels to dataframe column names. + """ + map_ = {k: k for k in cls._body_node_class.preview_outputs().keys()} + overrides = {} if cls._output_column_map is None else cls._output_column_map + for body_label, column_name in overrides.items(): + map_[body_label] = column_name + return map_ + + def __init__( + self, + *args, + label: Optional[str] = None, + parent: Optional[Composite] = None, + overwrite_save: bool = False, + run_after_init: bool = False, + storage_backend: Optional[Literal["h5io", "tinybase"]] = None, + save_after_run: bool = False, + strict_naming: bool = True, + body_node_executor: Optional[Executor] = None, + **kwargs, + ): + super().__init__( + *args, + label=label, + parent=parent, + overwrite_save=overwrite_save, + run_after_init=run_after_init, + storage_backend=storage_backend, + save_after_run=save_after_run, + strict_naming=strict_naming, + **kwargs, + ) + self.body_node_executor = None + + def _setup_node(self) -> None: + super()._setup_node() + input_nodes = [] + for channel in self.inputs: + n = self.create.standard.UserInput( + channel.default, label=channel.label, parent=self + ) + n.inputs.user_input.type_hint = channel.type_hint + channel.value_receiver = n.inputs.user_input + input_nodes.append(n) + self.starting_nodes = input_nodes + self._input_node_labels = tuple(n.label for n in input_nodes) + + def on_run(self): + self._build_body() + return super().on_run() + + def _build_body(self): + """ + Construct instances of the body node based on input length, and wire them to IO. + """ + iter_maps = dictionary_to_index_maps( + self.inputs.to_value_dict(), + nested_keys=self._iter_on, + zipped_keys=self._zip_on, + ) + + self._clean_existing_subgraph() + + self.dataframe = inputs_to_dataframe(len(iter_maps)) + self.dataframe.outputs.df.value_receiver = self.outputs.df + + for n, channel_map in enumerate(iter_maps): + body_node = self._body_node_class(label=f"body_{n}", parent=self) + body_node.executor = self.body_node_executor + row_collector = self._build_collector_node(n) + + self._connect_broadcast_input(body_node) + for label, i in channel_map.items(): + self._connect_looped_input(body_node, row_collector, label, i) + + self._collect_output_from_body(body_node, row_collector) + + self.dataframe.inputs[f"row_{n}"] = row_collector + + self.set_run_signals_to_dag_execution() + + def _clean_existing_subgraph(self): + for label in self.child_labels: + if label not in self._input_node_labels: + self.remove_child(label) + else: + # Re-run the user input node so it has up-to-date output, otherwise + # when we inject a getitem node -- which will try to run automatically + # -- it will see data it can work with, but if that data happens to + # have the wrong length it may successfully auto-run on the wrong thing + # and throw an error! + self.children[label].run( + run_data_tree=False, + run_parent_trees_too=False, + fetch_input=False, + # Data should simply be coming from the value link + # We just want to refresh the output + ) + # TODO: Instead of deleting _everything_ each time, try and re-use stuff + + def _build_collector_node(self, row_number): + # Iterated inputs + row_specification = { + key: (self._body_node_class.preview_inputs()[key][0], NOT_DATA) + for key in self._iter_on + self._zip_on + } + # Outputs + row_specification.update( + { + self.output_column_map[key]: (hint, NOT_DATA) + for key, hint in self._body_node_class.preview_outputs().items() + } + ) + return inputs_to_dict( + row_specification, parent=self, label=f"row_collector_{row_number}" + ) + + def _connect_broadcast_input(self, body_node: StaticNode) -> None: + """Connect broadcast macro input to each body node.""" + for broadcast_label in set(self.preview_inputs().keys()).difference( + self._iter_on + self._zip_on + ): + self.inputs[broadcast_label].value_receiver = body_node.inputs[ + broadcast_label + ] + + def _connect_looped_input( + self, + body_node: StaticNode, + row_collector: InputsToDict, + looped_input_label: str, + i: int, + ) -> None: + """Get item from macro input and connect it to body and collector nodes.""" + index_node = self.children[looped_input_label][i] # Inject getitem node + body_node.inputs[looped_input_label] = index_node + row_collector.inputs[looped_input_label] = index_node + + def _collect_output_from_body( + self, body_node: StaticNode, row_collector: InputsToDict + ) -> None: + """Pass body node output to the collector node.""" + for label, body_out in body_node.outputs.items(): + row_collector.inputs[self.output_column_map[label]] = body_out + + @classmethod + @lru_cache(maxsize=1) + def _build_inputs_preview(cls) -> dict[str, tuple[Any, Any]]: + preview = {} + for label, (hint, default) in cls._body_node_class.preview_inputs().items(): + # TODO: Leverage hint and default, listing if it's looped on + if label in cls._zip_on + cls._iter_on: + hint = list if hint is None else list[hint] + default = NOT_DATA # TODO: Figure out a generator pattern to get lists + preview[label] = (hint, default) + return preview + + @classmethod + def _build_outputs_preview(cls) -> dict[str, Any]: + return {"df": DataFrame} + + +def _for_node_class_name( + body_node_class: type[StaticNode], iter_on: tuple[str, ...], zip_on: tuple[str, ...] +): + iter_fields = ( + "" if len(iter_on) == 0 else "Iter" + "".join(k.title() for k in iter_on) + ) + zip_fields = "" if len(zip_on) == 0 else "Zip" + "".join(k.title() for k in zip_on) + return f"{For.__name__}{body_node_class.__name__}{iter_fields}{zip_fields}" + + +@classfactory +def for_node_factory( + body_node_class: type[StaticNode], + iter_on: tuple[str, ...] = (), + zip_on: tuple[str, ...] = (), + output_column_map: dict | None = None, + /, +): + return ( + _for_node_class_name(body_node_class, iter_on, zip_on), + (For,), + { + "_body_node_class": body_node_class, + "_iter_on": iter_on, + "_zip_on": zip_on, + }, + {"output_column_map": output_column_map}, + ) + + +def for_node( + body_node_class, + *node_args, + iter_on=(), + zip_on=(), + output_column_map: Optional[dict[str, str]] = None, + **node_kwargs, +): + """ + Makes a new :class:`For` node which internally creates instances of the + :param:`body_node_class` and loops input onto them in nested and/or zipped loop(s). + + Output is a single channel, `"df"`, which holds a :class:`pandas.DataFrame` whose + rows couple (looped) input to their respective body node outputs. + + The internal node structure gets re-created each run, so the same inputs must + consistently be iterated over, but their lengths can change freely. + + An executor can be applied to all body node instances at run-time by assigning it + to the :attr:`body_node_executor` attribute of the for-node. + + Args: + body_node_class type[StaticNode]: The class of node to loop on. + *node_args: Regular positional node arguments. + iter_on (tuple[str, ...]): Input labels in the :param:`body_node_class` to + nested-loop on. + zip_on (tuple[str, ...]): Input labels in the :param:`body_node_class` to + zip-loop on. + output_column_map (dict[str, str] | None): A map for generating dataframe + column names (values) from body node output channel labels (keys). + Necessary iff the body node has the same label for an output channel and + an input channel being looped over. (Default is None, just use the output + channel labels as columb names.) + **node_kwargs: Regular keyword node arguments. + + Returns: + (For): An instance of a dynamically-subclassed :class:`For` node. + + Examples: + >>> from pyiron_workflow import Workflow + >>> + >>> @Workflow.wrap.as_function_node("together") + ... def FiveTogether(a: int, b: int, c: int, d: int, e: str = "foobar"): + ... return (a, b, c, d, e), + >>> + >>> for_instance = Workflow.create.for_node( + ... FiveTogether, + ... iter_on=("a", "b"), + ... zip_on=("c", "d"), + ... a=[1, 2], + ... b=[3, 4, 5, 6], + ... c=[7, 8], + ... d=[9, 10, 11], + ... e="e" + ... ) + >>> + >>> out = for_instance() + >>> type(out.df) + + + Internally, the loop node has made a bunch of body nodes, as well as nodes to + index and collect data + >>> len(for_instance) + 48 + + We get one dataframe row for each possible combination of looped input + >>> len(out.df) + 16 + + We are stuck iterating on the fields we defined, but we can change the length + of the input and the loop node's body will get reconstructed at run-time to + accommodate this + >>> out = for_instance(a=[1], b=[3], d=[7]) + >>> len(for_instance), len(out) + (12, 1) + + Note that if we had simply returned each input individually, without any output + labels on the node, we'd need to specify a map on the for-node so that the + (looped) input and output columns on the resulting dataframe are all unique: + >>> @Workflow.wrap.as_function_node() + ... def FiveApart(a: int, b: int, c: int, d: int, e: str = "foobar"): + ... return a, b, c, d, e, + >>> + >>> for_instance = Workflow.create.for_node( + ... FiveApart, + ... iter_on=("a", "b"), + ... zip_on=("c", "d"), + ... a=[1, 2], + ... b=[3, 4, 5, 6], + ... c=[7, 8], + ... d=[9, 10, 11], + ... e="e", + ... output_column_map={ + ... "a": "out_a", + ... "b": "out_b", + ... "c": "out_c", + ... "d": "out_d" + ... } + ... ) + >>> + >>> out = for_instance() + >>> out.df.columns + Index(['a', 'b', 'c', 'd', 'out_a', 'out_b', 'out_c', 'out_d', 'e'], dtype='object') + + """ + for_node_factory.clear(_for_node_class_name(body_node_class, iter_on, zip_on)) + cls = for_node_factory(body_node_class, iter_on, zip_on, output_column_map) + cls.preview_io() + return cls(*node_args, **node_kwargs) diff --git a/pyiron_workflow/function.py b/pyiron_workflow/function.py index 7a34f8e8..cdaac343 100644 --- a/pyiron_workflow/function.py +++ b/pyiron_workflow/function.py @@ -1,23 +1,14 @@ from __future__ import annotations -import inspect -import warnings -from functools import partialmethod -from typing import Any, get_args, get_type_hints, Literal, Optional, TYPE_CHECKING - -from pyiron_workflow.channels import InputData, OutputData, NOT_DATA -from pyiron_workflow.has_interface_mixins import HasChannel -from pyiron_workflow.injection import OutputDataWithInjection -from pyiron_workflow.io import Inputs, Outputs -from pyiron_workflow.node import Node -from pyiron_workflow.output_parser import ParseOutput -from pyiron_workflow.snippets.colors import SeabornColors +from abc import ABC, abstractmethod +from typing import Any -if TYPE_CHECKING: - from pyiron_workflow.composite import Composite +from pyiron_workflow.io_preview import StaticNode, ScrapesIO +from pyiron_workflow.snippets.colors import SeabornColors +from pyiron_workflow.snippets.factory import classfactory -class Function(Node): +class Function(StaticNode, ScrapesIO, ABC): """ Function nodes wrap an arbitrary python function. @@ -49,34 +40,16 @@ class Function(Node): the same input order as the wrapped function. - A default label can be scraped from the name of the wrapped function - Args: - node_function (callable): The function determining the behaviour of the node. - label (str): The node's label. (Defaults to the node function's name.) - output_labels (Optional[str | list[str] | tuple[str]]): A name for each return - value of the node function OR a single label. (Default is None, which - scrapes output labels automatically from the source code of the wrapped - function.) This can be useful when returned values are not well named, e.g. - to make the output channel dot-accessible if it would otherwise have a label - that requires item-string-based access. Additionally, specifying a _single_ - label for a wrapped function that returns a tuple of values ensures that a - _single_ output channel (holding the tuple) is created, instead of one - channel for each return value. The default approach of extracting labels - from the function source code also requires that the function body contain - _at most_ one `return` expression, so providing explicit labels can be used - to circumvent this (at your own risk). - **kwargs: Any additional keyword arguments whose keyword matches the label of an - input channel will have their value assigned to that channel. - Examples: At the most basic level, to use nodes all we need to do is provide the `Function` class with a function and labels for its output, like so: - >>> from pyiron_workflow.function import Function + >>> from pyiron_workflow.function import function_node >>> >>> def mwe(x, y): ... return x+1, y-1 >>> - >>> plus_minus_1 = Function(mwe) + >>> plus_minus_1 = function_node(mwe) >>> >>> print(plus_minus_1.outputs["x+1"]) NOT_DATA @@ -129,10 +102,10 @@ class Function(Node): >>> plus_minus_1.outputs.to_value_dict() {'x+1': 3, 'y-1': 2} - We can also, optionally, provide initial values for some or all of the input and - labels for the output: + We can also, optionally, provide initial values for some or all of the input + and labels for the output: - >>> plus_minus_1 = Function(mwe, output_labels=("p1", "m1"), x=1) + >>> plus_minus_1 = function_node(mwe, output_labels=("p1", "m1"), x=1) >>> plus_minus_1.inputs.y = 2 >>> out = plus_minus_1.run() >>> out @@ -147,8 +120,8 @@ class Function(Node): (3, 2) We can make our node even more sensible by adding type - hints (and, optionally, default values) when defining the function that the node - wraps. + hints (and, optionally, default values) when defining the function that the + node wraps. The node will automatically figure out defaults and type hints for the IO channels from inspection of the wrapped function. @@ -159,7 +132,7 @@ class Function(Node): variety of common use cases. Note that getting "good" (i.e. dot-accessible) output labels can be achieved by using good variable names and returning those variables instead of using - :attr:`output_labels`. + :param:`output_labels`. If we try to assign a value of the wrong type, it will raise an error: >>> from typing import Union @@ -171,7 +144,7 @@ class Function(Node): ... p1, m1 = x+1, y-1 ... return p1, m1 >>> - >>> plus_minus_1 = Function(hinted_example) + >>> plus_minus_1 = function_node(hinted_example) >>> try: ... plus_minus_1.inputs.x = "not an int or float" ... except TypeError as e: @@ -207,21 +180,23 @@ class Function(Node): >>> plus_minus_1.ready, plus_minus_1.inputs.x.ready, plus_minus_1.inputs.y.ready (False, False, True) - In these examples, we've instantiated nodes directly from the base :class:`Function` - class, and populated their input directly with data. + In these examples, we've instantiated nodes directly from the base + :class:`Function` class, and populated their input directly with data. In practice, these nodes are meant to be part of complex workflows; that means both that you are likely to have particular nodes that get heavily re-used, and that you need the nodes to pass data to each other. - For reusable nodes, we want to create a sub-class of :class:`Function` that fixes some - of the node behaviour -- usually the :meth:`node_function` and :attr:`output_labels`. + For reusable nodes, we want to create a sub-class of :class:`Function` + that fixes some of the node behaviour -- i.e. the :meth:`node_function`. - This can be done most easily with the :func:`function_node` decorator, which takes a function - and returns a node class: + This can be done most easily with the :func:`as_function_node` decorator, which + takes a function and returns a node class. It also allows us to provide labels + for the return values, :param:output_labels, which are otherwise scraped from + the text of the function definition: - >>> from pyiron_workflow.function import function_node + >>> from pyiron_workflow.function import as_function_node >>> - >>> @function_node("p1", "m1") + >>> @as_function_node("p1", "m1") ... def my_mwe_node( ... x: int | float, y: int | float = 1 ... ) -> tuple[int | float, int | float]: @@ -241,24 +216,16 @@ class Function(Node): already defined as a `staticmethod`: >>> from typing import Literal, Optional + >>> from pyiron_workflow.function import Function >>> >>> class AlphabetModThree(Function): - ... def __init__( - ... self, - ... label: Optional[str] = None, - ... **kwargs - ... ): - ... super().__init__( - ... None, - ... label=label, - ... **kwargs - ... ) ... ... @staticmethod ... def node_function(i: int) -> Literal["a", "b", "c"]: ... letter = ["a", "b", "c"][i % 3] ... return letter + Finally, let's put it all together by using both of these nodes at once. Instead of setting input to a particular data value, we'll set it to be another node's output channel, thus forming a connection. @@ -268,7 +235,7 @@ class Function(Node): Let's put together a couple of nodes and then run in a "pull" paradigm to get the final node to run everything "upstream" then run itself: - >>> @function_node() + >>> @as_function_node() ... def adder_node(x: int = 0, y: int = 0) -> int: ... sum = x + y ... return sum @@ -295,7 +262,7 @@ class Function(Node): (like cyclic graphs). Here's our simple example from above using this other paradigm: - >>> @function_node() + >>> @as_function_node() ... def adder_node(x: int = 0, y: int = 0) -> int: ... sum = x + y ... return sum @@ -326,199 +293,28 @@ class Function(Node): guaranteed. """ - def __init__( - self, - node_function: callable, - *args, - label: Optional[str] = None, - parent: Optional[Composite] = None, - overwrite_save: bool = False, - run_after_init: bool = False, - storage_backend: Optional[Literal["h5io", "tinybase"]] = None, - save_after_run: bool = False, - output_labels: Optional[str | list[str] | tuple[str]] = None, - **kwargs, - ): - if not callable(node_function): - # Children of `Function` may explicitly provide a `node_function` static - # method so the node has fixed behaviour. - # In this case, the `__init__` signature should be changed so that the - # `node_function` argument is just always `None` or some other non-callable. - # If a callable `node_function` is not received, you'd better have it as an - # attribute already! - if not hasattr(self, "node_function"): - raise AttributeError( - f"If `None` is provided as a `node_function`, a `node_function` " - f"property must be defined instead, e.g. when making child classes" - f"of `Function` with specific behaviour" - ) - self._type_hints = get_type_hints(self.node_function) - else: - # If a callable node function is received, use it - self.node_function = node_function - self._type_hints = get_type_hints(node_function) - - super().__init__( - label=label if label is not None else self.node_function.__name__, - parent=parent, - save_after_run=save_after_run, - storage_backend=storage_backend, - # **kwargs, - ) - - self._inputs = None - self._outputs = None - self._output_labels = self._get_output_labels(output_labels) - # TODO: Parse output labels from the node function in case output_labels is None - - self.set_input_values(*args, **kwargs) - - def _get_output_labels(self, output_labels: str | list[str] | tuple[str] | None): - """ - If output labels are provided, turn convert them to a list if passed as a - string and return them, else scrape them from the source channel. - - Note: When the user explicitly provides output channels, they are taking - responsibility that these are correct, e.g. in terms of quantity, order, etc. - """ - if output_labels is None: - return self._scrape_output_labels() - elif isinstance(output_labels, str): - return [output_labels] - else: - return output_labels - - def _scrape_output_labels(self): - """ - Inspect the source code to scrape out strings representing the returned values. - _Only_ works for functions with a single `return` expression in their body. - - Will return expressions and function calls just fine, thus best practice is to - create well-named variables and return those so that the output labels stay - dot-accessible. - """ - parsed_outputs = ParseOutput(self.node_function).output - return [None] if parsed_outputs is None else parsed_outputs - - @property - def _input_args(self): - return inspect.signature(self.node_function).parameters + @staticmethod + @abstractmethod + def node_function(**kwargs) -> callable: + """What the node _does_.""" - @property - def inputs(self) -> Inputs: - if self._inputs is None: - self._inputs = Inputs(*self._build_input_channels()) - return self._inputs + @classmethod + def _io_defining_function(cls) -> callable: + return cls.node_function - @property - def outputs(self) -> Outputs: - if self._outputs is None: - self._outputs = Outputs(*self._build_output_channels(*self._output_labels)) - return self._outputs - - def _build_input_channels(self): - channels = [] - type_hints = self._type_hints - - for ii, (label, value) in enumerate(self._input_args.items()): - is_self = False - if label == "self": # `self` is reserved for the node object - if ii == 0: - is_self = True - else: - warnings.warn( - "`self` is used as an argument but not in the first" - " position, so it is treated as a normal function" - " argument. If it is to be treated as the node object," - " use it as a first argument" - ) - if label in self._init_keywords: - # We allow users to parse arbitrary kwargs as channel initialization - # So don't let them choose bad channel names - raise ValueError( - f"The Input channel name {label} is not valid. Please choose a " - f"name _not_ among {self._init_keywords}" - ) - - try: - type_hint = type_hints[label] - if is_self: - warnings.warn("type hint for self ignored") - except KeyError: - type_hint = None - - default = NOT_DATA # The standard default in DataChannel - if value.default is not inspect.Parameter.empty: - if is_self: - warnings.warn("default value for self ignored") - else: - default = value.default - - if not is_self: - channels.append( - InputData( - label=label, - owner=self, - default=default, - type_hint=type_hint, - ) - ) - return channels + @classmethod + def _build_outputs_preview(cls) -> dict[str, Any]: + preview = super(Function, cls)._build_outputs_preview() + return preview if len(preview) > 0 else {"None": type(None)} + # If clause facilitates functions with no return value - @property - def _init_keywords(self): - return list(inspect.signature(self.__init__).parameters.keys()) - - def _build_output_channels(self, *return_labels: str): - try: - type_hints = self._type_hints["return"] - if len(return_labels) > 1: - type_hints = get_args(type_hints) - if not isinstance(type_hints, tuple): - raise TypeError( - f"With multiple return labels expected to get a tuple of type " - f"hints, but got type {type(type_hints)}" - ) - if len(type_hints) != len(return_labels): - raise ValueError( - f"Expected type hints and return labels to have matching " - f"lengths, but got {len(type_hints)} hints and " - f"{len(return_labels)} labels: {type_hints}, {return_labels}" - ) - else: - # If there's only one hint, wrap it in a tuple so we can zip it with - # *return_labels and iterate over both at once - type_hints = (type_hints,) - except KeyError: - type_hints = [None] * len(return_labels) - - channels = [] - for label, hint in zip(return_labels, type_hints): - channels.append( - OutputDataWithInjection( - label=label, - owner=self, - type_hint=hint, - ) - ) - - return channels + def on_run(self, **kwargs): + return self.node_function(**kwargs) @property - def on_run(self): - return self.node_function - - @property - def run_args(self) -> dict: + def run_args(self) -> tuple[tuple, dict]: kwargs = self.inputs.to_value_dict() - if "self" in self._input_args: - if self.executor: - raise ValueError( - f"Function node {self.label} uses the `self` argument, but this " - f"can't yet be run with executors" - ) - kwargs["self"] = self - return kwargs + return (), kwargs def process_run_result(self, function_output: Any | tuple) -> Any | tuple: """ @@ -531,54 +327,6 @@ def process_run_result(self, function_output: Any | tuple) -> Any | tuple: out.value = value return function_output - def _convert_input_args_and_kwargs_to_input_kwargs(self, *args, **kwargs): - reverse_keys = list(self._input_args.keys())[::-1] - if len(args) > len(reverse_keys): - raise ValueError( - f"Received {len(args)} positional arguments, but the node {self.label}" - f"only accepts {len(reverse_keys)} inputs." - ) - - positional_keywords = reverse_keys[-len(args) :] if len(args) > 0 else [] # -0: - if len(set(positional_keywords).intersection(kwargs.keys())) > 0: - raise ValueError( - f"Cannot use {set(positional_keywords).intersection(kwargs.keys())} " - f"as both positional _and_ keyword arguments; args {args}, kwargs " - f"{kwargs}, reverse_keys {reverse_keys}, positional_keyworkds " - f"{positional_keywords}" - ) - - for arg in args: - key = positional_keywords.pop() - kwargs[key] = arg - - return kwargs - - def set_input_values(self, *args, **kwargs) -> None: - """ - Match positional and keyword arguments to input channels and update input - values. - - Args: - *args: Interpreted in the same order as node function arguments. - **kwargs: input label - input value (including channels for connection) - pairs. - """ - kwargs = self._convert_input_args_and_kwargs_to_input_kwargs(*args, **kwargs) - return super().set_input_values(**kwargs) - - def execute(self, *args, **kwargs): - kwargs = self._convert_input_args_and_kwargs_to_input_kwargs(*args, **kwargs) - return super().execute(**kwargs) - - def pull(self, *args, run_parent_trees_too=False, **kwargs): - kwargs = self._convert_input_args_and_kwargs_to_input_kwargs(*args, **kwargs) - return super().pull(run_parent_trees_too=run_parent_trees_too, **kwargs) - - def __call__(self, *args, **kwargs) -> None: - kwargs = self._convert_input_args_and_kwargs_to_input_kwargs(*args, **kwargs) - return super().__call__(**kwargs) - def to_dict(self): return { "label": self.label, @@ -596,46 +344,51 @@ def color(self) -> str: return SeabornColors.green -def function_node(*output_labels: str): - """ - A decorator for dynamically creating node classes from functions. - - Decorates a function. - Returns a `Function` subclass whose name is the camel-case version of the function - node, and whose signature is modified to exclude the node function and output labels - (which are explicitly defined in the process of using the decorator). - """ - output_labels = None if len(output_labels) == 0 else output_labels - - # One really subtle thing is that we manually parse the function type hints right - # here and include these as a class-level attribute. - # This is because on (de)(cloud)pickling a function node, somehow the node function - # method attached to it gets its `__globals__` attribute changed; it retains stuff - # _inside_ the function, but loses imports it used from the _outside_ -- i.e. type - # hints! I (@liamhuber) don't deeply understand _why_ (de)pickling is modifying the - # __globals__ in this way, but the result is that type hints cannot be parsed after - # the change. - # The final piece of the puzzle here is that because the node function is a _class_ - # level attribute, if you (de)pickle a node, _new_ instances of that node wind up - # having their node function's `__globals__` trimmed down in this way! - # So to keep the type hint parsing working, we snag and interpret all the type hints - # at wrapping time, when we are guaranteed to have all the globals available, and - # also slap them on as a class-level attribute. These get safely packed and returned - # when (de)pickling so we can keep processing type hints without trouble. - def as_node(node_function: callable): - return type( - node_function.__name__, - (Function,), # Define parentage - { - "__init__": partialmethod( - Function.__init__, - None, - output_labels=output_labels, - ), - "node_function": staticmethod(node_function), - "_type_hints": get_type_hints(node_function), - "__module__": node_function.__module__, - }, +@classfactory +def function_node_factory( + node_function: callable, validate_output_labels: bool, /, *output_labels +): + return ( + node_function.__name__, + (Function,), # Define parentage + { + "node_function": staticmethod(node_function), + "__module__": node_function.__module__, + "__qualname__": node_function.__qualname__, + "_output_labels": None if len(output_labels) == 0 else output_labels, + "_validate_output_labels": validate_output_labels, + }, + {}, + ) + + +def as_function_node(*output_labels, validate_output_labels=True): + def decorator(node_function): + function_node_factory.clear(node_function.__name__) # Force a fresh class + factory_made = function_node_factory( + node_function, validate_output_labels, *output_labels ) - - return as_node + factory_made._class_returns_from_decorated_function = node_function + factory_made.preview_io() + return factory_made + + return decorator + + +def function_node( + node_function, + *node_args, + output_labels=None, + validate_output_labels=True, + **node_kwargs, +): + if output_labels is None: + output_labels = () + elif isinstance(output_labels, str): + output_labels = (output_labels,) + function_node_factory.clear(node_function.__name__) # Force a fresh class + factory_made = function_node_factory( + node_function, validate_output_labels, *output_labels + ) + factory_made.preview_io() + return factory_made(*node_args, **node_kwargs) diff --git a/pyiron_workflow/has_interface_mixins.py b/pyiron_workflow/has_interface_mixins.py index 07bfe045..5638f0dc 100644 --- a/pyiron_workflow/has_interface_mixins.py +++ b/pyiron_workflow/has_interface_mixins.py @@ -74,10 +74,9 @@ def channel(self) -> Channel: class HasRun(ABC): """ - A mixin to guarantee that the :meth:`run` method exists, and can be called without - arguments. + A mixin to guarantee that the :meth:`run` method exists. """ @abstractmethod - def run(self, **kwargs): + def run(self, *args, **kwargs): pass diff --git a/pyiron_workflow/injection.py b/pyiron_workflow/injection.py index c0ea5e24..715e8550 100644 --- a/pyiron_workflow/injection.py +++ b/pyiron_workflow/injection.py @@ -11,7 +11,7 @@ from abc import ABC, abstractmethod from typing import Any, Optional, TYPE_CHECKING -from pyiron_workflow.channels import OutputData, NOT_DATA, InputData +from pyiron_workflow.channels import OutputData, NOT_DATA from pyiron_workflow.has_interface_mixins import HasChannel from pyiron_workflow.io import Outputs, HasIO @@ -44,7 +44,7 @@ def __init__( default: Optional[Any] = NOT_DATA, type_hint: Optional[Any] = None, strict_hints: bool = True, - value_receiver: Optional[InputData] = None, + value_receiver: Optional[OutputData] = None, ): # Override parent method to give the new owner type hint super().__init__( diff --git a/pyiron_workflow/io.py b/pyiron_workflow/io.py index cc962b2f..d2c9b402 100644 --- a/pyiron_workflow/io.py +++ b/pyiron_workflow/io.py @@ -7,9 +7,9 @@ from __future__ import annotations -import warnings from abc import ABC, abstractmethod from typing import Any +import warnings from pyiron_workflow.channels import ( Channel, @@ -387,7 +387,7 @@ def __lshift__(self, others): """ self.signals.input.accumulate_and_run << others - def set_input_values(self, **kwargs) -> None: + def set_input_values(self, *args, **kwargs) -> None: """ Match keywords to input channels and update their values. @@ -395,17 +395,41 @@ def set_input_values(self, **kwargs) -> None: keys. Args: + *args: values assigned to inputs in order of appearance. **kwargs: input key - input value (including channels for connection) pairs. + + Raises: + (ValueError): If more args are received than there are inputs available. + (ValueError): If there is any overlap between channels receiving values + from `args` and those from `kwargs`. + (ValueError): If any of the `kwargs` keys do not match available input + labels. """ + if len(args) > len(self.inputs.labels): + raise ValueError( + f"Received {len(args)} args, but only have {len(self.inputs.labels)} " + f"input channels available" + ) + keyed_args = {label: value for label, value in zip(self.inputs.labels, args)} + + if len(set(keyed_args.keys()).intersection(kwargs.keys())) > 0: + raise ValueError( + f"n args are interpreted using the first n input channels " + f"({self.inputs.labels}), but this conflicted with received kwargs " + f"({list(kwargs.keys())}) -- perhaps the input was ordered differently " + f"than expected?" + ) + + kwargs.update(keyed_args) + + if len(set(kwargs.keys()).difference(self.inputs.labels)) > 0: + raise ValueError( + f"Tried to set input {list(kwargs.keys())}, but one or more label was " + f"not found among available inputs: {self.inputs.labels}" + ) + for k, v in kwargs.items(): - if k in self.inputs.labels: - self.inputs[k] = v - else: - warnings.warn( - f"The keyword '{k}' was not found among input labels. If you are " - f"trying to update a class instance keyword, please use attribute " - f"assignment directly instead of calling this method" - ) + self.inputs[k] = v def copy_io( self, diff --git a/pyiron_workflow/io_preview.py b/pyiron_workflow/io_preview.py new file mode 100644 index 00000000..a55244f8 --- /dev/null +++ b/pyiron_workflow/io_preview.py @@ -0,0 +1,432 @@ +""" +Mixin classes for classes which offer previews of input and output at the _class_ level. + +The intent is for mixing with :class:`pyiron_workflow.node.Node`, and for the inputs +and outputs to be IO channels there, but in principle this should function just fine +independently. + +These previews need to be available at the class level so that suggestion menus and +ontologies can know how mixin classes relate to the rest of the world via input and +output without first having to instantiate them. +""" + +from __future__ import annotations + +import inspect +import warnings +from abc import ABC, abstractmethod +from functools import lru_cache, wraps +from textwrap import dedent +from types import FunctionType +from typing import ( + Any, + ClassVar, + get_args, + get_type_hints, + Literal, + Optional, + TYPE_CHECKING, +) + +from pyiron_workflow.channels import InputData, NOT_DATA +from pyiron_workflow.injection import OutputDataWithInjection, OutputsWithInjection +from pyiron_workflow.io import Inputs +from pyiron_workflow.node import Node +from pyiron_workflow.output_parser import ParseOutput +from pyiron_workflow.snippets.dotdict import DotDict + +if TYPE_CHECKING: + from pandas import DataFrame + + from pyiron_workflow.composite import Composite + + +class HasIOPreview(ABC): + """ + An interface mixin guaranteeing the class-level availability of input and output + previews. + + E.g. for :class:`pyiron_workflow.node.Node` that have input and output channels. + """ + + @classmethod + @abstractmethod + def _build_inputs_preview(cls) -> dict[str, tuple[Any, Any]]: + pass + + @classmethod + @abstractmethod + def _build_outputs_preview(cls) -> dict[str, Any]: + pass + + @classmethod + @lru_cache(maxsize=1) + def preview_inputs(cls) -> dict[str, tuple[Any, Any]]: + """ + Gives a class-level peek at the expected inputs. + + Returns: + dict[str, tuple[Any, Any]]: The input name and a tuple of its + corresponding type hint and default value. + """ + return cls._build_inputs_preview() + + @classmethod + @lru_cache(maxsize=1) + def preview_outputs(cls) -> dict[str, Any]: + """ + Gives a class-level peek at the expected outputs. + + Returns: + dict[str, tuple[Any, Any]]: The output name and its corresponding type hint. + """ + return cls._build_outputs_preview() + + @classmethod + def preview_io(cls) -> DotDict[str:dict]: + return DotDict( + {"inputs": cls.preview_inputs(), "outputs": cls.preview_outputs()} + ) + + +def builds_class_io(subclass_factory: callable[..., type[HasIOPreview]]): + """ + A decorator for factories producing subclasses of `HasIOPreview` to invoke + :meth:`preview_io` after the class is created, thus ensuring the IO has been + constructed at the class level. + """ + + @wraps(subclass_factory) + def wrapped(*args, **kwargs): + node_class = subclass_factory(*args, **kwargs) + node_class.preview_io() + return node_class + + return wrapped + + +class ScrapesIO(HasIOPreview, ABC): + """ + A mixin class for scraping IO channel information from a specific class method's + signature and returns. + + Requires that the (static and class) method :meth:`_io_defining_function` be + specified in child classes, as well as :meth:`_io_defining_function_uses_self`. + Optionally, :attr:`_output_labels` can be overridden at the class level to avoid + scraping the return signature for channel labels altogether. + + Since scraping returns is only possible when the function source code is available, + this can be bypassed by manually specifying the class attribute + :attr:`_output_labels`. + + Attributes: + _output_labels (): + _validate_output_labels (bool): Whether to + _io_defining_function_uses_self (bool): Whether the signature of the IO + defining function starts with self. When true, the first argument in the + :meth:`_io_defining_function` is ignored. (Default is False, use the entire + signature for specifying input.) + + Warning: + There are a number of class features which, for computational efficiency, get + calculated at first call and any subsequent calls return that initial value + (including on other instances, since these are class properties); these + depend on the :meth:`_io_defining_function` and its signature, which should + thus be left static from the time of class definition onwards. + """ + + @classmethod + @abstractmethod + def _io_defining_function(cls) -> callable: + """Must return a static method.""" + + _output_labels: ClassVar[tuple[str] | None] = None # None: scrape them + _validate_output_labels: ClassVar[bool] = True # True: validate against source code + _io_defining_function_uses_self: ClassVar[bool] = ( + False # False: use entire signature + ) + + @classmethod + def _build_inputs_preview(cls): + type_hints = cls._get_type_hints() + scraped: dict[str, tuple[Any, Any]] = {} + for i, (label, value) in enumerate(cls._get_input_args().items()): + if cls._io_defining_function_uses_self and i == 0: + continue # Skip the macro argument itself, it's like `self` here + elif label in cls._get_init_keywords(): + # We allow users to parse arbitrary kwargs as channel initialization + # So don't let them choose bad channel names + raise ValueError( + f"Trying to build input preview for {cls.__name__}, encountered an " + f"argument name that conflicts with __init__: {label}. Please " + f"choose a name _not_ among {cls._get_init_keywords()}" + ) + + try: + type_hint = type_hints[label] + except KeyError: + type_hint = None + + default = ( + NOT_DATA if value.default is inspect.Parameter.empty else value.default + ) + + scraped[label] = (type_hint, default) + return scraped + + @classmethod + def _build_outputs_preview(cls): + if cls._validate_output_labels: + cls._validate() # Validate output on first call + + labels = cls._get_output_labels() + if labels is None: + labels = [] + try: + type_hints = cls._get_type_hints()["return"] + if len(labels) > 1: + type_hints = get_args(type_hints) + if not isinstance(type_hints, tuple): + raise TypeError( + f"With multiple return labels expected to get a tuple of type " + f"hints, but {cls.__name__} got type {type(type_hints)}" + ) + if len(type_hints) != len(labels): + raise ValueError( + f"Expected type hints and return labels to have matching " + f"lengths, but {cls.__name__} got {len(type_hints)} hints and " + f"{len(labels)} labels: {type_hints}, {labels}" + ) + else: + # If there's only one hint, wrap it in a tuple, so we can zip it with + # *return_labels and iterate over both at once + type_hints = (type_hints,) + except KeyError: # If there are no return hints + type_hints = [None] * len(labels) + # Note that this nicely differs from `NoneType`, which is the hint when + # `None` is actually the hint! + return {label: hint for label, hint in zip(labels, type_hints)} + + @classmethod + def _get_output_labels(cls): + """ + Return output labels provided for the class, scraping them from the io-defining + function if they are not already available. + """ + if cls._output_labels is None: + cls._output_labels = cls._scrape_output_labels() + return cls._output_labels + + @classmethod + @lru_cache(maxsize=1) + def _get_type_hints(cls) -> dict: + """ + The result of :func:`typing.get_type_hints` on the io-defining function + """ + return get_type_hints(cls._io_defining_function()) + + @classmethod + @lru_cache(maxsize=1) + def _get_input_args(cls): + return inspect.signature(cls._io_defining_function()).parameters + + @classmethod + @lru_cache(maxsize=1) + def _get_init_keywords(cls): + return list(inspect.signature(cls.__init__).parameters.keys()) + + @classmethod + @lru_cache(maxsize=1) + def _scrape_output_labels(cls): + """ + Inspect :meth:`node_function` to scrape out strings representing the + returned values. + + _Only_ works for functions with a single `return` expression in their body. + + It will return expressions and function calls just fine, thus good practice is + to create well-named variables and return those so that the output labels stay + dot-accessible. + """ + return ParseOutput(cls._io_defining_function()).output + + @classmethod + def _validate(cls): + """ + Ensure that output_labels, if provided, are commensurate with graph creator + return values, if provided, and return them as a tuple. + """ + try: + cls._validate_degeneracy() + cls._validate_return_count() + except OSError: + warnings.warn( + f"Could not find the source code to validate {cls.__name__} output " + f"labels against the number of returned values -- proceeding without " + f"validation", + OutputLabelsNotValidated, + ) + + @classmethod + def _validate_degeneracy(cls): + output_labels = cls._get_output_labels() + if output_labels is not None and len(set(output_labels)) != len(output_labels): + raise ValueError( + f"{cls.__name__} must not have degenerate output labels: " + f"{output_labels}" + ) + + @classmethod + def _validate_return_count(cls): + output_labels = cls._get_output_labels() + graph_creator_returns = ParseOutput(cls._io_defining_function()).output + if graph_creator_returns is not None or output_labels is not None: + error_suffix = ( + f"but {cls.__name__} got return values: {graph_creator_returns} and " + f"labels: {output_labels}. If this intentional, you can bypass output " + f"validation making sure the class attribute `_validate_output_labels` " + f"is False." + ) + try: + if len(output_labels) != len(graph_creator_returns): + raise ValueError( + "The number of return values must exactly match the number of " + "output labels provided, " + error_suffix + ) + except TypeError: + raise TypeError( + f"Output labels and return values must either both or neither be " + f"present, " + error_suffix + ) + + +class OutputLabelsNotValidated(Warning): + pass + + +class StaticNode(Node, HasIOPreview, ABC): + """ + A node whose IO specification is available at the class level. + + Actual IO is then constructed from the preview at instantiation. + """ + + def __init__( + self, + *args, + label: Optional[str] = None, + parent: Optional[Composite] = None, + overwrite_save: bool = False, + run_after_init: bool = False, + storage_backend: Optional[Literal["h5io", "tinybase"]] = None, + save_after_run: bool = False, + **kwargs, + ): + super().__init__( + *args, + label=label, + parent=parent, + overwrite_save=overwrite_save, + run_after_init=run_after_init, + storage_backend=storage_backend, + save_after_run=save_after_run, + **kwargs, + ) + + def _setup_node(self) -> None: + super()._setup_node() + + self._inputs = Inputs( + *[ + InputData( + label=label, + owner=self, + default=default, + type_hint=type_hint, + ) + for label, (type_hint, default) in self.preview_inputs().items() + ] + ) + + self._outputs = OutputsWithInjection( + *[ + OutputDataWithInjection( + label=label, + owner=self, + type_hint=hint, + ) + for label, hint in self.preview_outputs().items() + ] + ) + + @property + def inputs(self) -> Inputs: + return self._inputs + + @property + def outputs(self) -> OutputsWithInjection: + return self._outputs + + def iter( + self, + body_node_executor=None, + output_column_map: Optional[dict[str, str]] = None, + **iterating_inputs, + ) -> DataFrame: + return self._loop( + "iter_on", + body_node_executor=body_node_executor, + output_column_map=output_column_map, + **iterating_inputs, + ) + + def zip( + self, + body_node_executor=None, + output_column_map: Optional[dict[str, str]] = None, + **iterating_inputs, + ) -> DataFrame: + return self._loop( + "zip_on", + body_node_executor=body_node_executor, + output_column_map=output_column_map, + **iterating_inputs, + ) + + def _loop( + self, + loop_style_key, + body_node_executor=None, + output_column_map=None, + **looping_inputs, + ): + loop_on = tuple(looping_inputs.keys()) + self._guarantee_names_are_input_channels(loop_on) + + broadcast_inputs = { + label: self.inputs[label].value + for label in set(self.inputs.labels).difference(loop_on) + } + + from pyiron_workflow.for_loop import for_node + + for_instance = for_node( + self.__class__, + **{ + loop_style_key: loop_on, + "output_column_map": output_column_map, + **looping_inputs, + **broadcast_inputs, + }, + ) + for_instance.body_node_executor = body_node_executor + + return for_instance().df + + def _guarantee_names_are_input_channels(self, presumed_input_keys: tuple[str]): + non_input_kwargs = set(presumed_input_keys).difference(self.inputs.labels) + if len(non_input_kwargs) > 0: + raise ValueError( + f"{self.label} cannot iterate on {non_input_kwargs} because they are " + f"not among input channels {self.inputs.labels}" + ) diff --git a/pyiron_workflow/job.py b/pyiron_workflow/job.py index 3a742842..1b825dd2 100644 --- a/pyiron_workflow/job.py +++ b/pyiron_workflow/job.py @@ -20,11 +20,14 @@ from __future__ import annotations +import base64 import inspect import os import sys -from pyiron_base import GenericJob, TemplateJob, JOB_CLASS_DICT +import cloudpickle + +from pyiron_base import TemplateJob, JOB_CLASS_DICT from pyiron_base.jobs.flex.pythonfunctioncontainer import ( PythonFunctionContainerJob, get_function_parameter_dict, @@ -103,35 +106,50 @@ def validate_ready_to_run(self): f"Node not ready:{nl}{self.input['node'].readiness_report}" ) + def save(self): + # DataContainer can't handle custom reconstructors, so convert the node to + # bytestream + self.input["node"] = base64.b64encode( + cloudpickle.dumps(self.input["node"]) + ).decode("utf-8") + super().save() + def run_static(self): # Overrides the parent method # Copy and paste except for the output update, which makes sure the output is # flat and not tested beneath "result" + + # Unpack the node + input_dict = self.input.to_builtin() + input_dict["node"] = cloudpickle.loads(base64.b64decode(self.input["node"])) + if ( self._executor_type is not None and "executor" in inspect.signature(self._function).parameters.keys() ): - input_dict = self.input.to_builtin() del input_dict["executor"] output = self._function( **input_dict, executor=self._get_executor(max_workers=self.server.cores) ) else: - output = self._function(**self.input.to_builtin()) + output = self._function(**input_dict) self.output.update(output) # DIFFERS FROM PARENT METHOD self.to_hdf() self.status.finished = True - def save(self): - # PythonFunctionContainerJob.save assumes that the job is being created - # exclusively from pyiron_base.Project.wrap_python_function, and therefore - # always dynamically renames the job based on the wrapped function and the - # input. - # Here, the jobs are created in the usual way, with the usual use of job name, - # so it is just confusing if this renaming happens; thus, we save as usual. - # If at any point PythonFunctionContainerJob.save behaves in the usual way, - # this override can be removed - GenericJob.save(self) + def get_input_node(self): + """ + On saving, we turn the input node into a bytestream so that the DataContainer + can store it. You might want to look at it again though, so you can use this + to unpack it + + Returns: + (Node): The input node as a node again + """ + if isinstance(self.input["node"], Node): + return self.input["node"] + else: + return cloudpickle.loads(base64.b64decode(self.input["node"])) JOB_CLASS_DICT[NodeOutputJob.__name__] = NodeOutputJob.__module__ diff --git a/pyiron_workflow/loops.py b/pyiron_workflow/loops.py new file mode 100644 index 00000000..ec303db5 --- /dev/null +++ b/pyiron_workflow/loops.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +import random +from textwrap import dedent +from typing import Optional + +import pyiron_workflow +from pyiron_workflow import Workflow +from pyiron_workflow.function import Function +from pyiron_workflow.macro import Macro +from pyiron_workflow.transform import inputs_to_list, list_to_outputs +from pyiron_workflow.node import Node + + +def while_loop( + loop_body_class: type[Node], + condition_class: type[Function], + internal_connection_map: dict[str, str], + inputs_map: Optional[dict[str, str]], + outputs_map: Optional[dict[str, str]], +) -> type[Macro]: + """ + An _extremely rough_ second draft of a for-loop meta-node. + + Takes body and condition node classes and builds a macro that makes a cyclic signal + connection between them and an "if" switch, i.e. when the body node finishes it + runs the condtion, which runs the switch, and as long as the condition result was + `True`, the switch loops back to run the body again. + We additionally allow four-tuples of (input node, input channel, output node, + output channel) labels to wire data connections inside the macro, e.g. to pass data + from the body to the condition. This is beastly syntax, but it will suffice for now. + Finally, you can set input and output maps as normal. + + Args: + loop_body_class (type[pyiron_workflow.node.Node]): The class for the + body of the while-loop. + condition_class (type[pyiron_workflow.function.Function]): A + single-output function node returning a `bool` controlling the while loop + exit condition (exits on False) + internal_connection_map (list[tuple[str, str, str, str]]): String tuples + giving (input node, input channel, output node, output channel) labels + connecting channel pairs inside the macro. + inputs_map (dict[str, str]): Define the inputs for the new macro like + `{body/condition class name}__{input channel}: {macro input channel name}` + outputs_map (dict[str, str]): Define the outputs for the new macro like + `{body/condition class name}__{output channel}: {macro output channel name}` + + Warnings: + The loop body and condition classes must be importable. E.g. they can come from + a node package or be defined in `__main__`, but not defined inside the scope of + some other function. + + Examples: + + >>> from pyiron_workflow import Workflow + >>> + >>> AddWhile = Workflow.create.meta.while_loop( + ... loop_body_class=Workflow.create.standard.Add, + ... condition_class=Workflow.create.standard.LessThan, + ... internal_connection_map=[ + ... ("Add", "add", "LessThan", "obj"), + ... ("Add", "add", "Add", "obj") + ... ], + ... inputs_map={ + ... "Add__obj": "a", + ... "Add__other": "b", + ... "LessThan__other": "cap" + ... }, + ... outputs_map={"Add__add": "total"} + ... ) + >>> + >>> wf = Workflow("do_while") + >>> wf.add_while = AddWhile(cap=10) + >>> + >>> wf.inputs_map = { + ... "add_while__a": "a", + ... "add_while__b": "b" + ... } + >>> wf.outputs_map = {"add_while__total": "total"} + >>> + >>> print(f"Finally, {wf(a=1, b=2).total}") + Finally, 11 + + >>> import random + >>> + >>> from pyiron_workflow import Workflow + >>> + >>> random.seed(0) # Set the seed so the output is consistent and doctest runs + >>> + >>> RandomWhile = Workflow.create.meta.while_loop( + ... loop_body_class=Workflow.create.standard.RandomFloat, + ... condition_class=Workflow.create.standard.GreaterThan, + ... internal_connection_map=[ + ... ("RandomFloat", "random", "GreaterThan", "obj") + ... ], + ... inputs_map={"GreaterThan__other": "threshold"}, + ... outputs_map={"RandomFloat__random": "capped_result"} + ... ) + >>> + >>> # Define workflow + >>> + >>> wf = Workflow("random_until_small_enough") + >>> + >>> ## Wire together the while loop and its condition + >>> + >>> wf.random_while = RandomWhile() + >>> + >>> ## Give convenient labels + >>> wf.inputs_map = {"random_while__threshold": "threshold"} + >>> wf.outputs_map = {"random_while__capped_result": "capped_result"} + >>> + >>> # Set a threshold and run + >>> print(f"Finally {wf(threshold=0.3).capped_result:.3f}") + Finally 0.259 + """ + + # Make sure each dynamic class is getting a unique name + io_hash = hash( + ",".join( + [ + "_".join(s for conn in internal_connection_map for s in conn), + "".join(f"{k}:{v}" for k, v in sorted(inputs_map.items())), + "".join(f"{k}:{v}" for k, v in sorted(outputs_map.items())), + ] + ) + ) + io_hash = str(io_hash).replace("-", "m") + node_name = f"{loop_body_class.__name__}While{condition_class.__name__}_{io_hash}" + + # Build code components that need an f-string, slash, etc. + output_labels = ", ".join(f'"{l}"' for l in outputs_map.values()).rstrip(" ") + input_args = ", ".join(l for l in inputs_map.values()).rstrip(" ") + + def get_kwargs(io_map: dict[str, str], node_class: type[Node]): + return ", ".join( + f'{k.split("__")[1]}={v}' + for k, v in io_map.items() + if k.split("__")[0] == node_class.__name__ + ).rstrip(" ") + + returns = ", ".join( + f'self.{l.split("__")[0]}.outputs.{l.split("__")[1]}' + for l in outputs_map.keys() + ).rstrip(" ") + + # Assemble components into a decorated while-loop macro + while_loop_code = dedent( + f""" + @Macro.wrap.as_macro_node({output_labels}) + def {node_name}(self, {input_args}): + from {loop_body_class.__module__} import {loop_body_class.__name__} + from {condition_class.__module__} import {condition_class.__name__} + + body = self.add_child( + {loop_body_class.__name__}( + label="{loop_body_class.__name__}", + {get_kwargs(inputs_map, loop_body_class)} + ) + ) + + condition = self.add_child( + {condition_class.__name__}( + label="{condition_class.__name__}", + {get_kwargs(inputs_map, condition_class)} + ) + ) + + self.switch = self.create.standard.If(condition=condition) + + for out_n, out_c, in_n, in_c in {str(internal_connection_map)}: + self.children[in_n].inputs[in_c] = self.children[out_n].outputs[out_c] + + + self.switch.signals.output.true >> body >> condition >> self.switch + self.starting_nodes = [body] + + return {returns} + """ + ) + + exec(while_loop_code) + return locals()[node_name] + + # def make_loop(macro): + # body_node = macro.add_child(loop_body_class(label=loop_body_class.__name__)) + # condition_node = macro.add_child( + # condition_class(label=condition_class.__name__) + # ) + # switch = macro.create.standard.If(label="switch", parent=macro) + # + # switch.inputs.condition = condition_node + # for out_n, out_c, in_n, in_c in internal_connection_map: + # macro.children[in_n].inputs[in_c] = macro.children[out_n].outputs[out_c] + # + # switch.signals.output.true >> body_node >> condition_node >> switch + # macro.starting_nodes = [body_node] + # + # macro.inputs_map = {} if inputs_map is None else inputs_map + # macro.outputs_map = {} if outputs_map is None else outputs_map + # + # return as_macro_node()(make_loop) diff --git a/pyiron_workflow/macro.py b/pyiron_workflow/macro.py index 52a7f98d..4c3727ba 100644 --- a/pyiron_workflow/macro.py +++ b/pyiron_workflow/macro.py @@ -5,23 +5,21 @@ from __future__ import annotations -from functools import partialmethod -import inspect -from typing import get_type_hints, Literal, Optional, TYPE_CHECKING +from abc import ABC, abstractmethod +import re +from typing import Literal, Optional, TYPE_CHECKING -from bidict import bidict - -from pyiron_workflow.channels import InputData, OutputData, NOT_DATA from pyiron_workflow.composite import Composite from pyiron_workflow.has_interface_mixins import HasChannel from pyiron_workflow.io import Outputs, Inputs -from pyiron_workflow.output_parser import ParseOutput +from pyiron_workflow.io_preview import StaticNode, ScrapesIO +from pyiron_workflow.snippets.factory import classfactory if TYPE_CHECKING: from pyiron_workflow.channels import Channel -class Macro(Composite): +class Macro(Composite, StaticNode, ScrapesIO, ABC): """ A macro is a composite node that holds a graph with a fixed interface, like a pre-populated workflow that is the same every time you instantiate it. @@ -30,23 +28,22 @@ class Macro(Composite): then builds a static IO interface for this graph. This callable must use the macro object itself as the first argument (e.g. adding nodes to it). - As with :class:`Workflow` objects, macros leverage `inputs_map` and `outputs_map` to - control macro-level IO access to child IO. - As with :class:`Workflow`, default behaviour is to expose all unconnected child IO. - The provided callable may optionally specify further args and kwargs, which are used - to pre-populate the macro with :class:`UserInput` nodes; + The provided callable may optionally specify further args and kwargs; these are + used to pre-populate the macro with :class:`UserInput` nodes, although they may + later be trimmed if the IO can be connected directly to child node IO without any + loss of functionality. This can be especially helpful when more than one child node needs access to the same input value. Similarly, the callable may return any number of child nodes' output channels (or - the node itself in the case of single-output nodes) and commensurate - :attr:`output_labels` to define macro-level output. + the node itself in the case of single-output nodes) as long as a commensurate + number of labels for these outputs were provided to the class constructor. These function-like definitions of the graph creator callable can be used - independently or together. - Each that is used switches its IO map to a "whitelist" paradigm, so any I/O _not_ - provided in the callable signature/return values and output labels will be disabled. - Manual modifications of the IO maps inside the callable always take priority over - this whitelisting behaviour, so you always retain full control over what IO is - exposed, and the whitelisting is only for your convenience. + to build only input xor output, or both together. + Macro input channel labels are scraped from the signature of the graph creator; + for output, output labels can be provided explicitly as a class attribute or, as a + fallback, they are scraped from the graph creator code return statement (stripping + off the "{first argument}.", where {first argument} is whatever the name of the + first argument is. Macro IO is _value linked_ to the child IO, so that their values stay synchronized, but the child nodes of a macro form an isolated sub-graph. @@ -61,15 +58,15 @@ class Macro(Composite): If only _one_ of these is specified, you'll get an error, but if you've provided both then no further checks of their validity/reasonableness are performed, so be careful. - Unlike :class:`Workflow`, this execution flow automation is set up once at instantiation; + Unlike :class:`Workflow`, this execution flow automation is set up once at + instantiation; If the macro is modified post-facto, you may need to manually re-invoke :meth:`configure_graph_execution`. Promises (in addition parent class promises): - IO is... - - Only built at instantiation, after child node replacement, or at request, so - it is "static" for improved efficiency + - Statically defined at the class level - By value, i.e. the macro has its own IO channel instances and children are duly encapsulated inside their own sub-graph - Value-linked to the values of their corresponding child nodes' IO -- i.e. @@ -86,18 +83,19 @@ class Macro(Composite): Let's consider the simplest case of macros that just consecutively add 1 to their input: - >>> from pyiron_workflow.macro import Macro + >>> from pyiron_workflow.macro import macro_node, Macro >>> >>> def add_one(x): ... result = x + 1 ... return result >>> - >>> def add_three_macro(macro): - ... macro.one = macro.create.Function(add_one) - ... macro.two = macro.create.Function(add_one, macro.one) - ... macro.three = macro.create.Function(add_one, macro.two) - ... macro.one >> macro.two >> macro.three - ... macro.starting_nodes = [macro.one] + >>> def add_three_macro(self, one__x): + ... self.one = self.create.function_node(add_one, x=one__x) + ... self.two = self.create.function_node(add_one, self.one) + ... self.three = self.create.function_node(add_one, self.two) + ... self.one >> self.two >> self.three + ... self.starting_nodes = [self.one] + ... return self.three In this case we had _no need_ to specify the execution order and starting nodes --it's just an extremely simple DAG after all! -- but it's done here to @@ -109,92 +107,110 @@ class Macro(Composite): io is constructed from unconnected owned-node IO by combining node and channel labels. - >>> macro = Macro(add_three_macro) + >>> macro = macro_node(add_three_macro, output_labels="three__result") >>> out = macro(one__x=3) >>> out.three__result 6 - If there's a particular macro we're going to use again and again, we might want - to consider making a new child class of :class:`Macro` that overrides the - :meth:`graph_creator` arg such that the same graph is always created. We could - override `__init__` the normal way, but it's even faster to just use - `partialmethod`: - - >>> from functools import partialmethod - >>> class AddThreeMacro(Macro): - ... @staticmethod - ... def graph_creator(self): - ... add_three_macro(self) - ... - ... __init__ = partialmethod( - ... Macro.__init__, - ... None, # We directly define the graph creator method on the class - ... ) - >>> - >>> macro = AddThreeMacro() - >>> macro(one__x=0).three__result - 3 - We can also nest macros, rename their IO, and provide access to internally-connected IO by inputs and outputs maps: - >>> def nested_macro(macro): - ... macro.a = macro.create.Function(add_one) - ... macro.b = macro.create.Macro(add_three_macro, one__x=macro.a) - ... macro.c = macro.create.Function( - ... add_one, x=macro.b.outputs.three__result + >>> def nested_macro(self, inp): + ... self.a = self.create.function_node(add_one, x=inp) + ... self.b = self.create.macro_node( + ... add_three_macro, one__x=self.a, output_labels="three__result" ... ) + ... self.c = self.create.function_node(add_one, x=self.b) + ... return self.c, self.b >>> - >>> macro = Macro( - ... nested_macro, - ... inputs_map={"a__x": "inp"}, - ... outputs_map={"c__result": "out", "b__three__result": "intermediate"}, + >>> macro = macro_node( + ... nested_macro, output_labels=("out", "intermediate") ... ) >>> macro(inp=1) - {'intermediate': 5, 'out': 6} + {'out': 6, 'intermediate': 5} Macros and workflows automatically generate execution flows when their data is acyclic. Let's build a simple macro with two independent tracks: - >>> def modified_flow_macro(macro): - ... macro.a = macro.create.Function(add_one, x=0) - ... macro.b = macro.create.Function(add_one, x=0) - ... macro.c = macro.create.Function(add_one, x=0) + >>> def modified_flow_macro(self, a__x=0, b__x=0): + ... self.a = self.create.function_node(add_one, x=a__x) + ... self.b = self.create.function_node(add_one, x=b__x) + ... self.c = self.create.function_node(add_one, x=self.b) + ... return self.a, self.c >>> - >>> m = Macro(modified_flow_macro) - >>> m(a__x=1, b__x=2, c__x=3) - {'a__result': 2, 'b__result': 3, 'c__result': 4} - - We can override which nodes get used to start by specifying the :attr:`starting_nodes` - property. - If we do this we also need to provide at least one connection among the run - signals, but beyond that the code doesn't hold our hands. + >>> m = macro_node(modified_flow_macro, output_labels=("a", "c")) + >>> m(a__x=1, b__x=2) + {'a': 2, 'c': 4} + + We can override which nodes get used to start by specifying the + :attr:`starting_nodes` property and (if necessary) reconfiguring the execution + signals. + Care should be taken here, as macro nodes may be creating extra input + nodes that need to be considered. + It's advisable to use :meth:`draw()` or to otherwise inspect the macro's + children and their connections before manually updating execution flows. + Let's use this and then observe how the `a` sub-node no longer gets run: - >>> m.starting_nodes = [m.b] # At least one starting node - >>> _ = m.b >> m.c # At least one run signal - >>> # We catch and ignore output -- it's needed for chaining, but screws up - >>> # doctests -- you don't normally need to catch it like this! - >>> m(a__x=1000, b__x=2000, c__x=3000) - {'a__result': 2, 'b__result': 2001, 'c__result': 3001} + >>> _ = m.disconnect_run() + >>> m.starting_nodes = [m.b] + >>> _ = m.b >> m.c + >>> m(a__x=1000, b__x=2000) + {'a': 2, 'c': 2002} + + (The `_` is just to catch and ignore output for the doctest, you don't + typically need this.) Note how the `a` node is no longer getting run, so the output is not updated! Manually controlling execution flow is necessary for cyclic graphs (cf. the while loop meta-node), but best to avoid when possible as it's easy to miss intended connections in complex graphs. + If there's a particular macro we're going to use again and again, we might want + to consider making a new class for it using the decorator, just like we do for + function nodes. If no output labels are explicitly provided, these are scraped + from the function return value, just like for function nodes (except the + initial `macro.` (or whatever the first argument is named) on any return values + is ignored): + + >>> @Macro.wrap.as_macro_node() + ... def AddThreeMacro(self, x): + ... add_three_macro(self, one__x=x) + ... # We could also simply have decorated that function to begin with + ... return self.three + >>> + >>> macro = AddThreeMacro() + >>> macro(x=0).three + 3 + + Alternatively (and not recommended) is to make a new child class of + :class:`Macro` that overrides the :meth:`graph_creator` arg such that + the same graph is always created. + + >>> class AddThreeMacro(Macro): + ... _output_labels = ["three"] + ... + ... @staticmethod + ... def graph_creator(self, x): + ... add_three_macro(self, one__x=x) + ... return self.three + >>> + >>> macro = AddThreeMacro() + >>> macro(x=0).three + 3 + We can also modify an existing macro at runtime by replacing nodes within it, as long as the replacement has fully compatible IO. There are three syntacic ways to do this. Let's explore these by going back to our `add_three_macro` and replacing each of its children with a node that adds 2 instead of 1. - >>> @Macro.wrap_as.function_node() + >>> @Macro.wrap.as_function_node() ... def add_two(x): ... result = x + 2 ... return result >>> - >>> adds_six_macro = Macro(add_three_macro) + >>> adds_six_macro = macro_node(add_three_macro, output_labels="three__result") >>> # With the replace method >>> # (replacement target can be specified by label or instance, >>> # the replacing node can be specified by instance or class) @@ -206,275 +222,120 @@ class Macro(Composite): >>> adds_six_macro(one__x=1) {'three__result': 7} - Instead of controlling the IO interface with dictionary maps, we can instead - provide a more :class:`Function(Node)`-like definition of the :meth:`graph_creator` by - adding args and/or kwargs to the signature (under the hood, this dynamically - creates new :class:`UserInput` nodes before running the rest of the graph creation), - and/or returning child channels (or whole children in the case of single-output - nodes) and providing commensurate :attr:`output_labels`. - This process switches us from the :class:`Workflow` default of exposing all - unconnected child IO, to a "whitelist" paradigm of _only_ showing the IO that - we exposed by our function defintion. - (Note: any `.inputs_map` or `.outputs_map` explicitly defined in the - :meth:`graph_creator` still takes precedence over this whitelisting! So you always - retain full control over what IO gets exposed.) - E.g., these two definitions are perfectly equivalent: - - >>> @Macro.wrap_as.macro_node("lout", "n_plus_2") - ... def LikeAFunction(macro, lin: list, n: int = 1): - ... macro.plus_two = n + 2 - ... macro.sliced_list = lin[n:macro.plus_two] - ... macro.double_fork = 2 * n - ... # ^ This is vestigial, just to show we don't need to blacklist it in a - ... # whitelist-paradigm - ... return macro.sliced_list, macro.plus_two.channel - >>> - >>> like_functions = LikeAFunction(lin=[1,2,3,4,5,6], n=2) - >>> like_functions() - {'n_plus_2': 4, 'lout': [3, 4]} - - >>> @Macro.wrap_as.macro_node() - ... def WithIOMaps(macro): - ... macro.list_in = macro.create.standard.UserInput() - ... macro.list_in.inputs.user_input.type_hint = list - ... macro.forked = macro.create.standard.UserInput(2) - ... macro.forked.inputs.user_input.type_hint = int - ... macro.n_plus_2 = macro.forked + 2 - ... macro.sliced_list = macro.list_in[macro.forked:macro.n_plus_2] - ... macro.double_fork = 2 * macro.forked - ... macro.inputs_map = { - ... "list_in__user_input": "lin", - ... macro.forked.inputs.user_input.scoped_label: "n", - ... "n_plus_2__other": None, - ... "list_in__user_input_Slice_forked__user_input_n_plus_2__add_None__step": None, - ... macro.double_fork.inputs.other.scoped_label: None, - ... } - ... macro.outputs_map = { - ... macro.sliced_list.outputs.getitem.scoped_label: "lout", - ... macro.n_plus_2.outputs.add.scoped_label: "n_plus_2", - ... "double_fork__rmul": None - ... } + It's possible for the macro to hold nodes which are not publicly exposed for + data and signal connections, but which will still internally execute and store + data, e.g.: + + >>> @Macro.wrap.as_macro_node("lout", "n_plus_2") + ... def LikeAFunction(self, lin: list, n: int = 1): + ... self.plus_two = n + 2 + ... self.sliced_list = lin[n:self.plus_two] + ... self.double_fork = 2 * n + ... return self.sliced_list, self.plus_two.channel >>> - >>> with_maps = WithIOMaps(lin=[1,2,3,4,5,6], n=2) - >>> with_maps() - {'n_plus_2': 4, 'lout': [3, 4]} + >>> like_functions = LikeAFunction(lin=[1,2,3,4,5,6], n=3) + >>> sorted(like_functions().items()) + [('lout', [4, 5]), ('n_plus_2', 5)] + + >>> like_functions.double_fork.value + 6 + - Here we've leveraged the macro-creating decorator, but this works the same way - using the :class:`Macro` class directly. """ - def __init__( - self, - graph_creator: callable[[Macro], None], - label: Optional[str] = None, - parent: Optional[Composite] = None, - overwrite_save: bool = False, - run_after_init: bool = False, - storage_backend: Optional[Literal["h5io", "tinybase"]] = None, - save_after_run: bool = False, - strict_naming: bool = True, - inputs_map: Optional[dict | bidict] = None, - outputs_map: Optional[dict | bidict] = None, - output_labels: Optional[str | list[str] | tuple[str]] = None, - **kwargs, - ): - if not callable(graph_creator): - # Children of `Function` may explicitly provide a `node_function` static - # method so the node has fixed behaviour. - # In this case, the `__init__` signature should be changed so that the - # `node_function` argument is just always `None` or some other non-callable. - # If a callable `node_function` is not received, you'd better have it as an - # attribute already! - if not hasattr(self, "graph_creator"): - raise AttributeError( - f"If `None` is provided as a `graph_creator`, a `graph_creator` " - f"property must be defined instead, e.g. when making child classes" - f"of `Macro` with specific behaviour" - ) - else: - # If a callable graph creator is received, use it - self.graph_creator = graph_creator - - super().__init__( - label=label if label is not None else self.graph_creator.__name__, - parent=parent, - save_after_run=save_after_run, - storage_backend=storage_backend, - strict_naming=strict_naming, - inputs_map=inputs_map, - outputs_map=outputs_map, - ) - output_labels = self._validate_output_labels(output_labels) + def _setup_node(self) -> None: + super()._setup_node() ui_nodes = self._prepopulate_ui_nodes_from_graph_creator_signature( - storage_backend=storage_backend + storage_backend=self.storage_backend ) returned_has_channel_objects = self.graph_creator(self, *ui_nodes) - self._configure_graph_execution() + if returned_has_channel_objects is None: + returned_has_channel_objects = () + elif isinstance(returned_has_channel_objects, HasChannel): + returned_has_channel_objects = (returned_has_channel_objects,) - # Update IO map(s) if a function-like graph creator interface was used - if len(ui_nodes) > 0: - self._whitelist_inputs_map(*ui_nodes) - if returned_has_channel_objects is not None: - if not isinstance(returned_has_channel_objects, tuple): - returned_has_channel_objects = (returned_has_channel_objects,) - self._whitelist_outputs_map(output_labels, *returned_has_channel_objects) + for node in ui_nodes: + self.inputs[node.label].value_receiver = node.inputs.user_input - self._inputs: Inputs = self._build_inputs() - self._outputs: Outputs = self._build_outputs() + for node, output_channel_label in zip( + returned_has_channel_objects, + () if self._output_labels is None else self._output_labels, + ): + node.channel.value_receiver = self.outputs[output_channel_label] - self.set_input_values(**kwargs) + remaining_ui_nodes = self._purge_single_use_ui_nodes(ui_nodes) + self._configure_graph_execution(remaining_ui_nodes) - def _validate_output_labels(self, output_labels) -> tuple[str]: - """ - Ensure that output_labels, if provided, are commensurate with graph creator - return values, if provided, and return them as a tuple. - """ - graph_creator_returns = ParseOutput(self.graph_creator).output - output_labels = ( - (output_labels,) if isinstance(output_labels, str) else output_labels - ) - if graph_creator_returns is not None or output_labels is not None: - error_suffix = ( - f"but {self.label} macro got return values: " - f"{graph_creator_returns} and labels: {output_labels}." - ) - try: - if len(output_labels) != len(graph_creator_returns): - raise ValueError( - "The number of return values in the graph creator must exactly " - "match the number of output labels provided, " + error_suffix - ) - except TypeError: - raise TypeError( - f"Output labels and graph creator return values must either both " - f"or neither be present, " + error_suffix + @staticmethod + @abstractmethod + def graph_creator(self, *args, **kwargs) -> callable: + """Build the graph the node will run.""" + + @classmethod + def _io_defining_function(cls) -> callable: + return cls.graph_creator + + _io_defining_function_uses_self = True + + @classmethod + def _scrape_output_labels(cls): + scraped_labels = super(Macro, cls)._scrape_output_labels() + + if scraped_labels is not None: + # Strip off the first argument, e.g. self.foo just becomes foo + self_argument = list(cls._get_input_args().keys())[0] + cleaned_labels = [ + re.sub(r"^" + re.escape(f"{self_argument}."), "", label) + for label in scraped_labels + ] + if any("." in label for label in cleaned_labels): + raise ValueError( + f"Tried to scrape cleaned labels for {cls.__name__}, but at least " + f"one of {cleaned_labels} still contains a '.' -- please provide " + f"explicit labels" ) - return () if output_labels is None else tuple(output_labels) + return cleaned_labels + else: + return scraped_labels def _prepopulate_ui_nodes_from_graph_creator_signature( self, storage_backend: Literal["h5io", "tinybase"] ): - hints_dict = get_type_hints(self.graph_creator) - interface_nodes = () - for i, (arg_name, inspected_value) in enumerate( - inspect.signature(self.graph_creator).parameters.items() - ): - if i == 0: - continue # Skip the macro argument itself, it's like `self` here - - default = ( - NOT_DATA - if inspected_value.default is inspect.Parameter.empty - else inspected_value.default + ui_nodes = [] + for label, (type_hint, default) in self.preview_inputs().items(): + n = self.create.standard.UserInput( + default, + label=label, + parent=self, + storage_backend=storage_backend, ) - node = self.create.standard.UserInput( - default, label=arg_name, parent=self, storage_backend=storage_backend - ) - node.inputs.user_input.default = default - try: - node.inputs.user_input.type_hint = hints_dict[arg_name] - except KeyError: - pass # If there's no hint that's fine - interface_nodes += (node,) - - return interface_nodes + n.inputs.user_input.type_hint = type_hint + ui_nodes.append(n) + return tuple(ui_nodes) - def _whitelist_inputs_map(self, *ui_nodes) -> None: + def _purge_single_use_ui_nodes(self, ui_nodes): """ - Updates the inputs map so each UI node's output channel is available directly - under the node label, and updates the map to disable all other input that - wasn't explicitly mapped already. + We (may) create UI nodes based on the :meth:`graph_creator` signature; + If these are connected to only a single node actually defined in the creator, + they are superfluous, and we can remove them -- linking the macro input + directly to the child node input. """ - self.inputs_map = self._hide_non_whitelisted_io( - self._whitelist_map( - self.inputs_map, tuple(n.label for n in ui_nodes), ui_nodes - ), - "inputs", - ) - - def _whitelist_outputs_map( - self, output_labels: tuple[str], *creator_returns: HasChannel - ): - """ - Updates the outputs map so objects returned by the graph creator directly - leverage the supplied output labels, and updates the map to disable all other - output that wasn't explicitly mapped already. - """ - for new_label, ui_node in zip(output_labels, creator_returns): - if not isinstance(ui_node, HasChannel): - raise TypeError( - f"Your node `{new_label}` does not have `channel`. There" - + " are following nodes that can be returned:" - + f" {self.node_labels}. More can be found from this page:" - + " https://github.com/pyiron/pyiron_workflow" - ) - self.outputs_map = self._hide_non_whitelisted_io( - self._whitelist_map(self.outputs_map, output_labels, creator_returns), - "outputs", - ) - - @staticmethod - def _whitelist_map( - io_map: bidict, new_labels: tuple[str], has_channel_objects: tuple[HasChannel] - ) -> bidict: - """ - Update an IO map to give new labels to the channels of a bunch of :class:`HasChannel` - objects. - """ - io_map = bidict({}) if io_map is None else io_map - for new_label, ui_node in zip(new_labels, has_channel_objects): - # White-list everything not already in the map - if ui_node.channel.scoped_label not in io_map.keys(): - io_map[ui_node.channel.scoped_label] = new_label - return io_map - - def _hide_non_whitelisted_io( - self, io_map: bidict, i_or_o: Literal["inputs", "outputs"] - ) -> dict: - """ - Make a new map dictionary with `None` entries for each channel that isn't - already in the provided map bidict. I.e. blacklist things we didn't whitelist. - """ - io_map = dict(io_map) - # We do it in two steps like this to leverage the bidict security on the setter - # Since bidict can't handle getting `None` (i.e. disable) for multiple keys - for node in self.children.values(): - for channel in getattr(node, i_or_o): - if channel.scoped_label not in io_map.keys(): - io_map[channel.scoped_label] = None - return io_map - - def _get_linking_channel( - self, - child_reference_channel: InputData | OutputData, - composite_io_key: str, - ) -> InputData | OutputData: - """ - Build IO by value: create a new channel just like the child's channel. - - In the case of input data, we also form a value link from the composite channel - down to the child channel, so that the child will stay up-to-date. - """ - composite_channel = child_reference_channel.__class__( - label=composite_io_key, - owner=self, - default=child_reference_channel.default, - type_hint=child_reference_channel.type_hint, - ) - composite_channel.value = child_reference_channel.value - - if isinstance(composite_channel, InputData): - composite_channel.strict_hints = child_reference_channel.strict_hints - composite_channel.value_receiver = child_reference_channel - elif isinstance(composite_channel, OutputData): - child_reference_channel.value_receiver = composite_channel - else: - raise TypeError( - "This should not be an accessible state, please contact the developers" - ) - - return composite_channel + remaining_ui_nodes = list(ui_nodes) + for macro_input in self.inputs: + target_node = macro_input.value_receiver.owner + if ( + target_node in ui_nodes # Value link is a UI node + and target_node.channel.value_receiver is None # That doesn't forward + # its value directly to the output + and len(target_node.channel.connections) <= 1 # And isn't forked to + # multiple children + ): + if len(target_node.channel.connections) == 1: + macro_input.value_receiver = target_node.channel.connections[0] + self.remove_child(target_node) + remaining_ui_nodes.remove(target_node) + return tuple(remaining_ui_nodes) @property def inputs(self) -> Inputs: @@ -518,7 +379,7 @@ def _replace_connection( c if c is not old_connection else new_connection for c in channel ] - def _configure_graph_execution(self): + def _configure_graph_execution(self, ui_nodes): run_signals = self.disconnect_run() has_signals = len(run_signals) > 0 @@ -527,6 +388,10 @@ def _configure_graph_execution(self): if has_signals and has_starters: # Assume the user knows what they're doing self._reconnect_run(run_signals) + # Then put the UI upstream of the original starting nodes + for n in self.starting_nodes: + n << ui_nodes + self.starting_nodes = ui_nodes if len(ui_nodes) > 0 else self.starting_nodes elif not has_signals and not has_starters: # Automate construction of the execution graph self.set_run_signals_to_dag_execution() @@ -606,37 +471,123 @@ def __setstate__(self, state): self.children[child].outputs[child_out].value_receiver = self.outputs[out] -def macro_node(*output_labels, **node_class_kwargs): - """ - A decorator for dynamically creating macro classes from graph-creating functions. - - Decorates a function. - Returns a :class:`Macro` subclass whose name is the camel-case version of the - graph-creating function, and whose signature is modified to exclude this function - and provided kwargs. - - Optionally takes output labels as args in case the node function uses the - like-a-function interface to define its IO. (The number of output labels must match - number of channel-like objects returned by the graph creating function _exactly_.) - - Optionally takes any keyword arguments of :class:`Macro`. - """ - output_labels = None if len(output_labels) == 0 else output_labels - - def as_node(graph_creator: callable[[Macro, ...], Optional[tuple[HasChannel]]]): - return type( - graph_creator.__name__, - (Macro,), # Define parentage - { - "__init__": partialmethod( - Macro.__init__, - None, - output_labels=output_labels, - **node_class_kwargs, - ), - "graph_creator": staticmethod(graph_creator), - "__module__": graph_creator.__module__, - }, +@classfactory +def macro_node_factory( + graph_creator: callable, validate_output_labels: bool, /, *output_labels +): + return ( + graph_creator.__name__, + (Macro,), # Define parentage + { + "graph_creator": staticmethod(graph_creator), + "__module__": graph_creator.__module__, + "_output_labels": None if len(output_labels) == 0 else output_labels, + "_validate_output_labels": validate_output_labels, + }, + {}, + ) + + +def as_macro_node(*output_labels, validate_output_labels=True): + def decorator(node_function): + macro_node_factory.clear(node_function.__name__) # Force a fresh class + factory_made = macro_node_factory( + node_function, validate_output_labels, *output_labels ) - - return as_node + factory_made._class_returns_from_decorated_function = node_function + factory_made.preview_io() + return factory_made + + return decorator + + +def macro_node( + node_function, + *node_args, + output_labels=None, + validate_output_labels=True, + **node_kwargs, +): + if output_labels is None: + output_labels = () + elif isinstance(output_labels, str): + output_labels = (output_labels,) + macro_node_factory.clear(node_function.__name__) # Force a fresh class + factory_made = macro_node_factory( + node_function, validate_output_labels, *output_labels + ) + factory_made.preview_io() + return factory_made(*node_args, **node_kwargs) + + +# as_macro_node = decorated_node_decorator_factory( +# Macro, +# Macro.graph_creator, +# decorator_docstring_additions="The first argument in the wrapped function is " +# "`self`-like and will receive the macro instance " +# "itself, and thus is ignored in the IO.", +# ) +# +# +# def macro_node( +# graph_creator, +# label: Optional[str] = None, +# parent: Optional[Composite] = None, +# overwrite_save: bool = False, +# run_after_init: bool = False, +# storage_backend: Optional[Literal["h5io", "tinybase"]] = None, +# save_after_run: bool = False, +# strict_naming: bool = True, +# output_labels: Optional[str | list[str] | tuple[str]] = None, +# validate_output_labels: bool = True, +# **kwargs, +# ): +# """ +# Creates a new child of :class:`Macro` using the provided +# :func:`graph_creator` and returns an instance of that. +# +# Quacks like a :class:`Composite` for the sake of creating and registering nodes. +# +# Beyond the standard :class:`Macro`, initialization allows the args... +# +# Args: +# graph_creator (callable): The function defining macro's graph. +# output_labels (Optional[str | list[str] | tuple[str]]): A name for each return +# value of the node function OR a single label. (Default is None, which +# scrapes output labels automatically from the source code of the wrapped +# function.) This can be useful when returned values are not well named, e.g. +# to make the output channel dot-accessible if it would otherwise have a label +# that requires item-string-based access. Additionally, specifying a _single_ +# label for a wrapped function that returns a tuple of values ensures that a +# _single_ output channel (holding the tuple) is created, instead of one +# channel for each return value. The default approach of extracting labels +# from the function source code also requires that the function body contain +# _at most_ one `return` expression, so providing explicit labels can be used +# to circumvent this (at your own risk), or to circumvent un-inspectable +# source code (e.g. a function that exists only in memory). +# """ +# if not callable(graph_creator): +# # `function_node` quacks like a class, even though it's a function and +# # dynamically creates children of `Macro` by providing the necessary +# # callable to the decorator +# raise AttributeError( +# f"Expected `graph_creator` to be callable but got {graph_creator}" +# ) +# +# if output_labels is None: +# output_labels = () +# elif isinstance(output_labels, str): +# output_labels = (output_labels,) +# +# return as_macro_node(*output_labels, validate_output_labels=validate_output_labels)( +# graph_creator +# )( +# label=label, +# parent=parent, +# overwrite_save=overwrite_save, +# run_after_init=run_after_init, +# storage_backend=storage_backend, +# save_after_run=save_after_run, +# strict_naming=strict_naming, +# **kwargs, +# ) diff --git a/pyiron_workflow/meta.py b/pyiron_workflow/meta.py deleted file mode 100644 index 85e10587..00000000 --- a/pyiron_workflow/meta.py +++ /dev/null @@ -1,312 +0,0 @@ -""" -Meta nodes are callables that create a node class instead of a node instance. -""" - -from __future__ import annotations - -from typing import Optional - -from pyiron_workflow.function import ( - Function, - function_node, -) -from pyiron_workflow.macro import Macro, macro_node -from pyiron_workflow.node import Node - - -def list_to_output(length: int, **node_class_kwargs) -> type[Function]: - """ - A meta-node that returns a node class with :param:`length` input channels and - maps these to a single output channel with type `list`. - """ - - def _list_to_many(length: int): - template = f""" -def __list_to_many(l: list): - {"; ".join([f"out{i} = l[{i}]" for i in range(length)])} - return [{", ".join([f"out{i}" for i in range(length)])}] - """ - exec(template) - return locals()["__list_to_many"] - - return function_node(**node_class_kwargs)(_list_to_many(length=length)) - - -def input_to_list(length: int, **node_class_kwargs) -> type[Function]: - """ - A meta-node that returns a node class with :param:`length` output channels and - maps an input list to these. - """ - - def _many_to_list(length: int): - template = f""" -def __many_to_list({", ".join([f"inp{i}=None" for i in range(length)])}): - return [{", ".join([f"inp{i}" for i in range(length)])}] - """ - exec(template) - return locals()["__many_to_list"] - - return function_node(**node_class_kwargs)(_many_to_list(length=length)) - - -def for_loop( - loop_body_class: type[Node], - length: int, - iterate_on: str | tuple[str] | list[str], - # TODO: -) -> type[Macro]: - """ - An _extremely rough_ first draft of a for-loop meta-node. - - Takes a node class, how long the loop should be, and which input(s) of the provided - node class should be looped over (given as strings of the channel labels) and - builds a macro that - - - Makes copies of the provided node class, i.e. the "body node" - - For each input channel specified to "loop over", creates a list-to-many node and - connects each of its outputs to their respective body node inputs - - For all other inputs, makes a 1:1 node and connects its output to _all_ of the - body nodes - - Relables the macro IO to match the passed node class IO so that list-ified IO - (i.e. the specified input and all output) is all caps - - Examples: - - >>> from pyiron_workflow import Workflow - >>> from pyiron_workflow.meta import for_loop - >>> - >>> @Workflow.wrap_as.function_node("div") - ... def Divide(numerator, denominator): - ... return numerator / denominator - >>> - >>> denominators = list(range(1, 5)) - >>> bulk_loop = Workflow.create.meta.for_loop( - ... Divide, - ... len(denominators), - ... iterate_on = ("denominator",), - ... )() - >>> bulk_loop.inputs.numerator = 1 - >>> bulk_loop.inputs.DENOMINATOR = denominators - >>> bulk_loop().DIV - [1.0, 0.5, 0.3333333333333333, 0.25] - - TODO: - - - Refactor like crazy, it's super hard to read and some stuff is too hard-coded - - Give some sort of access to flow control?? - - How to handle passing executors to the children? Maybe this is more - generically a Macro question? - - Is it possible to somehow dynamically adapt the held graph depending on the - length of the input values being iterated over? Tricky to keep IO well defined - - Allow a different mode, or make a different meta node, that makes all possible - pairs of body nodes given the input being looped over instead of just :param:`length` - - Provide enter and exit magic methods so we can `for` or `with` this fancy-like - """ - iterate_on = [iterate_on] if isinstance(iterate_on, str) else iterate_on - - def make_loop(macro): - macro.inputs_map = {} - macro.outputs_map = {} - body_nodes = [] - - # Parallelize over body nodes - for n in range(length): - body_nodes.append( - macro.add_child( - loop_body_class(label=f"{loop_body_class.__name__}_{n}") - ) - ) - - # Make input interface - for label, inp in body_nodes[0].inputs.items(): - # Don't rely on inp.label directly, since inputs may be a Composite IO - # panel that has a different key for this input channel than its label - - # Scatter a list of inputs to each node separately - if label in iterate_on: - interface = list_to_output(length)( - parent=macro, - label=label.upper(), - output_labels=[ - f"{loop_body_class.__name__}__{inp.label}_{i}" - for i in range(length) - ], - l=[inp.default] * length, - ) - # Connect each body node input to the input interface's respective output - for body_node, out in zip(body_nodes, interface.outputs): - body_node.inputs[label] = out - macro.inputs_map[f"{interface.label}__l"] = interface.label - # TODO: Don't hardcode __l - # Or distribute the same input to each node equally - else: - interface = macro.create.standard.UserInput( - label=label, - output_labels=label, - user_input=inp.default, - parent=macro, - ) - for body_node in body_nodes: - body_node.inputs[label] = interface - macro.inputs_map[f"{interface.label}__user_input"] = interface.label - # TODO: Don't hardcode __user_input - - # Make output interface: outputs to lists - for label, out in body_nodes[0].outputs.items(): - interface = input_to_list(length)( - parent=macro, - label=label.upper(), - output_labels=f"{loop_body_class.__name__}__{label}", - ) - # Connect each body node output to the output interface's respective input - for body_node, inp in zip(body_nodes, interface.inputs): - inp.connect(body_node.outputs[label]) - if body_node.executor: - raise NotImplementedError( - "Right now the output interface gets run after each body node," - "if the body nodes can run asynchronously we need something " - "more clever than that!" - ) - macro.outputs_map[ - f"{interface.label}__{loop_body_class.__name__}__{label}" - ] = interface.label - # TODO: Don't manually copy the output label construction - - return macro_node()(make_loop) - - -def while_loop( - loop_body_class: type[Node], - condition_class: type[Function], - internal_connection_map: dict[str, str], - inputs_map: Optional[dict[str, str]] = None, - outputs_map: Optional[dict[str, str]] = None, -) -> type[Macro]: - """ - An _extremely rough_ first draft of a for-loop meta-node. - - Takes body and condition node classes and builds a macro that makes a cyclic signal - connection between them and an "if" switch, i.e. when the body node finishes it - runs the condtion, which runs the switch, and as long as the condition result was - `True`, the switch loops back to run the body again. - We additionally allow four-tuples of (input node, input channel, output node, - output channel) labels to wire data connections inside the macro, e.g. to pass data - from the body to the condition. This is beastly syntax, but it will suffice for now. - Finally, you can set input and output maps as normal. - - Args: - loop_body_class (type[pyiron_workflow.node.Node]): The class for the - body of the while-loop. - condition_class (type[pyiron_workflow.function.Function]): A single-output - function node returning a `bool` controlling the while loop exit condition - (exits on False) - internal_connection_map (list[tuple[str, str, str, str]]): String tuples - giving (input node, input channel, output node, output channel) labels - connecting channel pairs inside the macro. - inputs_map Optional[dict[str, str]]: The inputs map as usual for a macro. - outputs_map Optional[dict[str, str]]: The outputs map as usual for a macro. - - Examples: - - >>> from pyiron_workflow import Workflow - >>> - >>> @Workflow.wrap_as.function_node() - ... def Add(a, b): - ... print(f"{a} + {b} = {a + b}") - ... return a + b - >>> - >>> @Workflow.wrap_as.function_node() - ... def LessThanTen(value): - ... return value < 10 - >>> - >>> AddWhile = Workflow.create.meta.while_loop( - ... loop_body_class=Add, - ... condition_class=Workflow.create.standard.LessThan, - ... internal_connection_map=[ - ... ("Add", "a + b", "LessThan", "obj"), - ... ("Add", "a + b", "Add", "a") - ... ], - ... inputs_map={"Add__a": "a", "Add__b": "b", "LessThan__other": "cap"}, - ... outputs_map={"Add__a + b": "total"} - ... ) - >>> - >>> wf = Workflow("do_while") - >>> wf.add_while = AddWhile(cap=10) - >>> - >>> wf.inputs_map = { - ... "add_while__a": "a", - ... "add_while__b": "b" - ... } - >>> wf.outputs_map = {"add_while__total": "total"} - >>> - >>> print(f"Finally, {wf(a=1, b=2).total}") - 1 + 2 = 3 - 3 + 2 = 5 - 5 + 2 = 7 - 7 + 2 = 9 - 9 + 2 = 11 - Finally, 11 - - >>> import random - >>> - >>> from pyiron_workflow import Workflow - >>> - >>> random.seed(0) - >>> - >>> @Workflow.wrap_as.function_node("random") - ... def RandomFloat(): - ... return random.random() - >>> - >>> @Workflow.wrap_as.function_node() - ... def GreaterThan(x: float, threshold: float): - ... gt = x > threshold - ... symbol = ">" if gt else "<=" - ... print(f"{x:.3f} {symbol} {threshold}") - ... return gt - >>> - >>> RandomWhile = Workflow.create.meta.while_loop( - ... loop_body_class=RandomFloat, - ... condition_class=GreaterThan, - ... internal_connection_map=[("RandomFloat", "random", "GreaterThan", "x")], - ... outputs_map={"RandomFloat__random": "capped_result"} - ... ) - >>> - >>> # Define workflow - >>> - >>> wf = Workflow("random_until_small_enough") - >>> - >>> ## Wire together the while loop and its condition - >>> - >>> wf.random_while = RandomWhile() - >>> - >>> ## Give convenient labels - >>> wf.inputs_map = {"random_while__GreaterThan__threshold": "threshold"} - >>> wf.outputs_map = {"random_while__capped_result": "capped_result"} - >>> - >>> # Set a threshold and run - >>> print(f"Finally {wf(threshold=0.3).capped_result:.3f}") - 0.844 > 0.3 - 0.758 > 0.3 - 0.421 > 0.3 - 0.259 <= 0.3 - Finally 0.259 - """ - - def make_loop(macro): - body_node = macro.add_child(loop_body_class(label=loop_body_class.__name__)) - condition_node = macro.add_child( - condition_class(label=condition_class.__name__) - ) - switch = macro.create.standard.If(label="switch", parent=macro) - - switch.inputs.condition = condition_node - for out_n, out_c, in_n, in_c in internal_connection_map: - macro.children[in_n].inputs[in_c] = macro.children[out_n].outputs[out_c] - - switch.signals.output.true >> body_node >> condition_node >> switch - macro.starting_nodes = [body_node] - - macro.inputs_map = {} if inputs_map is None else inputs_map - macro.outputs_map = {} if outputs_map is None else outputs_map - - return macro_node()(make_loop) diff --git a/pyiron_workflow/node.py b/pyiron_workflow/node.py index 08561920..ce5424a4 100644 --- a/pyiron_workflow/node.py +++ b/pyiron_workflow/node.py @@ -10,7 +10,6 @@ import sys from abc import ABC from concurrent.futures import Future -import platform from typing import Any, Literal, Optional, TYPE_CHECKING import warnings @@ -26,7 +25,6 @@ set_run_connections_according_to_linear_dag, ) from pyiron_workflow.snippets.colors import SeabornColors -from pyiron_workflow.snippets.has_post import AbstractHasPost from pyiron_workflow.working import HasWorkingDirectory if TYPE_CHECKING: @@ -48,7 +46,6 @@ class Node( HasH5ioStorage, HasTinybaseStorage, ABC, - metaclass=AbstractHasPost, ): """ Nodes are elements of a computational graph. @@ -111,36 +108,40 @@ class Node( the python process working directory - Nodes can run their computation using remote resources by setting an executor - Any executor must have a :meth:`submit` method with the same interface as - :class:`concurrent.futures.Executor`, must return a :class:`concurrent.futures.Future` - (or child thereof) object, and must be able to serialize dynamically - defined objects + :class:`concurrent.futures.Executor`, must return a + :class:`concurrent.futures.Future` (or child thereof) object. + - Standard available nodes are pickleable and work with + `concurrent.futures.ProcessPoolExecutor`, but if you define your node + somewhere that it can't be imported (e.g. `__main__` in a jupyter + notebook), or it is otherwise not pickleable (e.g. it holds un-pickleable + io data), you will need a more powerful executor, e.g. `pympipool.Executor`. - On executing this way, a futures object will be returned instead of the usual result, this future will also be stored as an attribute, and a callback will be registered with the executor - Post-execution processing -- e.g. updating output and firing signals -- will not occur until the futures object is finished and the callback fires. - - WARNING: Executors are currently only working when the node executable - function does not use `self` - NOTE: Executors are only allowed in a "push" paradigm, and you will get an exception if you try to :meth:`pull` and one of the upstream nodes uses an executor - - NOTE: Don't forget to :meth:`shutdown` any created executors outside of a `with` - context when you're done with them; we give a convenience method for this. + - NOTE: Don't forget to :meth:`shutdown` any created executors outside of a + `with` context when you're done with them; we give a convenience method for + this. - Nodes created from a registered package store their package identifier as a class attribute. - [ALPHA FEATURE] Nodes can be saved to and loaded from file if python >= 3.11. + - As long as you haven't put anything unpickleable on them, or defined them in + an unpicklable place (e.g. in the `` of another function), you can + simple (un)pickle nodes. There is no save/load interface for this right + now, just import pickle and do it. - Saving is triggered manually, or by setting a flag to save after the nodes runs. - - On instantiation, nodes will load automatically if they find saved content. + - At the end of instantiation, nodes will load automatically if they find saved + content. - Discovered content can instead be deleted with a kwarg. - You can't load saved content _and_ run after instantiation at once. - - The nodes must be somewhere importable, and the imported object must match - the type of the node being saved. This basically just rules out one edge - case where a node class is defined like - `SomeFunctionNode = Workflow.wrap_as.function_node()(some_function)`, since - then the new class gets the name `some_function`, which when imported is - the _function_ "some_function" and not the desired class "SomeFunctionNode". - This is checked for at save-time and will cause a nice early failure. + - The nodes must be defined somewhere importable, i.e. in a module, `__main__`, + and as a class property are all fine, but, e.g., inside the `` of + another function is not. - [ALPHA ISSUE] If the source code (cells, `.py` files...) for a saved graph is altered between saving and loading the graph, there are no guarantees about the loaded state; depending on the nature of the changes everything may @@ -155,9 +156,14 @@ class Node( the entire graph may be saved at once. - [ALPHA ISSUE] There are two possible back-ends for saving: one leaning on `tinybase.storage.GenericStorage` (in practice, - `H5ioStorage(GenericStorage)`), and the other, default back-end that uses - the `h5io` module directly. The backend used is always the one on the graph - root. + `H5ioStorage(GenericStorage)`), that is the default, and the other that + uses the `h5io` module directly. The backend used is always the one on the + graph root. + - [ALPHA ISSUE] The `h5io` backend is deprecated -- it can't handle custom + reconstructors (i.e. when `__reduce__` returns a tuple with some + non-standard callable as its first entry), and basically all our nodes do + that now! `tinybase` gets around this by falling back on `cloudpickle` when + its own interactions with `h5io` fail. - [ALPHA ISSUE] Restrictions on data: - For the `h5io` backend: Most data that can be pickled will be fine, but some classes will hit an edge case and throw an exception from `h5io` @@ -276,6 +282,22 @@ class Node( execution options are available as boolean flags. set_input_values: Allows input channels' values to be updated without any running. + + Note: + :meth:`__init__` ends with a routine :meth:`_after_node_setup` that may, + depending on instantiation arguments, try to actually execute the node. Since + child classes may need to get things done before this point, we want to make + sure that this happens _after_ all the other setup. This can be accomplished + by children (a) sticking stuff that is independent of `super().__init__` calls + before the super call, and (b) overriding :meth:`_setup_node(self)` to do any + remaining, parameter-free setup. This latter function gets called prior to any + execution. + + Initialization will also try to parse any outstanding `args` and `kwargs` as + input to the node's input channels. For node class developers, that means it's + also important that `Node` parentage appear to the right-most of the + inheritance set in the class definition, so that it's invokation of `__init__` + appears as late as possible with the minimal set of args and kwargs. """ package_identifier = None @@ -285,8 +307,8 @@ class Node( def __init__( self, - label: str, *args, + label: Optional[str] = None, parent: Optional[Composite] = None, overwrite_save: bool = False, run_after_init: bool = False, @@ -295,27 +317,43 @@ def __init__( **kwargs, ): """ - A mixin class for objects that can form nodes in the graph representation of a + A parent class for objects that can form nodes in the graph representation of a computational workflow. Args: label (str): A name for this node. - *args: Arguments passed on with `super`. + *args: Interpreted as node input data, in order of input channels. parent: (Composite|None): The composite node that owns this as a child. run_after_init (bool): Whether to run at the end of initialization. - **kwargs: Keyword arguments passed on with `super`. + **kwargs: Interpreted as node input data, with keys corresponding to + channel labels. """ super().__init__( - *args, - label=label, + label=self.__class__.__name__ if label is None else label, parent=parent, storage_backend=storage_backend, - **kwargs, ) self.save_after_run = save_after_run self._user_data = {} # A place for power-users to bypass node-injection - def __post__( + self._setup_node() + self._after_node_setup( + *args, + overwrite_save=overwrite_save, + run_after_init=run_after_init, + **kwargs, + ) + + def _setup_node(self) -> None: + """ + Called _before_ :meth:`Node.__init__` finishes. + + Child node classes can use this for any parameter-free node setup that should + happen _before_ :meth:`Node._after_node_setup` gets called. + """ + pass + + def _after_node_setup( self, *args, overwrite_save: bool = False, @@ -341,12 +379,15 @@ def __post__( f"`overwrite_save=True`)" ) self.load() + self.set_input_values(*args, **kwargs) elif run_after_init: try: + self.set_input_values(*args, **kwargs) self.run() except ReadinessError: pass - # Else neither loading nor running now -- no action required! + else: + self.set_input_values(*args, **kwargs) self.graph_root.tidy_working_directory() @property @@ -383,6 +424,7 @@ def _readiness_error_message(self) -> str: def run( self, + *args, run_data_tree: bool = False, run_parent_trees_too: bool = False, fetch_input: bool = True, @@ -413,7 +455,10 @@ def run( run_parent_trees_too (bool): Whether to recursively run the data tree in parent nodes (if any). (Default is False.) fetch_input (bool): Whether to first update inputs with the - highest-priority connections holding data. (Default is True.) + highest-priority connections holding data (i.e. the first valid + connection; and the most recently formed connections appear first + unless the connections list has been manually tampered with). (Default + is True.) check_readiness (bool): Whether to raise an exception if the node is not :attr:`ready` to run after fetching new input. (Default is True.) force_local_execution (bool): Whether to ignore any executor settings and @@ -435,7 +480,7 @@ def run( Kwargs updating input channel values happens _first_ and will get overwritten by any subsequent graph-based data manipulation. """ - self.set_input_values(**kwargs) + self.set_input_values(*args, **kwargs) if run_data_tree: self.run_data_tree(run_parent_trees_too=run_parent_trees_too) @@ -443,6 +488,9 @@ def run( if fetch_input: self.inputs.fetch() + if self.parent is not None: + self.parent.register_child_starting(self) + return super().run( check_readiness=check_readiness, force_local_execution=force_local_execution, @@ -491,7 +539,7 @@ def run_data_tree(self, run_parent_trees_too=False) -> None: disconnected_pairs, starters = set_run_connections_according_to_linear_dag( nodes ) - starter = starters[0] + data_tree_starters = list(set(starters).intersection(data_tree_nodes)) except Exception as e: # If the dag setup fails it will repair any connections it breaks before # raising the error, but we still need to repair our label changes @@ -499,14 +547,48 @@ def run_data_tree(self, run_parent_trees_too=False) -> None: node.label = label_map[modified_label] raise e - self.signals.disconnect_run() - # Don't let anything upstream trigger this node - try: - # If you're the only one in the data tree, there's nothing upstream to run - # Otherwise... - if starter is not self: - starter.run() # Now push from the top + parent_starting_nodes = ( + self.parent.starting_nodes if self.parent is not None else None + ) # We need these for state recovery later, even if we crash + + if len(data_tree_starters) == 1 and data_tree_starters[0] is self: + # If you're the only one in the data tree, there's nothing upstream to + # run. + pass + else: + for node in set(nodes.values()).difference(data_tree_nodes): + # Disconnect any nodes not in the data tree to avoid unnecessary + # execution + node.signals.disconnect_run() + + self.signals.disconnect_run() + # Don't let anything upstream trigger _this_ node + + if self.parent is None: + for starter in data_tree_starters: + starter.run() # Now push from the top + else: + # Run the special exec connections from above with the parent + + # Workflow parents will attempt to automate execution on run, + # undoing all our careful execution + # This heinous hack breaks in and stops that behaviour + # I recognize this is dirty, but let's be pragmatic about getting + # the features playing together. Workflows and pull are anyhow + # already both very annoying on their own... + from pyiron_workflow.workflow import Workflow + + if isinstance(self.parent, Workflow): + automated = self.parent.automate_execution + self.parent.automate_execution = False + + self.parent.starting_nodes = data_tree_starters + self.parent.run() + + # And revert our workflow hack + if isinstance(self.parent, Workflow): + self.parent.automate_execution = automated finally: # No matter what, restore the original connections and labels afterwards for modified_label, node in nodes.items(): @@ -514,17 +596,25 @@ def run_data_tree(self, run_parent_trees_too=False) -> None: node.signals.disconnect_run() for c1, c2 in disconnected_pairs: c1.connect(c2) + if self.parent is not None: + self.parent.starting_nodes = parent_starting_nodes def _finish_run(self, run_output: tuple | Future) -> Any | tuple: try: - return super()._finish_run(run_output=run_output) + processed_output = super()._finish_run(run_output=run_output) + if self.parent is not None: + self.parent.register_child_finished(self) + return processed_output finally: if self.save_after_run: self.save() def _finish_run_and_emit_ran(self, run_output: tuple | Future) -> Any | tuple: processed_output = self._finish_run(run_output) - self.signals.output.ran() + if self.parent is None: + self.signals.output.ran() + else: + self.parent.register_child_emitting_ran(self) return processed_output _finish_run_and_emit_ran.__doc__ = ( @@ -535,7 +625,7 @@ def _finish_run_and_emit_ran(self, run_output: tuple | Future) -> Any | tuple: """ ) - def execute(self, **kwargs): + def execute(self, *args, **kwargs): """ A shortcut for :meth:`run` with particular flags. @@ -546,6 +636,7 @@ def execute(self, **kwargs): right here, right now, and as-is. """ return self.run( + *args, run_data_tree=False, run_parent_trees_too=False, fetch_input=False, @@ -555,7 +646,7 @@ def execute(self, **kwargs): **kwargs, ) - def pull(self, run_parent_trees_too=False, **kwargs): + def pull(self, *args, run_parent_trees_too=False, **kwargs): """ A shortcut for :meth:`run` with particular flags. @@ -569,6 +660,7 @@ def pull(self, run_parent_trees_too=False, **kwargs): first pull. """ return self.run( + *args, run_data_tree=True, run_parent_trees_too=run_parent_trees_too, fetch_input=True, @@ -578,12 +670,12 @@ def pull(self, run_parent_trees_too=False, **kwargs): **kwargs, ) - def __call__(self, **kwargs) -> None: + def __call__(self, *args, **kwargs) -> None: """ A shortcut for :meth:`pull` that automatically runs the entire set of upstream data dependencies all the way to the parent-most graph object. """ - return self.pull(run_parent_trees_too=True, **kwargs) + return self.pull(*args, run_parent_trees_too=True, **kwargs) @property def ready(self) -> bool: @@ -647,16 +739,8 @@ def draw( Returns: (graphviz.graphs.Digraph): The resulting graph object. - Warnings: - Rendering a PDF format appears to not be working on Windows right now. """ - if format == "pdf" and platform.system() == "Windows": - warnings.warn( - "Graphviz does not appear to be playing well with Windows right now," - "this will probably fail and you will need to try a different format." - "If it _doesn't_ fail, please contact the developers by raising an " - "issue at github.com/pyiron/pyiron_workflow" - ) + if size is not None: size = f"{size[0]},{size[1]}" graph = GraphvizNode(self, depth=depth, rankdir=rankdir, size=size).graph @@ -737,185 +821,3 @@ def from_storage(self, storage): data_outputs = storage["outputs"] for label in data_outputs.list_groups(): self.outputs[label].from_storage(data_outputs[label]) - - - def iter_old(self, max_workers=1, cores_per_worker=1, executor=None, **kwargs): - from pympipool import Executor - import pandas as pd - - # Get the keys and lists from kwargs - keys = list(kwargs.keys()) - lists = list(kwargs.values()) - # print ('lists: ', lists) - - # Get the number of dimensions - num_dimensions = len(keys) - - # Get the length of each list - lengths = [len(lst) for lst in lists] - # print ('lengths: ', lengths, num_dimensions) - - # Initialize indices - indices = [0] * num_dimensions - - with Executor(cores_per_worker=cores_per_worker, max_workers=max_workers) as p: - # iter_dict = {'kwargs': kwargs} - iter_dict = {} - - # Create an empty dictionary to store the results - dict_lst = {} - # df_result = pd.DataFrame(columns=keys) - - # Perform multidimensional for loop - count = 0 - while indices[0] < lengths[0]: - # print (f'iter: indices {indices}, {lengths[0]} {count}') - # Access the current elements using indices - current_elements = [lists[i][indices[i]] for i in range(num_dimensions)] - - # Add current_elements as a dictionary - current_elements_kwarg = dict(zip(keys, current_elements)) - # self._iter_index = indices[i] - - # the following construct is used to get workflow related info via the _internal - # argument of the node function into the function body (where the workflow object - # is not accessible) - _internal = {} - _internal["iter_index"] = indices.copy() - current_elements_kwarg["_internal"] = _internal - - if executor is None: - out = self(**current_elements_kwarg) - else: - fs = p.submit(self, **current_elements_kwarg) - out = fs.result() - iter_dict[count] = out - count += 1 - - if hasattr(out, "items"): - for k, v in out.items(): - current_elements_kwarg[k] = v - else: - current_elements_kwarg[self.label] = out - - # Append the current_elements_kwarg to the dictionary - for k, v in current_elements_kwarg.items(): - if count == 1: - dict_lst[k] = [v] - else: - if k in dict_lst: - dict_lst[k].append(v) - else: - ValueError(f"New key appears at count {count}") - - # Update indices for the next iteration - indices[num_dimensions - 1] += 1 - # print ('indices: ', indices) - - # Update indices and carry-over if needed - for i in range(num_dimensions - 1, 0, -1): - # print ('dimensions: ', i, indices[i], lengths[i]) - if indices[i] == lengths[i]: - indices[i] = 0 - indices[i - 1] += 1 - - return pd.DataFrame(dict_lst) - - def iter(self, max_workers=1, executor=None, **kwargs): - from concurrent.futures import ThreadPoolExecutor, as_completed - import pandas as pd - - futures = [] - future_index_map = {} - out = [] - out_index = [] - - refs = to_list_of_kwargs(**kwargs) - df_refs = pd.DataFrame(refs) - # print("iter_refs: ", refs) - if max_workers < 2: - executor = None - else: - executor = max_workers - - print("max_workers: ", max_workers) - if executor is None: - for i, ref in enumerate(refs): - out.append(self(**ref)) - out_index.append(i) - else: - with ThreadPoolExecutor(max_workers=max_workers) as p: - for i, ref in enumerate(refs): - # use the class rather than the instance -> (type(self)) - future = p.submit(func, type(self), **ref) - future_index_map[future] = i - futures.append(future) - - for future in as_completed(futures): - out.append(future.result()) - out_index.append(future_index_map[future]) - - if len(out) > 0: - if not hasattr(out[0], "items"): - print("iter: add label") - df_dict = {self.label: out} - else: - df_dict = {} - for i, row in enumerate(out): - for key, value in row.items(): - if i == 0: - df_dict[key] = [value] - else: - df_dict[key].append(value) - - df_out = pd.DataFrame(df_dict) - - # try: - # df_out = pd.DataFrame(out, index=out_index).sort_index() - # - # except: - # print("iter out: ", out) - # return out - - return pd.concat([df_refs, df_out], axis=1) - - -def to_list_of_kwargs(**kwargs): - keys = list(kwargs.keys()) - lists = list(kwargs.values()) - - # Get the number of dimensions - num_dimensions = len(keys) - - # Get the length of each list - lengths = [len(lst) for lst in lists] - - # Initialize indices - indices = [0] * num_dimensions - - kwargs_list = [] - - # Perform multidimensional for loop - while indices[0] < lengths[0]: - # Access the current elements using indices - current_elements = [lists[i][indices[i]] for i in range(num_dimensions)] - - # Add current_elements as a dictionary - current_elements_kwarg = dict(zip(keys, current_elements)) - kwargs_list.append(current_elements_kwarg) - - # Update indices for the next iteration - indices[num_dimensions - 1] += 1 - - # Update indices and carry-over if needed - for i in range(num_dimensions - 1, 0, -1): - if indices[i] == lengths[i]: - indices[i] = 0 - indices[i - 1] += 1 - - return kwargs_list - - -def func(node, **kwargs): - # print("func (node): ", node, kwargs) - return node(**kwargs).run() \ No newline at end of file diff --git a/pyiron_workflow/node_library/atomistic/calculator/ase.py b/pyiron_workflow/node_library/atomistic/calculator/ase.py index 8b4090de..6714275b 100644 --- a/pyiron_workflow/node_library/atomistic/calculator/ase.py +++ b/pyiron_workflow/node_library/atomistic/calculator/ase.py @@ -1,8 +1,8 @@ -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node -@function_node() -def static(atoms=None, engine=None, _internal=None, keys_to_store=None): +@as_function_node("out") +def Static(atoms, engine=None, keys_to_store=None): from pyiron_workflow.node_library.atomistic.calculator.data import OutputCalcStatic if engine is None: @@ -17,15 +17,11 @@ def static(atoms=None, engine=None, _internal=None, keys_to_store=None): out.energy = atoms.get_potential_energy() out.forces = atoms.get_forces() - if _internal is not None: - out["iter_index"] = _internal[ - "iter_index" - ] # TODO: move _internal argument to decorator class return out.select(keys_to_store) -@function_node("structure", "out") -def minimize(atoms=None, engine=None, fmax=0.005, log_file="tmp.log"): +@as_function_node("structure", "out") +def Minimize(atoms, engine=None, fmax=0.005, log_file="tmp.log"): from ase.optimize import BFGS from pyiron_workflow.node_library.atomistic.calculator.data import ( OutputCalcMinimize, @@ -58,13 +54,13 @@ def minimize(atoms=None, engine=None, fmax=0.005, log_file="tmp.log"): out.final.forces = atoms_relaxed.get_forces() out.final.energy = atoms_relaxed.get_potential_energy() out.initial.energy = atoms.get_potential_energy() - print("energy: ", out.final.energy, out.initial.energy) + # print("energy: ", out.final.energy, out.initial.energy) # print("energy: ", out["energy"], "max_force: ", np.min(np.abs(out["forces"]))) return atoms_relaxed, out nodes = [ - static, - minimize, + Static, + Minimize, ] diff --git a/pyiron_workflow/node_library/atomistic/calculator/generic.py b/pyiron_workflow/node_library/atomistic/calculator/generic.py index 64639429..be9881e4 100644 --- a/pyiron_workflow/node_library/atomistic/calculator/generic.py +++ b/pyiron_workflow/node_library/atomistic/calculator/generic.py @@ -1,4 +1,4 @@ -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node from pyiron_workflow.node_library.atomistic.calculator.data import ( InputCalcMinimize, @@ -7,8 +7,8 @@ ) -@function_node("generic") -def static(structure=None, engine=None): # , keys_to_store=None): +@as_function_node("generic") +def Static(structure=None, engine=None): # , keys_to_store=None): output = engine( structure=structure, calculator=InputCalcStatic(), # keys_to_store=keys_to_store) @@ -16,4 +16,4 @@ def static(structure=None, engine=None): # , keys_to_store=None): return output.generic -nodes = [static] +nodes = [Static] diff --git a/pyiron_workflow/node_library/atomistic/engine/ase.py b/pyiron_workflow/node_library/atomistic/engine/ase.py index 1566bbeb..17602700 100644 --- a/pyiron_workflow/node_library/atomistic/engine/ase.py +++ b/pyiron_workflow/node_library/atomistic/engine/ase.py @@ -1,14 +1,14 @@ -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node -@function_node("engine") +@as_function_node("engine") def EMT(): from ase.calculators.emt import EMT return EMT() -@function_node("engine") +@as_function_node("engine") def M3GNet(): import matgl from matgl.ext.ase import M3GNetCalculator diff --git a/pyiron_workflow/node_library/atomistic/engine/lammps.py b/pyiron_workflow/node_library/atomistic/engine/lammps.py index 3f088b3c..5fb0cd15 100644 --- a/pyiron_workflow/node_library/atomistic/engine/lammps.py +++ b/pyiron_workflow/node_library/atomistic/engine/lammps.py @@ -5,7 +5,7 @@ # from pyiron_atomistics.atomistics.structure.atoms import Atoms -from pyiron_workflow.function import function_node, function_node +from pyiron_workflow.function import as_function_node from pyiron_workflow.workflow import Workflow from pyiron_workflow.node_library.atomistic.calculator.data import ( @@ -20,7 +20,7 @@ from pyiron_atomistics.lammps.control import LammpsControl -@function_node("calculator") +@as_function_node("calculator") def Calc(parameters): from pyiron_atomistics.lammps.control import LammpsControl @@ -41,7 +41,7 @@ def Calc(parameters): return calculator -@function_node("calculator") +@as_function_node("calculator") def CalcStatic(calculator_input: Optional[InputCalcStatic | dict] = None): calculator_kwargs = parse_input_kwargs(calculator_input, InputCalcStatic) calculator = LammpsControl() @@ -51,7 +51,7 @@ def CalcStatic(calculator_input: Optional[InputCalcStatic | dict] = None): return calculator -@function_node("calculator") +@as_function_node("calculator") def CalcMinimize(calculator_input: Optional[InputCalcMinimize | dict] = None): calculator_kwargs = parse_input_kwargs(calculator_input, InputCalcMinimize) calculator = LammpsControl() @@ -61,7 +61,7 @@ def CalcMinimize(calculator_input: Optional[InputCalcMinimize | dict] = None): return calculator -@function_node("calculator") +@as_function_node("calculator") def CalcMD(calculator_input: Optional[InputCalcMD | dict] = None): calculator_kwargs = parse_input_kwargs(calculator_input, InputCalcMD) calculator = LammpsControl() @@ -71,14 +71,20 @@ def CalcMD(calculator_input: Optional[InputCalcMD | dict] = None): return calculator -# @Workflow.wrap_as.function_node("path", "calc_mode", "bla") -@Workflow.wrap_as.function_node("path", "bla") -def InitLammps(structure=None, potential=None, calculator=None, working_directory=None): +@Workflow.wrap.as_function_node("path") +def InitLammps(working_directory, structure=None, potential=None, calculator=None): + + from pathlib import Path + + Path(working_directory).mkdir(parents=True, exist_ok=True) + # When this is a child node, it's running into trouble with it's parent's + # `Node._after_node_setup: self.graph_root.tidy_working_directory()` call + # which deletes the directory even though the path is A-OK. + # Just make the input mandatory for the node, and ensure the directory is there + import os from pyiron_atomistics.lammps.potential import LammpsPotential, LammpsPotentialFile - assert os.path.isdir(working_directory), "working directory missing" - pot = LammpsPotential() pot.df = LammpsPotentialFile().find_by_name(potential) pot.write_file(file_name="potential.inp", cwd=working_directory) @@ -88,14 +94,11 @@ def InitLammps(structure=None, potential=None, calculator=None, working_director structure.write(f, format="lammps-data", specorder=pot.get_element_lst()) calculator.write_file(file_name="control.inp", cwd=working_directory) - bla = "bla" - # print("Lammps_init: ", calculator.mode, bla) - # return os.path.abspath(working_directory), calculator.mode, bla - return os.path.abspath(working_directory), bla + return os.path.abspath(working_directory) -@function_node("log") +@as_function_node("log") def ParseLogFile(log_file): from pymatgen.io.lammps.outputs import parse_lammps_log @@ -107,7 +110,7 @@ def ParseLogFile(log_file): return log -@function_node("dump") +@as_function_node("dump") def ParseDumpFile(dump_file): from pymatgen.io.lammps.outputs import parse_lammps_dumps @@ -124,7 +127,7 @@ class ShellOutput: log: FileObject = FileObject() -@function_node("output", "dump", "log") +@as_function_node("output", "dump", "log") def Shell( command: str, environment: Optional[dict] = None, @@ -171,12 +174,11 @@ class GenericOutput: forces = [] -@function_node() +@as_function_node() def Collect( out_dump, out_log, calc_mode: str | LammpsControl | InputCalcMinimize | InputCalcMD | InputCalcStatic, - bla="", ): import numpy as np @@ -186,7 +188,6 @@ def Collect( OutputCalcMD, ) - print("Collect: ", calc_mode, bla) log = out_log[0] if isinstance(calc_mode, str) and calc_mode in ["static", "minimize", "md"]: @@ -217,7 +218,7 @@ def Collect( return generic -@function_node("potential") +@as_function_node("potential") def Potential(structure, name=None, index=0): from pyiron_atomistics.lammps.potential import list_potentials as lp @@ -232,7 +233,7 @@ def Potential(structure, name=None, index=0): return pot -@function_node("potentials") +@as_function_node("potentials") def ListPotentials(structure): from pyiron_atomistics.lammps.potential import list_potentials as lp @@ -249,7 +250,7 @@ def get_calculators(): return calc_dict -from pyiron_workflow.macro import macro_node +from pyiron_workflow.macro import as_macro_node # from pyiron_workflow.node_library.atomistic.engine.lammps import get_calculators # from pyiron_workflow.node_library.dev_tools import set_replacer @@ -257,54 +258,52 @@ def get_calculators(): from ase import Atoms -@macro_node("generic") +@as_macro_node("generic") def Code( wf, - structure=Atoms(), + structure=Atoms(), # TODO: No mutable defaults calculator=InputCalcStatic(), potential=None, ): from pyiron_contrib.tinybase.shell import ExecutablePathResolver - print("Lammps: ", structure) - wf.Potential = wf.create.atomistic.engine.lammps.Potential( + wf.potential_object = wf.create.atomistic.engine.lammps.Potential( structure=structure, name=potential ) - wf.ListPotentials = wf.create.atomistic.engine.lammps.ListPotentials( + wf.list_potentials = wf.create.atomistic.engine.lammps.ListPotentials( structure=structure ) wf.calc = wf.create.atomistic.engine.lammps.Calc(calculator) - wf.InitLammps = wf.create.atomistic.engine.lammps.InitLammps( + wf.init_lammps = wf.create.atomistic.engine.lammps.InitLammps( structure=structure, - potential=wf.Potential, + potential=wf.potential_object, calculator=wf.calc, # working_directory="test2", ) - wf.InitLammps.inputs.working_directory = ( - wf.InitLammps.working_directory.path.__str__() + wf.init_lammps.inputs.working_directory = ( + wf.init_lammps.working_directory.path.resolve().__str__() ) - wf.Shell = wf.create.atomistic.engine.lammps.Shell( + wf.shell = wf.create.atomistic.engine.lammps.Shell( command=ExecutablePathResolver(module="lammps", code="lammps").path(), - working_directory=wf.InitLammps.outputs.path, + working_directory=wf.init_lammps.outputs.path, ) wf.ParseLogFile = wf.create.atomistic.engine.lammps.ParseLogFile( - log_file=wf.Shell.outputs.log + log_file=wf.shell.outputs.log ) wf.ParseDumpFile = wf.create.atomistic.engine.lammps.ParseDumpFile( - dump_file=wf.Shell.outputs.dump + dump_file=wf.shell.outputs.dump ) - wf.Collect = wf.create.atomistic.engine.lammps.Collect( - bla=wf.InitLammps.outputs.bla, + wf.collect = wf.create.atomistic.engine.lammps.Collect( out_dump=wf.ParseDumpFile.outputs.dump, out_log=wf.ParseLogFile.outputs.log, calc_mode=wf.calc, ) - return wf.Collect + return wf.collect nodes = [ diff --git a/pyiron_workflow/node_library/atomistic/property/elastic.py b/pyiron_workflow/node_library/atomistic/property/elastic.py index 112260f0..bda735b3 100644 --- a/pyiron_workflow/node_library/atomistic/property/elastic.py +++ b/pyiron_workflow/node_library/atomistic/property/elastic.py @@ -1,6 +1,6 @@ import numpy as np -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node from pyiron_workflow.node_library.dev_tools import wf_data_class from dataclasses import field @@ -39,7 +39,7 @@ class DataStructureContainer: stress: list = field(default_factory=lambda: []) -@function_node() +@as_function_node() def elastic_constants(structure, calculator=None, engine=None): structure_table = generate_structures(structure).run() @@ -63,7 +63,7 @@ def elastic_constants(structure, calculator=None, engine=None): return elastic -@function_node() +@as_function_node() def symmetry_analysis(structure, parameters: InputElasticTensor = InputElasticTensor()): out = OutputElasticSymmetryAnalysis(structure) @@ -78,7 +78,7 @@ def symmetry_analysis(structure, parameters: InputElasticTensor = InputElasticTe return out -@function_node("structures") +@as_function_node("structures") def generate_structures( structure, parameters: InputElasticTensor = InputElasticTensor() ): @@ -168,7 +168,7 @@ class OutputElasticAnalysis: C_eigval: np.ndarray = field(default_factory=lambda: np.zeros(0)) -@function_node("structures") +@as_function_node("structures") def analyse_structures( data_df: DataStructureContainer, parameters: InputElasticTensor = InputElasticTensor(), diff --git a/pyiron_workflow/node_library/atomistic/property/phonons.py b/pyiron_workflow/node_library/atomistic/property/phonons.py index aaa6439c..0af6ebc7 100644 --- a/pyiron_workflow/node_library/atomistic/property/phonons.py +++ b/pyiron_workflow/node_library/atomistic/property/phonons.py @@ -1,16 +1,24 @@ +from dataclasses import asdict, dataclass from typing import Optional, Union +import warnings +from phonopy import Phonopy -# from pyiron_workflow.macro import Macro, macro_node -from pyiron_workflow.function import function_node, function_node -from pyiron_workflow.node_library.dev_tools import wf_data_class, parse_input_kwargs +from pyiron_workflow.function import as_function_node +from pyiron_workflow.macro import as_macro_node +from pyiron_workflow.transform import dataclass_node -from phonopy.api_phonopy import Phonopy +@as_function_node() +def PhonopyObject(structure): + # from phonopy import Phonopy + from structuretoolkit.common import atoms_to_phonopy + + return Phonopy(unitcell=atoms_to_phonopy(structure)) -@wf_data_class(doc_func=Phonopy.generate_displacements) -class InputPhonopyGenerateSupercells: +@dataclass +class GenerateSupercellsParameters: distance: float = 0.01 is_plusminus: Union[str, bool] = "auto" is_diagonal: bool = True @@ -22,109 +30,115 @@ class InputPhonopyGenerateSupercells: max_distance: Optional[float] = None -# @function_node() -def generate_supercells(phonopy, parameters: InputPhonopyGenerateSupercells): +# GenerateSupercellsParameters.__doc__ = Phonopy.generate_displacements + + +@as_function_node() +def GenerateSupercells( + phonopy: Phonopy, parameters: GenerateSupercellsParameters +) -> list: from structuretoolkit.common import phonopy_to_atoms - phonopy.generate_displacements(**parameters) + phonopy.generate_displacements(**asdict(parameters)) supercells = [phonopy_to_atoms(s) for s in phonopy.supercells_with_displacements] return supercells -@function_node("parameters") -def PhonopyParameters( - distance: float = 0.01, - is_plusminus: Union[str, bool] = "auto", - is_diagonal: bool = True, - is_trigonal: bool = False, - number_of_snapshots: Optional[int] = None, - random_seed: Optional[int] = None, - temperature: Optional[float] = None, - cutoff_frequency: Optional[float] = None, - max_distance: Optional[float] = None, -) -> dict: - return { - "distance": distance, - "is_plusminus": is_plusminus, - "is_diagonal": is_diagonal, - "is_trigonal": is_trigonal, - "number_of_snapshots": number_of_snapshots, - "random_seed": random_seed, - "temperature": temperature, - "cutoff_frequency": cutoff_frequency, - "max_distance": max_distance, - } - - -# The following function should be defined as a workflow macro (presently not possible) -@function_node() -def create_phonopy( +@as_macro_node("phonopy", "df") +def CreatePhonopy( + self, structure, + generate_supercells_parameters: GenerateSupercellsParameters, engine=None, - executor=None, - max_workers=1, - parameters: Optional[InputPhonopyGenerateSupercells | dict] = None, ): - from phonopy import Phonopy - from structuretoolkit.common import atoms_to_phonopy - import pyiron_workflow - phonopy = Phonopy(unitcell=atoms_to_phonopy(structure)) + self.phonopy = PhonopyObject(structure) + self.cells = GenerateSupercells( + self.phonopy, parameters=generate_supercells_parameters + ) + + from pyiron_workflow.node_library.atomistic.calculator.ase import Static + from pyiron_workflow.for_loop import for_node - cells = generate_supercells( - phonopy, - parameters=parse_input_kwargs(parameters, InputPhonopyGenerateSupercells), + self.gs = for_node( + Static, + iter_on=("atoms",), + atoms=self.cells, + engine=engine, ) - gs = pyiron_workflow.node_library.atomistic.calculator.ase.static(engine=engine) - df = gs.iter(atoms=cells, executor=executor, max_workers=max_workers) - phonopy.forces = df.forces - # could be automatized (out = collect(gs, log_level)) - out = {} - out["energies"] = df.energy - out["forces"] = df.forces - out["df"] = df + # from pyiron_workflow.node_library.standard import GetItem + # self.forces = for_node( + # GetItem, + # iter_on=("obj",), + # obj=self.gs.outputs.df["out"].to_list(), + # item="forces" + # )["getitem"] + self.forces = DictsToList(self.gs.outputs.df["out"], "forces") + + from pyiron_workflow.node_library.standard import SetAttr + + self.phonopy_with_forces = SetAttr(self.phonopy, "forces", self.forces) - return phonopy, out + return self.phonopy_with_forces, self.gs -@function_node() -def get_dynamical_matrix(phonopy, q=[0, 0, 0]): +@as_function_node() +def DictsToList(dictionaries, key): + """ + `atomistic.calculator.ase.Static` returns a dictionary of stuff; when we iterate + over it, our dataframe has this dictionary in each row. We want a way to get it + back per-column + + The old "iter" played nicely with a dictionary getting returned, where the new + "iter" and `For` node play nicely with individual values getting returned. + This is all OK, and it works, but it is clearly a friction point and we'll need to + do some polish for usability. + """ + return [d[key] for d in dictionaries] + + +@as_function_node() +def GetDynamicalMatrix(phonopy, q: tuple[int, int, int] = 3 * (0,)): import numpy as np - if phonopy.dynamical_matrix is None: + if phonopy.dynamical_matrix.dynamical_matrix is None: phonopy.produce_force_constants() phonopy.dynamical_matrix.run(q=q) dynamical_matrix = np.real_if_close(phonopy.dynamical_matrix.dynamical_matrix) - # print (dynamical_matrix) return dynamical_matrix -@function_node() -def get_eigenvalues(matrix): +@as_function_node() +def GetEigenvalues(matrix): import numpy as np ew = np.linalg.eigvalsh(matrix) return ew -@function_node() -def check_consistency(phonopy, tolerance: float = 1e-10): - dyn_matrix = get_dynamical_matrix(phonopy).run() - ew = get_eigenvalues(dyn_matrix).run() - - ew_lt_zero = ew[ew < -tolerance] - if len(ew_lt_zero) > 0: - print(f"WARNING: {len(ew_lt_zero)} imaginary modes exist") - has_imaginary_modes = True - else: - has_imaginary_modes = False +@as_function_node() +def HasImaginaryNodes(eigenvalues, tolerance: float = 1e-10) -> bool: + n_imaginary_nodes = len(eigenvalues[eigenvalues < -tolerance]) + if has_imaginary_modes := n_imaginary_nodes > 0: + warnings.warn(f"WARNING: {n_imaginary_nodes} imaginary modes exist") return has_imaginary_modes -@function_node() -def get_total_dos(phonopy, mesh=3 * [10]): +@as_macro_node() +def CheckConsistency(self, phonopy, tolerance: float = 1e-10): + self.dyn_matrix = GetDynamicalMatrix(phonopy) + self.ew = GetEigenvalues(self.dyn_matrix) + self.has_imaginary_modes = HasImaginaryNodes(self.ew, tolerance=tolerance) + + return self.has_imaginary_modes + + +@as_function_node() +def GetTotalDos(phonopy, mesh: Optional[tuple[int, int, int]] = None): + mesh = 3 * (10,) if mesh is None else mesh + from pandas import DataFrame phonopy.produce_force_constants() @@ -135,11 +149,11 @@ def get_total_dos(phonopy, mesh=3 * [10]): nodes = [ - # generate_supercells, - create_phonopy, - PhonopyParameters, - get_dynamical_matrix, - get_eigenvalues, - check_consistency, - get_total_dos, + CheckConsistency, + CreatePhonopy, + DictsToList, + GenerateSupercells, + GetDynamicalMatrix, + GetEigenvalues, + GetTotalDos, ] diff --git a/pyiron_workflow/node_library/atomistic/structure/build.py b/pyiron_workflow/node_library/atomistic/structure/build.py index def89f45..2a460b89 100644 --- a/pyiron_workflow/node_library/atomistic/structure/build.py +++ b/pyiron_workflow/node_library/atomistic/structure/build.py @@ -1,9 +1,8 @@ -from pyiron_workflow.function import function_node from pyiron_workflow.workflow import Workflow -@function_node("structure") -def bulk( +@Workflow.wrap.as_function_node("structure") +def Bulk( name, crystalstructure=None, a=None, @@ -27,20 +26,23 @@ def bulk( ) -@Workflow.wrap_as.macro_node("structure") -def cubic_bulk_cell( +@Workflow.wrap.as_macro_node("structure") +def CubicBulkCell( wf, element: str, cell_size: int = 1, vacancy_index: int | None = None ): from pyiron_workflow.node_library.atomistic.structure.transform import ( - create_vacancy, - repeat, + CreateVacancy, + Repeat, ) - wf.bulk = bulk(name=element, cubic=True) - wf.cell = repeat(structure=wf.bulk, repeat_scalar=cell_size) + wf.bulk = Bulk(name=element, cubic=True) + wf.cell = Repeat(structure=wf.bulk, repeat_scalar=cell_size) - wf.structure = create_vacancy(structure=wf.cell, index=vacancy_index) - return wf.structure # .outputs.structure + wf.structure = CreateVacancy(structure=wf.cell, index=vacancy_index) + return wf.structure -nodes = [bulk, cubic_bulk_cell] +nodes = [ + Bulk, + CubicBulkCell, +] diff --git a/pyiron_workflow/node_library/atomistic/structure/calc.py b/pyiron_workflow/node_library/atomistic/structure/calc.py index 8197271d..a64b953e 100644 --- a/pyiron_workflow/node_library/atomistic/structure/calc.py +++ b/pyiron_workflow/node_library/atomistic/structure/calc.py @@ -1,4 +1,4 @@ -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node from typing import Optional, Union # Huge savings when replacing pyiron_atomistics atoms class with ase one!! (> 5s vs 40 ms) @@ -6,9 +6,9 @@ from ase import Atoms -@function_node("structure") -def volume(structure: Optional[Atoms] = None) -> float: +@as_function_node("volume") +def Volume(structure: Optional[Atoms] = None) -> float: return structure.get_volume() -nodes = [volume] +nodes = [Volume] diff --git a/pyiron_workflow/node_library/atomistic/structure/transform.py b/pyiron_workflow/node_library/atomistic/structure/transform.py index 5940ff2e..ba308c42 100644 --- a/pyiron_workflow/node_library/atomistic/structure/transform.py +++ b/pyiron_workflow/node_library/atomistic/structure/transform.py @@ -1,4 +1,4 @@ -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node from typing import Optional, Union # Huge savings when replacing pyiron_atomistics atoms class with ase one!! (> 5s vs 40 ms) @@ -6,24 +6,22 @@ from ase import Atoms -@function_node("structure") -def repeat(structure: Atoms, repeat_scalar: int = 1) -> Atoms: +@as_function_node("structure") +def Repeat(structure: Atoms, repeat_scalar: int = 1) -> Atoms: return structure.repeat(repeat_scalar) -@function_node("structure") -def apply_strain( +@as_function_node("structure") +def ApplyStrain( structure: Optional[Atoms] = None, strain: Union[float, int] = 0 ) -> Optional[Atoms]: - # print("apply strain: ", strain) struct = structure.copy() - # struct.cell *= strain struct.apply_strain(strain) return struct -@function_node() -def create_vacancy(structure, index: int | None = 0): +@as_function_node() +def CreateVacancy(structure, index: int | None = 0): structure = structure.copy() if index is not None: del structure[index] @@ -31,11 +29,11 @@ def create_vacancy(structure, index: int | None = 0): return structure -@function_node("structure") -def rotate_axis_angle( +@as_function_node("structure") +def RotateAxisAngle( structure: Atoms, angle: float | int = 0, - axis: list = [0, 0, 1], + axis: list | tuple = (0, 0, 1), center=(0, 0, 0), rotate_cell: bool = False, ): @@ -61,8 +59,8 @@ def rotate_axis_angle( nodes = [ - repeat, - apply_strain, - create_vacancy, - rotate_axis_angle, + Repeat, + ApplyStrain, + CreateVacancy, + RotateAxisAngle, ] diff --git a/pyiron_workflow/node_library/atomistic_codes.py b/pyiron_workflow/node_library/atomistic_codes.py index 53fb1ae0..0ffb4508 100644 --- a/pyiron_workflow/node_library/atomistic_codes.py +++ b/pyiron_workflow/node_library/atomistic_codes.py @@ -1,52 +1,52 @@ -from pyiron_workflow.macro import macro_node +from pyiron_workflow.macro import as_macro_node from pyiron_workflow.node_library.atomistic.engine.lammps import get_calculators from pyiron_workflow.node_library.dev_tools import set_replacer from ase import Atoms -@macro_node("generic") +@as_macro_node("generic") def Lammps(wf, structure=Atoms(), potential=None): from pyiron_contrib.tinybase.shell import ExecutablePathResolver - wf.Potential = wf.create.atomistic.engine.lammps.Potential( + wf.potential_object = wf.create.atomistic.engine.lammps.Potential( structure=structure, name=potential ) - wf.ListPotentials = wf.create.atomistic.engine.lammps.ListPotentials( + wf.list_potentials = wf.create.atomistic.engine.lammps.ListPotentials( structure=structure ) wf.calc = wf.create.atomistic.engine.lammps.CalcStatic() wf.calc_select = set_replacer(wf.calc, get_calculators()) - wf.InitLammps = wf.create.atomistic.engine.lammps.InitLammps( + wf.init_lammps = wf.create.atomistic.engine.lammps.InitLammps( structure=structure, - potential=wf.Potential, + potential=wf.potential_object, calculator=wf.calc.outputs.calculator, - # working_directory="test2", ) - wf.InitLammps.inputs.working_directory = ( - wf.InitLammps.working_directory.path.__str__() + wf.init_lammps.inputs.working_directory = ( + wf.init_lammps.working_directory.path.resolve().__str__() ) - wf.Shell = wf.create.atomistic.engine.lammps.Shell( + + wf.shell = wf.create.atomistic.engine.lammps.Shell( command=ExecutablePathResolver(module="lammps", code="lammps").path(), - working_directory=wf.InitLammps.outputs.path, + working_directory=wf.init_lammps.outputs.path, ) - wf.ParseLogFile = wf.create.atomistic.engine.lammps.ParseLogFile( - log_file=wf.Shell.outputs.log + wf.parse_log_file = wf.create.atomistic.engine.lammps.ParseLogFile( + log_file=wf.shell.outputs.log ) - wf.ParseDumpFile = wf.create.atomistic.engine.lammps.ParseDumpFile( - dump_file=wf.Shell.outputs.dump + wf.parse_dump_file = wf.create.atomistic.engine.lammps.ParseDumpFile( + dump_file=wf.shell.outputs.dump ) - wf.Collect = wf.create.atomistic.engine.lammps.Collect( - out_dump=wf.ParseDumpFile.outputs.dump, - out_log=wf.ParseLogFile.outputs.log, + wf.collect = wf.create.atomistic.engine.lammps.Collect( + out_dump=wf.parse_dump_file.outputs.dump, + out_log=wf.parse_log_file.outputs.log, calc_mode=wf.calc.mode, # SVN gives output -> inject attribute getter node ) - return wf.Collect + return wf.collect nodes = [Lammps] diff --git a/pyiron_workflow/node_library/atomistics/calculator.py b/pyiron_workflow/node_library/atomistics/calculator.py index 99f2bae5..c5c6c313 100644 --- a/pyiron_workflow/node_library/atomistics/calculator.py +++ b/pyiron_workflow/node_library/atomistics/calculator.py @@ -1,18 +1,18 @@ from ase.units import Ry -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node -@function_node("calculator") +@as_function_node("calculator") def Emt(): from ase.calculators.emt import EMT return EMT() -@function_node("calculator") +@as_function_node("calculator") def Abinit( - label="abinit_evcurve", + ase_label="abinit_evcurve", nbands=32, ecut=10 * Ry, kpts=(3, 3, 3), @@ -22,7 +22,7 @@ def Abinit( from ase.calculators.abinit import Abinit return Abinit( - label=label, + label=ase_label, nbands=nbands, ecut=ecut, kpts=kpts, @@ -31,14 +31,14 @@ def Abinit( ) -@function_node("calculator") +@as_function_node("calculator") def Gpaw(xc="PBE", encut=300, kpts=(3, 3, 3)): from gpaw import GPAW, PW return GPAW(xc=xc, mode=PW(encut), kpts=kpts) -@function_node("calculator") +@as_function_node("calculator") def QuantumEspresso( pseudopotentials={"Al": "Al.pbe-n-kjpaw_psl.1.0.0.UPF"}, tstress=True, @@ -55,9 +55,9 @@ def QuantumEspresso( ) -@function_node("calculator") +@as_function_node("calculator") def Siesta( - label="siesta", + ase_label="siesta", xc="PBE", mesh_cutoff=200 * Ry, energy_shift=0.01 * Ry, @@ -70,7 +70,7 @@ def Siesta( from ase.calculators.siesta import Siesta return Siesta( - label=label, + label=ase_label, xc=xc, mesh_cutoff=mesh_cutoff, energy_shift=energy_shift, @@ -82,14 +82,14 @@ def Siesta( ) -@function_node("energy_dict") +@as_function_node("energy_dict") def CalcWithCalculator(task_dict, calculator): from atomistics.calculators.ase import evaluate_with_ase return evaluate_with_ase(task_dict=task_dict, ase_calculator=calculator) -@function_node("lammps_potential_dataframe") +@as_function_node("lammps_potential_dataframe") def LammpsPotential(potential_name, structure, resource_path): from atomistics.calculators.lammps import get_potential_dataframe @@ -97,7 +97,7 @@ def LammpsPotential(potential_name, structure, resource_path): return df_pot[df_pot.Name == potential_name].iloc[0] -@function_node("energy_dict") +@as_function_node("energy_dict") def Lammps(task_dict, potential_dataframe): from atomistics.calculators.lammps import evaluate_with_lammps diff --git a/pyiron_workflow/node_library/atomistics/macro.py b/pyiron_workflow/node_library/atomistics/macro.py index 31edaf50..aa09610a 100644 --- a/pyiron_workflow/node_library/atomistics/macro.py +++ b/pyiron_workflow/node_library/atomistics/macro.py @@ -1,4 +1,6 @@ -from pyiron_workflow.macro import Macro, macro_node +from phonopy.units import VaspToTHz + +from pyiron_workflow.macro import Macro, as_macro_node from pyiron_workflow.node_library.atomistics.calculator import CalcWithCalculator from pyiron_workflow.node_library.atomistics.task import ( ElasticMatrixTaskGenerator, @@ -9,35 +11,86 @@ ) -def atomistics_meta_macro(task_generator_node_class, macro_name) -> type[Macro]: - def generic_macro(wf: Macro) -> None: - wf.tasks = task_generator_node_class() +def atomistics_meta_creator(task_generator_node_class) -> type[Macro]: + def generic_creator(wf: Macro, calculator, **task_kwargs) -> None: + wf.tasks = task_generator_node_class(**task_kwargs) wf.structures = GenerateStructures(instance=wf.tasks) - wf.calc = CalcWithCalculator(task_dict=wf.structures) + wf.calc = CalcWithCalculator(task_dict=wf.structures, calculator=calculator) wf.fit = AnalyseStructures(instance=wf.tasks, output_dict=wf.calc) - inputs_map = { - # Dynamically expose _all_ task generator input directly on the macro - "tasks__" + s: s - for s in wf.tasks.inputs.labels - } - inputs_map["calc__calculator"] = "calculator" - wf.inputs_map = inputs_map - wf.outputs_map = {"fit__result_dict": "result_dict"} + return wf.fit - generic_macro.__name__ = macro_name + return generic_creator - return macro_node()(generic_macro) +@as_macro_node("result_dict") +def ElasticMatrix( + wf, + calculator, + structure, + num_of_point=5, + eps_range=0.05, + sqrt_eta=True, + fit_order=2, +): + return atomistics_meta_creator(ElasticMatrixTaskGenerator)( + wf, + calculator, + structure=structure, + num_of_point=num_of_point, + eps_range=eps_range, + sqrt_eta=sqrt_eta, + fit_order=fit_order, + ) -ElasticMatrix = atomistics_meta_macro(ElasticMatrixTaskGenerator, "ElasticMatrix") +@as_macro_node("result_dict") +def EnergyVolumeCurve( + wf, + calculator, + structure, + num_points=11, + fit_type="polynomial", + fit_order=3, + vol_range=0.05, + axes=("x", "y", "z"), + strains=None, +): + return atomistics_meta_creator(EvcurveTaskGenerator)( + wf, + calculator, + structure=structure, + num_points=num_points, + fit_type=fit_type, + fit_order=fit_order, + vol_range=vol_range, + axes=axes, + strains=strains, + ) -EnergyVolumeCurve = atomistics_meta_macro( - EvcurveTaskGenerator, - "EnergyVolumeCurve", -) -Phonons = atomistics_meta_macro(PhononsTaskGenerator, "Phonons") +@as_macro_node("result_dict") +def Phonons( + wf, + calculator, + structure, + interaction_range=10, + factor=VaspToTHz, + displacement=0.01, + dos_mesh=20, + primitive_matrix=None, + number_of_snapshots=None, +): + return atomistics_meta_creator(PhononsTaskGenerator)( + wf, + calculator, + structure=structure, + interaction_range=interaction_range, + factor=factor, + displacement=displacement, + dos_mesh=dos_mesh, + primitive_matrix=primitive_matrix, + number_of_snapshots=number_of_snapshots, + ) nodes = [ diff --git a/pyiron_workflow/node_library/atomistics/task.py b/pyiron_workflow/node_library/atomistics/task.py index 72f8e1cf..b066d5d2 100644 --- a/pyiron_workflow/node_library/atomistics/task.py +++ b/pyiron_workflow/node_library/atomistics/task.py @@ -1,8 +1,8 @@ from phonopy.units import VaspToTHz -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node -@function_node("task_generator") +@as_function_node("task_generator") def ElasticMatrixTaskGenerator( structure, num_of_point=5, eps_range=0.05, sqrt_eta=True, fit_order=2 ): @@ -17,7 +17,7 @@ def ElasticMatrixTaskGenerator( ) -@function_node("task_generator") +@as_function_node("task_generator") def EvcurveTaskGenerator( structure, num_points=11, @@ -40,7 +40,7 @@ def EvcurveTaskGenerator( ) -@function_node("task_generator") +@as_function_node("task_generator") def PhononsTaskGenerator( structure, interaction_range=10, @@ -63,17 +63,17 @@ def PhononsTaskGenerator( ) -@function_node("result_dict") +@as_function_node("result_dict") def AnalyseStructures(instance, output_dict): return instance.analyse_structures(output_dict=output_dict) -@function_node("task_dict") +@as_function_node("task_dict") def GenerateStructures(instance): return instance.generate_structures() -@function_node("structure") +@as_function_node("structure") def Bulk(element): from ase.build import bulk diff --git a/pyiron_workflow/node_library/databases/__init__.py b/pyiron_workflow/node_library/databases/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pyiron_workflow/node_library/databases/elasticity.py b/pyiron_workflow/node_library/databases/elasticity.py index ad60894a..c9bee166 100644 --- a/pyiron_workflow/node_library/databases/elasticity.py +++ b/pyiron_workflow/node_library/databases/elasticity.py @@ -1,9 +1,9 @@ -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node from typing import Optional -@function_node("dataframe") -def de_jong(max_index: int | None = None, filename="ec.json"): +@as_function_node("dataframe") +def DeJong(max_index: int | None = None, filename="ec.json"): """ Expects the file to be the "ec.json" database referenced by: Ref. de Jong et al. https://www.nature.com/articles/sdata20159#MOESM77 @@ -37,5 +37,5 @@ def de_jong(max_index: int | None = None, filename="ec.json"): nodes = [ - de_jong, + DeJong, ] diff --git a/pyiron_workflow/node_library/lammps.py b/pyiron_workflow/node_library/lammps.py index ec33a2e4..d33edb97 100644 --- a/pyiron_workflow/node_library/lammps.py +++ b/pyiron_workflow/node_library/lammps.py @@ -4,13 +4,12 @@ from typing import Optional, Union from pyiron_atomistics.atomistics.structure.atoms import Atoms -from pyiron_workflow.function import single_value_node, function_node from pyiron_workflow.workflow import Workflow from pyiron_workflow.node_library.dev_tools import VarType, FileObject -@single_value_node("calculator") +@Workflow.wrap.as_function_node("calculator") def CalcMD( temperature: VarType(dat_type=float, store=10) = 300, n_ionic_steps=1000, @@ -26,7 +25,7 @@ def CalcMD( return calculator -@single_value_node("calculator") +@Workflow.wrap.as_function_node("calculator") def CalcStatic(): from pyiron_atomistics.lammps.control import LammpsControl @@ -35,18 +34,12 @@ def CalcStatic(): return calculator -# TODO: The following function has been only introduced to mimic input variables for a macro -@single_value_node("structure") -def Structure(structure): - return structure - - -@single_value_node("path") +@Workflow.wrap.as_function_node("path") def InitLammps(structure=None, potential=None, calculator=None, working_directory=None): import os from pyiron_atomistics.lammps.potential import LammpsPotential, LammpsPotentialFile - assert os.path.isdir(working_directory), "working directory missing" + # assert os.path.isdir(working_directory), "working directory missing" pot = LammpsPotential() pot.df = LammpsPotentialFile().find_by_name(potential) @@ -65,7 +58,7 @@ def InitLammps(structure=None, potential=None, calculator=None, working_director return os.path.abspath(working_directory) -@single_value_node("log") +@Workflow.wrap.as_function_node("log") def ParseLogFile(log_file): from pymatgen.io.lammps.outputs import parse_lammps_log @@ -77,7 +70,7 @@ def ParseLogFile(log_file): return log -@single_value_node("dump") +@Workflow.wrap.as_function_node("dump") def ParseDumpFile(dump_file): from pymatgen.io.lammps.outputs import parse_lammps_dumps @@ -106,7 +99,7 @@ class ShellOutput(Storage): log: FileObject -@function_node("output", "dump", "log") +@Workflow.wrap.as_function_node("output", "dump", "log") def Shell( command: str, environment: Optional[dict] = None, @@ -152,7 +145,7 @@ class GenericOutput(Storage): forces: [] -@single_value_node("generic") +@Workflow.wrap.as_function_node("generic") def Collect(out_dump, out_log): import numpy as np @@ -168,7 +161,7 @@ def Collect(out_dump, out_log): return output -@single_value_node("potential") +@Workflow.wrap.as_function_node("potential") def Potential(structure, name=None, index=0): from pyiron_atomistics.lammps.potential import list_potentials as lp @@ -183,7 +176,7 @@ def Potential(structure, name=None, index=0): return pot -@single_value_node("potentials") +@Workflow.wrap.as_function_node("potentials") def ListPotentials(structure): from pyiron_atomistics.lammps.potential import list_potentials as lp @@ -191,19 +184,19 @@ def ListPotentials(structure): return potentials -@single_value_node("empty") +@Workflow.wrap.as_function_node("empty") def ListEmpty(): return [] -@single_value_node("structure") +@Workflow.wrap.as_function_node("structure") def Repeat( structure: Optional[Atoms] = None, repeat_scalar: int = 1 ) -> Optional[Atoms]: return structure.repeat(repeat_scalar) -@single_value_node("structure") +@Workflow.wrap.as_function_node("structure") def ApplyStrain( structure: Optional[Atoms] = None, strain: Union[float, int] = 0 ) -> Optional[Atoms]: @@ -222,7 +215,6 @@ def get_calculators(): nodes = [ - Structure, InitLammps, Potential, ListPotentials, diff --git a/pyiron_workflow/node_library/plotting.py b/pyiron_workflow/node_library/plotting.py index 81b020e9..dbe5afd0 100644 --- a/pyiron_workflow/node_library/plotting.py +++ b/pyiron_workflow/node_library/plotting.py @@ -8,10 +8,10 @@ import numpy as np -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node -@function_node("fig") +@as_function_node("fig") def Scatter( x: Optional[list | np.ndarray] = None, y: Optional[list | np.ndarray] = None ): diff --git a/pyiron_workflow/node_library/pyiron_atomistics.py b/pyiron_workflow/node_library/pyiron_atomistics.py index 39d87f6b..6e2b2c72 100644 --- a/pyiron_workflow/node_library/pyiron_atomistics.py +++ b/pyiron_workflow/node_library/pyiron_atomistics.py @@ -11,14 +11,15 @@ from pyiron_atomistics.atomistics.structure.atoms import Atoms from pyiron_atomistics.lammps.lammps import Lammps as LammpsJob -from pyiron_workflow.function import function_node +from pyiron_workflow.function import as_function_node -Bulk = function_node("structure")(_StructureFactory().bulk) +Bulk = as_function_node("structure")(_StructureFactory().bulk) Bulk.__name__ = "Bulk" +Bulk.__module__ = __name__ -@function_node("job") +@as_function_node("job") def Lammps(structure: Optional[Atoms] = None) -> LammpsJob: pr = Project(".") job = pr.atomistics.job.Lammps("NOTAREALNAME") @@ -84,7 +85,7 @@ def _run_and_remove_job(job, modifier: Optional[callable] = None, **modifier_kwa ) -@function_node( +@as_function_node( "cells", "displacements", "energy_pot", @@ -99,6 +100,7 @@ def _run_and_remove_job(job, modifier: Optional[callable] = None, **modifier_kwa "total_displacements", "unwrapped_positions", "volume", + validate_output_labels=False, ) def CalcStatic( job: AtomisticGenericJob, @@ -106,7 +108,7 @@ def CalcStatic( return _run_and_remove_job(job=job) -@function_node( +@as_function_node( "cells", "displacements", "energy_pot", @@ -121,6 +123,7 @@ def CalcStatic( "total_displacements", "unwrapped_positions", "volume", + validate_output_labels=False, ) def CalcMd( job: AtomisticGenericJob, @@ -153,7 +156,7 @@ def calc_md(job, n_ionic_steps, n_print, temperature, pressure): ) -@function_node( +@as_function_node( "cells", "displacements", "energy_pot", @@ -167,6 +170,7 @@ def calc_md(job, n_ionic_steps, n_print, temperature, pressure): "total_displacements", "unwrapped_positions", "volume", + validate_output_labels=False, ) def CalcMin( job: AtomisticGenericJob, diff --git a/pyiron_workflow/node_library/standard.py b/pyiron_workflow/node_library/standard.py index 727ed8c3..e7fd3160 100644 --- a/pyiron_workflow/node_library/standard.py +++ b/pyiron_workflow/node_library/standard.py @@ -4,13 +4,14 @@ from __future__ import annotations -from inspect import isclass +import random +from time import sleep from pyiron_workflow.channels import NOT_DATA, OutputSignal -from pyiron_workflow.function import Function, function_node +from pyiron_workflow.function import Function, as_function_node -@function_node() +@as_function_node() def UserInput(user_input): return user_input @@ -22,17 +23,18 @@ class If(Function): """ def __init__(self, **kwargs): - super().__init__(self.if_, output_labels="truth", **kwargs) + super().__init__(**kwargs) self.signals.output.true = OutputSignal("true", self) self.signals.output.false = OutputSignal("false", self) @staticmethod - def if_(condition): + def node_function(condition): if condition is NOT_DATA: raise TypeError( f"Logic 'If' node expected data other but got NOT_DATA as input." ) - return bool(condition) + truth = bool(condition) + return truth def process_run_result(self, function_output): """ @@ -46,7 +48,18 @@ def process_run_result(self, function_output): self.signals.output.false() -@function_node("slice") +@as_function_node("random") +def RandomFloat(): + return random.random() + + +@as_function_node("time") +def Sleep(t): + sleep(t) + return t + + +@as_function_node("slice") def Slice(start=None, stop=NOT_DATA, step=None): if start is None: if stop is None: @@ -64,189 +77,195 @@ def Slice(start=None, stop=NOT_DATA, step=None): return s +@as_function_node("object") +def SetAttr(obj, key: str, val): + setattr(obj, key, val) + return obj + + # A bunch of (but not all) standard operators # Return values based on dunder methods, where available -@function_node("str") +@as_function_node("str") def String(obj): return str(obj) -@function_node("bytes") +@as_function_node("bytes") def Bytes(obj): return bytes(obj) -@function_node("lt") +@as_function_node("lt") def LessThan(obj, other): return obj < other -@function_node("le") +@as_function_node("le") def LessThanEquals(obj, other): return obj <= other -@function_node("eq") +@as_function_node("eq") def Equals(obj, other): return obj == other -@function_node("neq") +@as_function_node("neq") def NotEquals(obj, other): return obj != other -@function_node("gt") +@as_function_node("gt") def GreaterThan(obj, other): return obj > other -@function_node("ge") +@as_function_node("ge") def GreaterThanEquals(obj, other): return obj >= other -@function_node("hash") +@as_function_node("hash") def Hash(obj): return hash(obj) -@function_node("bool") +@as_function_node("bool") def Bool(obj): return bool(obj) -@function_node("getattr") +@as_function_node("getattr") def GetAttr(obj, name): return getattr(obj, name) # These are not idempotent and thus not encouraged -# @function_node("none") +# @as_function_node("none") # def SetAttr(obj, name, value): # setattr(obj, name, value) # return None # # -# @function_node("none") +# @as_function_node("none") # def DelAttr(obj, name): # delattr(obj, name) # return None -@function_node("getitem") +@as_function_node("getitem") def GetItem(obj, item): return obj[item] -@function_node("dir") +@as_function_node("dir") def Dir(obj): return dir(obj) -@function_node("len") +@as_function_node("len") def Length(obj): return len(obj) -@function_node("in") +@as_function_node("in") def Contains(obj, other): return other in obj -@function_node("add") +@as_function_node("add") def Add(obj, other): return obj + other -@function_node("sub") +@as_function_node("sub") def Subtract(obj, other): return obj - other -@function_node("mul") +@as_function_node("mul") def Multiply(obj, other): return obj * other -@function_node("rmul") +@as_function_node("rmul") def RightMultiply(obj, other): return other * obj -@function_node("matmul") +@as_function_node("matmul") def MatrixMultiply(obj, other): return obj @ other -@function_node("truediv") +@as_function_node("truediv") def Divide(obj, other): return obj / other -@function_node("floordiv") +@as_function_node("floordiv") def FloorDivide(obj, other): return obj // other -@function_node("mod") +@as_function_node("mod") def Modulo(obj, other): return obj % other -@function_node("pow") +@as_function_node("pow") def Power(obj, other): return obj**other -@function_node("and") +@as_function_node("and") def And(obj, other): return obj & other -@function_node("xor") +@as_function_node("xor") def XOr(obj, other): return obj ^ other -@function_node("or") +@as_function_node("or") def Or(obj, other): return obj ^ other -@function_node("neg") +@as_function_node("neg") def Negative(obj): return -obj -@function_node("pos") +@as_function_node("pos") def Positive(obj): return +obj -@function_node("abs") +@as_function_node("abs") def Absolute(obj): return abs(obj) -@function_node("invert") +@as_function_node("invert") def Invert(obj): return ~obj -@function_node("int") +@as_function_node("int") def Int(obj): return int(obj) -@function_node("float") +@as_function_node("float") def Float(obj): return float(obj) -@function_node("round") +@as_function_node("round") def Round(obj): return round(obj) @@ -282,8 +301,11 @@ def Round(obj): Or, Positive, Power, + RandomFloat, RightMultiply, Round, + SetAttr, + Sleep, Slice, String, Subtract, diff --git a/pyiron_workflow/output_parser.py b/pyiron_workflow/output_parser.py index 2f88e71e..54a00309 100644 --- a/pyiron_workflow/output_parser.py +++ b/pyiron_workflow/output_parser.py @@ -3,6 +3,7 @@ """ import ast +from functools import lru_cache import inspect import re from textwrap import dedent @@ -26,7 +27,6 @@ class ParseOutput: def __init__(self, function): self._func = function - self._source = None self._output = self.get_parsed_output() @property @@ -57,10 +57,9 @@ def node_return(self): return None @property + @lru_cache(maxsize=1) def source(self): - if self._source is None: - self._source = self.dedented_source_string.split("\n")[:-1] - return self._source + return self.dedented_source_string.split("\n")[:-1] def get_string(self, node): string = "" diff --git a/pyiron_workflow/run.py b/pyiron_workflow/run.py index be50f71d..6cf31c67 100644 --- a/pyiron_workflow/run.py +++ b/pyiron_workflow/run.py @@ -6,7 +6,8 @@ from __future__ import annotations from abc import ABC, abstractmethod -from concurrent.futures import Executor as StdLibExecutor, Future +from concurrent.futures import Executor as StdLibExecutor, Future, ThreadPoolExecutor +from time import sleep from typing import Any, Optional from pyiron_workflow.has_interface_mixins import HasLabel, HasRun, UsesState @@ -50,9 +51,9 @@ class Runnable(UsesState, HasLabel, HasRun, ABC): An abstract class for interfacing with executors, etc. Child classes must define :meth:`on_run` and :attr:`.Runnable.run_args`, then the - :meth:`run` will invoke `on_run(**run_args)`. The :class:`Runnable` class then - handles the status of the run, passing the call off for remote execution, handling - any returned futures object, etc. + :meth:`run` will invoke `self.on_run(*run_args[0], **run_args[1])`. The + :class:`Runnable` class then handles the status of the run, passing the call off + for remote execution, handling any returned futures object, etc. Child classes can optionally override :meth:`process_run_result` to do something with the returned value of :meth:`on_run`, but by default the returned value just @@ -68,20 +69,19 @@ def __init__(self, *args, **kwargs): # This is a simply stop-gap as we work out more sophisticated ways to reference # (or create) an executor process without ever trying to pickle a `_thread.lock` self.future: None | Future = None + self._thread_pool_sleep_time = 1e-6 - @property @abstractmethod - def on_run(self) -> callable[..., Any | tuple]: + def on_run(self, *args, **kwargs) -> Any: # callable[..., Any | tuple]: """ What the :meth:`run` method actually does! """ - pass @property @abstractmethod - def run_args(self) -> dict: + def run_args(self) -> tuple[tuple, dict]: """ - Any data needed for :meth:`on_run`, will be passed as **kwargs. + Any data needed for :meth:`on_run`, will be passed as (*args, **kwargs). """ def process_run_result(self, run_output): @@ -168,24 +168,33 @@ def _run( finished_callback: callable, force_local_execution: bool, ) -> Any | tuple | Future: + args, kwargs = self.run_args + if "self" in kwargs.keys(): + raise ValueError( + f"{self.label} got 'self' as a run kwarg, but self is already the " + f"first positional argument passed to :meth:`on_run`." + ) if force_local_execution or self.executor is None: # Run locally - run_output = self.on_run(**self.run_args) + run_output = self.on_run(*args, **kwargs) return finished_callback(run_output) else: # Just blindly try to execute -- as we nail down the executor interaction # we'll want to fail more cleanly here. executor = self._parse_executor(self.executor) - kwargs = self.run_args - if "self" in kwargs.keys(): - raise ValueError( - f"{self.label} got 'self' as a run argument, but self cannot " - f"currently be combined with running on executors." - ) - self.future = executor.submit(self.on_run, **kwargs) + if isinstance(self.executor, ThreadPoolExecutor): + self.future = executor.submit(self.thread_pool_run, *args, **kwargs) + else: + self.future = executor.submit(self.on_run, *args, **kwargs) self.future.add_done_callback(finished_callback) return self.future + def thread_pool_run(self, *args, **kwargs): + # + result = self.on_run(*args, **kwargs) + sleep(self._thread_pool_sleep_time) + return result + @staticmethod def _parse_executor(executor) -> StdLibExecutor: """ diff --git a/pyiron_workflow/semantics.py b/pyiron_workflow/semantics.py index 3d2b111a..c45df94d 100644 --- a/pyiron_workflow/semantics.py +++ b/pyiron_workflow/semantics.py @@ -93,32 +93,12 @@ def semantic_root(self) -> Semantic: def __getstate__(self): state = super().__getstate__() state["_parent"] = None - # Comment on moving this to semantics) + # Regarding removing parent from state: # Basically we want to avoid recursion during (de)serialization; when the # parent object is deserializing itself, _it_ should know who its children are # and inform them of this. - # - # Original comment when this behaviour belonged to node) - # I am not at all confident that removing the parent here is the _right_ - # solution. - # In order to run composites on a parallel process, we ship off just the nodes - # and starting nodes. - # When the parallel process returns these, they're obviously different - # instances, so we re-parent them back to the receiving composite. - # At the same time, we want to make sure that the _old_ children get orphaned. - # Of course, we could do that directly in the composite method, but it also - # works to do it here. - # Something I like about this, is it also means that when we ship groups of - # nodes off to another process with cloudpickle, they're definitely not lugging - # along their parent, its connections, etc. with them! - # This is all working nicely as demonstrated over in the macro test suite. - # However, I have a bit of concern that when we start thinking about - # serialization for storage instead of serialization to another process, this - # might introduce a hard-to-track-down bug. - # For now, it works and I'm going to be super pragmatic and go for it, but - # for the record I am admitting that the current shallowness of my understanding - # may cause me/us headaches in the future. - # -Liam + # In the case the object gets passed to another process using __getstate__, + # this also avoids dragging our whole semantic parent graph along with us. return state diff --git a/pyiron_workflow/snippets/factory.py b/pyiron_workflow/snippets/factory.py new file mode 100644 index 00000000..17091fe7 --- /dev/null +++ b/pyiron_workflow/snippets/factory.py @@ -0,0 +1,437 @@ +""" +Tools for making dynamically generated classes unique, and their instances pickleable. + +Provides two main user-facing tools: :func:`classfactory`, which should be used +_exclusively_ as a decorator (this restriction pertains to namespace requirements for +re-importing), and `ClassFactory`, which can be used to instantiate a new factory from +some existing factory function. + +In both cases, the decorated function/input argument should be a pickleable function +taking only positional arguments, and returning a tuple suitable for use in dynamic +class creation via :func:`builtins.type` -- i.e. taking a class name, a tuple of base +classes, a dictionary of class attributes, and a dictionary of values to be expanded +into kwargs for `__subclass_init__`. + +The resulting factory produces classes that are (a) pickleable, and (b) the same object +as any previously built class with the same name. (Note: avoiding class degeneracy with +respect to class name is the responsibility of the person writing the factory function.) + +These classes are then themselves pickleable, and produce instances which are in turn +pickleable (so long as any data they've been fed as inputs or attributes is pickleable, +i.e. here the only pickle-barrier we resolve is that of having come from a dynamically +generated class). + +Since users need to build their own class factories returning classes with sensible +names, we also provide a helper function :func:`sanitize_callable_name`, which makes +sure a string is compliant with use as a class name. This is run internally on user- +provided names, and failure for the user name and sanitized name to match will give a +clear error message. + +Constructed classes can, in turn be used as bases in further class factories. +""" + +from __future__ import annotations + +from abc import ABC, ABCMeta +from functools import wraps +from importlib import import_module +from inspect import signature, Parameter +import pickle +from re import sub +from typing import ClassVar + + +class _SingleInstance(ABCMeta): + """Simple singleton pattern.""" + + _instance = None + + def __call__(cls, *args, **kwargs): + if cls._instance is None: + cls._instance = super(_SingleInstance, cls).__call__(*args, **kwargs) + return cls._instance + + +class _FactoryTown(metaclass=_SingleInstance): + """ + Makes sure two factories created around the same factory function are the same + factory object. + """ + + factories = {} + + @classmethod + def clear(cls): + """ + Remove factories. + + Can be useful if you're + """ + cls.factories = {} + + @staticmethod + def _factory_address(factory_function: callable) -> str: + return f"{factory_function.__module__}.{factory_function.__qualname__}" + + def get_factory(self, factory_function: callable[..., type]) -> _ClassFactory: + + self._verify_function_only_takes_positional_args(factory_function) + + address = self._factory_address(factory_function) + + try: + return self.factories[address] + except KeyError: + factory = self._build_factory(factory_function) + self.factories[address] = factory + return factory + + @staticmethod + def _build_factory(factory_function): + """ + Subclass :class:`_ClassFactory` and make an instance. + """ + new_factory_class = type( + sanitize_callable_name( + f"{factory_function.__module__}{factory_function.__qualname__}" + f"{factory_function.__name__.title()}" + f"{_ClassFactory.__name__}" + ).replace("_", ""), + (_ClassFactory,), + {}, + factory_function=factory_function, + ) + return wraps(factory_function)(new_factory_class()) + + @staticmethod + def _verify_function_only_takes_positional_args(factory_function: callable): + parameters = signature(factory_function).parameters.values() + if any( + p.kind not in [Parameter.POSITIONAL_ONLY, Parameter.VAR_POSITIONAL] + for p in parameters + ): + raise InvalidFactorySignature( + f"{_ClassFactory.__name__} can only be subclassed using factory " + f"functions that take exclusively positional arguments, but " + f"{factory_function.__name__} has the parameters {parameters}" + ) + + +_FACTORY_TOWN = _FactoryTown() + + +class InvalidFactorySignature(ValueError): + """When the factory function's arguments are not purely positional""" + + pass + + +class InvalidClassNameError(ValueError): + """When a string isn't a good class name""" + + pass + + +class _ClassFactory(metaclass=_SingleInstance): + """ + For making dynamically created classes the same class. + """ + + _decorated_as_classfactory: ClassVar[bool] = False + + def __init_subclass__(cls, /, factory_function, **kwargs): + super().__init_subclass__(**kwargs) + cls.factory_function = staticmethod(factory_function) + cls.class_registry = {} + + def __call__(self, *args) -> type[_FactoryMade]: + name, bases, class_dict, sc_init_kwargs = self.factory_function(*args) + self._verify_name_is_legal(name) + try: + return self.class_registry[name] + except KeyError: + factory_made = self._build_class( + name, + bases, + class_dict, + sc_init_kwargs, + args, + ) + self.class_registry[name] = factory_made + return factory_made + + @classmethod + def clear(cls, *class_names, skip_missing=True): + """ + Remove constructed class(es). + + Can be useful if you've updated the constructor and want to remove old + instances. + + Args: + *class_names (str): The names of classes to remove. Removes all of them + when empty. + skip_missing (bool): Whether to pass over key errors when a name is + requested that is not currently in the class registry. (Default is + True, let missing names pass silently.) + """ + if len(class_names) == 0: + cls.class_registry = {} + else: + for name in class_names: + try: + cls.class_registry.pop(name) + except KeyError as e: + if skip_missing: + continue + else: + raise KeyError(f"Could not find class {name}") + + def _build_class( + self, name, bases, class_dict, sc_init_kwargs, class_factory_args + ) -> type[_FactoryMade]: + + if "__module__" not in class_dict.keys(): + class_dict["__module__"] = self.factory_function.__module__ + if "__qualname__" not in class_dict.keys(): + class_dict["__qualname__"] = f"{self.__qualname__}.{name}" + sc_init_kwargs["class_factory"] = self + sc_init_kwargs["class_factory_args"] = class_factory_args + + if not any(_FactoryMade in base.mro() for base in bases): + bases = (_FactoryMade, *bases) + + return type(name, bases, class_dict, **sc_init_kwargs) + + @staticmethod + def _verify_name_is_legal(name): + sanitized_name = sanitize_callable_name(name) + if name != sanitized_name: + raise InvalidClassNameError( + f"The class name {name} failed to match with its sanitized version" + f"({sanitized_name}), please supply a valid class name." + ) + + def __reduce__(self): + if ( + self._decorated_as_classfactory + and "" not in self.factory_function.__qualname__ + ): + return ( + _import_object, + (self.factory_function.__module__, self.factory_function.__qualname__), + ) + else: + return (_FACTORY_TOWN.get_factory, (self.factory_function,)) + + +def _import_object(module_name, qualname): + module = import_module(module_name) + obj = module + for name in qualname.split("."): + obj = getattr(obj, name) + return obj + + +class _FactoryMade(ABC): + """ + A mix-in to make class-factory-produced classes pickleable. + + If the factory is used as a decorator for another function, it will conflict with + this function (i.e. the owned function will be the true function, and will mismatch + with imports from that location, which will return the post-decorator factory made + class). This can be resolved by setting the + :attr:`_class_returns_from_decorated_function` attribute to be the decorated + function in the decorator definition. + """ + + _class_returns_from_decorated_function: ClassVar[callable | None] = None + + def __init_subclass__(cls, /, class_factory, class_factory_args, **kwargs): + super().__init_subclass__(**kwargs) + cls._class_factory = class_factory + cls._class_factory_args = class_factory_args + cls._factory_town = _FACTORY_TOWN + + def __reduce__(self): + if ( + self._class_returns_from_decorated_function is not None + and "" + not in self._class_returns_from_decorated_function.__qualname__ + ): + # When we create a class by decorating some other function, this class + # conflicts with its own factory_function attribute in the namespace, so we + # rely on directly re-importing the factory + return ( + _instantiate_from_decorated, + ( + self._class_returns_from_decorated_function.__module__, + self._class_returns_from_decorated_function.__qualname__, + self.__getnewargs_ex__(), + ), + self.__getstate__(), + ) + else: + return ( + _instantiate_from_factory, + ( + self._class_factory, + self._class_factory_args, + self.__getnewargs_ex__(), + ), + self.__getstate__(), + ) + + def __getnewargs_ex__(self): + # Child classes can override this as needed + return (), {} + + def __getstate__(self): + # Python <3.11 compatibility + try: + return super().__getstate__() + except AttributeError: + return dict(self.__dict__) + + def __setstate__(self, state): + # Python <3.11 compatibility + try: + super().__setstate__(state) + except AttributeError: + self.__dict__.update(**state) + + +def _instantiate_from_factory(factory, factory_args, newargs_ex): + """ + Recover the dynamic class, then invoke its `__new__` to avoid instantiation (and + the possibility of positional args in `__init__`). + """ + cls = factory(*factory_args) + return cls.__new__(cls, *newargs_ex[0], **newargs_ex[1]) + + +def _instantiate_from_decorated(module, qualname, newargs_ex): + """ + In case the class comes from a decorated function, we need to import it directly. + """ + cls = _import_object(module, qualname) + return cls.__new__(cls, *newargs_ex[0], **newargs_ex[1]) + + +def classfactory( + factory_function: callable[..., tuple[str, tuple[type, ...], dict, dict]] +) -> _ClassFactory: + """ + A decorator for building dynamic class factories whose classes are unique and whose + terminal instances can be pickled. + + Under the hood, classes created by factories get dependence on + :class:`_FactoryMade` mixed in. This class leverages :meth:`__reduce__` and + :meth:`__init_subclass__` and uses up the class namespace :attr:`_class_factory` + and :attr:`_class_factory_args` to hold data (using up corresponding public + variable names in the :meth:`__init_subclass__` kwargs), so any interference with + these fields may cause unexpected side effects. For un-pickling, the dynamic class + gets recreated then its :meth:`__new__` is called using `__newargs_ex__`; a default + implementation returning no arguments is provided on :class:`_FactoryMade` but can + be overridden. + + Args: + factory_function (callable[..., tuple[str, tuple[type, ...], dict, dict]]): + A function returning arguments that would be passed to `builtins.type` to + dynamically generate a class. The function must accept exclusively + positional arguments + + Returns: + (type[_ClassFactory]): A new callable that returns unique classes whose + instances can be pickled. + + Notes: + If the :param:`factory_function` itself, or any data stored on instances of + its resulting class(es) cannot be pickled, then the instances will not be able + to be pickled. Here we only remove the trouble associated with pickling + dynamically created classes. + + If the `__init_subclass__` kwargs are exploited, remember that these are + subject to all the same "gotchas" as their regular non-factory use; namely, all + child classes must specify _all_ parent class kwargs in order to avoid them + getting overwritten by the parent class defaults! + + Dynamically generated classes can, in turn, be used as base classes for further + `@classfactory` decorated factory functions. + + Warnings: + Use _exclusively_ as a decorator. For an inline constructor for an existing + callable, use :class:`ClassFactory` instead. + + Examples: + >>> import pickle + >>> + >>> from pyiron_workflow.snippets.factory import classfactory + >>> + >>> class HasN(ABC): + ... '''Some class I want to make dynamically subclass.''' + ... def __init_subclass__(cls, /, n=0, s="foo", **kwargs): + ... super(HasN, cls).__init_subclass__(**kwargs) + ... cls.n = n + ... cls.s = s + ... + ... def __init__(self, x, y=0): + ... self.x = x + ... self.y = y + >>> + >>> @classfactory + ... def has_n_factory(n, s="wrapped_function", /): + ... return ( + ... f"{HasN.__name__}{n}{s}", # New class name + ... (HasN,), # Base class(es) + ... {}, # Class attributes dictionary + ... {"n": n, "s": s} + ... # dict of `builtins.type` kwargs (passed to `__init_subclass__`) + ... ) + >>> + >>> Has2 = has_n_factory(2, "my_dynamic_class") + >>> HasToo = has_n_factory(2, "my_dynamic_class") + >>> HasToo is Has2 + True + + >>> foo = Has2(42, y=-1) + >>> print(foo.n, foo.s, foo.x, foo.y) + 2 my_dynamic_class 42 -1 + + >>> reloaded = pickle.loads(pickle.dumps(foo)) # doctest: +SKIP + >>> print(reloaded.n, reloaded.s, reloaded.x, reloaded.y) # doctest: +SKIP + 2 my_dynamic_class 42 -1 # doctest: +SKIP + + """ + factory = _FACTORY_TOWN.get_factory(factory_function) + factory._decorated_as_classfactory = True + return factory + + +class ClassFactory: + """ + A constructor for new class factories. + + Use on existing class factory callables, _not_ as a decorator. + + Cf. the :func:`classfactory` decorator for more info. + """ + + def __new__(cls, factory_function): + return _FACTORY_TOWN.get_factory(factory_function) + + +def sanitize_callable_name(name: str): + """ + A helper class for sanitizing a string so it's appropriate as a class/function name. + """ + # Replace non-alphanumeric characters except underscores + sanitized_name = sub(r"\W+", "_", name) + # Ensure the name starts with a letter or underscore + if ( + len(sanitized_name) > 0 + and not sanitized_name[0].isalpha() + and sanitized_name[0] != "_" + ): + sanitized_name = "_" + sanitized_name + return sanitized_name diff --git a/pyiron_workflow/storage.py b/pyiron_workflow/storage.py index 95a6a30c..bb93f227 100644 --- a/pyiron_workflow/storage.py +++ b/pyiron_workflow/storage.py @@ -372,10 +372,6 @@ def _storage_interfaces(cls): interfaces["h5io"] = H5ioStorage return interfaces - @classmethod - def default_backend(cls): - return "h5io" - class HasTinybaseStorage(HasStorage, ABC): @classmethod @@ -391,3 +387,7 @@ def to_storage(self, storage: TinybaseStorage): @abstractmethod def from_storage(self, storage: TinybaseStorage): pass + + @classmethod + def default_backend(cls): + return "tinybase" diff --git a/pyiron_workflow/transform.py b/pyiron_workflow/transform.py new file mode 100644 index 00000000..7b203d1a --- /dev/null +++ b/pyiron_workflow/transform.py @@ -0,0 +1,441 @@ +""" +Transformer nodes convert many inputs into a single output, or vice-versa. +""" + +from __future__ import annotations + +from abc import ABC, abstractmethod +from dataclasses import is_dataclass, MISSING +import itertools +from typing import Any, ClassVar, Optional + +from pandas import DataFrame + +from pyiron_workflow.channels import NOT_DATA +from pyiron_workflow.io_preview import StaticNode, builds_class_io +from pyiron_workflow.snippets.factory import classfactory + + +class Transformer(StaticNode, ABC): + """ + Transformers are a special :class:`Constructed` case of :class:`StaticNode` nodes + that turn many inputs into a single output or vice-versa. + """ + + def to_dict(self): + pass # Vestigial abstract method + + +class FromManyInputs(Transformer, ABC): + _output_name: ClassVar[str] # Mandatory attribute for non-abstract subclasses + _output_type_hint: ClassVar[Any] = None + + # _build_inputs_preview required from parent class + # Inputs convert to `run_args` as a value dictionary + # This must be commensurate with the internal expectations of on_run + + @abstractmethod + def on_run(self, **inputs_to_value_dict) -> Any: + """Must take inputs kwargs""" + + @property + def run_args(self) -> tuple[tuple, dict]: + return (), self.inputs.to_value_dict() + + @classmethod + def _build_outputs_preview(cls) -> dict[str, Any]: + return {cls._output_name: cls._output_type_hint} + + def process_run_result(self, run_output: Any | tuple) -> Any | tuple: + self.outputs[self._output_name].value = run_output + return run_output + + +class ToManyOutputs(Transformer, ABC): + _input_name: ClassVar[str] # Mandatory attribute for non-abstract subclasses + _input_type_hint: ClassVar[Any] = None + _input_default: ClassVar[Any | NOT_DATA] = NOT_DATA + + # _build_outputs_preview still required from parent class + # Must be commensurate with the dictionary returned by transform_to_output + + @abstractmethod + def on_run(self, input_object) -> callable[..., Any | tuple]: + """Must take the single object to be transformed""" + + @property + def run_args(self) -> tuple[tuple, dict]: + return (self.inputs[self._input_name].value,), {} + + @classmethod + def _build_inputs_preview(cls) -> dict[str, tuple[Any, Any]]: + return {cls._input_name: (cls._input_type_hint, cls._input_default)} + + def process_run_result(self, run_output: dict[str, Any]) -> dict[str, Any]: + for k, v in run_output.items(): + self.outputs[k].value = v + return run_output + + +class _HasLength(Transformer, ABC): + _length: ClassVar[int] # Mandatory attribute for non-abstract subclasses + + +class InputsToList(_HasLength, FromManyInputs, ABC): + _output_name: ClassVar[str] = "list" + _output_type_hint: ClassVar[Any] = list + + def on_run(self, **inputs_to_value_dict): + return list(inputs_to_value_dict.values()) + + @classmethod + def _build_inputs_preview(cls) -> dict[str, tuple[Any, Any]]: + return {f"item_{i}": (None, NOT_DATA) for i in range(cls._length)} + + +class ListToOutputs(_HasLength, ToManyOutputs, ABC): + _input_name: ClassVar[str] = "list" + _input_type_hint: ClassVar[Any] = list + + def on_run(self, input_object: list): + return {f"item_{i}": v for i, v in enumerate(input_object)} + + @classmethod + def _build_outputs_preview(cls) -> dict[str, Any]: + return {f"item_{i}": None for i in range(cls._length)} + + +@builds_class_io +@classfactory +def inputs_to_list_factory(n: int, /) -> type[InputsToList]: + return ( + f"{InputsToList.__name__}{n}", + (InputsToList,), + {"_length": n}, + {}, + ) + + +def inputs_to_list(n: int, *node_args, **node_kwargs): + return inputs_to_list_factory(n)(*node_args, **node_kwargs) + + +@builds_class_io +@classfactory +def list_to_outputs_factory(n: int, /) -> type[ListToOutputs]: + return ( + f"{ListToOutputs.__name__}{n}", + (ListToOutputs,), + {"_length": n}, + {}, + ) + + +def list_to_outputs(n: int, /, *node_args, **node_kwargs) -> ListToOutputs: + return list_to_outputs_factory(n)(*node_args, **node_kwargs) + + +class InputsToDict(FromManyInputs, ABC): + _output_name: ClassVar[str] = "dict" + _output_type_hint: ClassVar[Any] = dict + _input_specification: ClassVar[ + list[str] | dict[str, tuple[Any | None, Any | NOT_DATA]] + ] + + def on_run(self, **inputs_to_value_dict): + return inputs_to_value_dict + + @classmethod + def _build_inputs_preview(cls) -> dict[str, tuple[Any | None, Any | NOT_DATA]]: + if isinstance(cls._input_specification, list): + return {key: (None, NOT_DATA) for key in cls._input_specification} + else: + return cls._input_specification + + @staticmethod + def hash_specification( + input_specification: list[str] | dict[str, tuple[Any | None, Any | NOT_DATA]] + ): + """For generating unique subclass names.""" + + if isinstance(input_specification, list): + return hash(tuple(input_specification)) + else: + flattened_tuple = tuple( + itertools.chain.from_iterable( + (key, *value) for key, value in input_specification.items() + ) + ) + try: + return hash(flattened_tuple) + except Exception as e: + raise ValueError( + f"To automatically generate a unique name for subclasses of " + f"{InputsToDict.__name__}, the input specification must be fully " + f"hashable, but it was not. Either pass fully hashable hints and " + f"defaults, or explicitly provide a class name suffix. Received " + f"specification: {input_specification}" + ) from e + + +@classfactory +def inputs_to_dict_factory( + input_specification: list[str] | dict[str, tuple[Any | None, Any | NOT_DATA]], + class_name_suffix: str | None, + /, +) -> type[InputsToDict]: + if class_name_suffix is None: + class_name_suffix = str( + InputsToDict.hash_specification(input_specification) + ).replace("-", "m") + return ( + f"{InputsToDict.__name__}{class_name_suffix}", + (InputsToDict,), + {"_input_specification": input_specification}, + {}, + ) + + +def inputs_to_dict( + input_specification: list[str] | dict[str, tuple[Any | None, Any | NOT_DATA]], + *node_args, + class_name_suffix: Optional[str] = None, + **node_kwargs, +): + """ + Build a new :class:`InputsToDict` subclass and instantiate it. + + Tries to automatically generate a subclass name by hashing the + :param:`input_specification`. If such hashing fails, you will instead _need_ to + provide an explicit :param:`class_name_suffix` + + Args: + input_specification (list[str] | dict[str, tuple[Any | None, Any | NOT_DATA]]): + The input channel names, or full input specification in the form + `{key: (type_hint, default_value))}`. + *node_args: Other args for the node instance. + class_name_suffix (str | None): The suffix to use in the class name. (Default + is None, try to generate the suffix by hashing :param:`input_specification`. + **node_kwargs: Other kwargs for the node instance. + + Returns: + (InputsToDict): A new node for transforming inputs into a dictionary. + """ + cls = inputs_to_dict_factory(input_specification, class_name_suffix) + cls.preview_io() + return cls(*node_args, **node_kwargs) + + +class InputsToDataframe(_HasLength, FromManyInputs, ABC): + """ + Turns inputs of dictionaries (all with the same keys) into a single + :class:`pandas.DataFrame`. + """ + + _output_name: ClassVar[str] = "df" + _output_type_hint: ClassVar[Any] = DataFrame + + def on_run(self, *rows: dict[str, Any]) -> Any: + df_dict = {} + for i, row in enumerate(rows): + for key, value in row.items(): + if i == 0: + df_dict[key] = [value] + else: + df_dict[key].append(value) + return DataFrame(df_dict) + + @property + def run_args(self) -> tuple[tuple, dict]: + return tuple(self.inputs.to_value_dict().values()), {} + + @classmethod + def _build_inputs_preview(cls) -> dict[str, tuple[Any, Any]]: + return {f"row_{i}": (dict, NOT_DATA) for i in range(cls._length)} + + +@classfactory +def inputs_to_dataframe_factory(n: int, /) -> type[InputsToDataframe]: + return ( + f"{InputsToDataframe.__name__}{n}", + (InputsToDataframe,), + {"_length": n}, + {}, + ) + + +def inputs_to_dataframe(n: int, *node_args, **node_kwargs): + cls = inputs_to_dataframe_factory(n) + cls.preview_io() + return cls(*node_args, **node_kwargs) + + +class DataclassNode(FromManyInputs, ABC): + """ + A base class for a node that converts inputs into a dataclass instance. + """ + + dataclass: ClassVar[type] # Mandatory in children, must pass `is_dataclass` + _output_name: ClassVar[str] = "dataclass" + + @classmethod + @property + def _dataclass_fields(cls): + return cls.dataclass.__dataclass_fields__ + + def _setup_node(self) -> None: + super()._setup_node() + # Then leverage default factories from the dataclass + for name, channel in self.inputs.items(): + if ( + channel.value is NOT_DATA + and self._dataclass_fields[name].default_factory is not MISSING + ): + self.inputs[name] = self._dataclass_fields[name].default_factory() + + def on_run(self, **inputs_to_value_dict): + return self.dataclass(**inputs_to_value_dict) + + @property + def run_args(self) -> tuple[tuple, dict]: + return (), self.inputs.to_value_dict() + + @classmethod + def _build_inputs_preview(cls) -> dict[str, tuple[Any, Any]]: + # Make a channel for each field + return { + name: (f.type, NOT_DATA if f.default is MISSING else f.default) + for name, f in cls._dataclass_fields.items() + } + + +@classfactory +def dataclass_node_factory(dataclass: type, /) -> type[DataclassNode]: + if not is_dataclass(dataclass): + raise TypeError( + f"{DataclassNode} expected to get a dataclass but {dataclass} failed " + f"`dataclasses.is_dataclass`." + ) + if type(dataclass) is not type: + raise TypeError( + f"{DataclassNode} expected to get a dataclass but {dataclass} is not " + f"type `type`." + ) + return ( + f"{DataclassNode.__name__}{dataclass.__name__}", + (DataclassNode,), + { + "dataclass": dataclass, + "_output_type_hint": dataclass, + }, + {}, + ) + + +def as_dataclass_node(dataclass: type): + """ + Decorates a dataclass as a dataclass node -- i.e. a node whose inputs correspond + to dataclass fields and whose output is an instance of the dataclass. + + The underlying dataclass can be accessed on the :attr:`.dataclass` class attribute + of the resulting node class. + + Leverages defaults (default factories) on dataclass fields to populate input + channel values at class defintion (instantiation). + + Args: + dataclass (type): A dataclass, i.e. class passing `dataclasses.is_dataclass`. + + Returns: + (type[DataclassNode]): A :class:`DataclassNode` subclass whose instances + transform inputs to an instance of that dataclass. + + Examples: + >>> from dataclasses import dataclass, field + >>> + >>> from pyiron_workflow import Workflow + >>> + >>> def some_list(): + ... return [1, 2, 3] + >>> + >>> @Workflow.wrap.as_dataclass_node + ... @dataclass + ... class Foo: + ... necessary: str + ... bar: str = "bar" + ... answer: int = 42 + ... complex_: list = field(default_factory=some_list) + >>> + >>> f = Foo() + >>> print(f.readiness_report) + DataclassNodeFoo readiness: False + STATE: + running: False + failed: False + INPUTS: + necessary ready: False + bar ready: True + answer ready: True + complex_ ready: True + + >>> f(necessary="input as a node kwarg") + Foo(necessary='input as a node kwarg', bar='bar', answer=42, complex_=[1, 2, 3]) + """ + cls = dataclass_node_factory(dataclass) + cls.preview_io() + return cls + + +def dataclass_node(dataclass: type, *node_args, **node_kwargs): + """ + Builds a dataclass node from a dataclass -- i.e. a node whose inputs correspond + to dataclass fields and whose output is an instance of the dataclass. + + The underlying dataclass can be accessed on the :attr:`.dataclass` class attribute + of the resulting node. + + Leverages defaults (default factories) on dataclass fields to populate input + channel values at class defintion (instantiation). + + Args: + dataclass (type): A dataclass, i.e. class passing `dataclasses.is_dataclass`. + *node_args: Other :class:`Node` positional arguments. + **node_kwargs: Other :class:`Node` keyword arguments. + + Returns: + (DataclassNode): An instance of the dynamically created :class:`DataclassNode` + subclass. + + Examples: + >>> from dataclasses import dataclass, field + >>> + >>> from pyiron_workflow import Workflow + >>> + >>> def some_list(): + ... return [1, 2, 3] + >>> + >>> @dataclass + ... class Foo: + ... necessary: str + ... bar: str = "bar" + ... answer: int = 42 + ... complex_: list = field(default_factory=some_list) + >>> + >>> f = Workflow.create.transformer.dataclass_node(Foo, label="my_dc") + >>> print(f.readiness_report) + my_dc readiness: False + STATE: + running: False + failed: False + INPUTS: + necessary ready: False + bar ready: True + answer ready: True + complex_ ready: True + + >>> f(necessary="input as a node kwarg") + Foo(necessary='input as a node kwarg', bar='bar', answer=42, complex_=[1, 2, 3]) + """ + cls = dataclass_node_factory(dataclass) + cls.preview_io() + return cls(*node_args, **node_kwargs) diff --git a/pyiron_workflow/workflow.py b/pyiron_workflow/workflow.py index 35672b0a..49fbcf06 100644 --- a/pyiron_workflow/workflow.py +++ b/pyiron_workflow/workflow.py @@ -8,20 +8,20 @@ from typing import Literal, Optional, TYPE_CHECKING +from bidict import bidict + from pyiron_workflow.composite import Composite from pyiron_workflow.io import Inputs, Outputs from pyiron_workflow.semantics import ParentMost if TYPE_CHECKING: - from bidict import bidict - from pyiron_workflow.channels import InputData, OutputData from pyiron_workflow.io import IO from pyiron_workflow.node import Node -class Workflow(Composite, ParentMost): +class Workflow(ParentMost, Composite): """ Workflows are a dynamic composite node -- i.e. they hold and run a collection of nodes (a subgraph) which can be dynamically modified (adding and removing nodes, @@ -53,12 +53,25 @@ class Workflow(Composite, ParentMost): - Workflows are living, their IO always reflects their current state of child nodes - Workflows are parent-most objects, they cannot be a sub-graph of a larger graph + Attribute: + inputs/outputs_map (bidict|None): Maps in the form + `{"node_label__channel_label": "some_better_name"}` that expose canonically + named channels of child nodes under a new name. This can be used both for re- + naming regular IO (i.e. unconnected child channels), as well as forcing the + exposure of irregular IO (i.e. child channels that are already internally + connected to some other child channel). Non-`None` values provided at input + can be in regular dictionary form, but get re-cast as a clean bidict to ensure + the bijective nature of the maps (i.e. there is a 1:1 connection between any + IO exposed at the :class:`Composite` level and the underlying channels). + children (bidict.bidict[pyiron_workflow.node.Node]): The owned nodes that + form the composite subgraph. + Examples: We allow adding nodes to workflows in five equivalent ways: >>> from pyiron_workflow.workflow import Workflow >>> - >>> @Workflow.wrap_as.function_node() + >>> @Workflow.wrap.as_function_node() ... def fnc(x=0): ... return x + 1 >>> @@ -94,7 +107,7 @@ class Workflow(Composite, ParentMost): Let's use these to explore a workflow's input and output, which are dynamically generated from the unconnected IO of its nodes: - >>> @Workflow.wrap_as.function_node("y") + >>> @Workflow.wrap.as_function_node("y") ... def plus_one(x: int = 0): ... return x + 1 >>> @@ -198,39 +211,126 @@ def __init__( inputs_map: Optional[dict | bidict] = None, outputs_map: Optional[dict | bidict] = None, automate_execution: bool = True, + **kwargs, ): + self._inputs_map = None + self._outputs_map = None + self.inputs_map = inputs_map + self.outputs_map = outputs_map + self._inputs = None + self._outputs = None + self.automate_execution = automate_execution + super().__init__( + *nodes, label=label, parent=None, + overwrite_save=overwrite_save, + run_after_init=run_after_init, save_after_run=save_after_run, storage_backend=storage_backend, strict_naming=strict_naming, - inputs_map=inputs_map, - outputs_map=outputs_map, + **kwargs, ) - self.automate_execution = automate_execution - for node in nodes: + def _after_node_setup( + self, + *args, + overwrite_save: bool = False, + run_after_init: bool = False, + **kwargs, + ): + + for node in args: self.add_child(node) + super()._after_node_setup( + overwrite_save=overwrite_save, run_after_init=run_after_init, **kwargs + ) - def _get_linking_channel( - self, - child_reference_channel: InputData | OutputData, - composite_io_key: str, - ) -> InputData | OutputData: - """ - Build IO by reference: just return the child's channel itself. - """ - return child_reference_channel + @property + def inputs_map(self) -> bidict | None: + self._deduplicate_nones(self._inputs_map) + return self._inputs_map + + @inputs_map.setter + def inputs_map(self, new_map: dict | bidict | None): + self._deduplicate_nones(new_map) + if new_map is not None: + new_map = bidict(new_map) + self._inputs_map = new_map + + @property + def outputs_map(self) -> bidict | None: + self._deduplicate_nones(self._outputs_map) + return self._outputs_map + + @outputs_map.setter + def outputs_map(self, new_map: dict | bidict | None): + self._deduplicate_nones(new_map) + if new_map is not None: + new_map = bidict(new_map) + self._outputs_map = new_map + + @staticmethod + def _deduplicate_nones(some_map: dict | bidict | None) -> dict | bidict | None: + if some_map is not None: + for k, v in some_map.items(): + if v is None: + some_map[k] = (None, f"{k} disabled") @property def inputs(self) -> Inputs: return self._build_inputs() + def _build_inputs(self): + return self._build_io("inputs", self.inputs_map) + @property def outputs(self) -> Outputs: return self._build_outputs() + def _build_outputs(self): + return self._build_io("outputs", self.outputs_map) + + def _build_io( + self, + i_or_o: Literal["inputs", "outputs"], + key_map: dict[str, str | None] | None, + ) -> Inputs | Outputs: + """ + Build an IO panel for exposing child node IO to the outside world at the level + of the composite node's IO. + + Args: + target [Literal["inputs", "outputs"]]: Whether this is I or O. + key_map [dict[str, str]|None]: A map between the default convention for + mapping child IO to composite IO (`"{node.label}__{channel.label}"`) and + whatever label you actually want to expose to the composite user. Also + allows non-standards channel exposure, i.e. exposing + internally-connected channels (which would not normally be exposed) by + providing a string-to-string map, or suppressing unconnected channels + (which normally would be exposed) by providing a string-None map. + + Returns: + (Inputs|Outputs): The populated panel. + """ + key_map = {} if key_map is None else key_map + io = Inputs() if i_or_o == "inputs" else Outputs() + for node in self.children.values(): + panel = getattr(node, i_or_o) + for channel in panel: + try: + io_panel_key = key_map[channel.scoped_label] + if not isinstance(io_panel_key, tuple): + # Tuples indicate that the channel has been deactivated + # This is a necessary misdirection to keep the bidict working, + # as we can't simply map _multiple_ keys to `None` + io[io_panel_key] = channel + except KeyError: + if not channel.connected: + io[channel.scoped_label] = channel + return io + def run( self, check_readiness: bool = True, @@ -309,9 +409,49 @@ def _signal_connections(self) -> list[tuple[tuple[str, str], tuple[str, str]]]: ) return signal_connections + def _rebuild_data_io(self): + """ + Try to rebuild the IO. + + If an error is encountered, revert back to the existing IO then raise it. + """ + old_inputs = self.inputs + old_outputs = self.outputs + connection_changes = [] # For reversion if there's an error + try: + self._inputs = self._build_inputs() + self._outputs = self._build_outputs() + for old, new in [(old_inputs, self.inputs), (old_outputs, self.outputs)]: + for old_channel in old: + if old_channel.connected: + # If the old channel was connected to stuff, we'd better still + # have a corresponding channel and be able to copy these, or we + # should fail hard. + # But, if it wasn't connected, we don't even care whether or not + # we still have a corresponding channel to copy to + new_channel = new[old_channel.label] + new_channel.copy_connections(old_channel) + swapped_conenctions = old_channel.disconnect_all() # Purge old + connection_changes.append( + (new_channel, old_channel, swapped_conenctions) + ) + except Exception as e: + for new_channel, old_channel, swapped_conenctions in connection_changes: + new_channel.disconnect(*swapped_conenctions) + old_channel.connect(*swapped_conenctions) + self._inputs = old_inputs + self._outputs = old_outputs + e.message = ( + f"Unable to rebuild IO for {self.label}; reverting to old IO." + f"{e.message}" + ) + raise e + def to_storage(self, storage): storage["package_requirements"] = list(self.package_requirements) storage["automate_execution"] = self.automate_execution + storage["inputs_map"] = self.inputs_map + storage["outputs_map"] = self.outputs_map super().to_storage(storage) storage["_data_connections"] = self._data_connections @@ -321,10 +461,23 @@ def to_storage(self, storage): storage["starting_nodes"] = [n.label for n in self.starting_nodes] def from_storage(self, storage): + from pyiron_contrib.tinybase.storage import GenericStorage + + self.inputs_map = ( + storage["inputs_map"].to_object() + if isinstance(storage["inputs_map"], GenericStorage) + else storage["inputs_map"] + ) + self.outputs_map = ( + storage["outputs_map"].to_object() + if isinstance(storage["outputs_map"], GenericStorage) + else storage["outputs_map"] + ) + self._reinstantiate_children(storage) self.automate_execution = storage["automate_execution"] - # Super call will rebuild the IO, so first get our automate_execution flag super().from_storage(storage) + self._rebuild_data_io() # To apply any map that was saved self._rebuild_connections(storage) def _reinstantiate_children(self, storage): @@ -363,6 +516,31 @@ def _rebuild_execution_graph(self, storage): self.children[label] for label in storage["starting_nodes"] ] + def __getstate__(self): + state = super().__getstate__() + + # Transform the IO maps into a datatype that plays well with h5io + # (Bidict implements a custom reconstructor, which hurts us) + state["_inputs_map"] = ( + None if self._inputs_map is None else dict(self._inputs_map) + ) + state["_outputs_map"] = ( + None if self._outputs_map is None else dict(self._outputs_map) + ) + + return state + + def __setstate__(self, state): + # Transform the IO maps back into the right class (bidict) + state["_inputs_map"] = ( + None if state["_inputs_map"] is None else bidict(state["_inputs_map"]) + ) + state["_outputs_map"] = ( + None if state["_outputs_map"] is None else bidict(state["_outputs_map"]) + ) + + super().__setstate__(state) + def save(self): if self.storage_backend == "tinybase" and any( node.package_identifier is None for node in self @@ -387,3 +565,23 @@ def _owned_io_panels(self) -> list[IO]: self.signals.input, self.signals.output, ] + + def replace_child( + self, owned_node: Node | str, replacement: Node | type[Node] + ) -> Node: + super().replace_child(owned_node=owned_node, replacement=replacement) + + # Finally, make sure the IO is constructible with this new node, which will + # catch things like incompatible IO maps + try: + # Make sure node-level IO is pointing to the new node and that macro-level + # IO gets safely reconstructed + self._rebuild_data_io() + except Exception as e: + # If IO can't be successfully rebuilt using this node, revert changes and + # raise the exception + self.replace_child(replacement, owned_node) # Guaranteed to work since + # replacement in the other direction was already a success + raise e + + return owned_node diff --git a/setup.py b/setup.py index be283d3a..dee637f3 100644 --- a/setup.py +++ b/setup.py @@ -32,23 +32,23 @@ 'cloudpickle==3.0.0', 'graphviz==0.20.3', 'h5io==0.2.2', - 'h5io_browser==0.0.9', - 'matplotlib==3.8.3', - 'pyiron_base==0.7.9', - 'pyiron_contrib==0.1.15', - 'pympipool==0.7.13', + 'h5io_browser==0.0.12', + 'matplotlib==3.8.4', + 'pandas==2.2.0', + 'pyiron_base==0.8.3', + 'pyiron_contrib==0.1.16', + 'pympipool==0.8.0', 'toposort==1.10', - 'typeguard==4.1.5', + 'typeguard==4.2.1', ], extras_require={ "node_library": [ 'ase==3.22.1', - 'atomistics==0.1.23', + 'atomistics==0.1.27', 'matgl==0.9.2', 'numpy==1.26.4', - 'pandas==2.2.0', - 'phonopy==2.21.2', - 'pyiron_atomistics==0.4.17', + 'phonopy==2.22.1', + 'pyiron_atomistics==0.5.4', ], }, cmdclass=versioneer.get_cmdclass(), diff --git a/tests/integration/test_parallel_speedup.py b/tests/integration/test_parallel_speedup.py index 45e8a332..cb42e19d 100644 --- a/tests/integration/test_parallel_speedup.py +++ b/tests/integration/test_parallel_speedup.py @@ -7,21 +7,17 @@ class TestParallelSpeedup(unittest.TestCase): def test_speedup(self): - @Workflow.wrap_as.function_node() - def Wait(t): - sleep(t) - return True def make_workflow(label): wf = Workflow(label) - wf.a = Wait(t) - wf.b = Wait(t) - wf.c = Wait(t) + wf.a = Workflow.create.standard.Sleep(t) + wf.b = Workflow.create.standard.Sleep(t) + wf.c = Workflow.create.standard.Sleep(t) wf.d = wf.create.standard.UserInput(t) wf.automate_execution = False return wf - t = 2.5 + t = 5 wf = make_workflow("serial") wf.a >> wf.b >> wf.c >> wf.d @@ -36,7 +32,7 @@ def make_workflow(label): wf.d << (wf.a, wf.b, wf.c) wf.starting_nodes = [wf.a, wf.b, wf.c] - with wf.create.Executor(max_workers=3, cores_per_worker=1) as executor: + with wf.create.ProcessPoolExecutor(max_workers=3) as executor: wf.a.executor = executor wf.b.executor = executor wf.c.executor = executor diff --git a/tests/integration/test_provenance.py b/tests/integration/test_provenance.py new file mode 100644 index 00000000..1cfbf6c0 --- /dev/null +++ b/tests/integration/test_provenance.py @@ -0,0 +1,108 @@ +from concurrent.futures import ThreadPoolExecutor +from time import sleep +import unittest + +from pyiron_workflow.workflow import Workflow + + +class TestProvenance(unittest.TestCase): + """ + Verify that the post-facto provenance record works, even under complex conditions + like nested composites and executors. + """ + + def setUp(self) -> None: + @Workflow.wrap.as_function_node() + def Slow(t): + sleep(t) + return t + + @Workflow.wrap.as_macro_node() + def Provenance(self, t): + self.fast = Workflow.create.standard.UserInput(t) + self.slow = Slow(t) + self.double = self.fast + self.slow + return self.double + + wf = Workflow("provenance") + wf.time = Workflow.create.standard.UserInput(2) + wf.prov = Provenance(t=wf.time) + wf.post = wf.prov + 2 + self.wf = wf + self.expected_post = { + wf.post.scoped_label: (2 * wf.time.inputs.user_input.value) + 2 + } + + def test_executed_provenance(self): + with ThreadPoolExecutor() as exe: + self.wf.prov.executor = exe + out = self.wf() + + self.assertDictEqual( + self.expected_post, + out, + msg="Sanity check that the graph is executing ok" + ) + + self.assertListEqual( + ['time', 'prov', 'post'], + self.wf.provenance_by_execution, + msg="Even with a child running on an executor, provenance should log" + ) + + self.assertListEqual( + self.wf.provenance_by_execution, + self.wf.provenance_by_completion, + msg="The workflow itself is serial and these should be identical." + ) + + self.assertListEqual( + ['t', 'slow', 'fast', 'double'], + self.wf.prov.provenance_by_execution, + msg="Later connections get priority over earlier connections, so we expect " + "the t-node to trigger 'slow' before 'fast'" + ) + + self.assertListEqual( + self.wf.prov.provenance_by_execution, + self.wf.prov.provenance_by_completion, + msg="The macro is running on an executor, but its children are in serial," + "so completion and execution order should be the same" + ) + + def test_execution_vs_completion(self): + + with ThreadPoolExecutor(max_workers=2) as exe: + self.wf.prov.fast.executor = exe + self.wf.prov.slow.executor = exe + out = self.wf() + + self.assertDictEqual( + self.expected_post, + out, + msg="Sanity check that the graph is executing ok" + ) + + self.assertListEqual( + ['t', 'slow', 'fast', 'double'], + self.wf.prov.provenance_by_execution, + msg="Later connections get priority over earlier connections, so we expect " + "the t-node to trigger 'slow' before 'fast'" + ) + + self.assertListEqual( + ['t', 'fast', 'slow', 'double'], + self.wf.prov.provenance_by_completion, + msg="Since 'slow' is slow it shouldn't _finish_ until after 'fast' (but " + "still before 'double' since 'double' depends on 'slow')" + ) + + self.assertListEqual( + self.wf.provenance_by_execution, + self.wf.provenance_by_completion, + msg="The workflow itself is serial and these should be identical." + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/integration/test_transform.py b/tests/integration/test_transform.py new file mode 100644 index 00000000..deef6453 --- /dev/null +++ b/tests/integration/test_transform.py @@ -0,0 +1,45 @@ +import pickle +import unittest + +from pyiron_workflow.transform import ( + inputs_to_list, + inputs_to_list_factory, + list_to_outputs, + list_to_outputs_factory +) + + +class TestTransform(unittest.TestCase): + def test_list(self): + n = 3 + inp = inputs_to_list(n, *list(range(n)), label="inp") + out = list_to_outputs(n, inp, label="out") + out() + self.assertListEqual( + list(range(3)), + out.outputs.to_list(), + msg="Expected behaviour here is an autoencoder" + ) + + inp_class = inputs_to_list_factory(n) + out_class = list_to_outputs_factory(n) + + self.assertIs( + inp_class, + inp.__class__, + msg="Regardless of origin, we expect to be constructing the exact same " + "class" + ) + self.assertIs(out_class, out.__class__) + + reloaded = pickle.loads(pickle.dumps(out)) + self.assertEqual( + out.label, + reloaded.label, + msg="Transformers should be pickleable" + ) + self.assertDictEqual( + out.outputs.to_value_dict(), + reloaded.outputs.to_value_dict(), + msg="Transformers should be pickleable" + ) diff --git a/tests/integration/test_workflow.py b/tests/integration/test_workflow.py index af7597f7..71e080de 100644 --- a/tests/integration/test_workflow.py +++ b/tests/integration/test_workflow.py @@ -1,13 +1,36 @@ import math +import pickle import random +import time import unittest +import pyiron_workflow.loops from pyiron_workflow._tests import ensure_tests_in_python_path from pyiron_workflow.channels import OutputSignal from pyiron_workflow.function import Function from pyiron_workflow.workflow import Workflow +@Workflow.wrap.as_function_node("random") +def RandomFloat() -> float: + return random.random() + + +@Workflow.wrap.as_function_node("gt") +def GreaterThan(x: float, threshold: float): + return x > threshold + + +def foo(x): + y = x + 2 + return y + + +@Workflow.wrap.as_function_node("my_output") +def Bar(x): + return x * x + + class TestTopology(unittest.TestCase): @classmethod def setUpClass(cls) -> None: @@ -19,7 +42,7 @@ def test_manually_constructed_cyclic_graph(self): Check that cyclic graphs run. """ - @Workflow.wrap_as.function_node() + @Workflow.wrap.as_function_node() def randint(low=0, high=20): rand = random.randint(low, high) print(f"Generating random number between {low} and {high}...{rand}!") @@ -32,17 +55,14 @@ class GreaterThanLimitSwitch(Function): """ def __init__(self, **kwargs): - super().__init__( - None, - output_labels="value_gt_limit", - **kwargs - ) + super().__init__(**kwargs) self.signals.output.true = OutputSignal("true", self) self.signals.output.false = OutputSignal("false", self) @staticmethod def node_function(value, limit=10): - return value > limit + value_gt_limit = value > limit + return value_gt_limit def process_run_result(self, function_output): """ @@ -57,7 +77,7 @@ def process_run_result(self, function_output): print(f"{self.inputs.value.value} <= {self.inputs.limit.value}") self.signals.output.false() - @Workflow.wrap_as.function_node("sqrt") + @Workflow.wrap.as_function_node("sqrt") def sqrt(value=0): root_value = math.sqrt(value) print(f"sqrt({value}) = {root_value}") @@ -85,50 +105,37 @@ def sqrt(value=0): def test_for_loop(self): Workflow.register("static.demo_nodes", "demo") - n = 5 - - bulk_loop = Workflow.create.meta.for_loop( - Workflow.create.demo.OptionallyAdd, - n, - iterate_on=("y",), - )() - base = 42 - to_add = list(range(n)) - out = bulk_loop( - x=base, # Sent equally to each body node - Y=to_add, # Distributed across body nodes + to_add = list(range(5)) + bulk_loop = Workflow.create.for_node( + Workflow.create.demo.OptionallyAdd, + iter_on=("y",), + x=base, # Broadcast + y=to_add # Scattered ) + out = bulk_loop() - for output, expectation in zip(out.SUM, [base + v for v in to_add]): + for output, expectation in zip( + out.df["sum"].values.tolist(), + [base + v for v in to_add] + ): self.assertAlmostEqual( output, expectation, - msg="Output should be list result of each individiual result" ) def test_while_loop(self): - import sys - limit = sys.getrecursionlimit() - sys.setrecursionlimit(2000) with self.subTest("Random"): random.seed(0) - @Workflow.wrap_as.function_node("random") - def RandomFloat() -> float: - return random.random() - - @Workflow.wrap_as.function_node("gt") - def GreaterThan(x: float, threshold: float): - return x > threshold - - RandomWhile = Workflow.create.meta.while_loop( + RandomWhile = pyiron_workflow.loops.while_loop( loop_body_class=RandomFloat, condition_class=GreaterThan, internal_connection_map=[ ("RandomFloat", "random", "GreaterThan", "x") ], + inputs_map={"GreaterThan__threshold": "threshold"}, outputs_map={"RandomFloat__random": "capped_result"} ) @@ -141,7 +148,7 @@ def GreaterThan(x: float, threshold: float): wf.random_while = RandomWhile() ## Give convenient labels - wf.inputs_map = {"random_while__GreaterThan__threshold": "threshold"} + wf.inputs_map = {"random_while__threshold": "threshold"} wf.outputs_map = {"random_while__capped_result": "capped_result"} self.assertAlmostEqual( @@ -149,11 +156,9 @@ def GreaterThan(x: float, threshold: float): 0.014041700164018955, # For this reason we set the random seed ) - sys.setrecursionlimit(limit) - with self.subTest("Self-data-loop"): - AddWhile = Workflow.create.meta.while_loop( + AddWhile = pyiron_workflow.loops.while_loop( loop_body_class=Workflow.create.standard.Add, condition_class=Workflow.create.standard.LessThan, internal_connection_map=[ @@ -206,6 +211,58 @@ def test_executor_and_creator_interaction(self): wf.after_pickling = wf.create.demo.OptionallyAdd(2, y=3) wf() + def test_executors(self): + executors = [ + Workflow.create.ProcessPoolExecutor, + Workflow.create.ThreadPoolExecutor, + Workflow.create.CloudpickleProcessPoolExecutor, + Workflow.create.PyMpiPoolExecutor + ] + + wf = Workflow("executed") + wf.a = Workflow.create.standard.UserInput(42) # Regular + wf.b = wf.a + 1 # Injected + wf.c = Workflow.create.function_node(foo, wf.b) # Instantiated from function + wf.d = Bar(wf.c) # From decorated function + + reference_output = wf() + + with self.subTest("Pickle sanity check"): + reloaded = pickle.loads(pickle.dumps(wf)) + self.assertDictEqual(reference_output, reloaded.outputs.to_value_dict()) + + for exe_cls in executors: + with self.subTest( + f"{exe_cls.__module__}.{exe_cls.__qualname__} entire workflow" + ): + with exe_cls() as exe: + wf.executor = exe + self.assertDictEqual( + reference_output, + wf().result().outputs.to_value_dict() + ) + self.assertFalse( + wf.running, + msg="The workflow should stop. For thread pool this required a " + "little sleep" + ) + wf.executor = None + + with self.subTest(f"{exe_cls.__module__}.{exe_cls.__qualname__} each node"): + with exe_cls() as exe: + for child in wf: + child.executor = exe + executed_output = wf() + self.assertDictEqual(reference_output, executed_output) + self.assertFalse( + any(n.running for n in wf), + msg=f"All children should be done running -- for thread pools this " + f"requires a very short sleep -- got " + f"{[(n.label, n.running) for n in wf]}" + ) + for child in wf: + child.executor = None + if __name__ == '__main__': unittest.main() diff --git a/tests/static/demo_nodes.py b/tests/static/demo_nodes.py index 6930d734..8f876a77 100644 --- a/tests/static/demo_nodes.py +++ b/tests/static/demo_nodes.py @@ -7,21 +7,21 @@ from pyiron_workflow import Workflow -@Workflow.wrap_as.function_node("sum") +@Workflow.wrap.as_function_node("sum") def OptionallyAdd(x: int, y: Optional[int] = None) -> int: y = 0 if y is None else y return x + y -@Workflow.wrap_as.macro_node("add_three") -def AddThree(macro, x: int) -> int: - macro.one = macro.create.standard.Add(x, 1) - macro.two = macro.create.standard.Add(macro.one, 1) - macro.three = macro.create.standard.Add(macro.two, 1) - return macro.three +@Workflow.wrap.as_macro_node("add_three") +def AddThree(self, x: int) -> int: + self.one = self.create.standard.Add(x, 1) + self.two = self.create.standard.Add(self.one, 1) + self.three = self.create.standard.Add(self.two, 1) + return self.three -@Workflow.wrap_as.function_node("add") +@Workflow.wrap.as_function_node("add") def AddPlusOne(obj, other): """The same IO labels as `standard.Add`, but with type hints and a boost.""" return obj + other + 1 @@ -31,6 +31,6 @@ def dynamic(x): return x + 1 -Dynamic = Workflow.wrap_as.function_node()(dynamic) +Dynamic = Workflow.wrap.as_function_node()(dynamic) nodes = [OptionallyAdd, AddThree, AddPlusOne, Dynamic] diff --git a/tests/static/faulty_node_package.py b/tests/static/faulty_node_package.py index 984e2ce1..b42375e2 100644 --- a/tests/static/faulty_node_package.py +++ b/tests/static/faulty_node_package.py @@ -5,7 +5,7 @@ from pyiron_workflow import Workflow -@Workflow.wrap_as.function_node("sum") +@Workflow.wrap.as_function_node("sum") def Add(x: int, y: int) -> int: return x + y diff --git a/tests/static/forgetful_node_package.py b/tests/static/forgetful_node_package.py index da7d1487..aab60089 100644 --- a/tests/static/forgetful_node_package.py +++ b/tests/static/forgetful_node_package.py @@ -5,7 +5,7 @@ from pyiron_workflow import Workflow -@Workflow.wrap_as.function_node("sum") +@Workflow.wrap.as_function_node("sum") def Add(x: int, y: int) -> int: return x + y diff --git a/tests/static/nodes_subpackage/demo_nodes.py b/tests/static/nodes_subpackage/demo_nodes.py index c282d3b4..bbbc7e41 100644 --- a/tests/static/nodes_subpackage/demo_nodes.py +++ b/tests/static/nodes_subpackage/demo_nodes.py @@ -7,7 +7,7 @@ from pyiron_workflow import Workflow -@Workflow.wrap_as.function_node("sum") +@Workflow.wrap.as_function_node("sum") def OptionallyAdd(x: int, y: Optional[int] = None) -> int: y = 0 if y is None else y return x + y diff --git a/tests/static/nodes_subpackage/subsub_package/demo_nodes.py b/tests/static/nodes_subpackage/subsub_package/demo_nodes.py index c282d3b4..bbbc7e41 100644 --- a/tests/static/nodes_subpackage/subsub_package/demo_nodes.py +++ b/tests/static/nodes_subpackage/subsub_package/demo_nodes.py @@ -7,7 +7,7 @@ from pyiron_workflow import Workflow -@Workflow.wrap_as.function_node("sum") +@Workflow.wrap.as_function_node("sum") def OptionallyAdd(x: int, y: Optional[int] = None) -> int: y = 0 if y is None else y return x + y diff --git a/tests/static/nodes_subpackage/subsub_sibling/demo_nodes.py b/tests/static/nodes_subpackage/subsub_sibling/demo_nodes.py index c282d3b4..bbbc7e41 100644 --- a/tests/static/nodes_subpackage/subsub_sibling/demo_nodes.py +++ b/tests/static/nodes_subpackage/subsub_sibling/demo_nodes.py @@ -7,7 +7,7 @@ from pyiron_workflow import Workflow -@Workflow.wrap_as.function_node("sum") +@Workflow.wrap.as_function_node("sum") def OptionallyAdd(x: int, y: Optional[int] = None) -> int: y = 0 if y is None else y return x + y diff --git a/tests/unit/snippets/test_factory.py b/tests/unit/snippets/test_factory.py new file mode 100644 index 00000000..282c9594 --- /dev/null +++ b/tests/unit/snippets/test_factory.py @@ -0,0 +1,546 @@ +from __future__ import annotations + +from abc import ABC +import pickle +from typing import ClassVar +import unittest + +import cloudpickle + +from pyiron_workflow.snippets.factory import ( + _ClassFactory, + _FactoryMade, + ClassFactory, + classfactory, + InvalidClassNameError, + InvalidFactorySignature, + sanitize_callable_name +) + + +class HasN(ABC): + def __init_subclass__(cls, /, n=0, s="foo", **kwargs): + super().__init_subclass__(**kwargs) + cls.n = n + cls.s = s + + def __init__(self, x, *args, y=0, **kwargs): + super().__init__(*args, **kwargs) + self.x = x + self.y = y + + +@classfactory +def has_n_factory(n, s="wrapped_function", /): + return ( + f"{HasN.__name__}{n}{s}", + (HasN,), + {}, + {"n": n, "s": s} + ) + + +def undecorated_function(n, s="undecorated_function", /): + return ( + f"{HasN.__name__}{n}{s}", + (HasN,), + {}, + {"n": n, "s": s} + ) + + +def takes_kwargs(n, /, s="undecorated_function"): + return ( + f"{HasN.__name__}{n}{s}", + (HasN,), + {}, + {"n": n, "s": s} + ) + + +class FactoryOwner: + @staticmethod + @classfactory + def has_n_factory(n, s="decorated_method", /): + return ( + f"{HasN.__name__}{n}{s}", + (HasN,), + {}, + {"n": n, "s": s} + ) + + +Has2 = has_n_factory(2, "factory_made") # For testing repeated inheritance + + +class HasM(ABC): + def __init_subclass__(cls, /, m=0, **kwargs): + super(HasM, cls).__init_subclass__(**kwargs) + cls.m = m + + def __init__(self, z, *args, **kwargs): + super().__init__(*args, **kwargs) + self.z = z + + +@classfactory +def has_n2_m_factory(m, /): + return ( + f"HasN2M{m}", + (Has2, HasM), + {}, + {"m": m, "n": Has2.n, "s": Has2.s} + ) + + +@classfactory +def has_m_n2_factory(m, /): + return ( + f"HasM{m}N2", + (HasM, Has2,), + {}, + {"m": m} + ) + + +class AddsNandX(ABC): + fnc: ClassVar[callable] + n: ClassVar[int] + + def __init__(self, x): + self.x = x + + def add_to_function(self, *args, **kwargs): + return self.fnc(*args, **kwargs) + self.n + self.x + + +@classfactory +def adder_factory(fnc, n, /): + return ( + f"{AddsNandX.__name__}{fnc.__name__}", + (AddsNandX,), + { + "fnc": staticmethod(fnc), + "n": n, + "_class_returns_from_decorated_function": fnc + }, + {}, + ) + + +def add_to_this_decorator(n): + def wrapped(fnc): + factory_made = adder_factory(fnc, n) + factory_made._class_returns_from_decorated_function = fnc + return factory_made + return wrapped + + +@add_to_this_decorator(5) +def adds_5_plus_x(y: int): + return y + + +class TestClassfactory(unittest.TestCase): + + def test_factory_initialization(self): + self.assertTrue( + issubclass(has_n_factory.__class__, _ClassFactory), + msg="Creation by decorator should yield a subclass" + ) + self.assertTrue( + issubclass(ClassFactory(undecorated_function).__class__, _ClassFactory), + msg="Creation by public instantiator should yield a subclass" + ) + + factory = has_n_factory(2, "foo") + self.assertTrue( + issubclass(factory, HasN), + msg=f"Resulting class should inherit from the base" + ) + self.assertEqual(2, factory.n, msg="Factory args should get interpreted") + self.assertEqual("foo", factory.s, msg="Factory kwargs should get interpreted") + + def test_factory_uniqueness(self): + f1 = classfactory(undecorated_function) + f2 = classfactory(undecorated_function) + + self.assertIs( + f1, + f2, + msg="Repeatedly packaging the same function should give the exact same " + "factory" + ) + self.assertIsNot( + f1, + has_n_factory, + msg="Factory degeneracy is based on the actual wrapped function, we don't " + "do any parsing for identical behaviour inside those functions." + ) + + def test_factory_pickle(self): + with self.subTest("By decoration"): + reloaded = pickle.loads(pickle.dumps(has_n_factory)) + self.assertIs(has_n_factory, reloaded) + + with self.subTest("From instantiation"): + my_factory = ClassFactory(undecorated_function) + reloaded = pickle.loads(pickle.dumps(my_factory)) + self.assertIs(my_factory, reloaded) + + with self.subTest("From qualname by decoration"): + my_factory = FactoryOwner().has_n_factory + reloaded = pickle.loads(pickle.dumps(my_factory)) + self.assertIs(my_factory, reloaded) + + def test_class_creation(self): + n2 = has_n_factory(2, "something") + self.assertEqual( + 2, + n2.n, + msg="Factory args should be getting parsed" + ) + self.assertEqual( + "something", + n2.s, + msg="Factory kwargs should be getting parsed" + ) + self.assertTrue( + issubclass(n2, HasN), + msg="" + ) + self.assertTrue( + issubclass(n2, HasN), + msg="Resulting classes should inherit from the requested base(s)" + ) + + with self.assertRaises( + InvalidClassNameError, + msg="Invalid class names should raise an error" + ): + has_n_factory( + 2, + "our factory function uses this as part of the class name, but spaces" + "are not allowed!" + ) + + def test_class_uniqueness(self): + n2 = has_n_factory(2) + + self.assertIs( + n2, + has_n_factory(2), + msg="Repeatedly creating the same class should give the exact same class" + ) + self.assertIsNot( + n2, + has_n_factory(2, "something_else"), + msg="Sanity check" + ) + + def test_bad_factory_function(self): + with self.assertRaises( + InvalidFactorySignature, + msg="For compliance with __reduce__, we can only use factory functions " + "that strictly take positional arguments" + ): + ClassFactory(takes_kwargs) + + def test_instance_creation(self): + foo = has_n_factory(2, "used")(42, y=43) + self.assertEqual( + 2, foo.n, msg="Class attributes should be inherited" + ) + self.assertEqual( + "used", foo.s, msg="Class attributes should be inherited" + ) + self.assertEqual( + 42, foo.x, msg="Initialized args should be captured" + ) + self.assertEqual( + 43, foo.y, msg="Initialized kwargs should be captured" + ) + self.assertIsInstance( + foo, + HasN, + msg="Instances should inherit from the requested base(s)" + ) + self.assertIsInstance( + foo, + _FactoryMade, + msg="Instances should get :class:`_FactoryMade` mixed in." + ) + + def test_instance_pickle(self): + foo = has_n_factory(2, "used")(42, y=43) + reloaded = pickle.loads(pickle.dumps(foo)) + self.assertEqual( + foo.n, reloaded.n, msg="Class attributes should be reloaded" + ) + self.assertEqual( + foo.s, reloaded.s, msg="Class attributes should be reloaded" + ) + self.assertEqual( + foo.x, reloaded.x, msg="Initialized args should be reloaded" + ) + self.assertEqual( + foo.y, reloaded.y, msg="Initialized kwargs should be reloaded" + ) + self.assertIsInstance( + reloaded, + HasN, + msg="Instances should inherit from the requested base(s)" + ) + self.assertIsInstance( + reloaded, + _FactoryMade, + msg="Instances should get :class:`_FactoryMade` mixed in." + ) + + def test_decorated_method(self): + msg = "It should be possible to have class factories as methods on a class" + foo = FactoryOwner().has_n_factory(2)(42, y=43) + reloaded = pickle.loads(pickle.dumps(foo)) + self.assertEqual(foo.n, reloaded.n, msg=msg) + self.assertEqual(foo.s, reloaded.s, msg=msg) + self.assertEqual(foo.x, reloaded.x, msg=msg) + self.assertEqual(foo.y, reloaded.y, msg=msg) + + def test_factory_inside_a_function(self): + @classfactory + def internal_factory(n, s="unimportable_scope", /): + return ( + f"{HasN.__name__}{n}{s}", + (HasN,), + {}, + {"n": n, "s": s} + ) + + foo = internal_factory(2)(1, y=0) + self.assertEqual(2, foo.n, msg="Nothing should stop the factory from working") + self.assertEqual( + "unimportable_scope", + foo.s, + msg="Nothing should stop the factory from working" + ) + self.assertEqual(1, foo.x, msg="Nothing should stop the factory from working") + self.assertEqual(0, foo.y, msg="Nothing should stop the factory from working") + with self.assertRaises( + AttributeError, + msg="`internal_factory` is defined only locally inside the scope of " + "another function, so we don't expect it to be pickleable whether it's " + "a class factory or not!" + ): + pickle.dumps(foo) + + reloaded = cloudpickle.loads(cloudpickle.dumps(foo)) + self.assertTupleEqual( + (foo.n, foo.s, foo.x, foo.y), + (reloaded.n, reloaded.s, reloaded.x, reloaded.y), + msg="Cloudpickle is powerful enough to overcome this limitation." + ) + + # And again with a factory from the instance constructor + def internally_undecorated(n, s="undecorated_unimportable", /): + return ( + f"{HasN.__name__}{n}{s}", + (HasN,), + {}, + {"n": n, "s": s} + ) + factory_instance = ClassFactory(internally_undecorated) + bar = factory_instance(2)(1, y=0) + self.assertTupleEqual( + (2, "undecorated_unimportable", 1, 0), + (bar.n, bar.s, bar.x, bar.y), + msg="Sanity check" + ) + + with self.assertRaises( + AttributeError, + msg="The relevant factory function is only in " + ): + pickle.dumps(bar) + + reloaded = cloudpickle.loads(cloudpickle.dumps(bar)) + self.assertTupleEqual( + (bar.n, bar.s, bar.x, bar.y), + (reloaded.n, reloaded.s, reloaded.x, reloaded.y), + msg="Cloudpickle is powerful enough to overcome this limitation." + ) + + + def test_repeated_inheritance(self): + n2m3 = has_n2_m_factory(3)(5, 6) + m3n2 = has_m_n2_factory(3)(5, 6) + + self.assertListEqual( + [3, 2, "factory_made"], + [n2m3.m, n2m3.n, n2m3.s], + msg="Sanity check on class property inheritance" + ) + self.assertListEqual( + [3, 0, "foo"], # n and s defaults from HasN! + [m3n2.m, m3n2.n, m3n2.s], + msg="When exploiting __init_subclass__, each subclass must take care to " + "specify _all_ parent class __init_subclass__ kwargs, or they will " + "revert to the default behaviour. This is totally normal python " + "behaviour, and here we just verify that we're vulnerable to the same " + "'gotcha' as the rest of the language." + ) + self.assertListEqual( + [5, 6], + [n2m3.x, n2m3.z], + msg="Sanity check on instance inheritance" + ) + self.assertListEqual( + [m3n2.z, m3n2.x], + [n2m3.x, n2m3.z], + msg="Inheritance order should impact arg order, also completely as usual " + "for python classes" + ) + reloaded_nm = pickle.loads(pickle.dumps(n2m3)) + self.assertListEqual( + [n2m3.m, n2m3.n, n2m3.s, n2m3.z, n2m3.x, n2m3.y], + [ + reloaded_nm.m, + reloaded_nm.n, + reloaded_nm.s, + reloaded_nm.z, + reloaded_nm.x, + reloaded_nm.y + ], + msg="Pickling behaviour should not care that one of the parents was itself " + "a factory made class." + ) + + reloaded_mn = pickle.loads(pickle.dumps(m3n2)) + self.assertListEqual( + [m3n2.m, m3n2.n, m3n2.s, m3n2.z, m3n2.x, m3n2.y], + [ + reloaded_mn.m, + reloaded_mn.n, + reloaded_mn.s, + reloaded_mn.z, + reloaded_mn.x, + reloaded_nm.y + ], + msg="Pickling behaviour should not care about the order of bases." + ) + + def test_clearing_town(self): + + self.assertGreater(len(Has2._factory_town.factories), 0, msg="Sanity check") + + Has2._factory_town.clear() + self.assertEqual( + len(Has2._factory_town.factories), + 0, + msg="Town should get cleared" + ) + + ClassFactory(undecorated_function) + self.assertEqual( + len(Has2._factory_town.factories), + 1, + msg="Has2 exists in memory and the factory town has forgotten about it, " + "but it still knows about the factory town and can see the newly " + "created one." + ) + + def test_clearing_class_register(self): + self.assertGreater( + len(has_n_factory.class_registry), + 0, + msg="Sanity. We expect to have created at least one class up in the header." + ) + has_n_factory.clear() + self.assertEqual( + len(has_n_factory.class_registry), + 0, + msg="Clear should remove all instances" + ) + n_new = 3 + for i in range(n_new): + has_n_factory(i) + self.assertEqual( + len(has_n_factory.class_registry), + n_new, + msg="Should see the new constructed classes" + ) + + def test_other_decorators(self): + """ + In case the factory-produced class itself comes from a decorator, we need to + check that name conflicts between the class and decorated function are handled. + """ + a5 = adds_5_plus_x(2) + self.assertIsInstance(a5, AddsNandX) + self.assertIsInstance(a5, _FactoryMade) + self.assertEqual(5, a5.n) + self.assertEqual(2, a5.x) + self.assertEqual( + 1 + 5 + 2, # y + n=5 + x=2 + a5.add_to_function(1), + msg="Should execute the function as part of call" + ) + + reloaded = pickle.loads(pickle.dumps(a5)) + self.assertEqual(a5.n, reloaded.n) + self.assertIs(a5.fnc, reloaded.fnc) + self.assertEqual(a5.x, reloaded.x) + + def test_other_decorators_inside_locals(self): + @add_to_this_decorator(6) + def adds_6_plus_x(y: int): + return y + + a6 = adds_6_plus_x(42) + self.assertEqual( + 1 + 42 + 6, + a6.add_to_function(1), + msg="Nothing stops us from creating and running these" + ) + with self.assertRaises( + AttributeError, + msg="We can't find the function defined to import and recreate" + "the factory" + ): + pickle.dumps(a6) + + reloaded = cloudpickle.loads(cloudpickle.dumps(a6)) + self.assertTupleEqual( + (a6.n, a6.x), + (reloaded.n, reloaded.x), + msg="Cloudpickle is powerful enough to overcome this limitation." + ) + + +class TestSanitization(unittest.TestCase): + + def test_simple_string(self): + self.assertEqual(sanitize_callable_name("SimpleString"), "SimpleString") + + def test_string_with_spaces(self): + self.assertEqual( + sanitize_callable_name("String with spaces"), "String_with_spaces" + ) + + def test_string_with_special_characters(self): + self.assertEqual(sanitize_callable_name("a!@#$%b^&*()c"), "a_b_c") + + def test_string_with_numbers_at_start(self): + self.assertEqual(sanitize_callable_name("123Class"), "_123Class") + + def test_empty_string(self): + self.assertEqual(sanitize_callable_name(""), "") + + def test_string_with_only_special_characters(self): + self.assertEqual(sanitize_callable_name("!@#$%"), "_") + + def test_string_with_only_numbers(self): + self.assertEqual(sanitize_callable_name("123456"), "_123456") + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/unit/snippets/test_singleton.py b/tests/unit/snippets/test_singleton.py index d18c848a..7953445e 100644 --- a/tests/unit/snippets/test_singleton.py +++ b/tests/unit/snippets/test_singleton.py @@ -17,4 +17,3 @@ def __init__(self): if __name__ == '__main__': unittest.main() - diff --git a/tests/unit/test_channels.py b/tests/unit/test_channels.py index 977b7a82..21c43529 100644 --- a/tests/unit/test_channels.py +++ b/tests/unit/test_channels.py @@ -147,8 +147,8 @@ def test_fetch(self): self.no.value = NOT_DATA self.ni1.value = 1 - self.ni1.connect(self.no_empty) self.ni1.connect(self.no) + self.ni1.connect(self.no_empty) self.assertEqual( self.ni1.value, @@ -218,7 +218,7 @@ def test_copy_connections(self): self.ni2.copy_connections(self.ni1) self.assertListEqual( self.ni2.connections, - [self.no_empty, *self.ni1.connections], + [*self.ni1.connections, self.no_empty], msg="Copying should be additive, existing connections should still be there" ) diff --git a/tests/unit/test_composite.py b/tests/unit/test_composite.py index ede9a50c..abb8a501 100644 --- a/tests/unit/test_composite.py +++ b/tests/unit/test_composite.py @@ -1,11 +1,10 @@ import unittest -from bidict import ValueDuplicationError - from pyiron_workflow._tests import ensure_tests_in_python_path from pyiron_workflow.channels import NOT_DATA from pyiron_workflow.composite import Composite -from pyiron_workflow.io import Outputs, Inputs, ConnectionCopyError, ValueCopyError +from pyiron_workflow.injection import OutputsWithInjection +from pyiron_workflow.io import Inputs, ConnectionCopyError, ValueCopyError from pyiron_workflow.topology import CircularDataFlowError @@ -19,15 +18,26 @@ def __init__(self, label): super().__init__(label=label) def _get_linking_channel(self, child_reference_channel, composite_io_key): - return child_reference_channel # IO by reference + pass # Shouldn't even be abstract honestly + # return child_reference_channel # IO by reference @property def inputs(self) -> Inputs: - return self._build_inputs() # Dynamic IO reflecting current children + # Dynamic IO reflecting current children + inp = Inputs() + for child in self: + for channel in child.inputs: + inp[channel.scoped_label] = channel + return inp @property - def outputs(self) -> Outputs: - return self._build_outputs() # Dynamic IO reflecting current children + def outputs(self) -> OutputsWithInjection: + # Dynamic IO reflecting current children + out = OutputsWithInjection() + for child in self: + for channel in child.outputs: + out[channel.scoped_label] = channel + return out class TestComposite(unittest.TestCase): @@ -41,7 +51,7 @@ def setUp(self) -> None: super().setUp() def test_node_decorator_access(self): - @Composite.wrap_as.function_node("y") + @Composite.wrap.as_function_node("y") def foo(x: int = 0) -> int: return x + 1 @@ -53,7 +63,7 @@ def foo(x: int = 0) -> int: ) comp = self.comp - @comp.wrap_as.function_node("y") + @comp.wrap.as_function_node("y") def bar(x: int = 0) -> int: return x + 2 @@ -88,9 +98,9 @@ def test_creator_access_and_registration(self): def test_node_addition(self): # Validate the four ways to add a node - self.comp.add_child(Composite.create.Function(plus_one, label="foo")) - self.comp.baz = self.comp.create.Function(plus_one, label="whatever_baz_gets_used") - Composite.create.Function(plus_one, label="qux", parent=self.comp) + self.comp.add_child(Composite.create.function_node(plus_one, label="foo")) + self.comp.baz = self.comp.create.function_node(plus_one, label="whatever_baz_gets_used") + Composite.create.function_node(plus_one, label="qux", parent=self.comp) self.assertListEqual( list(self.comp.children.keys()), ["foo", "baz", "qux"], @@ -105,7 +115,7 @@ def test_node_addition(self): ) def test_node_access(self): - node = Composite.create.Function(plus_one) + node = Composite.create.function_node(plus_one) self.comp.child = node self.assertIs( self.comp.child, @@ -140,8 +150,8 @@ def test_node_access(self): self.comp.not_a_child_or_attribute def test_node_removal(self): - self.comp.owned = Composite.create.Function(plus_one) - node = Composite.create.Function(plus_one) + self.comp.owned = Composite.create.function_node(plus_one) + node = Composite.create.function_node(plus_one) self.comp.foo = node # Add it to starting nodes manually, otherwise it's only there at run time self.comp.starting_nodes = [self.comp.foo] @@ -176,25 +186,25 @@ def test_node_removal(self): ) def test_label_uniqueness(self): - self.comp.foo = Composite.create.Function(plus_one) + self.comp.foo = Composite.create.function_node(plus_one) self.comp.strict_naming = True # Validate name preservation for each node addition path with self.assertRaises(AttributeError, msg="We have 'foo' at home"): - self.comp.add_child(self.comp.create.Function(plus_one, label="foo")) + self.comp.add_child(self.comp.create.function_node(plus_one, label="foo")) with self.assertRaises( AttributeError, msg="The provided label is ok, but then assigning to baz should give " "trouble since that name is already occupied" ): - self.comp.foo = Composite.create.Function(plus_one, label="whatever") + self.comp.foo = Composite.create.function_node(plus_one, label="whatever") with self.assertRaises(AttributeError, msg="We have 'foo' at home"): - Composite.create.Function(plus_one, label="foo", parent=self.comp) + Composite.create.function_node(plus_one, label="foo", parent=self.comp) with self.assertRaises(AttributeError, msg="The parent already has 'foo'"): - node = Composite.create.Function(plus_one, label="foo") + node = Composite.create.function_node(plus_one, label="foo") node.parent = self.comp with self.subTest("Make sure trivial re-assignment has no impact"): @@ -213,7 +223,7 @@ def test_label_uniqueness(self): ) self.comp.strict_naming = False - self.comp.add_child(Composite.create.Function(plus_one, label="foo")) + self.comp.add_child(Composite.create.function_node(plus_one, label="foo")) self.assertEqual( 2, len(self.comp), @@ -228,8 +238,8 @@ def test_label_uniqueness(self): def test_singular_ownership(self): comp1 = AComposite("one") - comp1.node1 = comp1.create.Function(plus_one) - node2 = comp1.create.Function( + comp1.node1 = comp1.create.function_node(plus_one) + node2 = comp1.create.function_node( plus_one, label="node2", parent=comp1, x=comp1.node1.outputs.y ) self.assertTrue(node2.connected, msg="Sanity check that node connection works") @@ -246,11 +256,11 @@ def test_singular_ownership(self): ) def test_replace(self): - n1 = Composite.create.Function(plus_one) - n2 = Composite.create.Function(plus_one) - n3 = Composite.create.Function(plus_one) + n1 = Composite.create.function_node(plus_one) + n2 = Composite.create.function_node(plus_one) + n3 = Composite.create.function_node(plus_one) - @Composite.wrap_as.function_node("y", "minus") + @Composite.wrap.as_function_node("y", "minus") def x_plus_minus_z(x: int = 0, z=2) -> tuple[int, int]: """ A commensurate but different node: has _more_ than the necessary channels, @@ -260,11 +270,11 @@ def x_plus_minus_z(x: int = 0, z=2) -> tuple[int, int]: replacement = x_plus_minus_z() - @Composite.wrap_as.function_node("y") + @Composite.wrap.as_function_node("y") def different_input_channel(z: int = 0) -> int: return z + 10 - @Composite.wrap_as.function_node("z") + @Composite.wrap.as_function_node("z") def different_output_channel(x: int = 0) -> int: return x + 100 @@ -273,17 +283,15 @@ def different_output_channel(x: int = 0) -> int: self.comp.n3 = n3 self.comp.n2.inputs.x = self.comp.n1 self.comp.n3.inputs.x = self.comp.n2 - self.comp.inputs_map = {"n1__x": "x"} - self.comp.outputs_map = {"n3__y": "y"} self.comp.set_run_signals_to_dag_execution() with self.subTest("Verify success cases"): - self.assertEqual(3, self.comp.run().y, msg="Sanity check") + self.assertEqual(3, self.comp.run().n3__y, msg="Sanity check") self.comp.replace_child(n1, replacement) - out = self.comp.run(x=0) + out = self.comp.run(n1__x=0) self.assertEqual( - (0+2) + 1 + 1, out.y, msg="Should be able to replace by instance" + (0+2) + 1 + 1, out.n3__y, msg="Should be able to replace by instance" ) self.assertEqual( 0 - 2, out.n1__minus, msg="Replacement output should also appear" @@ -297,17 +305,17 @@ def different_output_channel(x: int = 0) -> int: ) self.comp.replace_child("n2", replacement) - out = self.comp.run(x=0) + out = self.comp.run(n1__x=0) self.assertEqual( - (0 + 1) + 2 + 1, out.y, msg="Should be able to replace by label" + (0 + 1) + 2 + 1, out.n3__y, msg="Should be able to replace by label" ) self.assertEqual(1 - 2, out.n2__minus) self.comp.replace_child(replacement, n2) self.comp.replace_child(n3, x_plus_minus_z) - out = self.comp.run(x=0) + out = self.comp.run(n1__x=0) self.assertEqual( - (0 + 1) + 2 + 1, out.y, msg="Should be able to replace with a class" + (0 + 1) + 2 + 1, out.n3__y, msg="Should be able to replace with a class" ) self.assertEqual(2 - 2, out.n3__minus) self.assertIsNot( @@ -321,7 +329,7 @@ def different_output_channel(x: int = 0) -> int: self.comp.n1 = x_plus_minus_z self.assertEqual( (0+2) + 1 + 1, - self.comp.run(x=0).y, + self.comp.run(n1__x=0).n3__y, msg="Assigning a new _class_ to an existing node should be a shortcut " "for replacement" ) @@ -330,7 +338,7 @@ def different_output_channel(x: int = 0) -> int: self.comp.n1 = different_input_channel self.assertEqual( (0 + 10) + 1 + 1, - self.comp.run(n1__z=0).y, + self.comp.run(n1__z=0).n3__y, msg="Different IO should be compatible as long as what's missing is " "not connected" ) @@ -339,14 +347,14 @@ def different_output_channel(x: int = 0) -> int: self.comp.n3 = different_output_channel self.assertEqual( (0 + 1) + 1 + 100, - self.comp.run(x=0).n3__z, + self.comp.run(n1__x=0).n3__z, msg="Different IO should be compatible as long as what's missing is " "not connected" ) self.comp.replace_child(self.comp.n3, n3) with self.subTest("Verify failure cases"): - self.assertEqual(3, self.comp.run().y, msg="Sanity check") + self.assertEqual(3, self.comp.run().n3__y, msg="Sanity check") another_comp = AComposite("another") another_node = x_plus_minus_z(parent=another_comp) @@ -372,7 +380,7 @@ def different_output_channel(x: int = 0) -> int: ): self.comp.replace_child(replacement, another_node) - @Composite.wrap_as.function_node("y") + @Composite.wrap.as_function_node("y") def wrong_hint(x: float = 0) -> float: return x + 1.1 @@ -396,13 +404,13 @@ def wrong_hint(x: float = 0) -> float: self.assertEqual( 3, - self.comp.run().y, + self.comp.run().n3__y, msg="Failed replacements should always restore the original state " "cleanly" ) def test_working_directory(self): - self.comp.plus_one = Composite.create.Function(plus_one) + self.comp.plus_one = Composite.create.function_node(plus_one) self.assertTrue( str(self.comp.plus_one.working_directory.path).endswith(self.comp.plus_one.label), msg="Child nodes should have their own working directories nested inside" @@ -410,9 +418,9 @@ def test_working_directory(self): self.comp.working_directory.delete() # Clean up def test_length(self): - self.comp.child = Composite.create.Function(plus_one) + self.comp.child = Composite.create.function_node(plus_one) l1 = len(self.comp) - self.comp.child2 = Composite.create.Function(plus_one) + self.comp.child2 = Composite.create.function_node(plus_one) self.assertEqual( l1 + 1, len(self.comp), @@ -420,9 +428,9 @@ def test_length(self): ) def test_run(self): - self.comp.n1 = self.comp.create.Function(plus_one, x=0) - self.comp.n2 = self.comp.create.Function(plus_one, x=self.comp.n1) - self.comp.n3 = self.comp.create.Function(plus_one, x=42) + self.comp.n1 = self.comp.create.function_node(plus_one, x=0) + self.comp.n2 = self.comp.create.function_node(plus_one, x=self.comp.n1) + self.comp.n3 = self.comp.create.function_node(plus_one, x=42) self.comp.n1 >> self.comp.n2 self.comp.starting_nodes = [self.comp.n1] @@ -440,9 +448,9 @@ def test_run(self): def test_set_run_signals_to_dag(self): # Like the run test, but manually invoking this first - self.comp.n1 = self.comp.create.Function(plus_one, x=0) - self.comp.n2 = self.comp.create.Function(plus_one, x=self.comp.n1) - self.comp.n3 = self.comp.create.Function(plus_one, x=42) + self.comp.n1 = self.comp.create.function_node(plus_one, x=0) + self.comp.n2 = self.comp.create.function_node(plus_one, x=self.comp.n1) + self.comp.n3 = self.comp.create.function_node(plus_one, x=42) self.comp.set_run_signals_to_dag_execution() self.comp.run() self.assertEqual( @@ -469,9 +477,9 @@ def test_set_run_signals_to_dag(self): self.comp.set_run_signals_to_dag_execution() def test_return(self): - self.comp.n1 = Composite.create.Function(plus_one, x=0) + self.comp.n1 = Composite.create.function_node(plus_one, x=0) not_dottable_string = "can't dot this" - not_dottable_name_node = self.comp.create.Function( + not_dottable_name_node = self.comp.create.function_node( plus_one, x=42, label=not_dottable_string, parent=self.comp ) self.comp.starting_nodes = [self.comp.n1, not_dottable_name_node] @@ -497,94 +505,9 @@ def test_return(self): msg="Should always be able to fall back to item access with crazy labels" ) - def test_io_maps(self): - # input and output, renaming, accessing connected, and deactivating disconnected - self.comp.n1 = Composite.create.Function(plus_one, x=0) - self.comp.n2 = Composite.create.Function(plus_one, x=self.comp.n1) - self.comp.n3 = Composite.create.Function(plus_one, x=self.comp.n2) - self.comp.m = Composite.create.Function(plus_one, x=42) - self.comp.inputs_map = { - "n1__x": "x", # Rename - "n2__x": "intermediate_x", # Expose - "m__x": None, # Hide - } - self.comp.outputs_map = { - "n3__y": "y", # Rename - "n2__y": "intermediate_y", # Expose, - "m__y": None, # Hide - } - self.assertIn("x", self.comp.inputs.labels, msg="Should be renamed") - self.assertIn("y", self.comp.outputs.labels, msg="Should be renamed") - self.assertIn("intermediate_x", self.comp.inputs.labels, msg="Should be exposed") - self.assertIn("intermediate_y", self.comp.outputs.labels, msg="Should be exposed") - self.assertNotIn("m__x", self.comp.inputs.labels, msg="Should be hidden") - self.assertNotIn("m__y", self.comp.outputs.labels, msg="Should be hidden") - self.assertNotIn("m__y", self.comp.outputs.labels, msg="Should be hidden") - - self.comp.set_run_signals_to_dag_execution() - out = self.comp.run() - self.assertEqual( - 3, - out.y, - msg="New names should be propagated to the returned value" - ) - self.assertNotIn( - "m__y", - list(out.keys()), - msg="IO filtering should be evident in returned value" - ) - self.assertEqual( - 43, - self.comp.m.outputs.y.value, - msg="The child channel should still exist and have run" - ) - self.assertEqual( - 1, - self.comp.inputs.intermediate_x.value, - msg="IO should be up-to-date post-run" - ) - self.assertEqual( - 2, - self.comp.outputs.intermediate_y.value, - msg="IO should be up-to-date post-run" - ) - - def test_io_map_bijectivity(self): - with self.assertRaises( - ValueDuplicationError, - msg="Should not be allowed to map two children's channels to the same label" - ): - self.comp.inputs_map = {"n1__x": "x", "n2__x": "x"} - - self.comp.inputs_map = {"n1__x": "x"} - with self.assertRaises( - ValueDuplicationError, - msg="Should not be allowed to update a second child's channel onto an " - "existing mapped channel" - ): - self.comp.inputs_map["n2__x"] = "x" - - with self.subTest("Ensure we can use None to turn multiple off"): - self.comp.inputs_map = {"n1__x": None, "n2__x": None} # At once - # Or in a row - self.comp.inputs_map = {} - self.comp.inputs_map["n1__x"] = None - self.comp.inputs_map["n2__x"] = None - self.comp.inputs_map["n3__x"] = None - self.assertEqual( - 3, - len(self.comp.inputs_map), - msg="All entries should be stored" - ) - self.assertEqual( - 0, - len(self.comp.inputs), - msg="No IO should be left exposed" - ) - def test_de_activate_strict_connections(self): self.comp.sub_comp = AComposite("sub") - self.comp.sub_comp.n1 = Composite.create.Function(plus_one, x=0) + self.comp.sub_comp.n1 = Composite.create.function_node(plus_one, x=0) self.assertTrue( self.comp.sub_comp.n1.inputs.x.strict_hints, msg="Sanity check that test starts in the expected condition" @@ -603,8 +526,8 @@ def test_de_activate_strict_connections(self): def test_graph_info(self): top = AComposite("topmost") top.middle_composite = AComposite("middle_composite") - top.middle_composite.deep_node = Composite.create.Function(plus_one) - top.middle_function = Composite.create.Function(plus_one) + top.middle_composite.deep_node = Composite.create.function_node(plus_one) + top.middle_function = Composite.create.function_node(plus_one) with self.subTest("test_graph_path"): self.assertEqual( diff --git a/tests/unit/test_for_loop.py b/tests/unit/test_for_loop.py new file mode 100644 index 00000000..69d01808 --- /dev/null +++ b/tests/unit/test_for_loop.py @@ -0,0 +1,373 @@ +import random +from concurrent.futures import ThreadPoolExecutor +from itertools import product +from time import perf_counter +import unittest + +from pandas import DataFrame + +from pyiron_workflow.for_loop import ( + dictionary_to_index_maps, + for_node, + UnmappedConflictError, + MapsToNonexistentOutputError +) +from pyiron_workflow.function import as_function_node +from pyiron_workflow.macro import as_macro_node +from pyiron_workflow.node_library.standard import Add, Sleep +from pyiron_workflow.transform import inputs_to_list + + +class TestDictionaryToIndexMaps(unittest.TestCase): + + def test_no_keys(self): + data = {"key": 5} + with self.assertRaises(ValueError): + dictionary_to_index_maps(data) + + def test_empty_nested_keys(self): + data = {"key1": [1, 2, 3], "key2": [4, 5, 6]} + with self.assertRaises(ValueError): + dictionary_to_index_maps(data, nested_keys=()) + + def test_empty_zipped_keys(self): + data = {"key1": [1, 2, 3], "key2": [4, 5, 6]} + with self.assertRaises(ValueError): + dictionary_to_index_maps(data, zipped_keys=()) + + def test_nested_non_iterable_data(self): + data = {"key1": [1, 2, 3], "key2": 5} + with self.assertRaises(TypeError): + dictionary_to_index_maps(data, nested_keys=("key1", "key2")) + + def test_zipped_non_iterable_data(self): + data = {"key1": [1, 2, 3], "key2": 5} + with self.assertRaises(TypeError): + dictionary_to_index_maps(data, zipped_keys=("key1", "key2")) + + def test_valid_data_nested_only(self): + data = {"key1": [1, 2, 3], "key2": [4, 5]} + nested_keys = ("key1", "key2") + expected_maps = tuple( + {nested_keys[i]: idx for i, idx in enumerate(indices)} + for indices in product(range(len(data["key1"])), range(len(data["key2"]))) + ) + self.assertEqual( + expected_maps, + dictionary_to_index_maps(data, nested_keys=nested_keys), + ) + + def test_valid_data_zipped_only(self): + data = {"key1": [1, 2, 3], "key2": [4, 5]} + zipped_keys = ("key1", "key2") + expected_maps = tuple( + {key: idx for key in zipped_keys} + for idx in range(min(len(data["key1"]), len(data["key2"]))) + ) + self.assertEqual( + expected_maps, + dictionary_to_index_maps(data, zipped_keys=zipped_keys), + ) + + def test_valid_data_nested_and_zipped(self): + data = { + "nested1": [2, 3], + "nested2": [4, 5, 6], + "zipped1": [7, 8, 9, 10], + "zipped2": [11, 12, 13, 14, 15] + } + nested_keys = ("nested1", "nested2") + zipped_keys = ("zipped1", "zipped2") + expected_maps = tuple( + { + nested_keys[0]: n_idx, + nested_keys[1]: n_idx2, + zipped_keys[0]: z_idx, + zipped_keys[1]: z_idx2 + } + for n_idx, n_idx2 in product( + range(len(data["nested1"])), + range(len(data["nested2"])) + ) + for z_idx, z_idx2 in zip( + range(len(data["zipped1"])), + range(len(data["zipped2"])) + ) + ) + self.assertEqual( + expected_maps, + dictionary_to_index_maps(data, nested_keys=nested_keys, zipped_keys=zipped_keys), + ) + + +@as_function_node("together") +def FiveTogether( + a: int = 0, + b: int = 1, + c: int = 2, + d: int = 3, + e: str = "foobar", +): + return (a, b, c, d, e,), + + +class TestForNode(unittest.TestCase): + def test_iter_only(self): + for_instance = for_node( + FiveTogether, + iter_on=("a", "b",), + a=[42, 43, 44], + b=[13, 14], + ) + out = for_instance(e="iter") + self.assertIsInstance(out.df, DataFrame,) + self.assertEqual( + len(out.df), + 3 * 2, + msg="Expect nested loops" + ) + self.assertListEqual( + out.df.columns.tolist(), + ["a", "b", "together"], + msg="Dataframe should only hold output and _looped_ input" + ) + self.assertTupleEqual( + out.df["together"][1], + ((42, 14, 2, 3, "iter"),), + msg="Iter should get nested, broadcast broadcast, else take default" + ) + + def test_zip_only(self): + for_instance = for_node( + FiveTogether, + zip_on=("c", "d",), + e="zip" + ) + out = for_instance(c=[100, 101], d=[-1, -2, -3]) + self.assertEqual( + len(out.df), + 2, + msg="Expect zipping with the python convention of truncating to shortest" + ) + self.assertListEqual( + out.df.columns.tolist(), + ["c", "d", "together"], + msg="Dataframe should only hold output and _looped_ input" + ) + self.assertTupleEqual( + out.df["together"][1], + ((0, 1, 101, -2, "zip"),), + msg="Zipped should get zipped, broadcast broadcast, else take default" + ) + + def test_iter_and_zip(self): + for_instance = for_node( + FiveTogether, + iter_on=("a", "b",), + a=[42, 43, 44], + b=[13, 14], + zip_on=("c", "d",), + e="both" + ) + out = for_instance(c=[100, 101], d=[-1, -2, -3]) + self.assertEqual( + len(out.df), + 3 * 2 * 2, + msg="Zipped stuff is nested with the individually nested fields" + ) + self.assertListEqual( + out.df.columns.tolist(), + ["a", "b", "c", "d", "together"], + msg="Dataframe should only hold output and _looped_ input" + ) + # We don't actually care if the order of nesting changes, but make sure the + # iters are getting nested and zipped stay together + self.assertTupleEqual( + out.df["together"][0], + ((42, 13, 100, -1, "both"),), + msg="All start" + ) + self.assertTupleEqual( + out.df["together"][1], + ((42, 13, 101, -2, "both"),), + msg="Bump zipped together" + ) + self.assertTupleEqual( + out.df["together"][2], + ((42, 14, 100, -1, "both"),), + msg="Back to start of zipped, bump _one_ iter" + ) + + def test_dynamic_length(self): + for_instance = for_node( + FiveTogether, + iter_on=("a", "b",), + a=[42, 43, 44], + b=[13, 14], + zip_on=("c", "d",), + c=[100, 101], + d=[-1, -2, -3] + ) + self.assertEqual( + 3 * 2 * 2, + len(for_instance().df), + msg="Sanity check" + ) + self.assertEqual( + 1, + len(for_instance(a=[0], b=[1], c=[2]).df), + msg="Should be able to re-run with different input lengths" + ) + + def test_column_mapping(self): + @as_function_node() + def FiveApart( + a: int = 0, + b: int = 1, + c: int = 2, + d: int = 3, + e: str = "foobar", + ): + return a, b, c, d, e, + + with self.subTest("Successful map"): + for_instance = for_node( + FiveApart, + iter_on=("a", "b"), + zip_on=("c", "d"), + a=[1, 2], + b=[3, 4, 5], + c=[7, 8], + d=[9, 10, 11], + e="e", + output_column_map={ + "a": "out_a", + "b": "out_b", + "c": "out_c", + "d": "out_d" + } + ) + self.assertEqual( + 4 + 5, # loop inputs + outputs + len(for_instance().df.columns), + msg="When all conflicting names are remapped, we should have no trouble" + ) + + with self.subTest("Insufficient map"): + with self.assertRaises( + UnmappedConflictError, + msg="Leaving conflicting channels unmapped should raise an error" + ): + for_node( + FiveApart, + iter_on=("a", "b"), + zip_on=("c", "d"), + a=[1, 2], + b=[3, 4, 5], + c=[7, 8], + d=[9, 10, 11], + e="e", + output_column_map={ + # "a": "out_a", + "b": "out_b", + "c": "out_c", + "d": "out_d" + } + ) + + with self.subTest("Excessive map"): + with self.assertRaises( + MapsToNonexistentOutputError, + msg="Trying to map something that isn't there should raise an error" + ): + for_node( + FiveApart, + iter_on=("a", "b"), + zip_on=("c", "d"), + a=[1, 2], + b=[3, 4, 5], + c=[7, 8], + d=[9, 10, 11], + e="e", + output_column_map={ + "a": "out_a", + "b": "out_b", + "c": "out_c", + "d": "out_d", + "not_a_key_on_the_body_node_outputs": "anything" + } + ) + + def test_body_node_executor(self): + t_sleep = 2 + for_parallel = for_node( + Sleep, + iter_on=("t",) + ) + t_start = perf_counter() + n_procs = 4 + with ThreadPoolExecutor(max_workers=n_procs) as exe: + for_parallel.body_node_executor = exe + for_parallel(t=n_procs*[t_sleep]) + dt = perf_counter() - t_start + grace = 1.25 + self.assertLess( + dt, + grace * t_sleep, + msg=f"Parallelization over children should result in faster completion. " + f"Expected limit {grace} x {t_sleep} = {grace * t_sleep} -- got {dt}" + ) + + def test_with_connections(self): + length_y = 3 + + @as_macro_node() + def LoopInside(self, x: list, y: int): + self.to_list = inputs_to_list( + length_y, y, y, y + ) + self.loop = for_node( + Add, + iter_on=("obj", "other",), + obj=x, + other=self.to_list + ) + return self.loop + + x, y = [1], 2 + li = LoopInside([1], 2) + df = li().loop + self.assertIsInstance(df, DataFrame) + self.assertEqual(length_y * len(x), len(df)) + self.assertEqual( + x[0] + y, + df["add"][0], + msg="Just make sure the loop is actually running" + ) + x, y = [2, 3], 4 + df = li(x, y).loop + self.assertEqual(length_y * len(x), len(df)) + self.assertEqual( + x[-1] + y, + df["add"][len(df) - 1], + msg="And make sure that we can vary the length still" + ) + + def test_node_access_points(self): + n = FiveTogether(1, 2, e="instance") + + with self.subTest("Iter"): + df = n.iter(c=[3, 4], d=[5, 6]) + self.assertIsInstance(df, DataFrame) + self.assertEqual(2 * 2, len(df)) + self.assertTupleEqual(df["together"][1][0], (1, 2, 3, 6, "instance",)) + + with self.subTest("Zip"): + df = n.zip(c=[3, 4], d=[5, 6]) + self.assertIsInstance(df, DataFrame) + self.assertEqual(2, len(df)) + self.assertTupleEqual(df["together"][1][0], (1, 2, 4, 6, "instance",)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/unit/test_function.py b/tests/unit/test_function.py index 645cfd6e..7d922d1f 100644 --- a/tests/unit/test_function.py +++ b/tests/unit/test_function.py @@ -1,11 +1,10 @@ +import pickle from typing import Optional, Union import unittest -import warnings -from pyiron_workflow.channels import NOT_DATA, ChannelConnectionError -from pyiron_workflow.function import Function, function_node +from pyiron_workflow.channels import NOT_DATA +from pyiron_workflow.function import function_node, as_function_node from pyiron_workflow.io import ConnectionCopyError, ValueCopyError -from pyiron_workflow.create import Executor def throw_error(x: Optional[int] = None): @@ -39,11 +38,11 @@ def multiple_branches(x): class TestFunction(unittest.TestCase): def test_instantiation(self): with self.subTest("Void function is allowable"): - void_node = Function(void) + void_node = function_node(void) self.assertEqual(len(void_node.outputs), 1) with self.subTest("Args and kwargs at initialization"): - node = Function(plus_one) + node = function_node(plus_one) self.assertIs( NOT_DATA, node.outputs.y.value, @@ -64,7 +63,7 @@ def test_instantiation(self): f"change or something?" ) - node = Function(no_default, 1, y=2, output_labels="output") + node = function_node(no_default, 1, y=2, output_labels="output") node.run() self.assertEqual( no_default(1, 2), @@ -80,11 +79,11 @@ def test_instantiation(self): with self.assertRaises(ValueError): # Can't pass more args than the function takes - Function(returns_multiple, 1, 2, 3) + function_node(returns_multiple, 1, 2, 3) with self.subTest("Initializing with connections"): - node = Function(plus_one, x=2) - node2 = Function(plus_one, x=node.outputs.y) + node = function_node(plus_one, x=2) + node2 = function_node(plus_one, x=node.outputs.y) self.assertIs( node2.inputs.x.connections[0], node.outputs.y, @@ -95,14 +94,14 @@ def test_instantiation(self): self.assertEqual(4, node2.outputs.y.value, msg="Initialize from connection") def test_defaults(self): - with_defaults = Function(plus_one) + with_defaults = function_node(plus_one) self.assertEqual( with_defaults.inputs.x.value, 1, msg=f"Expected to get the default provided in the underlying function but " f"got {with_defaults.inputs.x.value}", ) - without_defaults = Function(no_default) + without_defaults = function_node(no_default) self.assertIs( without_defaults.inputs.x.value, NOT_DATA, @@ -117,37 +116,52 @@ def test_defaults(self): def test_label_choices(self): with self.subTest("Automatically scrape output labels"): - n = Function(plus_one) + n = function_node(plus_one) self.assertListEqual(n.outputs.labels, ["y"]) with self.subTest("Allow overriding them"): - n = Function(no_default, output_labels=("sum_plus_one",)) + n = function_node(no_default, output_labels=("sum_plus_one",)) self.assertListEqual(n.outputs.labels, ["sum_plus_one"]) with self.subTest("Allow forcing _one_ output channel"): - n = Function(returns_multiple, output_labels="its_a_tuple") + n = function_node( + returns_multiple, + output_labels="its_a_tuple", + validate_output_labels=False, + ) self.assertListEqual(n.outputs.labels, ["its_a_tuple"]) with self.subTest("Fail on multiple return values"): with self.assertRaises(ValueError): # Can't automatically parse output labels from a function with multiple # return expressions - Function(multiple_branches) + function_node(multiple_branches) with self.subTest("Override output label scraping"): - switch = Function(multiple_branches, output_labels="bool") + with self.assertRaises( + ValueError, + msg="Multiple return branches can't be parsed" + ): + switch = function_node(multiple_branches, output_labels="bool") + self.assertListEqual(switch.outputs.labels, ["bool"]) + + switch = function_node( + multiple_branches, + output_labels="bool", + validate_output_labels=False + ) self.assertListEqual(switch.outputs.labels, ["bool"]) def test_default_label(self): - n = Function(plus_one) + n = function_node(plus_one) self.assertEqual(plus_one.__name__, n.label) def test_availability_of_node_function(self): - @function_node() + @as_function_node() def linear(x): return x - @function_node() + @as_function_node() def bilinear(x, y): xy = linear.node_function(x) * linear.node_function(y) return xy @@ -160,7 +174,7 @@ def bilinear(x, y): ) def test_statuses(self): - n = Function(plus_one) + n = function_node(plus_one) self.assertTrue(n.ready) self.assertFalse(n.running) self.assertFalse(n.failed) @@ -176,64 +190,8 @@ def test_statuses(self): self.assertFalse(n.running) self.assertTrue(n.failed) - def test_with_self(self): - def with_self(self, x: float) -> float: - # Note: Adding internal state to the node like this goes against the best - # practice of keeping nodes "functional". Following python's paradigm of - # giving users lots of power, we want to guarantee that this behaviour is - # _possible_. - if "some_counter" in self._user_data: - self._user_data["some_counter"] += 1 - else: - self._user_data["some_counter"] = 1 - return x + 0.1 - - node = Function(with_self, output_labels="output") - self.assertTrue( - "x" in node.inputs.labels, - msg=f"Expected to find function input 'x' in the node input but got " - f"{node.inputs.labels}" - ) - self.assertFalse( - "self" in node.inputs.labels, - msg="Expected 'self' to be filtered out of node input, but found it in the " - "input labels" - ) - node.inputs.x = 1.0 - node.run() - self.assertEqual( - node.outputs.output.value, - 1.1, - msg="Basic node functionality appears to have failed" - ) - self.assertEqual( - node._user_data["some_counter"], - 1, - msg="Function functions should be able to modify attributes on the node " - "object." - ) - - node.executor = Executor() - with self.assertRaises( - ValueError, - msg="We haven't implemented any way to update a function node's `self` when" - "it runs on an executor, so trying to do so should fail hard" - ): - node.run() - node.executor_shutdown() # Shouldn't get this far, but if we do shutdown - node.executor = None - - def with_messed_self(x: float, self) -> float: - return x + 0.1 - - with warnings.catch_warnings(record=True) as warning_list: - node = Function(with_messed_self) - self.assertTrue("self" in node.inputs.labels) - - self.assertEqual(len(warning_list), 1) - def test_call(self): - node = Function(no_default, output_labels="output") + node = function_node(no_default, output_labels="output") with self.subTest("Ensure desired failures occur"): with self.assertRaises(ValueError): @@ -267,30 +225,11 @@ def test_call(self): msg="__call__ should allow updating only _some_ input before running" ) - with self.subTest("Check that bad kwargs don't stop good ones"): - with self.assertWarns(Warning): - original_label = node.label - node(4, label="won't get read", y=5, foobar="not a kwarg of any sort") - - self.assertEqual( - node.label, - original_label, - msg="You should only be able to update input on a call, that's " - "what the warning is for!" - ) - self.assertTupleEqual( - (node.inputs.x.value, node.inputs.y.value), - (4, 5), - msg="The warning should not prevent other data from being parsed" - ) - - with self.assertWarns(Warning): - # It's also fine if you just have a typo in your kwarg or whatever, - # there should just be a warning that the data didn't get updated - node(some_randome_kwaaaaarg="foo") + with self.assertRaises(ValueError, msg="Check that bad kwargs raise an error"): + node(4, label="won't get read", y=5, foobar="not a kwarg of any sort") def test_return_value(self): - node = Function(plus_one) + node = function_node(plus_one) with self.subTest("Run on main process"): node.inputs.x = 2 @@ -312,14 +251,14 @@ def test_return_value(self): ) def test_copy_connections(self): - node = Function(plus_one) + node = function_node(plus_one) - upstream = Function(plus_one) - to_copy = Function(plus_one, x=upstream.outputs.y) - downstream = Function(plus_one, x=to_copy.outputs.y) + upstream = function_node(plus_one) + to_copy = function_node(plus_one, x=upstream.outputs.y) + downstream = function_node(plus_one, x=to_copy.outputs.y) upstream >> to_copy >> downstream - wrong_io = Function( + wrong_io = function_node( returns_multiple, x=upstream.outputs.y, y=upstream.outputs.y ) downstream.inputs.x.connect(wrong_io.outputs.y) @@ -336,7 +275,7 @@ def plus_one_hinted(x: int = 0) -> int: y = x + 1 return y - hinted_node = Function(plus_one_hinted) + hinted_node = function_node(plus_one_hinted) with self.subTest("Ensure failed copies fail cleanly"): with self.assertRaises(ConnectionCopyError, msg="Wrong labels"): @@ -376,12 +315,12 @@ def plus_one_hinted(x: int = 0) -> int: ) def test_copy_values(self): - @function_node() + @as_function_node() def reference(x=0, y: int = 0, z: int | float = 0, omega=None, extra_here=None): out = 42 return out - @function_node() + @as_function_node() def all_floats(x=1.1, y=1.1, z=1.1, omega=NOT_DATA, extra_there=None) -> float: out = 42.1 return out @@ -426,7 +365,7 @@ def all_floats(x=1.1, y=1.1, z=1.1, omega=NOT_DATA, extra_there=None) -> float: # Note also that these nodes each have extra channels the other doesn't that # are simply ignored - @function_node() + @as_function_node() def extra_channel(x=1, y=1, z=1, not_present=42): out = 42 return out @@ -449,8 +388,8 @@ def extra_channel(x=1, y=1, z=1, not_present=42): ref._copy_values(extra, fail_hard=True) def test_easy_output_connection(self): - n1 = Function(plus_one) - n2 = Function(plus_one) + n1 = function_node(plus_one) + n2 = function_node(plus_one) n2.inputs.x = n1 @@ -468,7 +407,7 @@ def test_easy_output_connection(self): "in this case default->plus_one->plus_one = 1 + 1 +1 = 3" ) - at_instantiation = Function(plus_one, x=n1) + at_instantiation = function_node(plus_one, x=n1) self.assertIn( n1.outputs.y, at_instantiation.inputs.x.connections, msg="The parsing of Single-output functions' output as a connection should " @@ -478,11 +417,11 @@ def test_easy_output_connection(self): def test_nested_declaration(self): # It's really just a silly case of running without a parent, where you don't # store references to all the nodes declared - node = Function( + node = function_node( plus_one, - x=Function( + x=function_node( plus_one, - x=Function( + x=function_node( plus_one, x=2 ) @@ -504,7 +443,7 @@ def __getitem__(self, item): def returns_foo() -> Foo: return Foo() - single_output = Function(returns_foo, output_labels="foo") + single_output = function_node(returns_foo, output_labels="foo") self.assertEqual( single_output.connected, @@ -551,6 +490,32 @@ def returns_foo() -> Foo: ): single_output._some_nonexistant_private_var + def test_void_return(self): + """Test extensions to the `ScrapesIO` mixin.""" + + @as_function_node() + def NoReturn(x): + y = x + 1 + + self.assertDictEqual( + {"None": type(None)}, + NoReturn.preview_outputs(), + msg="Functions without a return value should be permissible, although it " + "is not interesting" + ) + # Honestly, functions with no return should probably be made illegal to + # encourage functional setups... + + def test_pickle(self): + n = function_node(plus_one, 5, output_labels="p1") + n() + reloaded = pickle.loads(pickle.dumps(n)) + self.assertListEqual(n.outputs.labels, reloaded.outputs.labels) + self.assertDictEqual( + n.outputs.to_value_dict(), + reloaded.outputs.to_value_dict() + ) + if __name__ == '__main__': unittest.main() diff --git a/tests/unit/test_io.py b/tests/unit/test_io.py index 628a9dac..b71a49c8 100644 --- a/tests/unit/test_io.py +++ b/tests/unit/test_io.py @@ -227,8 +227,44 @@ def test_init(self): def test_set_input_values(self): has_io = Dummy() has_io.inputs["input_channel"] = InputData("input_channel", has_io) - has_io.set_input_values(input_channel="value") - self.assertEqual(has_io.inputs["input_channel"].value, "value") + has_io.inputs["more_input"] = InputData("more_input", has_io) + + has_io.set_input_values("v1", "v2") + self.assertDictEqual( + {"input_channel": "v1", "more_input": "v2"}, + has_io.inputs.to_value_dict(), + msg="Args should be set by order of channel appearance" + ) + has_io.set_input_values(more_input="v4", input_channel="v3") + self.assertDictEqual( + {"input_channel": "v3", "more_input": "v4"}, + has_io.inputs.to_value_dict(), + msg="Kwargs should be set by key-label matching" + ) + has_io.set_input_values("v5", more_input="v6") + self.assertDictEqual( + {"input_channel": "v5", "more_input": "v6"}, + has_io.inputs.to_value_dict(), + msg="Mixing and matching args and kwargs is permissible" + ) + + with self.assertRaises( + ValueError, + msg="More args than channels is disallowed" + ): + has_io.set_input_values(1, 2, 3) + + with self.assertRaises( + ValueError, + msg="A channel updating from both args and kwargs is disallowed" + ): + has_io.set_input_values(1, input_channel=2) + + with self.assertRaises( + ValueError, + msg="Kwargs not among input is disallowed" + ): + has_io.set_input_values(not_a_channel=42) def test_connected_and_disconnect(self): has_io1 = Dummy(label="io1") @@ -287,7 +323,7 @@ def test_lshift_operator(self): has_io1 << (has_io2, has_io3) print(has_io1.signals.input.accumulate_and_run.connections) self.assertListEqual( - [has_io2.signals.output.ran, has_io3.signals.output.ran], + [has_io3.signals.output.ran, has_io2.signals.output.ran], has_io1.signals.input.accumulate_and_run.connections, msg="Left shift should accommodate groups of connections" ) diff --git a/tests/unit/test_io_preview.py b/tests/unit/test_io_preview.py new file mode 100644 index 00000000..d7cdddae --- /dev/null +++ b/tests/unit/test_io_preview.py @@ -0,0 +1,195 @@ +from abc import ABC, abstractmethod +from textwrap import dedent +import unittest + +from pyiron_workflow.channels import NOT_DATA +from pyiron_workflow.io_preview import ScrapesIO, OutputLabelsNotValidated +from pyiron_workflow.snippets.factory import classfactory + + +class ScrapesFromDecorated(ScrapesIO): + @classmethod + def _io_defining_function(cls) -> callable: + return cls._decorated_function + + +@classfactory +def scraper_factory( + io_defining_function, + validate_output_labels, + io_defining_function_uses_self, + /, + *output_labels, +): + return ( + io_defining_function.__name__, + (ScrapesFromDecorated,), # Define parentage + { + "_decorated_function": staticmethod(io_defining_function), + "__module__": io_defining_function.__module__, + "_output_labels": None if len(output_labels) == 0 else output_labels, + "_validate_output_labels": validate_output_labels, + "_io_defining_function_uses_self": io_defining_function_uses_self + }, + {}, + ) + + +def as_scraper( + *output_labels, + validate_output_labels=True, + io_defining_function_uses_self=False, +): + def scraper_decorator(fnc): + scraper_factory.clear(fnc.__name__) # Force a fresh class + factory_made = scraper_factory( + fnc, validate_output_labels, io_defining_function_uses_self, *output_labels + ) + factory_made._class_returns_from_decorated_function = fnc + factory_made.preview_io() + return factory_made + return scraper_decorator + + +class TestIOPreview(unittest.TestCase): + # FROM FUNCTION + def test_void(self): + @as_scraper() + def AbsenceOfIOIsPermissible(): + nothing = None + + def test_preview_inputs(self): + @as_scraper() + def Mixed(x, y: int = 42): + """Has (un)hinted and with(out)-default input""" + return x + y + + self.assertDictEqual( + {"x": (None, NOT_DATA), "y": (int, 42)}, + Mixed.preview_inputs(), + msg="Input specifications should be available at the class level, with or " + "without type hints and/or defaults provided." + ) + + with self.subTest("Protected"): + with self.assertRaises( + ValueError, + msg="Inputs must not overlap with __init__ signature terms" + ): + @as_scraper() + def Selfish(self, x): + return x + + def test_preview_outputs(self): + + with self.subTest("Plain"): + @as_scraper() + def Return(x): + return x + + self.assertDictEqual( + {"x": None}, + Return.preview_outputs(), + msg="Should parse without label or hint." + ) + + with self.subTest("Labeled"): + @as_scraper("y") + def LabeledReturn(x) -> None: + return x + + self.assertDictEqual( + {"y": type(None)}, + LabeledReturn.preview_outputs(), + msg="Should parse with label and hint." + ) + + with self.subTest("Hint-return count mismatch"): + with self.assertRaises( + ValueError, + msg="Should fail when scraping incommensurate hints and returns" + ): + @as_scraper() + def HintMismatchesScraped(x) -> int: + y, z = 5.0, 5 + return x, y, z + + with self.assertRaises( + ValueError, + msg="Should fail when provided labels are incommensurate with hints" + ): + @as_scraper("xo", "yo", "zo") + def HintMismatchesProvided(x) -> int: + y, z = 5.0, 5 + return x, y, z + + with self.subTest("Provided-scraped mismatch"): + with self.assertRaises( + ValueError, + msg="The nuber of labels -- if explicitly provided -- must be commensurate " + "with the number of returned items" + ): + @as_scraper("xo", "yo") + def LabelsMismatchScraped(x) -> tuple[int, float]: + y, z = 5.0, 5 + return x + + @as_scraper("x0", "x1", validate_output_labels=False) + def IgnoreScraping(x) -> tuple[int, float]: + x = (5, 5.5) + return x + + self.assertDictEqual( + {"x0": int, "x1": float}, + IgnoreScraping.preview_outputs(), + msg="Returned tuples can be received by force" + ) + + with self.subTest("Multiple returns"): + with self.assertRaises( + ValueError, + msg="Branched returns cannot be scraped and will fail on validation" + ): + @as_scraper("truth") + def Branched(x) -> bool: + if x <= 0: + return False + else: + return True + + @as_scraper("truth", validate_output_labels=False) + def Branched(x) -> bool: + if x <= 0: + return False + else: + return True + self.assertDictEqual( + {"truth": bool}, + Branched.preview_outputs(), + msg="We can force-override this at our own risk." + ) + + with self.subTest("Uninspectable function"): + def _uninspectable(): + template = dedent(f""" + def __source_code_not_available(x): + return x + """) + exec(template) + return locals()["__source_code_not_available"] + + f = _uninspectable() + + with self.assertRaises( + OSError, + msg="If the source code cannot be inspected for output labels, they " + "_must_ be provided." + ): + as_scraper()(f) + + with self.assertWarns( + OutputLabelsNotValidated, + msg="If provided labels cannot be validated against the source code, " + "a warning should be issued" + ): + as_scraper("y")(f) diff --git a/tests/unit/test_job.py b/tests/unit/test_job.py index 009275c5..1fb867ba 100644 --- a/tests/unit/test_job.py +++ b/tests/unit/test_job.py @@ -1,4 +1,5 @@ from abc import ABC, abstractmethod +import pickle import sys from time import sleep import unittest @@ -7,12 +8,7 @@ from pyiron_workflow import Workflow from pyiron_workflow.channels import NOT_DATA import pyiron_workflow.job # To get the job classes registered - - -@Workflow.wrap_as.function_node("t") -def Sleep(t): - sleep(t) - return t +from pyiron_workflow.node import Node class _WithAJob(unittest.TestCase, ABC): @@ -29,39 +25,11 @@ def tearDown(self) -> None: class TestNodeOutputJob(_WithAJob): - def make_a_job_from_node(self, node, job_name=None): - job = self.pr.create.job.NodeOutputJob( - node.label if job_name is None else job_name - ) + def make_a_job_from_node(self, node): + job = self.pr.create.job.NodeOutputJob(node.label) job.input["node"] = node return job - @unittest.skipIf(sys.version_info < (3, 11), "Storage will only work in 3.11+") - def test_job_name_override(self): - job_name = "my_name" - job = self.make_a_job_from_node( - Workflow.create.standard.UserInput(42), - job_name=job_name - ) - self.assertEqual( - job_name, - job.job_name, - msg="Sanity check" - ) - try: - job.save() - self.assertEqual( - job_name, - job.job_name, - msg="Standard behaviour for the parent class is to dynamically rename " - "the job at save time; since we create these jobs as usual from " - "the job creator, this is just confusing and we want to avoid it. " - "If this behaviour is every changed in pyiron_base, the override " - "and this test can both be removed." - ) - finally: - job.remove() - @unittest.skipIf(sys.version_info >= (3, 11), "Storage should only work in 3.11+") def test_clean_failure(self): with self.assertRaises( @@ -76,6 +44,9 @@ def test_clean_failure(self): def test_node(self): node = Workflow.create.standard.UserInput(42) nj = self.make_a_job_from_node(node) + + self.assertIsInstance(nj.get_input_node(), Node, msg="Sanity check") + nj.run() self.assertEqual( 42, @@ -83,10 +54,23 @@ def test_node(self): msg="A single node should run just as well as a workflow" ) + self.assertIsInstance( + nj.input["node"], + str, + msg="On saving, we convert the input to a bytestream so DataContainer can " + "handle storing it." + ) + self.assertIsInstance( + nj.get_input_node(), + Node, + msg="But we might want to look at it again, so make sure this convenience " + "method works." + ) + @unittest.skipIf(sys.version_info < (3, 11), "Storage will only work in 3.11+") def test_modal(self): modal_wf = Workflow("modal_wf") - modal_wf.sleep = Sleep(0) + modal_wf.sleep = Workflow.create.standard.Sleep(0) modal_wf.out = modal_wf.create.standard.UserInput(modal_wf.sleep) nj = self.make_a_job_from_node(modal_wf) @@ -163,24 +147,19 @@ def test_bad_input(self): @unittest.skipIf(sys.version_info < (3, 11), "Storage will only work in 3.11+") def test_unloadable(self): - @Workflow.wrap_as.function_node("y") + @Workflow.wrap.as_function_node("y") def not_importable_directy_from_module(x): return x + 1 nj = self.make_a_job_from_node(not_importable_directy_from_module(42)) + nj.run() self.assertEqual( 43, nj.output.y, - msg="Things should run fine locally" + msg="Factory made objects should be able to be cloudpickled even when they " + "can't be pickled." ) - with self.assertRaises( - AttributeError, - msg="We have promised that you'll hit trouble if you try to load a job " - "whose nodes are not all importable directly from their module" - # h5io also has this limitation, so I suspect that may be the source - ): - self.pr.load(nj.job_name) @unittest.skipIf(sys.version_info < (3, 11), "Storage will only work in 3.11+") def test_shorter_name(self): @@ -209,43 +188,52 @@ def test_clean_failure(self): def test_node(self): node = Workflow.create.standard.UserInput(42) nj = self.make_a_job_from_node(node) - nj.run() - self.assertEqual( - 42, - nj.node.outputs.user_input.value, - msg="A single node should run just as well as a workflow" - ) + try: + nj.run() + self.assertEqual( + 42, + nj.node.outputs.user_input.value, + msg="A single node should run just as well as a workflow" + ) + finally: + try: + node.storage.delete() + except FileNotFoundError: + pass @unittest.skipIf(sys.version_info < (3, 11), "Storage will only work in 3.11+") def test_modal(self): modal_wf = Workflow("modal_wf") - modal_wf.sleep = Sleep(0) + modal_wf.sleep = Workflow.create.standard.Sleep(0) modal_wf.out = modal_wf.create.standard.UserInput(modal_wf.sleep) nj = self.make_a_job_from_node(modal_wf) - nj.run() - self.assertTrue( - nj.status.finished, - msg="The interpreter should not release until the job is done" - ) - self.assertEqual( - 0, - nj.node.outputs.out__user_input.value, - msg="The node should have run, and since it's modal there's no need to " - "update the instance" - ) + try: + nj.run() + self.assertTrue( + nj.status.finished, + msg="The interpreter should not release until the job is done" + ) + self.assertEqual( + 0, + nj.node.outputs.out__user_input.value, + msg="The node should have run, and since it's modal there's no need to " + "update the instance" + ) - lj = self.pr.load(nj.job_name) - self.assertIsNot( - lj, - nj, - msg="The loaded job should be a new instance." - ) - self.assertEqual( - nj.node.outputs.out__user_input.value, - lj.node.outputs.out__user_input.value, - msg="The loaded job should still have all the same values" - ) + lj = self.pr.load(nj.job_name) + self.assertIsNot( + lj, + nj, + msg="The loaded job should be a new instance." + ) + self.assertEqual( + nj.node.outputs.out__user_input.value, + lj.node.outputs.out__user_input.value, + msg="The loaded job should still have all the same values" + ) + finally: + modal_wf.storage.delete() @unittest.skipIf(sys.version_info < (3, 11), "Storage will only work in 3.11+") def test_nonmodal(self): @@ -253,31 +241,35 @@ def test_nonmodal(self): nonmodal_node.out = Workflow.create.standard.UserInput(42) nj = self.make_a_job_from_node(nonmodal_node) - nj.run(run_mode="non_modal") - self.assertFalse( - nj.status.finished, - msg=f"The local process should released immediately per non-modal " - f"style, but got status {nj.status}" - ) - while not nj.status.finished: - sleep(0.1) - self.assertTrue( - nj.status.finished, - msg="The job status should update on completion" - ) - self.assertIs( - nj.node.outputs.out__user_input.value, - NOT_DATA, - msg="As usual with remote processes, we expect to require a data read " - "before the local instance reflects its new state." - ) - lj = self.pr.load(nj.job_name) - self.assertEqual( - 42, - lj.node.outputs.out__user_input.value, - msg="The loaded job should have the finished values" - ) + try: + nj.run(run_mode="non_modal") + self.assertFalse( + nj.status.finished, + msg=f"The local process should released immediately per non-modal " + f"style, but got status {nj.status}" + ) + while not nj.status.finished: + sleep(0.1) + self.assertTrue( + nj.status.finished, + msg="The job status should update on completion" + ) + self.assertIs( + nj.node.outputs.out__user_input.value, + NOT_DATA, + msg="As usual with remote processes, we expect to require a data read " + "before the local instance reflects its new state." + ) + + lj = self.pr.load(nj.job_name) + self.assertEqual( + 42, + lj.node.outputs.out__user_input.value, + msg="The loaded job should have the finished values" + ) + finally: + nonmodal_node.storage.delete() @unittest.skipIf(sys.version_info < (3, 11), "Storage will only work in 3.11+") def test_bad_workflow(self): diff --git a/tests/unit/test_macro.py b/tests/unit/test_macro.py index f6950ab3..d594e1d1 100644 --- a/tests/unit/test_macro.py +++ b/tests/unit/test_macro.py @@ -1,15 +1,13 @@ -import sys from concurrent.futures import Future -from functools import partialmethod - +import pickle +import sys from time import sleep import unittest - from pyiron_workflow._tests import ensure_tests_in_python_path from pyiron_workflow.channels import NOT_DATA -from pyiron_workflow.function import Function -from pyiron_workflow.macro import Macro, macro_node +from pyiron_workflow.function import function_node +from pyiron_workflow.macro import Macro, macro_node, as_macro_node from pyiron_workflow.topology import CircularDataFlowError @@ -18,36 +16,24 @@ def add_one(x): return result -def add_three_macro(macro): - macro.one = Function(add_one) - Function(add_one, macro.one, label="two", parent=macro) - macro.add_child(Function(add_one, macro.two, label="three")) +def add_three_macro(self, one__x): + self.one = function_node(add_one, x=one__x) + function_node(add_one, self.one, label="two", parent=self) + self.add_child(function_node(add_one, self.two, label="three")) # Cover a handful of addition methods, # although these are more thoroughly tested in Workflow tests + return self.three def wrong_return_macro(macro): - macro.one = Function(add_one) + macro.one = function_node(add_one) return 3 class TestMacro(unittest.TestCase): - def test_static_input(self): - m = Macro(add_three_macro) - inp = m.inputs - inp_again = m.inputs - self.assertIs( - inp, inp_again, msg="Should not be rebuilding just to look at it" - ) - m._rebuild_data_io() - new_inp = m.inputs - self.assertIsNot( - inp, new_inp, msg="After rebuild we should get a new object" - ) - def test_io_independence(self): - m = Macro(add_three_macro) + m = macro_node(add_three_macro, output_labels="three__result") self.assertIsNot( m.inputs.one__x, m.one.inputs.x, @@ -65,7 +51,7 @@ def test_io_independence(self): ) def test_value_links(self): - m = Macro(add_three_macro) + m = macro_node(add_three_macro, output_labels="three__result") self.assertIs( m.one.inputs.x, m.inputs.one__x.value_receiver, @@ -96,21 +82,24 @@ def test_value_links(self): def test_execution_automation(self): fully_automatic = add_three_macro - def fully_defined(macro): - add_three_macro(macro) - macro.one >> macro.two >> macro.three - macro.starting_nodes = [macro.one] + def fully_defined(self, one__x): + add_three_macro(self, one__x=one__x) + self.one >> self.two >> self.three + self.starting_nodes = [self.one] + return self.three - def only_order(macro): - add_three_macro(macro) - macro.two >> macro.three + def only_order(self, one__x): + add_three_macro(self, one__x=one__x) + self.two >> self.three + return self.three - def only_starting(macro): - add_three_macro(macro) - macro.starting_nodes = [macro.one] + def only_starting(self, one__x): + add_three_macro(self, one__x=one__x) + self.starting_nodes = [self.one] + return self.three - m_auto = Macro(fully_automatic) - m_user = Macro(fully_defined) + m_auto = macro_node(fully_automatic, output_labels="three__result") + m_user = macro_node(fully_defined, output_labels="three__result") x = 0 expected = add_one(add_one(add_one(x))) @@ -129,24 +118,24 @@ def only_starting(macro): # We don't yet check for _crappy_ user-defined execution, # But we should make sure it's at least valid in principle with self.assertRaises(ValueError): - Macro(only_order) + macro_node(only_order, output_labels="three__result") with self.assertRaises(ValueError): - Macro(only_starting) + macro_node(only_starting, output_labels="three__result") def test_default_label(self): - m = Macro(add_three_macro) + m = macro_node(add_three_macro, output_labels="three__result") self.assertEqual( m.label, add_three_macro.__name__, msg="Label should be automatically generated" ) label = "custom_name" - m2 = Macro(add_three_macro, label=label) + m2 = macro_node(add_three_macro, label=label, output_labels="three__result") self.assertEqual(m2.label, label, msg="Should be able to specify a label") def test_creation_from_decorator(self): - m = Macro(add_three_macro) + m = as_macro_node("three__result")(add_three_macro)() self.assertIs( m.outputs.three__result.value, @@ -157,6 +146,7 @@ def test_creation_from_decorator(self): input_x = 1 expected_value = add_one(add_one(add_one(input_x))) + print(m.inputs.labels, m.outputs.labels, m.child_labels) out = m(one__x=input_x) # Take kwargs to set input at runtime self.assertEqual( @@ -172,13 +162,12 @@ def test_creation_from_decorator(self): def test_creation_from_subclass(self): class MyMacro(Macro): - def build_graph(self): - add_three_macro(self) + _output_labels = ("three__result",) - __init__ = partialmethod( - Macro.__init__, - build_graph, - ) + @staticmethod + def graph_creator(self, one__x): + add_three_macro(self, one__x) + return self.three x = 0 m = MyMacro(one__x=x) @@ -190,35 +179,35 @@ def build_graph(self): ) def test_nesting(self): - def nested_macro(macro): - macro.a = Function(add_one) - macro.b = Macro( + def nested_macro(self, a__x): + self.a = function_node(add_one, a__x) + self.b = macro_node( add_three_macro, - one__x=macro.a, - outputs_map={"two__result": "intermediate_result"} + one__x=self.a, + output_labels="three__result" ) - macro.c = Macro( + self.c = macro_node( add_three_macro, - one__x=macro.b.outputs.three__result, - outputs_map={"two__result": "intermediate_result"} + one__x=self.b.outputs.three__result, + output_labels="three__result" ) - macro.d = Function( + self.d = function_node( add_one, - x=macro.c.outputs.three__result, + x=self.c.outputs.three__result, ) - macro.a >> macro.b >> macro.c >> macro.d - macro.starting_nodes = [macro.a] + self.a >> self.b >> self.c >> self.d + self.starting_nodes = [self.a] # This definition of the execution graph is not strictly necessary in this # simple DAG case; we just do it to make sure nesting definied/automatic - # macros works ok - macro.outputs_map = {"b__intermediate_result": "deep_output"} + # selfs works ok + return self.d - m = Macro(nested_macro) + m = macro_node(nested_macro, output_labels="d__result") self.assertEqual(m(a__x=0).d__result, 8) def test_with_executor(self): - macro = Macro(add_three_macro) - downstream = Function(add_one, x=macro.outputs.three__result) + macro = macro_node(add_three_macro, output_labels="three__result") + downstream = function_node(add_one, x=macro.outputs.three__result) macro >> downstream # Manually specify since we'll run the macro but look # at the downstream output, and none of this is happening in a workflow @@ -296,8 +285,8 @@ def test_with_executor(self): macro.executor_shutdown() def test_pulling_from_inside_a_macro(self): - upstream = Function(add_one, x=2) - macro = Macro(add_three_macro, one__x=upstream) + upstream = function_node(add_one, x=2) + macro = macro_node(add_three_macro, one__x=upstream, output_labels="three__result") macro.inputs.one__x = 0 # Set value # Now macro.one.inputs.x has both value and a connection @@ -323,14 +312,14 @@ def grab_x_and_run(node): with self.subTest("When the local scope has cyclic data flow"): def cyclic_macro(macro): - macro.one = Function(add_one) - macro.two = Function(add_one, x=macro.one) + macro.one = function_node(add_one) + macro.two = function_node(add_one, x=macro.one) macro.one.inputs.x = macro.two macro.one >> macro.two macro.starting_nodes = [macro.one] # We need to manually specify execution since the data flow is cyclic - m = Macro(cyclic_macro) + m = macro_node(cyclic_macro) initial_labels = list(m.children.keys()) @@ -359,9 +348,11 @@ def grab_connections(macro): ) with self.subTest("When the parent scope has cyclic data flow"): - n1 = Function(add_one, label="n1", x=0) - n2 = Function(add_one, label="n2", x=n1) - m = Macro(add_three_macro, label="m", one__x=n2) + n1 = function_node(add_one, label="n1", x=0) + n2 = function_node(add_one, label="n2", x=n1) + m = macro_node( + add_three_macro, label="m", one__x=n2, output_labels="three__result" + ) self.assertEqual( 0 + 1 + 1 + (1 + 1 + 1), @@ -404,9 +395,9 @@ def fail_at_zero(x): y = 1 / x return y - n1 = Function(fail_at_zero, x=0) - n2 = Function(add_one, x=n1, label="n1") - n_not_used = Function(add_one) + n1 = function_node(fail_at_zero, x=0) + n2 = function_node(add_one, x=n1, label="n1") + n_not_used = function_node(add_one) n_not_used >> n2 # Just here to make sure it gets restored with self.assertRaises( @@ -425,105 +416,60 @@ def fail_at_zero(x): msg="Original connections should get restored on upstream failure" ) - def test_output_labels_vs_return_values(self): - def no_return(macro): - macro.foo = macro.create.standard.UserInput() + def test_efficient_signature_interface(self): + with self.subTest("Forked input"): + @as_macro_node("output") + def MutlipleUseInput(self, x): + self.n1 = self.create.standard.UserInput(x) + self.n2 = self.create.standard.UserInput(x) + return self.n1 - Macro(no_return) # Neither is fine + m = MutlipleUseInput() + self.assertEqual( + 2 + 1, + len(m), + msg="Signature input that is forked to multiple children should result " + "in the automatic creation of a new node to manage the forking." - with self.assertRaises( - TypeError, - msg="Output labels and return values must match" - ): - Macro(no_return, output_labels="not_None") + ) - @macro_node("some_return") - def HasReturn(macro): - macro.foo = macro.create.standard.UserInput() - return macro.foo + with self.subTest("Single destination input"): + @as_macro_node("output") + def SingleUseInput(self, x): + self.n = self.create.standard.UserInput(x) + return self.n - HasReturn() # Both is fine + m = SingleUseInput() + self.assertEqual( + 1, + len(m), + msg=f"Signature input with only one destination should not create an " + f"interface node. Found nodes {m.child_labels}" + ) - with self.assertRaises( - TypeError, - msg="Output labels and return values must match" - ): - HasReturn(output_labels=None) # Override those gotten by the decorator + with self.subTest("Mixed input"): + @as_macro_node("output") + def MixedUseInput(self, x, y): + self.n1 = self.create.standard.UserInput(x) + self.n2 = self.create.standard.UserInput(y) + self.n3 = self.create.standard.UserInput(y) + return self.n1 - with self.assertRaises( - ValueError, - msg="Output labels and return values must have commensurate length" - ): - HasReturn(output_labels=["one_label", "too_many"]) - - def test_maps_vs_functionlike_definitions(self): - """ - Check that the full-detail IO maps and the white-listing like-a-function - approach are equivalent - """ - @macro_node() - def WithIOMaps(macro): - macro.list_in = macro.create.standard.UserInput() - macro.list_in.inputs.user_input.type_hint = list - macro.forked = macro.create.standard.UserInput(2) - macro.forked.inputs.user_input.type_hint = int - macro.n_plus_2 = macro.forked + 2 - macro.sliced_list = macro.list_in[macro.forked:macro.n_plus_2] - macro.double_fork = 2 * macro.forked - macro.inputs_map = { - "list_in__user_input": "lin", - macro.forked.inputs.user_input.scoped_label: "n", - "n_plus_2__other": None, - "list_in__user_input_Slice_forked__user_input_n_plus_2__add_None__step": None, - macro.double_fork.inputs.other.scoped_label: None, - } - macro.outputs_map = { - macro.sliced_list.outputs.getitem.scoped_label: "lout", - macro.n_plus_2.outputs.add.scoped_label: "n_plus_2", - "double_fork__rmul": None - } - - @macro_node("lout", "n_plus_2") - def LikeAFunction(macro, lin: list, n: int = 2): - macro.plus_two = n + 2 - macro.sliced_list = lin[n:macro.plus_two] - macro.double_fork = 2 * n - # ^ This is vestigial, just to show we don't need to blacklist it - # Test returning both a single value node and an output channel, - # even though here we could just use the node both times - return macro.sliced_list, macro.plus_two.channel - - n = 1 # Override the default - lin = [1, 2, 3, 4, 5, 6] - expected_input_labels = ["lin", "n"] - expected_result = {"n_plus_2": 3, "lout": [2, 3]} - - for MacroClass in [WithIOMaps, LikeAFunction]: - with self.subTest(f"{MacroClass.__name__}"): - macro = MacroClass(n=n, lin=lin) - self.assertListEqual(macro.inputs.labels, expected_input_labels) - self.assertDictEqual(macro(), expected_result) - - # Make sure whatever the user defines takes precedence, even over whitelists - override_io_maps = LikeAFunction( - my_lin=[1, 2, 3, 4], - inputs_map={ - "n__user_input": None, - "lin__user_input": "my_lin", - }, - outputs_map={ - "sliced_list__getitem": None, - "plus_two__add": None, - "lin__user_input": "the_input_list", - } - ) - # Manually set the required input data we hid from the macro IO - # (You wouldn't ever actually hide necessary IO like this, this is just for the - # silly test) - # override_io_maps.n.inputs.user_input = 1 - # ^ If default is not working you'd need this - self.assertListEqual(override_io_maps.inputs.labels, ["my_lin"]) - self.assertDictEqual(override_io_maps(), {"the_input_list": [1, 2, 3, 4]}) + m = MixedUseInput() + self.assertEqual( + 3 + 1, + len(m), + msg=f"Mixing forked and single-use input should not cause problems. " + f"Expected four children but found {m.child_labels}" + ) + + with self.subTest("Pass through"): + @as_macro_node("output") + def PassThrough(self, x): + return x + + m = PassThrough() + print(m.child_labels, m.inputs, m.outputs) @unittest.skipIf(sys.version_info < (3, 11), "Storage will only work in 3.11+") def test_storage_for_modified_macros(self): @@ -537,73 +483,115 @@ def test_storage_for_modified_macros(self): label="m", x=0, storage_backend=backend ) original_result = macro() - macro.replace_child(macro.two, Macro.create.demo.AddPlusOne()) + macro.replace_child( + macro.two, + Macro.create.demo.AddPlusOne() + ) - if backend == "h5io": - # Go really wild and actually change the interface to the node - # By replacing one of the terminal nodes - macro.remove_child(macro.three) - macro.five = Macro.create.standard.Add(macro.two, 1) - macro.two >> macro.five - macro._rebuild_data_io() # Need this because of the - # explicitly created node! - # Note that it destroys our output labeling, since the new - # output never existed modified_result = macro() - macro.save() - reloaded = Macro.create.demo.AddThree( - label="m", storage_backend=backend - ) - self.assertDictEqual( - modified_result, - reloaded.outputs.to_value_dict(), - msg="Updated IO should have been (de)serialized" - ) - self.assertSetEqual( - set(macro.children.keys()), - set(reloaded.children.keys()), - msg="All nodes should have been (de)serialized." - ) # Note that this snags the _new_ one in the case of h5io! - self.assertEqual( - Macro.create.demo.AddThree.__name__, - reloaded.__class__.__name__, - msg=f"LOOK OUT! This all (de)serialized nicely, but what we " - f"loaded is _falsely_ claiming to be an " - f"{Macro.create.demo.AddThree.__name__}. This is " - f"not any sort of technical error -- what other class name " - f"would we load? -- but is a deeper problem with saving " - f"modified objects that we need ot figure out some better " - f"solution for later." - ) - rerun = reloaded() - if backend == "h5io": + with self.assertRaises( + TypeError, msg="h5io can't handle custom reconstructors" + ): + macro.save() + else: + macro.save() + reloaded = Macro.create.demo.AddThree( + label="m", storage_backend=backend + ) self.assertDictEqual( modified_result, - rerun, - msg="Rerunning should re-execute the _modified_ " - "functionality" + reloaded.outputs.to_value_dict(), + msg="Updated IO should have been (de)serialized" ) - elif backend == "tinybase": - self.assertDictEqual( - original_result, - rerun, - msg="Rerunning should re-execute the _original_ " - "functionality" + self.assertSetEqual( + set(macro.children.keys()), + set(reloaded.children.keys()), + msg="All nodes should have been (de)serialized." + ) # Note that this snags the _new_ one in the case of h5io! + self.assertEqual( + Macro.create.demo.AddThree.__name__, + reloaded.__class__.__name__, + msg=f"LOOK OUT! This all (de)serialized nicely, but what we " + f"loaded is _falsely_ claiming to be an " + f"{Macro.create.demo.AddThree.__name__}. This is " + f"not any sort of technical error -- what other class name " + f"would we load? -- but is a deeper problem with saving " + f"modified objects that we need ot figure out some better " + f"solution for later." ) - else: - raise ValueError(f"Unexpected backend {backend}?") + rerun = reloaded() + + if backend == "tinybase": + self.assertDictEqual( + original_result, + rerun, + msg="Rerunning should re-execute the _original_ " + "functionality" + ) + else: + raise ValueError(f"Unexpected backend {backend}?") finally: macro.storage.delete() - def test_wrong_return(self): + def test_output_label_stripping(self): + """Test extensions to the `ScrapesIO` mixin.""" + + @as_macro_node() + def OutputScrapedFromFilteredReturn(macro): + macro.foo = macro.create.standard.UserInput() + return macro.foo + + self.assertListEqual( + ["foo"], + list(OutputScrapedFromFilteredReturn.preview_outputs().keys()), + msg="The first, self-like argument, should get stripped from output labels" + ) + with self.assertRaises( - TypeError, - msg="Macro returning object without channel did not raise an error" + ValueError, + msg="Return values with extra dots are not permissible as scraped labels" ): - Macro(wrong_return_macro) + @as_macro_node() + def ReturnHasDot(macro): + macro.foo = macro.create.standard.UserInput() + return macro.foo.outputs.user_input + + def test_pickle(self): + m = macro_node(add_three_macro) + m(1) + reloaded_m = pickle.loads(pickle.dumps(m)) + self.assertTupleEqual( + m.child_labels, + reloaded_m.child_labels, + msg="Spot check values are getting reloaded correctly" + ) + self.assertDictEqual( + m.outputs.to_value_dict(), + reloaded_m.outputs.to_value_dict(), + msg="Spot check values are getting reloaded correctly" + ) + self.assertTrue( + reloaded_m.two.connected, + msg="The macro should reload with all its child connections" + ) + + self.assertTrue(m.two.connected, msg="Sanity check") + reloaded_two = pickle.loads(pickle.dumps(m.two)) + self.assertFalse( + reloaded_two.connected, + msg="Children are expected to be de-parenting on serialization, so that if " + "we ship them off to another process, they don't drag their whole " + "graph with them" + ) + self.assertEqual( + m.two.outputs.to_value_dict(), + reloaded_two.outputs.to_value_dict(), + msg="The remainder of the child node state should be recovering just " + "fine on (de)serialization, this is a spot-check" + ) if __name__ == '__main__': diff --git a/tests/unit/test_node.py b/tests/unit/test_node.py index ce84b572..c929661c 100644 --- a/tests/unit/test_node.py +++ b/tests/unit/test_node.py @@ -1,7 +1,5 @@ from concurrent.futures import Future import os -import platform -from subprocess import CalledProcessError import sys from typing import Literal, Optional import unittest @@ -22,24 +20,11 @@ def add_one(x): class ANode(Node): """To de-abstract the class""" - def __init__( - self, - label, - overwrite_save=False, - run_after_init=False, - storage_backend: Optional[Literal["h5io", "tinybase"]] = None, - save_after_run: bool = False, - x=None, - ): - super().__init__( - label=label, save_after_run=save_after_run, storage_backend=storage_backend - ) + def _setup_node(self) -> None: self._inputs = Inputs(InputData("x", self, type_hint=int)) self._outputs = OutputsWithInjection( OutputDataWithInjection("y", self, type_hint=int), ) - if x is not None: - self.inputs.x = x @property def inputs(self) -> Inputs: @@ -49,13 +34,12 @@ def inputs(self) -> Inputs: def outputs(self) -> OutputsWithInjection: return self._outputs - @property - def on_run(self): - return add_one + def on_run(self, *args, **kwargs): + return add_one(*args) @property def run_args(self) -> dict: - return {"x": self.inputs.x.value} + return (self.inputs.x.value,), {} def process_run_result(self, run_output): self.outputs.y.value = run_output @@ -67,12 +51,12 @@ def to_dict(self): class TestNode(unittest.TestCase): def setUp(self): - self.n1 = ANode("start", x=0) - self.n2 = ANode("middle", x=self.n1.outputs.y) - self.n3 = ANode("end", x=self.n2.outputs.y) + self.n1 = ANode(label="start", x=0) + self.n2 = ANode(label="middle", x=self.n1.outputs.y) + self.n3 = ANode(label="end", x=self.n2.outputs.y) def test_set_input_values(self): - n = ANode("some_node") + n = ANode() n.set_input_values(x=2) self.assertEqual( 2, @@ -80,8 +64,8 @@ def test_set_input_values(self): msg="Post-instantiation update of inputs should also work" ) - n.set_input_values(y=3) - # Missing keys may throw a warning, but are otherwise allowed to pass + with self.assertRaises(ValueError, msg="Non-input-channel kwargs not allowed"): + n.set_input_values(z=3) with self.assertRaises( TypeError, @@ -322,29 +306,17 @@ def test_draw(self): for fmt in ["pdf", "png"]: with self.subTest(f"Testing with format {fmt}"): - if fmt == "pdf" and platform.system() == "Windows": - with self.assertRaises( - CalledProcessError, - msg="Graphviz doesn't seem to be happy about the " - "combindation PDF format and Windows right now. We " - "throw a warning for it in `Node.draw`, so if this " - "test ever fails and this combination _doesn't_ fail, " - "remove this extra bit of testing and remove the " - "warning." - ): - self.n1.draw(save=True, format=fmt) - else: - self.n1.draw(save=True, format=fmt) - expected_name = self.n1.label + "_graph." + fmt - # That name is just an implementation detail, update it as - # needed - self.assertTrue( - self.n1.working_directory.path.joinpath( - expected_name - ).is_file(), - msg="If `save` is called, expect the rendered image to " - "exist in the working directory" - ) + self.n1.draw(save=True, format=fmt) + expected_name = self.n1.label + "_graph." + fmt + # That name is just an implementation detail, update it as + # needed + self.assertTrue( + self.n1.working_directory.path.joinpath( + expected_name + ).is_file(), + msg="If `save` is called, expect the rendered image to " + "exist in the working directory" + ) user_specified_name = "foo" self.n1.draw(filename=user_specified_name, format=fmt) @@ -366,12 +338,12 @@ def test_run_after_init(self): ) self.assertEqual( 1, - ANode("right_away", run_after_init=True, x=0).outputs.y.value, + ANode(label="right_away", run_after_init=True, x=0).outputs.y.value, msg="With run_after_init, the node should run right away" ) def test_graph_info(self): - n = ANode("n") + n = ANode() self.assertEqual( n.semantic_delimiter + n.label, @@ -388,7 +360,7 @@ def test_graph_info(self): ) def test_single_value(self): - node = ANode("n") + node = ANode(label="n") self.assertIs( node.outputs.y, node.channel, @@ -404,7 +376,7 @@ def test_single_value(self): "on the single (with-injection) output" ) - node2 = ANode("n2") + node2 = ANode(label="n2") node2.inputs.x = node self.assertListEqual( [node.outputs.y], @@ -446,14 +418,14 @@ def test_storage(self): self.n1.save() x = self.n1.inputs.x.value - reloaded = ANode(self.n1.label, x=x, storage_backend=backend) + reloaded = ANode(label=self.n1.label, x=x, storage_backend=backend) self.assertEqual( y, reloaded.outputs.y.value, msg="Nodes should load by default if they find a save file" ) - clean_slate = ANode(self.n1.label, x=x, overwrite_save=True) + clean_slate = ANode(label=self.n1.label, x=x, overwrite_save=True) self.assertIs( clean_slate.outputs.y.value, NOT_DATA, @@ -461,7 +433,10 @@ def test_storage(self): ) run_right_away = ANode( - self.n1.label, x=x, run_after_init=True, storage_backend=backend + label=self.n1.label, + x=x, + run_after_init=True, + storage_backend=backend ) self.assertEqual( y, @@ -476,11 +451,17 @@ def test_storage(self): "once" ): ANode( - self.n1.label, x=x, run_after_init=True, storage_backend=backend + label=self.n1.label, + x=x, + run_after_init=True, + storage_backend=backend ) force_run = ANode( - self.n1.label, x=x, run_after_init=True, overwrite_save=True + label=self.n1.label, + x=x, + run_after_init=True, + overwrite_save=True ) self.assertEqual( y, @@ -495,9 +476,14 @@ def test_save_after_run(self): for backend in Node.allowed_backends(): with self.subTest(backend): try: - ANode("just_run", x=0, run_after_init=True, storage_backend=backend) + ANode( + label="just_run", + x=0, + run_after_init=True, + storage_backend=backend + ) saves = ANode( - "run_and_save", + label="run_and_save", x=0, run_after_init=True, save_after_run=True, @@ -505,7 +491,7 @@ def test_save_after_run(self): ) y = saves.outputs.y.value - not_reloaded = ANode("just_run", storage_backend=backend) + not_reloaded = ANode(label="just_run", storage_backend=backend) self.assertIs( NOT_DATA, not_reloaded.outputs.y.value, @@ -513,7 +499,7 @@ def test_save_after_run(self): "to load" ) - find_saved = ANode("run_and_save", storage_backend=backend) + find_saved = ANode(label="run_and_save", storage_backend=backend) self.assertEqual( y, find_saved.outputs.y.value, diff --git a/tests/unit/test_run.py b/tests/unit/test_run.py index 25d5583a..7f9fde4f 100644 --- a/tests/unit/test_run.py +++ b/tests/unit/test_run.py @@ -17,7 +17,7 @@ def on_run(self, **kwargs): @property def run_args(self): - return {"foo": 42} + return (), {"foo": 42} def process_run_result(self, run_output): self.processed = dict(run_output) diff --git a/tests/unit/test_transform.py b/tests/unit/test_transform.py new file mode 100644 index 00000000..ffd121aa --- /dev/null +++ b/tests/unit/test_transform.py @@ -0,0 +1,231 @@ +from dataclasses import dataclass, field, is_dataclass +import pickle +import random +import unittest + +from pandas import DataFrame + +from pyiron_workflow.channels import NOT_DATA +from pyiron_workflow.transform import ( + Transformer, + as_dataclass_node, + dataclass_node, + inputs_to_dataframe, + inputs_to_dict, + inputs_to_list, + list_to_outputs, +) + + +class TestTransformer(unittest.TestCase): + def test_pickle(self): + n = inputs_to_list(3, "a", "b", "c", run_after_init=True) + self.assertListEqual( + ["a", "b", "c"], + n.outputs.list.value, + msg="Sanity check" + ) + reloaded = pickle.loads(pickle.dumps(n)) + self.assertListEqual( + n.outputs.list.value, + reloaded.outputs.list.value, + msg="Transformer nodes should be (un)pickleable" + ) + self.assertIsInstance(reloaded, Transformer) + + def test_inputs_to_list(self): + n = inputs_to_list(3, "a", "b", "c", run_after_init=True) + self.assertListEqual(["a", "b", "c"], n.outputs.list.value) + + def test_list_to_outputs(self): + l = ["a", "b", "c", "d", "e"] + n = list_to_outputs(5, l, run_after_init=True) + self.assertEqual(l, n.outputs.to_list()) + + def test_inputs_to_dict(self): + with self.subTest("List specification"): + d = {"c1": 4, "c2": 5} + n = inputs_to_dict(list(d.keys()), **d, run_after_init=True) + self.assertDictEqual( + d, + n.outputs.dict.value, + msg="Verify structure and ability to pass kwargs" + ) + + with self.subTest("Dict specification"): + d = {"c1": 4, "c2": 5} + default = 42 + hint = int + spec = {k: (int, default) for k in d.keys()} + n = inputs_to_dict(spec, run_after_init=True) + self.assertIs( + n.inputs[list(d.keys())[0]].type_hint, + hint, + msg="Spot check hint recognition" + ) + self.assertDictEqual( + {k: default for k in d.keys()}, + n.outputs.dict.value, + msg="Verify structure and ability to pass defaults" + ) + + with self.subTest("Explicit suffix"): + suffix = "MyName" + n = inputs_to_dict(["c1", "c2"], class_name_suffix="MyName") + self.assertTrue( + n.__class__.__name__.endswith(suffix) + ) + + with self.subTest("Only hashable"): + unhashable_spec = {"c1": (list, ["an item"])} + with self.assertRaises( + ValueError, + msg="List instances are not hashable, we should not be able to auto-" + "generate a class name from this." + ): + inputs_to_dict(unhashable_spec) + + n = inputs_to_dict(unhashable_spec, class_name_suffix="Bypass") + self.assertListEqual(n.inputs.labels, list(unhashable_spec.keys())) + key = list(unhashable_spec.keys())[0] + self.assertIs(unhashable_spec[key][0], n.inputs[key].type_hint) + self.assertListEqual(unhashable_spec[key][1], n.inputs[key].value) + + def test_inputs_to_dataframe(self): + l = 3 + n = inputs_to_dataframe(l) + for i in range(l): + n.inputs[f"row_{i}"] = {"x": i, "xsq": i*i} + n() + self.assertIsInstance( + n.outputs.df.value, + DataFrame, + msg="Confirm output type" + ) + self.assertListEqual( + [i*i for i in range(3)], + n.outputs.df.value["xsq"].to_list(), + msg="Spot check values" + ) + + d1 = {"a": 1, "b": 1} + d2 = {"a": 1, "c": 2} + with self.assertRaises( + KeyError, + msg="If the input rows don't have commensurate keys, we expect to get the " + "relevant pandas error" + ): + n(row_0=d1, row_1=d1, row_2=d2) + + n = inputs_to_dataframe(l) # Freshly instantiate to remove failed status + d3 = {"a": 1} + with self.assertRaises( + ValueError, + msg="If the input rows don't have commensurate length, we expect to get " + "the relevant pandas error" + ): + n(row_0=d1, row_1=d3, row_2=d1) + + def test_dataclass_node(self): + # Note: We'd need to declare the generator and classes outside the of + # this test function if we wanted them to be pickleable, but we test the + # pickleability of transformers elsewhere so just keep stuff tidy by declaring + # locally for this test + + def some_generator(): + return [1, 2, 3] + + with self.subTest("From instantiator"): + @dataclass + class DC: + necessary: str + with_default: int = 42 + with_factory: list = field(default_factory=some_generator) + + n = dataclass_node(DC, label="direct_instance") + self.assertIs( + n.dataclass, + DC, + msg="Underlying dataclass should be accessible" + ) + self.assertListEqual( + list(DC.__dataclass_fields__.keys()), + n.inputs.labels, + msg="Inputs should correspond exactly to fields" + ) + self.assertIs( + DC, + n.outputs.dataclass.type_hint, + msg="Output type hint should get automatically set" + ) + key = random.choice(n.inputs.labels) + self.assertIs( + DC.__dataclass_fields__[key].type, + n.inputs[key].type_hint, + msg="Spot-check input type hints are pulled from dataclass fields" + ) + self.assertFalse( + n.inputs.necessary.ready, + msg="Fields with no default and no default factory should not be ready" + ) + self.assertTrue( + n.inputs.with_default.ready, + msg="Fields with default should be ready" + ) + self.assertTrue( + n.inputs.with_factory.ready, + msg="Fields with default factory should be ready" + ) + self.assertListEqual( + n.inputs.with_factory.value, + some_generator(), + msg="Verify the generator is being used to set the intial value" + ) + out = n(necessary="something") + self.assertIsInstance( + out, + DC, + msg="Node should output an instance of the dataclass" + ) + + with self.subTest("From decorator"): + @as_dataclass_node + @dataclass + class DecoratedDC: + necessary: str + with_default: int = 42 + with_factory: list = field(default_factory=some_generator) + + n_cls = DecoratedDC(label="decorated_instance") + + self.assertTrue( + is_dataclass(n_cls.dataclass), + msg="Underlying dataclass should be available on node class" + ) + prev = n_cls.preview_inputs() + key = random.choice(list(prev.keys())) + self.assertIs( + n_cls._dataclass_fields[key].type, + prev[key][0], + msg="Spot-check input type hints are pulled from dataclass fields" + ) + self.assertIs( + prev["necessary"][1], + NOT_DATA, + msg="Field has no default" + ) + self.assertEqual( + n_cls._dataclass_fields["with_default"].default, + prev["with_default"][1], + msg="Fields with default should get scraped" + ) + self.assertIs( + prev["with_factory"][1], + NOT_DATA, + msg="Fields with default factory won't see their default until " + "instantiation" + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/unit/test_workflow.py b/tests/unit/test_workflow.py index f82cff7a..1d6b7f89 100644 --- a/tests/unit/test_workflow.py +++ b/tests/unit/test_workflow.py @@ -1,8 +1,11 @@ from concurrent.futures import Future +import pickle import sys from time import sleep import unittest +from bidict import ValueDuplicationError + from pyiron_workflow._tests import ensure_tests_in_python_path from pyiron_workflow.channels import NOT_DATA from pyiron_workflow.semantics import ParentMostError @@ -16,11 +19,23 @@ def plus_one(x=0): return y -@Workflow.wrap_as.function_node("y") +@Workflow.wrap.as_function_node("y") def PlusOne(x: int = 0): return x + 1 +@Workflow.wrap.as_function_node() +def five(sleep_time=0.): + sleep(sleep_time) + five = 5 + return five + + +@Workflow.wrap.as_function_node("sum") +def sum(a, b): + return a + b + + class TestWorkflow(unittest.TestCase): @classmethod def setUpClass(cls) -> None: @@ -29,9 +44,9 @@ def setUpClass(cls) -> None: def test_io(self): wf = Workflow("wf") - wf.n1 = wf.create.Function(plus_one) - wf.n2 = wf.create.Function(plus_one) - wf.n3 = wf.create.Function(plus_one) + wf.n1 = wf.create.function_node(plus_one) + wf.n2 = wf.create.function_node(plus_one) + wf.n3 = wf.create.function_node(plus_one) inp = wf.inputs inp_again = wf.inputs @@ -41,7 +56,7 @@ def test_io(self): n_in = len(wf.inputs) n_out = len(wf.outputs) - wf.n4 = wf.create.Function(plus_one) + wf.n4 = wf.create.function_node(plus_one) self.assertEqual( n_in + 1, len(wf.inputs), msg="Workflow IO should be drawn from its nodes" ) @@ -71,6 +86,93 @@ def test_io(self): wf.n2.outputs.y, wf.outputs.intermediate, msg="IO should be by reference" ) self.assertNotIn(wf.n3.outputs.y, wf.outputs, msg="IO should be hidable") + + def test_io_maps(self): + # input and output, renaming, accessing connected, and deactivating disconnected + wf = Workflow("wf") + wf.n1 = Workflow.create.function_node(plus_one, x=0) + wf.n2 = Workflow.create.function_node(plus_one, x=wf.n1) + wf.n3 = Workflow.create.function_node(plus_one, x=wf.n2) + wf.m = Workflow.create.function_node(plus_one, x=42) + wf.inputs_map = { + "n1__x": "x", # Rename + "n2__x": "intermediate_x", # Expose + "m__x": None, # Hide + } + wf.outputs_map = { + "n3__y": "y", # Rename + "n2__y": "intermediate_y", # Expose, + "m__y": None, # Hide + } + self.assertIn("x", wf.inputs.labels, msg="Should be renamed") + self.assertIn("y", wf.outputs.labels, msg="Should be renamed") + self.assertIn("intermediate_x", wf.inputs.labels, msg="Should be exposed") + self.assertIn("intermediate_y", wf.outputs.labels, msg="Should be exposed") + self.assertNotIn("m__x", wf.inputs.labels, msg="Should be hidden") + self.assertNotIn("m__y", wf.outputs.labels, msg="Should be hidden") + self.assertNotIn("m__y", wf.outputs.labels, msg="Should be hidden") + + wf.set_run_signals_to_dag_execution() + out = wf.run() + self.assertEqual( + 3, + out.y, + msg="New names should be propagated to the returned value" + ) + self.assertNotIn( + "m__y", + list(out.keys()), + msg="IO filtering should be evident in returned value" + ) + self.assertEqual( + 43, + wf.m.outputs.y.value, + msg="The child channel should still exist and have run" + ) + self.assertEqual( + 1, + wf.inputs.intermediate_x.value, + msg="IO should be up-to-date post-run" + ) + self.assertEqual( + 2, + wf.outputs.intermediate_y.value, + msg="IO should be up-to-date post-run" + ) + + def test_io_map_bijectivity(self): + wf = Workflow("wf") + with self.assertRaises( + ValueDuplicationError, + msg="Should not be allowed to map two children's channels to the same label" + ): + wf.inputs_map = {"n1__x": "x", "n2__x": "x"} + + wf.inputs_map = {"n1__x": "x"} + with self.assertRaises( + ValueDuplicationError, + msg="Should not be allowed to update a second child's channel onto an " + "existing mapped channel" + ): + wf.inputs_map["n2__x"] = "x" + + with self.subTest("Ensure we can use None to turn multiple off"): + wf.inputs_map = {"n1__x": None, "n2__x": None} # At once + # Or in a row + wf.inputs_map = {} + wf.inputs_map["n1__x"] = None + wf.inputs_map["n2__x"] = None + wf.inputs_map["n3__x"] = None + self.assertEqual( + 3, + len(wf.inputs_map), + msg="All entries should be stored" + ) + self.assertEqual( + 0, + len(wf.inputs), + msg="No IO should be left exposed" + ) def test_is_parentmost(self): wf = Workflow("wf") @@ -91,8 +193,8 @@ def test_is_parentmost(self): def test_with_executor(self): wf = Workflow("wf") - wf.a = wf.create.Function(plus_one) - wf.b = wf.create.Function(plus_one, x=wf.a) + wf.a = wf.create.function_node(plus_one) + wf.b = wf.create.function_node(plus_one, x=wf.a) original_a = wf.a wf.executor = wf.create.Executor() @@ -137,16 +239,6 @@ def test_with_executor(self): def test_parallel_execution(self): wf = Workflow("wf") - @Workflow.wrap_as.function_node() - def five(sleep_time=0.): - sleep(sleep_time) - five = 5 - return five - - @Workflow.wrap_as.function_node("sum") - def sum(a, b): - return a + b - wf.slow = five(sleep_time=1) wf.fast = five() wf.sum = sum(a=wf.fast, b=wf.slow) @@ -189,10 +281,10 @@ def sum(a, b): def test_call(self): wf = Workflow("wf") - wf.a = wf.create.Function(plus_one) - wf.b = wf.create.Function(plus_one) + wf.a = wf.create.function_node(plus_one) + wf.b = wf.create.function_node(plus_one) - @Workflow.wrap_as.function_node("sum") + @Workflow.wrap.as_function_node("sum") def sum_(a, b): return a + b @@ -219,8 +311,8 @@ def sum_(a, b): def test_return_value(self): wf = Workflow("wf") - wf.a = wf.create.Function(plus_one) - wf.b = wf.create.Function(plus_one, x=wf.a) + wf.a = wf.create.function_node(plus_one) + wf.b = wf.create.function_node(plus_one, x=wf.a) with self.subTest("Run on main process"): return_on_call = wf(a__x=1) @@ -241,7 +333,7 @@ def test_return_value(self): ) def test_execution_automation(self): - @Workflow.wrap_as.function_node("out") + @Workflow.wrap.as_function_node("out") def foo(x, y): return x + y @@ -309,15 +401,17 @@ def matches_expectations(results): cyclic() def test_pull_and_executors(self): - def add_three_macro(macro): - macro.one = Workflow.create.Function(plus_one) - macro.two = Workflow.create.Function(plus_one, x=macro.one) - macro.three = Workflow.create.Function(plus_one, x=macro.two) + @Workflow.wrap.as_macro_node("three__result") + def add_three_macro(self, one__x): + self.one = Workflow.create.function_node(plus_one, x=one__x) + self.two = Workflow.create.function_node(plus_one, x=self.one) + self.three = Workflow.create.function_node(plus_one, x=self.two) + return self.three wf = Workflow("pulling") - wf.n1 = Workflow.create.Function(plus_one, x=0) - wf.m = Workflow.create.Macro(add_three_macro, one__x=wf.n1) + wf.n1 = Workflow.create.function_node(plus_one, x=0) + wf.m = add_three_macro(one__x=wf.n1) self.assertEquals( (0 + 1) + (1 + 1), @@ -350,27 +444,34 @@ def add_three_macro(macro): def test_storage_values(self): for backend in Workflow.allowed_backends(): with self.subTest(backend): - wf = Workflow("wf", storage_backend=backend) try: + print("Trying", backend) + wf = Workflow("wf", storage_backend=backend) wf.register("static.demo_nodes", domain="demo") wf.inp = wf.create.demo.AddThree(x=0) wf.out = wf.inp.outputs.add_three + 1 wf_out = wf() three_result = wf.inp.three.outputs.add.value - wf.save() - - reloaded = Workflow("wf", storage_backend=backend) - self.assertEqual( - wf_out.out__add, - reloaded.outputs.out__add.value, - msg="Workflow-level data should get reloaded" - ) - self.assertEqual( - three_result, - reloaded.inp.three.value, - msg="Child data arbitrarily deep should get reloaded" - ) + if backend == "h5io": + with self.assertRaises( + TypeError, + msg="h5io can't handle custom reconstructors" + ): + wf.save() + else: + wf.save() + reloaded = Workflow("wf", storage_backend=backend) + self.assertEqual( + wf_out.out__add, + reloaded.outputs.out__add.value, + msg="Workflow-level data should get reloaded" + ) + self.assertEqual( + three_result, + reloaded.inp.three.value, + msg="Child data arbitrarily deep should get reloaded" + ) finally: # Clean up after ourselves wf.storage.delete() @@ -388,9 +489,20 @@ def test_storage_scopes(self): for backend in Workflow.allowed_backends(): with self.subTest(backend): try: - wf.storage_backend = backend - wf.save() - Workflow(wf.label, storage_backend=backend) + for backend in Workflow.allowed_backends(): + if backend == "h5io": + with self.subTest(backend): + with self.assertRaises( + TypeError, + msg="h5io can't handle custom reconstructors" + ): + wf.storage_backend = backend + wf.save() + else: + with self.subTest(backend): + wf.storage_backend = backend + wf.save() + Workflow(wf.label, storage_backend=backend) finally: wf.storage.delete() @@ -408,31 +520,37 @@ def test_storage_scopes(self): wf.save() finally: wf.remove_child(wf.import_type_mismatch) + wf.storage.delete() if "h5io" in Workflow.allowed_backends(): wf.add_child(PlusOne(label="local_but_importable")) try: - wf.storage_backend = "h5io" - wf.save() - Workflow(wf.label, storage_backend="h5io") + with self.assertRaises( + TypeError, msg="h5io can't handle custom reconstructors" + ): + wf.storage_backend = "h5io" + wf.save() finally: wf.storage.delete() if "tinybase" in Workflow.allowed_backends(): - with self.assertRaises( - NotImplementedError, - msg="Storage docs for tinybase claim all children must be registered " - "nodes" - ): - wf.storage_backend = "tinybase" - wf.save() + try: + with self.assertRaises( + NotImplementedError, + msg="Storage docs for tinybase claim all children must be registered " + "nodes" + ): + wf.storage_backend = "tinybase" + wf.save() + finally: + wf.storage.delete() if "h5io" in Workflow.allowed_backends(): with self.subTest("Instanced node"): - wf.direct_instance = Workflow.create.Function(plus_one) + wf.direct_instance = Workflow.create.function_node(plus_one) try: with self.assertRaises( - TypeError, + TypeNotFoundError, msg="No direct node instances, only children with functions as " "_class_ attribtues" ): @@ -443,7 +561,7 @@ def test_storage_scopes(self): wf.storage.delete() with self.subTest("Unimportable node"): - @Workflow.wrap_as.function_node("y") + @Workflow.wrap.as_function_node("y") def UnimportableScope(x): return x @@ -462,6 +580,19 @@ def UnimportableScope(x): wf.remove_child(wf.unimportable_scope) wf.storage.delete() + def test_pickle(self): + wf = Workflow("wf") + wf.register("static.demo_nodes", domain="demo") + wf.inp = wf.create.demo.AddThree(x=0) + wf.out = wf.inp.outputs.add_three + 1 + wf_out = wf() + reloaded = pickle.loads(pickle.dumps(wf)) + self.assertDictEqual( + wf_out, + reloaded.outputs.to_value_dict(), + msg="Pickling should work" + ) + if __name__ == '__main__': unittest.main()