{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# VTK\\'s Next Generation API\n\nThis requires a pre-release version of VTK:\n\n``` bash\npip install --extra-index-url https://wheels.vtk.org vtk==9.3.20240629.dev0\n```\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import magpylib as magpy\nimport numpy as np\n\n# for factory overrides\nfrom pyvista.examples.downloads import _download_archive_file_or_folder\nfrom vtkmodules.util.data_model import * # noqa: F403\nfrom vtkmodules.util.execution_model import select_ports\nfrom vtkmodules.util.numpy_support import vtk_to_numpy\nfrom vtkmodules.vtkCommonCore import vtkIdList\nfrom vtkmodules.vtkCommonDataModel import vtkDataObjectTreeIterator, vtkImageData\nfrom vtkmodules.vtkFiltersExtraction import vtkExtractBlockUsingDataAssembly\nfrom vtkmodules.vtkFiltersFlowPaths import vtkStreamTracer\nfrom vtkmodules.vtkFiltersSources import vtkSphereSource\nfrom vtkmodules.vtkIOParallelXML import vtkXMLPartitionedDataSetCollectionWriter\n\n# Utility function to save simulation input/output datasets to filesystem\nfrom vtkmodules.vtkIOXML import vtkXMLImageDataWriter, vtkXMLPolyDataWriter\nfrom vtkmodules.vtkRenderingCore import (\n vtkActor,\n vtkCompositePolyDataMapper,\n vtkLightKit,\n vtkPolyDataMapper,\n vtkRenderer,\n vtkRenderWindow,\n vtkRenderWindowInteractor,\n)\n\n\ndef build_magnetic_coils(mesh, current=1000):\n magpy_coils = magpy.Collection()\n\n # Extract blocks under the \"coils\" node.\n coil_extractor = vtkExtractBlockUsingDataAssembly(\n assembly_name=\"Assembly\", selector=\"//coils\", input_data=mesh\n )\n coil_extractor.Update()\n\n # Build a magpy current source for every coil.\n coil_iter = vtkDataObjectTreeIterator(data_set=coil_extractor.output)\n coil_iter.visit_only_leaves = True\n coil_iter.InitTraversal()\n while not coil_iter.IsDoneWithTraversal():\n line = vtkIdList()\n coil = coil_iter.current_data_object\n coil.lines.InitTraversal()\n while coil.lines.GetNextCell(line):\n vertices = [coil.points[line.GetId(i)] for i in range(line.GetNumberOfIds())]\n magpy_coils.add(magpy.current.Polyline(vertices=vertices, current=current))\n coil_iter.GoToNextItem()\n\n return magpy_coils\n\n\ndef save_dataset(dataset, file_name) -> None:\n if file_name.endswith(\".vti\"):\n writer = vtkXMLImageDataWriter()\n elif file_name.endswith(\".vtp\"):\n writer = vtkXMLPolyDataWriter()\n elif file_name.endswith(\".vtpc\"):\n writer = vtkXMLPartitionedDataSetCollectionWriter()\n writer.input_data_object = dataset\n writer.file_name = file_name\n writer.Write()\n\n\n# Creates a render window interactor, connects it to a render window.\n# Switch the interactor style such that left mouse click and drag orbit the camera\n# around the camera's focal point.\ninteractor = vtkRenderWindowInteractor()\ninteractor.interactor_style.SetCurrentStyleToTrackballCamera()\n\nwindow = vtkRenderWindow(size=(1280, 720), interactor=interactor)\n\nrenderer = vtkRenderer(automatic_light_creation=False, background=(1.0, 1.0, 1.0))\nwindow.AddRenderer(renderer)\n\n# Uses light kit for better lit scenes than the default in VTK.\nlight_kit = vtkLightKit()\nlight_kit.AddLightsToRenderer(renderer)\n\nimport pathlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Load input mesh from a vtkPartitionedDataSetCollection file\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from vtkmodules.vtkIOXML import vtkXMLPartitionedDataSetCollectionReader\n\npath = _download_archive_file_or_folder(\"reactor.zip\", target_file=\"\")\n\nreader = vtkXMLPartitionedDataSetCollectionReader()\nreader.file_name = pathlib.Path(path + \"/reactor/\" + \"mesh.vtpc\")\nreader.Update()\nreactor = reader.output\n\nactor = vtkActor()\nactor.mapper = (reactor >> vtkCompositePolyDataMapper()).last\n# Make the toroid translucent so we can look at objects inside it.\nactor.property.opacity = 0.2\nrenderer.AddActor(actor)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Construct magpy coil objects for each coil in the reactor mesh.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "coils = build_magnetic_coils(reactor, current=1000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Compute B, H in a 32x32x32 grid\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "grid = vtkImageData(extent=(-16, 16, -16, 16, -16, 16), spacing=(0.1, 0.1, 0.1))\ngrid_points = vtk_to_numpy(grid.points.data)\nb = coils.getB(grid_points) * 1000\ngrid.point_data.set_array(\"B (mT)\", b)\nh = coils.getH(grid_points)\ngrid.point_data.set_array(\"H (A/m)\", h)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Show coils\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "magpy.show(coils, arrow=True)\nsave_dataset(grid, \"data/solution.vti\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Compute streamlines of B field induced by toroidal coils.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "trace_streamlines = vtkStreamTracer(\n integrator_type=vtkStreamTracer.RUNGE_KUTTA45,\n integration_direction=vtkStreamTracer.BOTH,\n initial_integration_step=0.2,\n maximum_propagation=3.2,\n)\ntrace_streamlines.SetInputArrayToProcess(0, 0, 0, 0, \"B (mT)\")\n\ncreate_sphere = vtkSphereSource(theta_resolution=16)\n\ngrid >> select_ports(0, trace_streamlines)\ncreate_sphere >> select_ports(1, trace_streamlines)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Visualize streamlines\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from vtkmodules.vtkFiltersCore import vtkTubeFilter\n\nactor = vtkActor()\nactor.mapper = (\n trace_streamlines >> vtkTubeFilter(number_of_sides=3, radius=0.00383538) >> vtkPolyDataMapper()\n).last\nrenderer.AddActor(actor)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Animate the disk position such that it oscillates between y=-1 and y=1.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from itertools import cycle\n\n\nclass vtkTimerCallback: # noqa: N801\n def __init__(self, sphere, window, nsteps=10) -> None:\n half_nsteps = int(nsteps / 2)\n self.radii = cycle(\n np.append(np.linspace(0, 0.8, half_nsteps), np.linspace(0.8, 0, half_nsteps))\n )\n self.sphere = sphere\n self.window = window\n\n def execute(self, obj, event) -> None:\n self.sphere.radius = next(self.radii)\n self.window.Render()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sign up to receive TimerEvent\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "cb = vtkTimerCallback(create_sphere, window, nsteps=250)\ninteractor.RemoveObservers(\"TimerEvent\")\ninteractor.AddObserver(\"TimerEvent\", cb.execute)\ncb.timerId = interactor.CreateRepeatingTimer(2)\n\nrenderer.ResetCamera()\nwindow.Render()\ninteractor.Start()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.11" } }, "nbformat": 4, "nbformat_minor": 0 }