Commit 7b4c3f2d authored by Martin Bauer's avatar Martin Bauer
Browse files

Refactoring of plotting and stencil plotting

- stencil plotting & transformation now in ps.stencil
- additional documentation & notebooks
parent 0998f2e1
[flake8] [flake8]
max-line-length=120 max-line-length=120
exclude=pystencils/jupytersetup.py, exclude=pystencils/jupyter.py,
pystencils/plot2d.py pystencils/plot.py
pystencils/session.py pystencils/session.py
ignore = W293 W503 W291 ignore = W293 W503 W291
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"3.84 ms ± 36.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" "3.93 ms ± 40 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
] ]
} }
], ],
...@@ -132,7 +132,7 @@ ...@@ -132,7 +132,7 @@
], ],
"source": [ "source": [
"plt.figure(figsize=(3,3))\n", "plt.figure(figsize=(3,3))\n",
"ps.visualize_stencil_expression(symbolic_description.rhs)" "ps.stencil.plot_expression(symbolic_description.rhs)"
] ]
}, },
{ {
...@@ -180,7 +180,7 @@ ...@@ -180,7 +180,7 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"639 µs ± 35 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n" "643 µs ± 8.66 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
] ]
} }
], ],
...@@ -615,7 +615,7 @@ ...@@ -615,7 +615,7 @@
"</svg>\n" "</svg>\n"
], ],
"text/plain": [ "text/plain": [
"<graphviz.files.Source at 0x7fc7dc51b2e8>" "<graphviz.files.Source at 0x7ff8a018e7f0>"
] ]
}, },
"execution_count": 19, "execution_count": 19,
...@@ -995,129 +995,129 @@ ...@@ -995,129 +995,129 @@
"<g id=\"graph0\" class=\"graph\" transform=\"scale(.9826 .9826) rotate(0) translate(4 472)\">\n", "<g id=\"graph0\" class=\"graph\" transform=\"scale(.9826 .9826) rotate(0) translate(4 472)\">\n",
"<title>%3</title>\n", "<title>%3</title>\n",
"<polygon fill=\"#ffffff\" stroke=\"transparent\" points=\"-4,4 -4,-472 692.083,-472 692.083,4 -4,4\"/>\n", "<polygon fill=\"#ffffff\" stroke=\"transparent\" points=\"-4,4 -4,-472 692.083,-472 692.083,4 -4,4\"/>\n",
"<!-- 140495254316984 -->\n", "<!-- 140704680405368 -->\n",
"<g id=\"node1\" class=\"node\">\n", "<g id=\"node1\" class=\"node\">\n",
"<title>140495254316984</title>\n", "<title>140704680405368</title>\n",
"<ellipse fill=\"#a056db\" stroke=\"#000000\" cx=\"219.8449\" cy=\"-450\" rx=\"107.781\" ry=\"18\"/>\n", "<ellipse fill=\"#a056db\" stroke=\"#000000\" cx=\"219.8449\" cy=\"-450\" rx=\"107.781\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"219.8449\" y=\"-446.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Func: kernel (dst,img,w_2)</text>\n", "<text text-anchor=\"middle\" x=\"219.8449\" y=\"-446.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Func: kernel (dst,img,w_2)</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254318440 -->\n", "<!-- 140704680405256 -->\n",
"<g id=\"node11\" class=\"node\">\n", "<g id=\"node11\" class=\"node\">\n",
"<title>140495254318440</title>\n", "<title>140704680405256</title>\n",
"<ellipse fill=\"#dbc256\" stroke=\"#000000\" cx=\"219.8449\" cy=\"-378\" rx=\"31.6951\" ry=\"18\"/>\n", "<ellipse fill=\"#dbc256\" stroke=\"#000000\" cx=\"219.8449\" cy=\"-378\" rx=\"31.6951\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"219.8449\" y=\"-374.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Block</text>\n", "<text text-anchor=\"middle\" x=\"219.8449\" y=\"-374.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Block</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254316984&#45;&gt;140495254318440 -->\n", "<!-- 140704680405368&#45;&gt;140704680405256 -->\n",
"<g id=\"edge10\" class=\"edge\">\n", "<g id=\"edge10\" class=\"edge\">\n",
"<title>140495254316984&#45;&gt;140495254318440</title>\n", "<title>140704680405368&#45;&gt;140704680405256</title>\n",
"<path fill=\"none\" stroke=\"#000000\" d=\"M219.8449,-431.8314C219.8449,-424.131 219.8449,-414.9743 219.8449,-406.4166\"/>\n", "<path fill=\"none\" stroke=\"#000000\" d=\"M219.8449,-431.8314C219.8449,-424.131 219.8449,-414.9743 219.8449,-406.4166\"/>\n",
"<polygon fill=\"#000000\" stroke=\"#000000\" points=\"223.345,-406.4132 219.8449,-396.4133 216.345,-406.4133 223.345,-406.4132\"/>\n", "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"223.345,-406.4132 219.8449,-396.4133 216.345,-406.4133 223.345,-406.4132\"/>\n",
"</g>\n", "</g>\n",
"<!-- 140495254317656 -->\n", "<!-- 140704680405032 -->\n",
"<g id=\"node2\" class=\"node\">\n", "<g id=\"node2\" class=\"node\">\n",
"<title>140495254317656</title>\n", "<title>140704680405032</title>\n",
"<ellipse fill=\"#56db7f\" stroke=\"#000000\" cx=\"144.8449\" cy=\"-306\" rx=\"61.99\" ry=\"18\"/>\n", "<ellipse fill=\"#56db7f\" stroke=\"#000000\" cx=\"144.8449\" cy=\"-306\" rx=\"61.99\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"144.8449\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">_data_img_22</text>\n", "<text text-anchor=\"middle\" x=\"144.8449\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">_data_img_22</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254316256 -->\n", "<!-- 140704680404416 -->\n",
"<g id=\"node3\" class=\"node\">\n", "<g id=\"node3\" class=\"node\">\n",
"<title>140495254316256</title>\n", "<title>140704680404416</title>\n",
"<ellipse fill=\"#3498db\" stroke=\"#000000\" cx=\"295.8449\" cy=\"-306\" rx=\"70.6878\" ry=\"18\"/>\n", "<ellipse fill=\"#3498db\" stroke=\"#000000\" cx=\"295.8449\" cy=\"-306\" rx=\"70.6878\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"295.8449\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Loop over dim 0</text>\n", "<text text-anchor=\"middle\" x=\"295.8449\" y=\"-302.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Loop over dim 0</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254316032 -->\n", "<!-- 140704680404080 -->\n",
"<g id=\"node10\" class=\"node\">\n", "<g id=\"node10\" class=\"node\">\n",
"<title>140495254316032</title>\n", "<title>140704680404080</title>\n",
"<ellipse fill=\"#dbc256\" stroke=\"#000000\" cx=\"295.8449\" cy=\"-234\" rx=\"31.6951\" ry=\"18\"/>\n", "<ellipse fill=\"#dbc256\" stroke=\"#000000\" cx=\"295.8449\" cy=\"-234\" rx=\"31.6951\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"295.8449\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Block</text>\n", "<text text-anchor=\"middle\" x=\"295.8449\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Block</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254316256&#45;&gt;140495254316032 -->\n", "<!-- 140704680404416&#45;&gt;140704680404080 -->\n",
"<g id=\"edge7\" class=\"edge\">\n", "<g id=\"edge7\" class=\"edge\">\n",
"<title>140495254316256&#45;&gt;140495254316032</title>\n", "<title>140704680404416&#45;&gt;140704680404080</title>\n",
"<path fill=\"none\" stroke=\"#000000\" d=\"M295.8449,-287.8314C295.8449,-280.131 295.8449,-270.9743 295.8449,-262.4166\"/>\n", "<path fill=\"none\" stroke=\"#000000\" d=\"M295.8449,-287.8314C295.8449,-280.131 295.8449,-270.9743 295.8449,-262.4166\"/>\n",
"<polygon fill=\"#000000\" stroke=\"#000000\" points=\"299.345,-262.4132 295.8449,-252.4133 292.345,-262.4133 299.345,-262.4132\"/>\n", "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"299.345,-262.4132 295.8449,-252.4133 292.345,-262.4133 299.345,-262.4132\"/>\n",
"</g>\n", "</g>\n",
"<!-- 140495254318496 -->\n", "<!-- 140704681164528 -->\n",
"<g id=\"node4\" class=\"node\">\n", "<g id=\"node4\" class=\"node\">\n",
"<title>140495254318496</title>\n", "<title>140704681164528</title>\n",
"<ellipse fill=\"#56db7f\" stroke=\"#000000\" cx=\"57.8449\" cy=\"-162\" rx=\"57.6901\" ry=\"18\"/>\n", "<ellipse fill=\"#56db7f\" stroke=\"#000000\" cx=\"57.8449\" cy=\"-162\" rx=\"57.6901\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"57.8449\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">_data_dst_00</text>\n", "<text text-anchor=\"middle\" x=\"57.8449\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">_data_dst_00</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254316592 -->\n", "<!-- 140704680403520 -->\n",
"<g id=\"node5\" class=\"node\">\n", "<g id=\"node5\" class=\"node\">\n",
"<title>140495254316592</title>\n", "<title>140704680403520</title>\n",
"<ellipse fill=\"#56db7f\" stroke=\"#000000\" cx=\"208.8449\" cy=\"-162\" rx=\"74.9875\" ry=\"18\"/>\n", "<ellipse fill=\"#56db7f\" stroke=\"#000000\" cx=\"208.8449\" cy=\"-162\" rx=\"74.9875\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"208.8449\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">_data_img_22_01</text>\n", "<text text-anchor=\"middle\" x=\"208.8449\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">_data_img_22_01</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254317320 -->\n", "<!-- 140704680403352 -->\n",
"<g id=\"node6\" class=\"node\">\n", "<g id=\"node6\" class=\"node\">\n",
"<title>140495254317320</title>\n", "<title>140704680403352</title>\n",
"<ellipse fill=\"#56db7f\" stroke=\"#000000\" cx=\"383.8449\" cy=\"-162\" rx=\"81.7856\" ry=\"18\"/>\n", "<ellipse fill=\"#56db7f\" stroke=\"#000000\" cx=\"383.8449\" cy=\"-162\" rx=\"81.7856\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"383.8449\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">_data_img_22_0m1</text>\n", "<text text-anchor=\"middle\" x=\"383.8449\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">_data_img_22_0m1</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254318664 -->\n", "<!-- 140704680404024 -->\n",
"<g id=\"node7\" class=\"node\">\n", "<g id=\"node7\" class=\"node\">\n",
"<title>140495254318664</title>\n", "<title>140704680404024</title>\n",
"<ellipse fill=\"#3498db\" stroke=\"#000000\" cx=\"554.8449\" cy=\"-162\" rx=\"70.6878\" ry=\"18\"/>\n", "<ellipse fill=\"#3498db\" stroke=\"#000000\" cx=\"554.8449\" cy=\"-162\" rx=\"70.6878\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"554.8449\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Loop over dim 1</text>\n", "<text text-anchor=\"middle\" x=\"554.8449\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Loop over dim 1</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254318776 -->\n", "<!-- 140704680404360 -->\n",
"<g id=\"node9\" class=\"node\">\n", "<g id=\"node9\" class=\"node\">\n",
"<title>140495254318776</title>\n", "<title>140704680404360</title>\n",
"<ellipse fill=\"#dbc256\" stroke=\"#000000\" cx=\"554.8449\" cy=\"-90\" rx=\"31.6951\" ry=\"18\"/>\n", "<ellipse fill=\"#dbc256\" stroke=\"#000000\" cx=\"554.8449\" cy=\"-90\" rx=\"31.6951\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"554.8449\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Block</text>\n", "<text text-anchor=\"middle\" x=\"554.8449\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">Block</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254318664&#45;&gt;140495254318776 -->\n", "<!-- 140704680404024&#45;&gt;140704680404360 -->\n",
"<g id=\"edge2\" class=\"edge\">\n", "<g id=\"edge2\" class=\"edge\">\n",
"<title>140495254318664&#45;&gt;140495254318776</title>\n", "<title>140704680404024&#45;&gt;140704680404360</title>\n",
"<path fill=\"none\" stroke=\"#000000\" d=\"M554.8449,-143.8314C554.8449,-136.131 554.8449,-126.9743 554.8449,-118.4166\"/>\n", "<path fill=\"none\" stroke=\"#000000\" d=\"M554.8449,-143.8314C554.8449,-136.131 554.8449,-126.9743 554.8449,-118.4166\"/>\n",
"<polygon fill=\"#000000\" stroke=\"#000000\" points=\"558.345,-118.4132 554.8449,-108.4133 551.345,-118.4133 558.345,-118.4132\"/>\n", "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"558.345,-118.4132 554.8449,-108.4133 551.345,-118.4133 558.345,-118.4132\"/>\n",
"</g>\n", "</g>\n",
"<!-- 140495254317040 -->\n", "<!-- 140704680403968 -->\n",
"<g id=\"node8\" class=\"node\">\n", "<g id=\"node8\" class=\"node\">\n",
"<title>140495254317040</title>\n", "<title>140704680403968</title>\n",
"<ellipse fill=\"#56db7f\" stroke=\"#000000\" cx=\"554.8449\" cy=\"-18\" rx=\"133.4768\" ry=\"18\"/>\n", "<ellipse fill=\"#56db7f\" stroke=\"#000000\" cx=\"554.8449\" cy=\"-18\" rx=\"133.4768\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"554.8449\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">_data_dst_00[_stride_dst_1*ctr_1]</text>\n", "<text text-anchor=\"middle\" x=\"554.8449\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">_data_dst_00[_stride_dst_1*ctr_1]</text>\n",
"</g>\n", "</g>\n",
"<!-- 140495254318776&#45;&gt;140495254317040 -->\n", "<!-- 140704680404360&#45;&gt;140704680403968 -->\n",
"<g id=\"edge1\" class=\"edge\">\n", "<g id=\"edge1\" class=\"edge\">\n",
"<title>140495254318776&#45;&gt;140495254317040</title>\n", "<title>140704680404360&#45;&gt;140704680403968</title>\n",
"<path fill=\"none\" stroke=\"#000000\" d=\"M554.8449,-71.8314C554.8449,-64.131 554.8449,-54.9743 554.8449,-46.4166\"/>\n", "<path fill=\"none\" stroke=\"#000000\" d=\"M554.8449,-71.8314C554.8449,-64.131 554.8449,-54.9743 554.8449,-46.4166\"/>\n",
"<polygon fill=\"#000000\" stroke=\"#000000\" points=\"558.345,-46.4132 554.8449,-36.4133 551.345,-46.4133 558.345,-46.4132\"/>\n", "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"558.345,-46.4132 554.8449,-36.4133 551.345,-46.4133 558.345,-46.4132\"/>\n",
"</g>\n", "</g>\n",
"<!-- 140495254316032&#45;&gt;140495254318496 -->\n", "<!-- 140704680404080&#45;&gt;140704681164528 -->\n",
"<g id=\"edge3\" class=\"edge\">\n", "<g id=\"edge3\" class=\"edge\">\n",
"<title>140495254316032&#45;&gt;140495254318496</title>\n", "<title>140704680404080&#45;&gt;140704681164528</title>\n",
"<path fill=\"none\" stroke=\"#000000\" d=\"M267.6085,-225.4579C228.6723,-213.6789 157.8187,-192.2442 109.3243,-177.5736\"/>\n", "<path fill=\"none\" stroke=\"#000000\" d=\"M267.6085,-225.4579C228.6723,-213.6789 157.8187,-192.2442 109.3243,-177.5736\"/>\n",
"<polygon fill=\"#000000\" stroke=\"#000000\" points=\"110.2227,-174.1888 99.6376,-174.6432 108.1957,-180.8889 110.2227,-174.1888\"/>\n", "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"110.2227,-174.1888 99.6376,-174.6432 108.1957,-180.8889 110.2227,-174.1888\"/>\n",
"</g>\n", "</g>\n",
"<!-- 140495254316032&#45;&gt;140495254316592 -->\n", "<!-- 140704680404080&#45;&gt;140704680403520 -->\n",
"<g id=\"edge4\" class=\"edge\">\n", "<g id=\"edge4\" class=\"edge\">\n",
"<title>140495254316032&#45;&gt;140495254316592</title>\n", "<title>140704680404080&#45;&gt;140704680403520</title>\n",
"<path fill=\"none\" stroke=\"#000000\" d=\"M277.8184,-219.0816C266.2777,-209.5306 251.0436,-196.9231 237.8284,-185.9864\"/>\n", "<path fill=\"none\" stroke=\"#000000\" d=\"M277.8184,-219.0816C266.2777,-209.5306 251.0436,-196.9231 237.8284,-185.9864\"/>\n",
"<polygon fill=\"#000000\" stroke=\"#000000\" points=\"239.8475,-183.1143 229.9121,-179.4349 235.3845,-188.507 239.8475,-183.1143\"/>\n", "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"239.8475,-183.1143 229.9121,-179.4349 235.3845,-188.507 239.8475,-183.1143\"/>\n",
"</g>\n", "</g>\n",
"<!-- 140495254316032&#45;&gt;140495254317320 -->\n", "<!-- 140704680404080&#45;&gt;140704680403352 -->\n",
"<g id=\"edge5\" class=\"edge\">\n", "<g id=\"edge5\" class=\"edge\">\n",
"<title>140495254316032&#45;&gt;140495254317320</title>\n", "<title>140704680404080&#45;&gt;140704680403352</title>\n",
"<path fill=\"none\" stroke=\"#000000\" d=\"M314.0785,-219.0816C325.7519,-209.5306 341.1611,-196.9231 354.5282,-185.9864\"/>\n", "<path fill=\"none\" stroke=\"#000000\" d=\"M314.0785,-219.0816C325.7519,-209.5306 341.1611,-196.9231 354.5282,-185.9864\"/>\n",
"<polygon fill=\"#000000\" stroke=\"#000000\" points=\"357.0123,-188.4762 362.5355,-179.4349 352.5796,-183.0585 357.0123,-188.4762\"/>\n", "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"357.0123,-188.4762 362.5355,-179.4349 352.5796,-183.0585 357.0123,-188.4762\"/>\n",
"</g>\n", "</g>\n",
"<!-- 140495254316032&#45;&gt;140495254318664 -->\n", "<!-- 140704680404080&#45;&gt;140704680404024 -->\n",
"<g id=\"edge6\" class=\"edge\">\n", "<g id=\"edge6\" class=\"edge\">\n",
"<title>140495254316032&#45;&gt;140495254318664</title>\n", "<title>140704680404080&#45;&gt;140704680404024</title>\n",
"<path fill=\"none\" stroke=\"#000000\" d=\"M324.552,-226.0196C365.9645,-214.5073 443.366,-192.9903 496.9349,-178.0985\"/>\n", "<path fill=\"none\" stroke=\"#000000\" d=\"M324.552,-226.0196C365.9645,-214.5073 443.366,-192.9903 496.9349,-178.0985\"/>\n",
"<polygon fill=\"#000000\" stroke=\"#000000\" points=\"497.9488,-181.4495 506.646,-175.3989 496.0739,-174.7052 497.9488,-181.4495\"/>\n", "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"497.9488,-181.4495 506.646,-175.3989 496.0739,-174.7052 497.9488,-181.4495\"/>\n",
"</g>\n", "</g>\n",
"<!-- 140495254318440&#45;&gt;140495254317656 -->\n", "<!-- 140704680405256&#45;&gt;140704680405032 -->\n",
"<g id=\"edge8\" class=\"edge\">\n", "<g id=\"edge8\" class=\"edge\">\n",
"<title>140495254318440&#45;&gt;140495254317656</title>\n", "<title>140704680405256&#45;&gt;140704680405032</title>\n",
"<path fill=\"none\" stroke=\"#000000\" d=\"M203.571,-362.3771C193.8398,-353.0351 181.2651,-340.9635 170.2498,-330.3888\"/>\n", "<path fill=\"none\" stroke=\"#000000\" d=\"M203.571,-362.3771C193.8398,-353.0351 181.2651,-340.9635 170.2498,-330.3888\"/>\n",
"<polygon fill=\"#000000\" stroke=\"#000000\" points=\"172.5648,-327.7594 162.9271,-323.3589 167.7171,-332.8091 172.5648,-327.7594\"/>\n", "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"172.5648,-327.7594 162.9271,-323.3589 167.7171,-332.8091 172.5648,-327.7594\"/>\n",
"</g>\n", "</g>\n",
"<!-- 140495254318440&#45;&gt;140495254316256 -->\n", "<!-- 140704680405256&#45;&gt;140704680404416 -->\n",
"<g id=\"edge9\" class=\"edge\">\n", "<g id=\"edge9\" class=\"edge\">\n",
"<title>140495254318440&#45;&gt;140495254316256</title>\n", "<title>140704680405256&#45;&gt;140704680404416</title>\n",
"<path fill=\"none\" stroke=\"#000000\" d=\"M236.3357,-362.3771C246.1257,-353.1023 258.7558,-341.137 269.86,-330.6172\"/>\n", "<path fill=\"none\" stroke=\"#000000\" d=\"M236.3357,-362.3771C246.1257,-353.1023 258.7558,-341.137 269.86,-330.6172\"/>\n",
"<polygon fill=\"#000000\" stroke=\"#000000\" points=\"272.3977,-333.0344 277.2501,-323.6161 267.5835,-327.9527 272.3977,-333.0344\"/>\n", "<polygon fill=\"#000000\" stroke=\"#000000\" points=\"272.3977,-333.0344 277.2501,-323.6161 267.5835,-327.9527 272.3977,-333.0344\"/>\n",
"</g>\n", "</g>\n",
...@@ -1125,7 +1125,7 @@ ...@@ -1125,7 +1125,7 @@
"</svg>\n" "</svg>\n"
], ],
"text/plain": [ "text/plain": [
"<graphviz.files.Source at 0x7fc798393fd0>" "<graphviz.files.Source at 0x7ff84a432e10>"
] ]
}, },
"execution_count": 32, "execution_count": 32,
......
...@@ -70,13 +70,6 @@ ...@@ -70,13 +70,6 @@
"execution_count": 3, "execution_count": 3,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"!\n"
]
},
{ {
"data": { "data": {
"image/png": "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\n", "image/png": "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\n",
...@@ -188,9 +181,8 @@ ...@@ -188,9 +181,8 @@
" timeloop(10)\n", " timeloop(10)\n",
" result = None\n", " result = None\n",
"else:\n", "else:\n",
" ps_notebook.set_display_mode('video')\n", " ani = ps.plot.scalar_field_animation(timeloop, rescale=True, frames=300)\n",
" ani = ps.plot2d.scalar_field_animation(timeloop, rescale=True, frames=300)\n", " result = ps.jupyter.display_as_html_video(ani)\n",
" result = ps_notebook.display_animation(ani)\n",
"result" "result"
] ]
} }
......
This diff is collapsed.
This diff is collapsed.
...@@ -9,6 +9,7 @@ API Reference ...@@ -9,6 +9,7 @@ API Reference
datahandling.rst datahandling.rst
configuration.rst configuration.rst
field.rst field.rst
stencil.rst
finite_differences.rst finite_differences.rst
plot.rst plot.rst
ast.rst ast.rst
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Plotting and Animation Plotting and Animation
********************** **********************
.. automodule:: pystencils.plot2d .. automodule:: pystencils.plot
:members: :members:
*******
Stencil
*******
.. automodule:: pystencils.stencil
:members:
...@@ -15,6 +15,7 @@ It is a good idea to download them and run them directly to be able to play arou ...@@ -15,6 +15,7 @@ It is a good idea to download them and run them directly to be able to play arou
/notebooks/05_tutorial_phasefield_spinodal_decomposition.ipynb /notebooks/05_tutorial_phasefield_spinodal_decomposition.ipynb
/notebooks/06_tutorial_phasefield_dentritic_growth.ipynb /notebooks/06_tutorial_phasefield_dentritic_growth.ipynb
/notebooks/demo_assignment_collection.ipynb /notebooks/demo_assignment_collection.ipynb
/notebooks/demo_plotting_and_animation.ipynb
/notebooks/demo_derivatives.ipynb /notebooks/demo_derivatives.ipynb
/notebooks/demo_benchmark.ipynb /notebooks/demo_benchmark.ipynb
/notebooks/demo_wave_equation.ipynb /notebooks/demo_wave_equation.ipynb
...@@ -10,8 +10,8 @@ from .assignment import Assignment, assignment_from_stencil ...@@ -10,8 +10,8 @@ from .assignment import Assignment, assignment_from_stencil
from .sympyextensions import SymbolCreator from .sympyextensions import SymbolCreator
from .datahandling import create_data_handling from .datahandling import create_data_handling
from .kernel_decorator import kernel from .kernel_decorator import kernel
from .stencils import visualize_stencil_expression
from . import fd from . import fd
from . import stencil as stencil
__all__ = ['Field', 'FieldType', 'fields', __all__ = ['Field', 'FieldType', 'fields',
...@@ -26,4 +26,4 @@ __all__ = ['Field', 'FieldType', 'fields', ...@@ -26,4 +26,4 @@ __all__ = ['Field', 'FieldType', 'fields',
'create_data_handling', 'create_data_handling',
'kernel', 'kernel',
'fd', 'fd',
'visualize_stencil_expression'] 'stencil']
...@@ -159,8 +159,8 @@ class FiniteDifferenceStencilDerivation: ...@@ -159,8 +159,8 @@ class FiniteDifferenceStencilDerivation:
self.is_isotropic = is_isotropic self.is_isotropic = is_isotropic
def visualize(self): def visualize(self):
from pystencils.stencils import visualize_stencil from pystencils.stencil import plot
visualize_stencil(self.stencil, data=self.weights) plot(self.stencil, data=self.weights)
def apply(self, field_access: Field.Access): def apply(self, field_access: Field.Access):
f = field_access f = field_access
......
...@@ -8,7 +8,7 @@ from sympy.core.cache import cacheit ...@@ -8,7 +8,7 @@ from sympy.core.cache import cacheit
from pystencils.alignedarray import aligned_empty from pystencils.alignedarray import aligned_empty
from pystencils.data_types import create_type, StructType from pystencils.data_types import create_type, StructType
from pystencils.kernelparameters import FieldShapeSymbol, FieldStrideSymbol from pystencils.kernelparameters import FieldShapeSymbol, FieldStrideSymbol
from pystencils.stencils import offset_to_direction_string, direction_string_to_offset from pystencils.stencil import offset_to_direction_string, direction_string_to_offset
from pystencils.sympyextensions import is_integer_sequence from pystencils.sympyextensions import is_integer_sequence
import pickle import pickle
import hashlib import hashlib
......
import pystencils.plot2d as plt import pystencils.plot as plt
import matplotlib.animation as animation import matplotlib.animation as animation
from IPython.display import HTML from IPython.display import HTML
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
...@@ -125,13 +125,13 @@ def display_in_extra_window(*_, **__): ...@@ -125,13 +125,13 @@ def display_in_extra_window(*_, **__):
# ------- Version 3: Animation is shown in images that are updated directly in website -------------- # ------- Version 3: Animation is shown in images that are updated directly in website --------------
def display_as_html_image(animation, show=True, iterations=10000, *args, **kwargs): def display_as_html_image(animation, show=True, *args, **kwargs):
from IPython import display from IPython import display
try: try:
if show: if show:
animation._init_draw() animation._init_draw()
for i in range(iterations): for _ in animation.frame_seq:
if show: if show:
fig = plt.gcf() fig = plt.gcf()
display.display(fig) display.display(fig)
......
...@@ -5,7 +5,6 @@ matplotlib normally uses. ...@@ -5,7 +5,6 @@ matplotlib normally uses.
""" """
from matplotlib.pyplot import * from matplotlib.pyplot import *
from itertools import cycle from itertools import cycle
from matplotlib.text import Text
def vector_field(array, step=2, **kwargs): def vector_field(array, step=2, **kwargs):
...@@ -67,6 +66,26 @@ def scalar_field(array, **kwargs): ...@@ -67,6 +66,26 @@ def scalar_field(array, **kwargs):
return res return res
def scalar_field_surface(array, **kwargs):
"""Plots scalar field as 3D surface
Args:
array: the two dimensional numpy array to plot
kwargs: keyword arguments passed to :func:`mpl_toolkits.mplot3d.Axes3D.plot_surface`
"""
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
fig = gcf()
ax = fig.add_subplot(111, projection='3d')
x, y = np.meshgrid(np.arange(array.shape[0]), np.arange(array.shape[1]), indexing='ij')
kwargs.setdefault('rstride', 2)
kwargs.setdefault('cstride', 2)
kwargs.setdefault('color', 'b')
kwargs.setdefault('cmap', cm.coolwarm)
return ax.plot_surface(x, y, array, **kwargs)
def scalar_field_alpha_value(array, color, clip=False, **kwargs): def scalar_field_alpha_value(array, color, clip=False, **kwargs):
"""Plots an image with same color everywhere, using the array values as transparency. """Plots an image with same color everywhere, using the array values as transparency.
...@@ -158,6 +177,7 @@ def phase_plot(phase_field: np.ndarray, linewidth=1.0, clip=True) -> None: ...@@ -158,6 +177,7 @@ def phase_plot(phase_field: np.ndarray, linewidth=1.0, clip=True) -> None:
for i in range(phase_field.shape[-1]): for i in range(phase_field.shape[-1]):
scalar_field_contour(phase_field[..., i], levels=[0.5], colors='k', linewidths=[linewidth]) scalar_field_contour(phase_field[..., i], levels=[0.5], colors='k', linewidths=[linewidth])
def sympy_function(expr, x_values=None, **kwargs): def sympy_function(expr, x_values=None, **kwargs):
"""Plots the graph of a sympy term that depends on one symbol only. """Plots the graph of a sympy term that depends on one symbol only.
...@@ -307,14 +327,12 @@ def scalar_field_animation(run_function, plot_setup_function=lambda *_: None, re ...@@ -307,14 +327,12 @@ def scalar_field_animation(run_function, plot_setup_function=lambda *_: None, re
return animation.FuncAnimation(fig, update_figure, interval=interval, frames=frames) return animation.FuncAnimation(fig, update_figure, interval=interval, frames=frames)
def surface_plot_animation(run_function, frames=90, interval=30, **kwargs): def surface_plot_animation(run_function, frames=90, interval=30, zlim=None, **kwargs):
"""Animation of scalar field as 3D plot.""" """Animation of scalar field as 3D plot."""
from mpl_toolkits.mplot3d import Axes3D from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation import matplotlib.animation as animation
import matplotlib.pyplot as plt
from matplotlib import cm from matplotlib import cm
fig = gcf()
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d') ax = fig.add_subplot(111, projection='3d')
data = run_function() data = run_function()
x, y = np.meshgrid(np.arange(data.shape[0]), np.arange(data.shape[1]), indexing='ij') x, y = np.meshgrid(np.arange(data.shape[0]), np.arange(data.shape[1]), indexing='ij')
...@@ -323,13 +341,15 @@ def surface_plot_animation(run_function, frames=90, interval=30, **kwargs): ...@@ -323,13 +341,15 @@ def surface_plot_animation(run_function, frames=90, interval=30, **kwargs):
kwargs.setdefault('color', 'b') kwargs.setdefault('color', 'b')
kwargs.setdefault('cmap', cm.coolwarm) kwargs.setdefault('cmap', cm.coolwarm)
ax.plot_surface(x, y, data, **kwargs) ax.plot_surface(x, y, data, **kwargs)
ax.set_zlim(-1.0, 1.0) if zlim is not None:
ax.set_zlim(*zlim)
def update_figure(*_): def update_figure(*_):
d = run_function() d = run_function()
ax.clear() ax.clear()
plot = ax.plot_surface(x, y, d, **kwargs) plot = ax.plot_surface(x, y, d, **kwargs)
ax.set_zlim(-1.0, 1.0) if zlim is not None:
ax.set_zlim(*zlim)
return plot,