diff --git a/diffphys-code-ns-v1.ipynb b/diffphys-code-ns-v1.ipynb deleted file mode 100644 index 442340a..0000000 --- a/diffphys-code-ns-v1.ipynb +++ /dev/null @@ -1,656 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "o4JZ84moBKMr" - }, - "source": [ - "# Differentiable Fluid Simulations\n", - "\n", - "\n", - "**(old phiflow1.x version)**\n", - "\n", - "\n", - "Next, we'll target a more complex example with the Navier-Stokes equations as model. We'll target a 2D case with velocity $\\mathbf{u}$, no explicit viscosity term, and a marker density $d$ that drives a simple Boussinesq buoyancy term $\\eta d$ adding a force along the y dimension:\n", - "\n", - "$\\begin{aligned}\n", - " \\frac{\\partial u_x}{\\partial{t}} + \\mathbf{u} \\cdot \\nabla u_x &= - \\frac{1}{\\rho} \\nabla p \n", - " \\\\\n", - " \\frac{\\partial u_y}{\\partial{t}} + \\mathbf{u} \\cdot \\nabla u_y &= - \\frac{1}{\\rho} \\nabla p + \\eta d\n", - " \\\\\n", - " \\text{s.t.} \\quad \\nabla \\cdot \\mathbf{u} &= 0,\n", - " \\\\\n", - " \\frac{\\partial d}{\\partial{t}} + \\mathbf{u} \\cdot \\nabla d &= 0 \n", - "\\end{aligned}$\n", - "\n", - "As optimization objective we'll consider a more difficult variant of the previous example: the state of the observed density $d$ should match a given target after $n=20$ steps of simulation. In contrast to before, the marker $d$ cannot be modified in any way, but only the initial state of the velocity $\\mathbf{u}$ at $t=0$. This gives us a split between observable quantities for the loss formulation, and quantities that we can interact with during the optimization (or later on via NNs).\n", - "\n", - "First, let's get the loading of python modules out of the way:" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "id": "da1uZcDXdVcF" - }, - "outputs": [], - "source": [ - "#!pip install --upgrade --quiet git+https://github.com/tum-pbs/PhiFlow@1.5\n", - "# # %tensorflow_version 1.x # colab\n", - "\n", - "#!pip install --upgrade --quiet phiflow\n", - "\n", - "from phi.flow import * # The Dash GUI is not supported on Google Colab, ignore the warning\n", - "import pylab" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "BVV1IKVqDfLl" - }, - "source": [ - "## Setting up the simulation\n", - "\n", - "To make things a bit more interesting - and to move a bit closer to a NN training process - let's set up of four fluid simulations that run in parallel, i.e. a mini batch similar to DL training. In phiflow we can directly pass a `batch_size=4` parameter to the `Fluid` object. Each fluid simulation is fully independent. In this case they differ by having circular Inflows at different locations.\n", - "\n", - "Like before, let's plot the marker density after a few steps of simulation (each call to `step()` now updates all four simulations). Note that the boundaries between the four simulations are not visible in the image, but it shows four completely separate density states. The different inflow positions in conjunction with the solid wall boundaries (zero Dirichlet for velocity, and Neumann for pressure), result in four different end states of the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "id": "WrA3IXDxv31P" - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "world = World()\n", - "fluid = world.add(Fluid(Domain([40, 32], boundaries=CLOSED), buoyancy_factor=0.05, batch_size=4), physics=IncompressibleFlow())\n", - "centers = [[5,10], [5,12], [5,14], [5,16]]\n", - "world.add(Inflow(Sphere(center=centers, radius=3), rate=0.2));\n", - "\n", - "for frame in range(20):\n", - " world.step(dt=1.5)\n", - "\n", - "pylab.imshow(np.concatenate(fluid.density.data[...,0], axis=1), origin='lower', cmap='magma')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we see four simulations, (0) to (3). This final density of simulation (0), with the curved plume on the far left, will be our **reference state**, while the initial velocity of the other three will be modified in the optimization procedure below." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rdSTbMoaS0Uz" - }, - "source": [ - "## Differentiation\n", - "\n", - "The simulation we just computed was using purely (non-differentiable) operations from numpy.\n", - "To enable differentiability, we need to build a TensorFlow graph that computes this result.\n", - "\n", - "(Note, the first line of the next block, `%tensorflow_version 1.x` is only necessary when running in environments that by default have newer tensorflow versions installed, e.g., `colab`. Uncomment if you're running this notebook there.)" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "mphMP0sYIOz-" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Could not load resample cuda libraries: CUDA binaries not found at /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/cuda/build/resample.so. Run \"python setup.py cuda\" to compile them\n", - "WARNING:tensorflow:From /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/util.py:119: The name tf.AUTO_REUSE is deprecated. Please use tf.compat.v1.AUTO_REUSE instead.\n", - "\n", - "WARNING:tensorflow:From /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/profiling.py:12: The name tf.RunOptions is deprecated. Please use tf.compat.v1.RunOptions instead.\n", - "\n", - "WARNING:tensorflow:From /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/profiling.py:13: The name tf.RunMetadata is deprecated. Please use tf.compat.v1.RunMetadata instead.\n", - "\n", - "WARNING:tensorflow:From /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/session.py:17: The name tf.Session is deprecated. Please use tf.compat.v1.Session instead.\n", - "\n", - "WARNING:tensorflow:From /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/session.py:18: The name tf.get_default_graph is deprecated. Please use tf.compat.v1.get_default_graph instead.\n", - "\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/flow.py:15: UserWarning: TensorFlow-CUDA solver is not available. To compile it, download phiflow sources and run\n", - "$ python setup.py tf_cuda\n", - "before reinstalling phiflow.\n", - " warnings.warn(\"TensorFlow-CUDA solver is not available. To compile it, download phiflow sources and run\\n$ python setup.py tf_cuda\\nbefore reinstalling phiflow.\")\n" - ] - } - ], - "source": [ - "# %tensorflow_version 1.x\n", - "from phi.tf.flow import * # Causes deprecation warnings with TF 1.15\n", - "import pylab\n", - "session = Session(None) # Used to run the TensorFlow graph" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3mpyowRYUSS4" - }, - "source": [ - "Now that we have imported `phi.tf.fluid`, let's set up the simulation just like before. But now, we want to start from a velocity that we can modify, i.e. a variable. Then we can optimize these initial velocities so that all simulations arrive at a final state that is similar to the first simulation from the previous example. I.e., the state shown in the left-most image above.\n", - "\n", - "This is a fairly tough task: we're producing diffent dynamics by changing the boundary conditions (the marker inflow position), and an optimizer should now find a single initial velocity state, that gives the same state as simulation `0` above at $t=30$. Thus, after 20 steps with $\\Delta t=1.5$ the simulation should reproduce a different set of boundary conditions from the velocity state. It would be much easier to simply change the position of the marker inflow to arrive at this goal, but -- to make things a bit more difficult -- the inflow is _not_ a degree of freedom. The optimizer can only change the velocity $\\mathbf{u}$ at time $t=0$.\n", - "\n", - "To achieve this, we create a TensorFlow variable for the velocity at t=0.\n", - "It is initialized with zeros (like with the NumPy simulation above) and can later be used as a target for optimization." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "id": "NlJMJikaHOL6" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING:tensorflow:From /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/session.py:28: The name tf.global_variables_initializer is deprecated. Please use tf.compat.v1.global_variables_initializer instead.\n", - "\n", - "WARNING:tensorflow:From /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/session.py:29: The name tf.train.Saver is deprecated. Please use tf.compat.v1.train.Saver instead.\n", - "\n" - ] - } - ], - "source": [ - "world = World()\n", - "fluid = world.add(Fluid(Domain([40, 32], boundaries=CLOSED), buoyancy_factor=0.05, batch_size=4), physics=IncompressibleFlow())\n", - "world.add(Inflow(Sphere(center=centers, radius=3), rate=0.2));\n", - "fluid.velocity = variable(fluid.velocity) # create TensorFlow variable\n", - "initial_state = fluid.state # Remember the state at t=0 for later visualization\n", - "session.initialize_variables()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vSdGIEzCgq5-" - }, - "source": [ - "The simulation now contains variables in the initial state.\n", - "Since all later states depend on the value of the variable, the `step` method cannot directly compute concrete state values.\n", - "Instead, `world.step` will extend the TensorFlow graph by the operations needed to perform the step.\n", - "\n", - "To execute the graph with actual data, we can use `session.run`, just like with regular TensorFlow 1.x. While `run` would usually be used to infer predictions from a learning model, it now executes the graph of simulation steps." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "id": "wSrIezfWHjcQ" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING:tensorflow:From /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/tf_backend.py:167: The name tf.sparse_tensor_dense_matmul is deprecated. Please use tf.sparse.sparse_dense_matmul instead.\n", - "\n", - "WARNING:tensorflow:From /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v1.5/phi/tf/tf_backend.py:61: The name tf.div_no_nan is deprecated. Please use tf.math.divide_no_nan instead.\n", - "\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "world.step()\n", - "pylab.imshow(np.concatenate(session.run(fluid.density).data[...,0], axis=1), origin='lower', cmap='magma')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "iJc6UdYHhtOH" - }, - "source": [ - "Let's build a graph for the full simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "id": "b9xHtdDQRrjL" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Building graph for time step 0\n", - "Building graph for time step 1\n", - "Building graph for time step 2\n", - "Building graph for time step 3\n", - "Building graph for time step 4\n", - "Building graph for time step 5\n", - "Building graph for time step 6\n", - "Building graph for time step 7\n", - "Building graph for time step 8\n", - "Building graph for time step 9\n", - "Building graph for time step 10\n", - "Building graph for time step 11\n", - "Building graph for time step 12\n", - "Building graph for time step 13\n", - "Building graph for time step 14\n", - "Building graph for time step 15\n", - "Building graph for time step 16\n", - "Building graph for time step 17\n", - "Building graph for time step 18\n", - "Building graph for time step 19\n" - ] - } - ], - "source": [ - "for step in range(20):\n", - " print('Building graph for time step %d' % step)\n", - " world.step(dt=1.5)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "2VQ92g2rs6wM" - }, - "source": [ - "When calling `session.run` now, the full simulation is evaluated using TensorFlow operations.\n", - "This will take advantage of your GPU, if available.\n", - "If you compile ΦFlow with [CUDA support](https://github.com/tum-pbs/PhiFlow/blob/master/documentation/Installation_Instructions.md), the TensorFlow graph will also use optimized operators for efficient simulation and training runs.\n", - "\n", - "The `session.run()` call of the following code block will now retrieve the final fluid density, and for that it actually needs to process all 20 simulation steps of the TF graph that we just constructed." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "id": "TA6Ibs-mXsTc" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Computing frames...\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "print('Computing frames...')\n", - "original_state = np.concatenate(session.run(fluid.density).data[...,0], axis=1)\n", - "pylab.imshow(original_state, origin='lower', cmap='magma')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "IClfRMfoyGUa" - }, - "source": [ - "Next, we define the *loss* function. This is the value we want to decrease via optimization.\n", - "For this example, we want the marker densities of all final simulation states to match the left-most one, called `target`, in terms of an $L^2$ norm.\n", - "\n", - "For the optimizer, we choose again gradient descent for this example." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "id": "7KPpyIwjYETi" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "WARNING:tensorflow:From /usr/local/lib/python3.7/site-packages/tensorflow_core/python/ops/math_grad.py:1375: where (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n", - "Instructions for updating:\n", - "Use tf.where in 2.0, which has the same broadcast rule as np.where\n", - "Initial loss: 65.417114\n" - ] - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "target = session.run(fluid.density).data[0,...]\n", - "loss = math.l2_loss(fluid.density.data[1:,...] - target)\n", - "optim = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(loss)\n", - "session.initialize_variables()\n", - "\n", - "print('Initial loss: %f' % session.run(loss))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "AALD66-N0U5F" - }, - "source": [ - "With the loss and optimizer set up, all that's left is to run the actual optimization." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "id": "pvvF6xqmaRLX" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Optimization step 0, loss 65.417122 . The first step sets up the adjoint graph.\n", - "Optimization step 1, loss 61.038647 \n", - "Optimization step 2, loss 55.527832 \n", - "Optimization step 10, loss 43.914444 \n", - "Optimization step 20, loss 40.739956 \n", - "Optimization step 30, loss 39.523136 \n", - "Optimization step 40, loss 39.361752 \n", - "Optimization step 50, loss 39.348873 \n", - "Optimization step 60, loss 37.156372 \n", - "Optimization step 70, loss 37.729256 \n", - "Optimization step 80, loss 35.622345 \n", - "Optimization step 90, loss 35.464912 \n" - ] - } - ], - "source": [ - "for optim_step in range(100):\n", - " _, loss_value = session.run([optim, loss])\n", - " if optim_step<3 or optim_step%10==0: \n", - " print('Optimization step %d, loss %f %s' % (optim_step, loss_value, '' if optim_step else '. The first step sets up the adjoint graph.'))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The loss should have gone down significantly, from above 60 to below 40, and now we can visualize how well the reconstruction for the last frame turned out for simulations 1 to 3." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": { - "id": "SQBtCmhZaYYj" - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "optimized_state = np.concatenate(session.run(fluid.density).data[...,0], axis=1)\n", - "pylab.title(\"Original state at t=20 (top), and optimized version (bottom):\")\n", - "pylab.imshow( np.concatenate([optimized_state, original_state],axis=0), origin='lower', cmap='magma')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HP7aDQfpKifp" - }, - "source": [ - "Naturally, the image on the left is the same (this is simulation (0), our reference), and the other three simulations now exhibit a noticeable curved shape towards the left. This is especially visible for the far right simulation (3), which was moving straight up in its original state, and now has an off-center plume shape that tries to match the reference on the left. \n", - "\n", - "Note that this simulation (like both others) needs to \"work\" with a fixed inflow, hence it cannot simply \"produce\" marker density out of the blue to match the target. Also it needs to take into account how the non-linear model equations change the state of the system over the course of 20 time steps. So the optimization goal is quite tough, and it is not possible to exactly satisfy the constraints to match simulation (0) in this scenario. This is exactly what makes it an interesting test case, though.\n", - "\n", - "Now we can also have a look at the now-optimized initial velocity field. Here we see the y-components (from `data[...,0]`)." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": { - "id": "i7ZahlUudex8" - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXAAAACYCAYAAAD9ebJkAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8li6FKAAAgAElEQVR4nO2deZAkV3ngf19VV/dcmhlpEDpmdLESh2BBEDJIizEOBBhx27AcBiwWYS0RYNCasC2Md/HuWhHgZTHY4pA4zAhkhCwJJGvBBgsImUswIFlGlxEIgUajczRXz0wfVd/+kS8zv+x6WVlVXX1kz/eL6KisPF6+9zL71Xe974mq4jiO49SPxlJXwHEcxxkOH8Adx3Fqig/gjuM4NcUHcMdxnJriA7jjOE5N8QHccRynpvgAfoggIl8VkXN6HP+EiPz3Psv6loi8dXS1GxwR+XMR+fw8y3iDiHxthHWaEJHbROSYUZU5p/x9IvK4Ia+9VUR+c8T1+ayI/EXYfqqIfHeU5TvV+ABeY0TkFyLy/H7OVdWzVXVruO7NIvLtOcffpqr/eyHquVxR1ctU9YXpdxFRETl5HkWeB9ygqjvmW7fYj6SqrlPVnw9Tnqo+WVW/Nd969Sj/FmCXiLxsoe7hdOMDuOOMjrcBn1vqSiwhlwH/dakrcSjhA/gKIZWqReSDIvKoiNwtImeb498SkbeKyJOATwBnBpV8Vzhu1eHDReQ6EXkolHWdiGzpow5Hi8h+Edlk9j0jlNOKnP9xEfngnH3XiMgfhu1jReSqcP3dIvLOHvd+eTAT7AptfZI5dpyIXB3KeURELrJ9FrZvCKf/a+iX14rIT6xEKSItEXlYRJ4euf/xwOOAG82+DSJyabjvPSLyZyLSMPf+johcJCK7ReQOETkrHLsQeA5wUahLWt9MQwjP62PBNLYvlHW0iHw4PLM7bD2tthb6aF/4mwzlnhiOvVREbg7nfFdEnmrKeLqI/FhE9orIF4FVc7rhW8BZIjJR9pyc0eID+MriWcCdwGOAvwQ+LSJiT1DV20kkxe8FlXxjpJwG8LfACcDxwAHgoqqbq+r9JP/ErzG73wRcrqozkUu+ALw2raOIHA68ELg8DHT/APwrsBk4CzhfRH5rbiEi8vhQ1vnAkcBXgH8QkXERaQLXAfcAJ4ayLo/U/TfC5tNCv3wRuBR4ozntxcAOVb0p0pb/CPxcVWfNvr8BNpAM7M8Ffg/4L+b4s4CfkTyv9wFXi8gRqvpe4F+Ad4S6vCNyP0j6+c/C9VPA94Afh+9XAh+KXaSqG0O564CPhHttDwP+Z0ik6E3AxcC1ktj2x4Evk2gYRwB/D7xqTrnbgRngCSX1dUaMD+Ari3tU9ZOq2ga2AscARw1aiKo+oqpXqep+Vd0LXEgyAPXDVsKgFwbP11NuVvgXQEmkTYBXk/yw3Af8GnCkqv4vVZ0Ott9PAq+LlPNa4P+p6tfDD8UHgdXAfwKeCRwL/JGqTqrqQVX9dqSMGJ8HXiwi68P3N/Voy0Zgb/oltP11wHtUda+q/gL4v6GMlAeBD6vqTPjBuBN4SZ91A/iSqv5IVQ8CXwIOquql4fl/EejSFCwi8lrgd4FXhX47D7hYVW9U1XbwmUwBZ4S/lqnvlcAPI8XuDX3hLAI+gK8s7k83VHV/2Fw3aCEiskZELg5q/x7gBmBjGJSquAY4VUROAl4A7FbVH4RybzWq+3M0yaR2OckgD8lgclnYPgE4Nqjyu4Kp50+J/yAdSyJhA6CqHeBXJNL2cSQ/bLOR63oSfki+A7xKRDYCZ5v6zeVR4DDz/TEkA949Zt89oU4p27WYTe6e0JZ+ecBsH4h8L332Qdq+CPhtVX0o7D4BePecPj8u1OnYkvrO5TBg1wBtcObB2FJXwFkSqlJQvptEDX6Wqt4vIqcBNwHS+zJQ1YMicgWJFP5EjMSqqk+OXPIF4Gsi8n4Sk8Jvh/2/Au5W1VOq7gncR2LCACCYZI4DtpNIkMeLyNgwgziJRvFWkv+V7wUzQYxbgJPMfR4mMSecANwWzjk+1Clls4iIGRSPB64N2wuWJlREHktiDnn7HHPQr4ALVfXCyDXPLanvz8w5m4FxEk3CWQRcAj80eQDYEuyaMQ4jkeB2icgRJPbZQbgUeDPwciqiMsIA8jDwKeCfVDWV3n4A7BWRPxGR1SLSFJGniMivRYq5AniJiJwlibP03SQD93dDOTuA94vIWhFZJSLPLqnOAyT2asuXgWcA7wrtKmvHvcBdJCYbghnjCuBCETlMRE4A/pDELJPyWOCdwTn6n4Enkdjvy+oyb0RkjMQ+/nlVvWLO4U8CbxORZ0nCWhF5iYgcRmJfnzX1/Z20rYbnAt9Q1alR19uJ4wP4ock3gFuB+0Xk4cjxD5PYkB8Gvg/84yCFq+p3gA7wY1WNqdlz+Tvg+eEzLaMNvBQ4DbibfJDfELnfnSQS/9+E814GvCzYztvh+8nAL4F7SWzmMf4c2BrMB68JZR8ArgJOAq6uaMfFFG3cfwBMAj8Hvh3a9xlz/EbglFDnC4FXq+oj4dhHgFeHiJK/rrjvIGwh8Tmcb8xZ+0TkeFXdBvw+iWnlUZIfpDcDqOo08Dvh+06SPpzbH28giXByFgnxBR2chUBEvgH8nap+aqnrMl9E5H8Aj1fVN1acN0FiajqrajKPiLwZeKuq/vrIKrqEhHDDi1X1zKWuy6GE28CdkRPMHM8AXrHUdZkvwYR0LkXJOkowHZy64JVahoSZmD54LzJuQnFGiohsBf4ZOD+EINYWEfl9EsfeV1X1hqrzHWexcROK4zhOTXEJ3HEcp6b4AO44jlNTFtWJ2TxsrY5tOnwxb+k486Ny6lIP3DpZZD592Q8ruL+n79n+sKoeOXf/og7gY5sO5+j3/cFi3tJx5ocP4KNjKQbwUT6Dha5/D375lgui8ynchOI4jlNTPA68ziyERHCoSo1VfSnDdExJoTrnczFZLClynv0pfdazGERnLuqkJ0T2VSEl27EHNkx/jvAZuATuOI5TU1wCd4qk0oEVNkYofC470rY1+m9k39Kh7TituKjqeBX9agjzlfqHkjj7lLbteX3ex16ibXNR2p9W6o7ti0nbtrqF90IiN62o73weax/P1CVwx3GcmuIDuOM4Tk1xE0qdGcqxVsF8VfnFZkRd0K9ZJDm5v5uK0Z8rzSn9tqOsnrEyF+T96PO8iv4s9HdaT7HH8xtJDzFTO3Y7Yu4w/SKzFSaUcB+1ZpOOOaERsZcM08cjfC4ugTuO49QUH8Adx3FqiptQDlHKTAbR5JRVZpWFUNVjLEncdH+xvxI5r9CX7ZjZxEZNDF61qjotmlllRJSZTdL9hT4O22pOtCaUrO+NuSQ1oUg736fmPjrWbcopbFdFnMSumVPffhjEnOcSuOM4Tk1xCfwQIxpzWzghfHYqpMPF8nUulsBYJSGVtFeiDriwoXFnmHZtMIDzuCRWut9ntCTOzkj8dBUFaTwRoxvGuZhu2/UM1LStPR2uNZqPzCTbjdn8Nh07AqanWqm83xewNA68+/q+JWyPA3ccx1m5+ADuOI5TU9yEUmMGil3uWVB3mQXlrUqTkzmfo2CxTCdDxL1LZMq3GPU+6w5TdNvGFkcdmpHt6DTvErvJvNLezjOuOVpmbGc8Lj6Pl+9972az07XdtKYWs33gYDPZZ/q6GcwqjSnT3jX5Ne1mRTX6nfJfqFNJWZFzh8ElcMdxnJriEvihRsWsN43MYOvb8TWIFLgQyffnM4t0SCk0lbAKE/qCdGj7tRNzYhbub8oM5xZmBFY1LSa19xv2thCUaQoZEWncOs4L2kd3A8aCY3N8LI8JbJntqYOtZGN3PsQ19yflpJI4QGfC3EbLa1tKRCqPSt0DOXD7P9UlcMdxnJriA7jjOE5NqTShiMgq4AZgIpx/paq+T0ROAi4HNgE/At6kqtPlJTnLibJZb9lsNjuDzTiCMrU+kgSoVE3sZdoYRF9dxom2JOJMa7Vylb4928y2YwvDiG1b6O+CESJi+oqafSL5nKL7yhiVQzO6Uk4kjzZkNofSxXUayZdOp1veHG/mfTwxlgd4T61LhrZ9D+Y2kta+5LMxk18/c1hJ/WPEnPXZLNHufZXlVN1uRHHgU8DzVPVpwGnAi0TkDOADwF+p6snAo8C5/VXLcRzHGQWVA7gmhN8uWuFPgecBV4b9W4FXLkgNHcdxnCh9RaGISJPETHIy8FHgZ8AuVU11lnuBzQtSQ6ecTG2en2khZk7RssVgU0uAjXuOLEcWT4pVsh1jKc0lFaYJtTqwplO6u+vbMup9ezxX72cPRP7tbMBJu7sauZmqos7LJVlV1JRTYp7JEk/FI3U02CfaxtY32+qWPdeO5RbcY9bvAeDfJ9Zn+1r7klJtFMqBo8w90wRZ9n2Obdt3v9/UFCX0YybpRV9OTFVtq+ppwBbgmcAT+72BiJwnIttEZFt73+SQ1XQcx3HmMlAcuKruEpFvAmcCG0VkLEjhW4DtJddcAlwCMHHilmUiHjiW4szCNOVmSTKrIH1Iszu1Z4FYqtRhUtWW0e+b1G/89LD0iB0eMzMH167Kxb4De1YlGyVpSxsh7akNi041H5u8qTYLR8eIPvd48q/ObLecOT2dDF0zE/mx8Wau5Rw+sR+Auzbm/T42lTg0Vz2aa0Z7tJUX2pjzCVEJvGo1Icu8JOw+nm+lBC4iR4rIxrC9GngBcDvwTeDV4bRzgGuGrafjOI4zOP1I4McAW4MdvAFcoarXichtwOUi8hfATcCnF7CejuM4zhwqB3BVvQV4emT/z0ns4c5SkalY1iHU37R3LXEopXmW29ZxafW0VjgQcVwWc4jHTCgVOuFCGNgWuMzMoWn6q9Ppbue6ialse2eID+/M5v9+ah3JQcMvxOf3MkPB6Mwpo3KCDlOfshWKQn92jFkvDeU+OJGbQDrm+iNaib/t6E2782vkKAAmHjqYV7OdX5/PcbBmk3wzM51E3v1KU8kQ/dFPsjqfiek4jlNTfAB3HMepKZ6N8FCganq0OZ5m0mtbM0HLmFjGkuMaUXcLsd/FQN7ufXWh3/hqG0ARpny3jSllbSuPhphYnRgADkxGIiDIp3qr1aHTFAcLIXItx9jx2H6zAnFqpppsjWf79s/m2+vHEjPJkw5/INt306rEhDL20J5sn8yaufTpHAcTYSXRKJR5ZuKswBc1dhzHOQRwCbzGpJJAbBZgKdm5Jge4ub4Z8iwXJIpW7qFL4791xsxgSyVN67yrWhR5mVEIr46GKPeeRViYnRmckNPGSdkw12xYewCAAztXmzLzzcZ0cNqNdTunC7fuV3Iuez0WUvIeZYx6RIOTqUT2nNmXS917Nq7KtlvBE/zUdfdm+36w7mlJMbtyCbzRzieQZ07MZrfUDUbyrpxd2ft43/iixo7jOCsXH8Adx3FqiptQakyeeMruNaaRSDxyftBMVTbnZQvHWqfaeD71ODPAtM0JqYWlLI53ORPz6cZ8VAPk4U7NKel0b4BZk8t6w0TiYHtgVd6vOp0fT5MttXOLQHy5r+jSXVXHFzjOe5jiqxKfxfaFd7axL+/j3QfyDpvRxCP5hIn7sn3TwV/ZMTmZbG7wzEkZM5tA1uaRmUhGgEvgjuM4NcUl8DoTWQ3EOtPS/VFJ3EqMZoZb6sRU48hpGQm8k0resxFpexlJJgNT5gjOHMUlhzOxrNuxaVfh2T+TO9seszpJr79qjUlwNZtLj40waVNiS/eU0cvBtkAL6va8fpC6D1q2IXX4Akzuzp3C+ztJf5/Y2pXtm1kf3u3ZXOyWPP9VPGWvVbbm0zcL5DB2CdxxHKem+ADuOI5TU9yEshKwap51YsYWYU132tVPTL7lLF55LNeBx81qMgf2h0VirQklu3yF5Kq2VMTYaxYI3m266hj1fHI6N6EcvTbp2zQeHGDqYD4rszkdTGMF01cw5Qyy8ssCzxjsSWxFnrLj0esrKtro7nd25324c3otAEc183e7vSExBUozN21Jbh0scWKa44s8Y7WfR+USuOM4Tk3xAdxxHKemuAllpWHNKbHFhjuRE2eMmplO2TbT58fHclvAZIiWEHONjkem39eNYbXjLNmV2ZWVle/cP9Wdt3rT6v3Zvl0Ta7LtLDbZRHJk07zLlvuKLa67XJ7HfM0lMdIIK9MfY/vzch44mAR9r5HcdDWxIYm/l4mJvBgbXhTLcb9cEn2V4BK44zhOTamUwEXkOOBS4CiS39JLVPUjInIE8EXgROAXwGtU9dGFq6rTRZXkkgqHMUm8sAJM/juezh5sGAl8dSuXwHcGh6dZP5Z2LuQcevS5YPPUwe60p0dM5DMC75tYn21nK/LYWOrKWZf9JVpaNIZdrDpa1pxPi3m3GybB2vY967tO3bQ+6e/G+jyFrE3ZG/s/ic+6HaBt85Dg+7myHwl8Fni3qp4KnAG8XUROBS4ArlfVU4Drw3fHcRxnkagcwFV1h6r+OGzvJVmRfjPwCmBrOG0r8MqFqqTjOI7TzUBOTBE5kWSB4xuBo1R1Rzh0P4mJxVlENDZdOTb1N6Jqa8coaGZzajJR78fX5NON143nC/Km8d/SnqeKXHX5YvuORhm3nJ5r4rjbB/PY433TiRPtxLU7s30bVucL7U6m1hDrxExTGxQclzEnZkU9q1guPrs+62GdmPYRPLpzHQC7O3m/bl6XLHC8b+MR+Yl20eKYCaVwsyE6t99rhjS19O3EFJF1wFXA+aq6xx5TVaWky0XkPBHZJiLb2iYLmOM4jjM/+hrARaRFMnhfpqpXh90PiMgx4fgxwIOxa1X1ElU9XVVPb65bO4o6O47jOPQXhSLAp4HbVfVD5tC1wDnA+8PnNQtSQ6eULCe3jSgxamCWJDASG26jTOzSXTKZvBKyNs+Ut3Ein/JNajoZJtvcIBroMGaMUbEQccsm3/eeg4kJZXUz7+NNq3LtdF96qq1Huq+wyO7g1VjWDPJ8YyYjaw7ZmZgC72vnpqtjVycmlNs2bsn2dfLD1e/nMFEo/RIrsw+zSj828GcDbwL+TURuDvv+lGTgvkJEzgXuAV7TZ1Udx3GcEVA5gKvqtyn/bTprtNVxBiHL410iiWXSeCHZ1ZxjgJqVYRqPJDMGOyY2fEMrl8BT52VljDIDHO+X7txOo6N00eI+K19RHzExyvsmk9msdqHjTSYm/O7wX9mwiZaa3c9yKMfXQkiPwzCi51fwHRtpOp2Vecd0Hlvx2NZeAG7eaCYu2JWnGpFFwqMJwXq/H2W543tRuZB2CStNCXMcxzlk8AHccRynpngyqzoTWSotuuit1elSq4tRA+2ixZ2xYEKZzvXRTa1cvU9NKA27FFW/S6qNUuXP2jZ4kfH7xAofYfmGmcmkjw+08wRXR47vy7Y742msvbkoS2ZVkqu6B5Uq/UI4h6vi6quouCbNwy6FOHAzLT6Y+G6ePCHbt3kiyfQxvT5/t+2ixmOtpMNnZ6xnM3bvyD6tOKGMNClX1ULaJbgE7jiOU1NcAq8zsdSw0n28IAxF0o42m7lHsr06iH1GAl/XNDMxw6k2cdBQ9BsmWOVQnK9js0qSr0hSFU0ilW5bR7H5T5MwK3PXTL4I74mrHsm20/WNTZRhvlpMmRMz1o4qJ9hSznaNMYgzPNLeTstoJ6Htd+zNnZiPX30/ADNr8oIa01ajSaX6ihjZTi73ZhOa7WsaW0TcEnlnJaKdjiqZleM4jrMM8QHccRynprgJpc5oxAPS6T5csEKE4x2jizeNstZcm3gn2wdyE8oq4+nJnJjG+ZMuvqul6n0PM0OZs2vR1fuSm1eaISKdnGJnTVq/WOjDBw/keanXbcyTLrWDCaU1adX77jLjand3O8Tsizo0y/p6PlayqvzlleaU/lYWKhST+4SzCcM7JrvzgndMGLh1FM+GXPeFmcumHml8eMHAkk7OtIuEWxNK5P2ImcHsc8vu2cf/gEvgjuM4NcUHcMdxnJriJpQ6E8k7HVVTrUUgu8bsMz/jrfHZ4nlAw8ybT7U7G5iSlVUWPtsrV/UgecH7jUiZr/klltu7rMx+RaCmUcVD8rCdB/KFjFdJHnLSXh0SjhkzVRaFUpGzWug2PWhZ2oPMBFf2EGLByT1vPzoKSaoiJrjI3INCAE7o78mp3F4y2UmSiHVaxnzYzq/qhMRX6f8AFE0o7UgO/NSsUjCbxLZtPQs5yLtrrwN0skvgjuM4NcUl8BqTOQ+tVGalrfDzXJiImV5jTlMT19ocT8S+1WtyibBVmBIYzjto7xkRfaPSdrdEZx06RQdbpICYNL7QyZlS4a9Ey9GolFqhAoTnNXkwvhp0eyJI20biy2KUhwjtLkjtpr+0a6PryvBhry8eWjAKDuDutmfvSpmGFvpuair3bD48kziNZ/Pwexom1r6TOuhNHLhNbJXfMyL22/ej3VsCJ9adBVG6fxXSJXDHcZya4gO44zhOTXETSp2JqPfFcOZuM0OWBMhc0+l0q4nr1+RxyWsbuccyNdeMHehOHFSliRd2Rab0S0TNLJ2WPOo48ZLysn4qcwSmjttBFsQNYtPMdPzfrzPR/Ywya4aNSy4rP70k6jw25oF0OZrKvoykDphv/1eZ2Gye7rFO9zXt1Hlor4/08VTex/dNbQBgalN+n3Gzum+aX98WYx2XnWBqLNwzZsKLJbYqm2eQ+pFtHPgAYrVL4I7jODWlcgAXkc+IyIMi8hOz7wgR+bqI/DR8Hr6w1XQcx3Hm0o8J5bPARcClZt8FwPWq+n4RuSB8/5PRV8/pScwTb3+SY1Pt07hVe5oJTJ1tJWr1RDOPPDl6bHe23QlO/dakiQ1P1cyCCtw9lb4yv3FsKn5ZZIrOOQ/MtGW6zxuEiApcMFM1I+1sdF9TjAfuvk3bLFvXMQWkceJ22bq07xo2c+RsHnjfK+e3NbuIidXXdvc07qiqvwSx32La2QwmFBsR0q7K+Jdlb8zb/ou9mwAYf3xuN2ncmU+1Txf07hyWlz07kw+RaZRKNPKl5OXO3ptI5Elywpxy6P0s51IpgavqDcDOObtfAWwN21uBV/Z/S8dxHGcUDOvEPEpVd4Tt+4Gjep3sLDCROG8A1YgUG3O6mGtSp8+siQ0/YezR/PKwAPLEbitSBLHcigOxlWMq4qMllsDI3ibmsKpMimS25yONl12bxijb3N8xSS3S73bVo4YV0SeS7bGpvEPT2GQpk9Qi9Ut3NUzdrASfOq/V3CcqHhZuFNG2RkVBAs/LT2dGzhqNoz0TkT3t9aHNq8x8hocm1wLwgadele374MVvzLb3/IfkPZ7dZO5zMDK9OHIfqzFo26o5aUE2pt8cTrdLYvWrmLcTU5NRojwAQeQ8EdkmItva+ybLTnMcx3EGZNgB/AEROQYgfD5YdqKqXqKqp6vq6c11a4e8neM4jjOXYU0o1wLnAO8Pn9eMrEbO4FiVq2NVsdjxyD5bVFAZ9x6cyPad3Mq3m4clU+1bu8z0+kbyGolZHLnvJdNKyNTMZvyizKE1qun1BZ9rmVMvPcFspvWzJoXU/FTW3nS/MV2Mm3QFzVWJyaA1md9ofKI7ydhMVX+G4x3ThvExa0JJ7tmRsa5rgHhysH67NmZiqXouJcv8jY8l9Zwxiw1ryN1d5vhMTRsTrTwxVVrOC1fnloCP3boj226ceUJStikHa6qJJNVKn0dj3JhQTB+nz9g6pNVmpkibVIj5H+FUehH5AvA94Akicq+InEsycL9ARH4KPD98dxzHcRaRSglcVV9fcuisEdfFGZSI8BddDSQSRljAhquF0LbJ/bnU3TKxZ6tWhRynDSMNhc1mq9tBZuupsTBAgw0TE0mdduZ4TEqR3mUWGEIryPrWdmHEKagmJDAWMhgtezq/Zk/HZFiKPKPxsdmufVONPBlW5q+OpjXNK2QTNY2Hy2esw9mElGZO8KgjeDiJsSemyKaRYltBcrZhk5lkPN4tdUOuGTXNvsMmbA7khNl7t2fb7fHjAdiwJj9vak/+f5A9F5usajY4go2m2LBaTurkLNPqOj36uA9tx2diOo7j1BQfwB3HcWqKJ7NaCXRPekx2x2KY+3V8GefNzVO5SpnGIU9uyVX+bOZgQZWuSlzdnz3DntUwamqqeaoOYEIZFVbsSa1UFQvZFuN8w6dpzy37jsu20xl/k4/N/z2nwozAVeP5Mj2VceBpnHcnvlRSK8y2lTFzsbUyxJzgCxH/nWL6aGws9/Q1wj07JiZbgumCVcZsYv2N0v1OzoT47N2dPFFb8zGb8otCkRPGXCUtG98dbmBjumdCHzfym8taY0OLhatHkmEV8spn/V3d1y6BO47j1BQfwB3HcWqKm1BWAlXxxhWxzMVcRslFdvr153aemW2n05kfPcVGoSQqp03OFMvjXcxlLV3nFfIghzqLSaqFmd6fRZ8Upt/H5t9XxR7P+YRiFEksIiBmRohdYy0XkXzhaiJCbnpkc9f1+07Ibzp9IAkZieVMB9OPseOF3O95H04Ec0xhGri5PLpkX78MkcKgkM7b9HEWxz4bmfJvUxhE5gRYE9vB2WS4+6PtL8r27X3Oyfn14dSp2XxYtFP6NbXb2WiX9J236QjytapNtBRxov+jI4wDdxzHcZYnLoGvAAoCoRVIYxJULLFUQfrsFhW+88Dj8sPh+NTRNng83Ho2MmvN1mmYOOyCF9PG/Ia2tSsk7IWgQsrNzytzYgbtwjjIHt69ruvyqSON9hH6dupgq+s8W2bpc02raTWe1NFnQ9ht6tleq80U+qDCydm/Ty7DSuCzQcq1izxnObVi75lhysSON0NDb/hpLnW3TjOJq9Yl/X3QpJCVmPYx1r2vMZPXrW3/D2IaXp+BBP0EgrsE7jiOU1N8AHccx6kpbkJZaRTionvoaiVOzMz5Y1TtR3bl6n2qduvq7sRV2lt7L6lv7+oWVpOJzBKvnEk/TFKlAYgm1erXVGBjlKe61faO6eO0H6J5sG2R1lEs3fYO69TTPs0ulcms+o0NLyT86jaHlJHmpteCSSp82HfCOmtDzLeZwpAlyLLv9tRjTcz3mmS7kDQrlsfbmPLSKfRizSYRE8pChdK7BO44jlNTfAB3HMepKW5CWWEU1LNOD/d/mVc8VflMTPes2UEaLuUAAAfBSURBVG6tTrMRdqvdpYsWz8NiYcssLrk2j2iH6LUVKv1AcbyRfbE6WXOGnX09HtTyZndaw0LcfCw2PVbPkjju6MLAMdNambmtF1VRF5Hj1oTWjvVnTNwsM7FFTE151I0xM5lIoLGQc72wkLExp6TPoxAbnqYlt2200TKxvh9h4JRL4I7jODXFJfA6UzXJMCLZVEqX6bntuNclm21pJaRUUiyJyY3F0sYdlnY7liCrT4ab1JZfUpDqQrx5qQQemXGaJijSeH/EE2CZTe0+nk08LalHtrhuoQ9DOSUSeJrcSUuetY5KvOvXMWraa2f1Zqvz2NWZsoWj4zN5OyEuuzObH+80I8/KxnSHsgrSu1WCxtL76NxLerwf4bNkxmj02cTmapTgErjjOE5N8QHccRynpszLhCIiLwI+QpK251Oq6mtjLiapNaOQBCoW92yvSZ1dJepZLK7Z6MCdA6kemZ9gl5CKXJKpthpbasyoxcVFabtPtnG+sWRZUfo1p0STYlFYKitG1t3SrVYXYp2rUgvYEOmpZtc1ad9oia6eOdZiJh2bR93GQIfY80JfFkS6iGO0X/W+Kv5euk0Xtg9mpk1cfHAuykQeF6/7k+PWRNIwDsnsXTL3TGPDyxZCnp2OmZTMZmSORFafsvckix2PH872D5MwrEexlYhIE/gocDZwKvB6ETl12PIcx3GcwZiPBP5M4C5V/TmAiFwOvAK4bRQVc6rRIPmqdc5YwTX2655uF6R2sx2TJKzAkZZvM3t2Ip4cK5Gm18QWdi2cZ5xYIXyrsJCtFdqCk6vMERill9BeWGUnIk0Xzo1Ij7bfYuF3VfUo9LF03aeqjzvp7L+IM9QKu6mUacssLMhsT44v5NNd3yoijryq9Km2njYFbl63IA2bWY8xpcy+H5lzsuSdiyckM2Wl75ytbux/sPAuhHrGtFTI37teYbE9mI8NfDPwK/P93rCvWAeR80Rkm4hsa++bnMftHMdxHMuCOzFV9RJVPV1VT2+uW7vQt3McxzlkmI8JZTtwnPm+JewrZfqe7Q//8i0XTAIPz+O+y43HsLLaAyuvTd6e5c9Ka9Oo23NCbKdor4x1PRCRMeDfgbNIBu4fAr+rqrdWXLdNVU8f6qbLkJXWHlh5bfL2LH9WWpsWqz1DS+CqOisi7wD+icTd8ZmqwdtxHMcZHfOKA1fVrwBfGVFdHMdxnAFYipmYlyzBPReSldYeWHlt8vYsf1ZamxalPUPbwB3HcZylxXOhOI7j1JRFHcBF5EUicqeI3CUiFyzmvUeBiBwnIt8UkdtE5FYReVfYf4SIfF1Efho+D1/qug6CiDRF5CYRuS58P0lEbgzP6YsiMr7UdRwEEdkoIleKyB0icruInFnnZyQi/y28bz8RkS+IyKo6PSMR+YyIPCgiPzH7os9DEv46tOsWEXnG0tW8nJI2/Z/wzt0iIl8SkY3m2HtCm+4Ukd8aVT0WbQBfIblTZoF3q+qpwBnA20MbLgCuV9VTgOvD9zrxLuB28/0DwF+p6snAo8C5S1Kr4fkI8I+q+kTgaSRtq+UzEpHNwDuB01X1KSQRX6+jXs/os8CL5uwrex5nA6eEv/OAjy9SHQfls3S36evAU1T1qSQh1u8BCGPE64Anh2s+FsbDebOYEniWO0VVp4E0d0ptUNUdqvrjsL2XZGDYTNKOreG0rcArl6aGgyMiW4CXAJ8K3wV4HnBlOKVu7dkA/AbwaQBVnVbVXdT4GZFEi60Ocy/WADuo0TNS1RuAnXN2lz2PVwCXasL3gY0icszi1LR/Ym1S1a+parrM/fdJJjdC0qbLVXVKVe8G7iIZD+fNYg7gfeVOqQsiciLwdOBG4ChV3REO3Q8ctUTVGoYPA39MnqZqE7DLvIh1e04nAQ8BfxvMQp8SkbXU9Bmp6nbgg8AvSQbu3cCPqPczgvLnsVLGibcAXw3bC9Ymd2IOgYisA64CzlfVPfaYJmE9tQjtEZGXAg+q6o+Wui4jZAx4BvBxVX06MMkcc0nNntHhJBLcScCxwFq6VfdaU6fn0Q8i8l4Sc+tlC32vxRzAB86dshwRkRbJ4H2Zql4ddj+Qqnnh88Glqt+APBt4uYj8gsSk9TwS+/HGoK5D/Z7TvcC9qnpj+H4lyYBe12f0fOBuVX1IVWeAq0meW52fEZQ/j1qPEyLyZuClwBs0j9FesDYt5gD+Q+CU4D0fJzHqX7uI9583wT78aeB2Vf2QOXQtcE7YPge4ZrHrNgyq+h5V3aKqJ5I8j2+o6huAbwKvDqfVpj0Aqno/8CsReULYdRZJjvpaPiMS08kZIrImvH9pe2r7jAJlz+Na4PdCNMoZwG5jalnWSLJC2R8DL1fV/ebQtcDrRGRCRE4icdD+YCQ3VdVF+wNeTOKd/Rnw3sW894jq/+skqt4twM3h78UkduPrgZ8C/wwcsdR1HaJtvwlcF7YfF16wu4C/ByaWun4DtuU0YFt4Tl8GDq/zMwL+J3AH8BPgc8BEnZ4R8AUS+/0MiYZ0btnzIFnG4KNhjPg3kuibJW9Dn226i8TWnY4NnzDnvze06U7g7FHVw2diOo7j1BR3YjqO49QUH8Adx3Fqig/gjuM4NcUHcMdxnJriA7jjOE5N8QHccRynpvgA7jiOU1N8AHccx6kp/x9dSJjenOt9fgAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "optimized_velocity_field = session.run(initial_state.velocity).at_centers()\n", - "\n", - "pylab.title('Initial y-velocity (optimized)')\n", - "pylab.imshow(np.concatenate(optimized_velocity_field.data[...,0], axis=1), origin='lower')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This nicely shows how the initial perturbation of the velocity field grows for the larger inflow deviations towards the right:\n", - "\n", - "For comlpeteness, here are the x-components:" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": { - "id": "Pqw5BDxmdkut" - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "pylab.title('Initial x-velocity (optimized)')\n", - "pylab.imshow(np.concatenate(optimized_velocity_field.data[...,1], axis=1), origin='lower')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ooqVxCPM8PXl" - }, - "source": [ - "This example illustrates how the differentiable physics approach can easily be extended towards significanlty more complex PDEs. Above, we've optimized for a mini-batch of 20 steps of a full Navier-Stokes solver.\n", - "\n", - "This is a powerful basis to bring NNs into the picture! As you might have noticed, our degrees of freedom were still a regular grid, and we've solved a single inverse problem. There was more than one case to solve, but nonetheless no question of generalization and no neural network structure." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "Differentiable Physics with Fluid Simulations.ipynb", - "provenance": [] - }, - "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.8.5" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/diffphys-code-ns-v2a.ipynb b/diffphys-code-ns-v2a.ipynb deleted file mode 100644 index ff67f16..0000000 --- a/diffphys-code-ns-v2a.ipynb +++ /dev/null @@ -1,533 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "o4JZ84moBKMr" - }, - "source": [ - "# Differentiable Fluid Simulations\n", - "\n", - "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", - "\n", - "**test version, rough... not newest one**\n", - "\n", - "\n", - "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", - "\n", - "\n", - "\n", - "Next, we'll target a more complex example with the Navier-Stokes equations as model. We'll target a 2D case with velocity $\\mathbf{u}$, no explicit viscosity term, and a marker density $d$ that drives a simple Boussinesq buoyancy term $\\eta d$ adding a force along the y dimension:\n", - "\n", - "$\\begin{aligned}\n", - " \\frac{\\partial u_x}{\\partial{t}} + \\mathbf{u} \\cdot \\nabla u_x &= - \\frac{1}{\\rho} \\nabla p \n", - " \\\\\n", - " \\frac{\\partial u_y}{\\partial{t}} + \\mathbf{u} \\cdot \\nabla u_y &= - \\frac{1}{\\rho} \\nabla p + \\eta d\n", - " \\\\\n", - " \\text{s.t.} \\quad \\nabla \\cdot \\mathbf{u} &= 0,\n", - " \\\\\n", - " \\frac{\\partial d}{\\partial{t}} + \\mathbf{u} \\cdot \\nabla d &= 0 \n", - "\\end{aligned}$\n", - "\n", - "As optimization objective we'll consider a more difficult variant of the previous example: the state of the observed density $d$ should match a given target after $n=20$ steps of simulation. In contrast to before, the marker $d$ cannot be modified in any way, but only the initial state of the velocity $\\mathbf{u}$ at $t=0$. This gives us a split between observable quantities for the loss formulation, and quantities that we can interact with during the optimization (or later on via NNs).\n", - "\n", - "First, let's get the loading of python modules out of the way:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "id": "da1uZcDXdVcF" - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v2/phi/physics/_boundaries.py:396: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", - " return self.velocity is 0 and self.angular_velocity is 0\n", - "/Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v2/phi/physics/_boundaries.py:396: SyntaxWarning: \"is\" with a literal. Did you mean \"==\"?\n", - " return self.velocity is 0 and self.angular_velocity is 0\n", - "/Users/thuerey/miniconda3/envs/tf/lib/python3.8/_collections_abc.py:743: MatplotlibDeprecationWarning: The global colormaps dictionary is no longer considered public API.\n", - " for key in self._mapping:\n", - "/Users/thuerey/miniconda3/envs/tf/lib/python3.8/_collections_abc.py:744: MatplotlibDeprecationWarning: The global colormaps dictionary is no longer considered public API.\n", - " yield (key, self._mapping[key])\n" - ] - } - ], - "source": [ - "#!pip install --upgrade --quiet phiflow\n", - "#!pip install --upgrade --quiet git+https://github.com/tum-pbs/PhiFlow@develop\n", - "from phi.flow import * # The Dash GUI is not supported on Google Colab, ignore the warning\n", - "import pylab" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "BVV1IKVqDfLl" - }, - "source": [ - "## Setting up the simulation\n", - "\n", - "To make things a bit more interesting - and to move a bit closer to a NN training process - let's set up of four fluid simulations that run in parallel, i.e. a mini batch similar to DL training. \n", - "\n", - "While the simulation setup is very similar to before, we'll define an additional batch dimension in addition to the spatial dimensions `x` and `y`. Below, this additional dimension is triggered by inflow tensor, which contains an named `nbatch` dimension, in addition to the spatial ones (the name `batch` is alredy taken). The two `print` statements below illustrate this behavior: they print the tensor shapes with all dimensions, and next only the spatial ones.\n", - "\n", - "TODO xxx **In phiflow we can directly pass a `batch_size=4` parameter to the `Fluid` object. Each fluid simulation is fully independent. In this case they differ by having circular Inflows at different locations.**\n", - "TODO ,explain expansion!\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "id": "WrA3IXDxv31P" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Inflow shape: (nbatch=4, x=32, y=40)\n", - "Inflow shape, spatial only: (x=32, y=40)\n", - "Initial velocity shape: (x=32, y=40, vector=2) \n", - "\n", - "Velocity shape after 1 step: (nbatch=4, x=32, y=40, vector=2)\n" - ] - } - ], - "source": [ - "domain = Domain(x=32, y=40, boundaries=CLOSED, bounds=Box[0:32, 0:40])\n", - "\n", - "smoke = domain.scalar_grid(0) # sampled at cell centers\n", - "velocity = domain.staggered_grid(0) # sampled in staggered form at face centers \n", - "\n", - "sphere_centers = [(10,5), (12,5), (19,5), (16,5)]\n", - "inflow_location = math.tensor(sphere_centers, names='nbatch,vector')\n", - "#inflow_location = math.tensor([(4, 5), (8, 5), (12, 5), (16, 5)], names='inflow_loc,vector')\n", - "inflow = domain.grid(Sphere(center=inflow_location, radius=3)) * 0.6\n", - "\n", - "\n", - "#print(f\"DEBUG : {velocity.shape.nbatch}\")\n", - "\n", - "print(f\"Inflow shape: {inflow.shape}\")\n", - "print(f\"Inflow shape, spatial only: {inflow.shape.spatial}\")\n", - "print(f\"Initial velocity shape: {velocity.shape} \\n\")\n", - "\n", - "# world = World()\n", - "# fluid = world.add(Fluid(Domain([40, 32], boundaries=CLOSED), buoyancy_factor=0.05, batch_size=4), physics=IncompressibleFlow())\n", - "# centers = [[5,10], [5,12], [5,14], [5,16]]\n", - "# world.add(Inflow(Sphere(center=centers, radius=3), rate=0.2));\n", - " #world.step(dt=1.5)\n", - " #for _ in range(20):\n", - "\n", - "for frame in range(20):\n", - " smoke = advect.mac_cormack(smoke, velocity, dt=1) + inflow\n", - " buoyancy_force = smoke * (0, 0.5) >> velocity\n", - " velocity = advect.semi_lagrangian(velocity, velocity, dt=1) + buoyancy_force\n", - " velocity, _, _, _ = fluid.make_incompressible(velocity, domain)\n", - " if frame==0:\n", - " print(f\"Velocity shape after 1 step: {velocity.shape}\")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**TODO** explain nbatch\n", - "phiflow automatically expands dimensions...\n", - "\n", - "Like before, let's plot the marker density after a few steps of simulation (each call to `step()` now updates all four simulations). Note that the boundaries between the four simulations are not visible in the image, but it shows four completely separate density states. The different inflow positions in conjunction with the solid wall boundaries (zero Dirichlet for velocity, and Neumann for pressure), result in four different end states of the simulation." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": { - "needs_background": "light" - }, - "output_type": "display_data" - } - ], - "source": [ - "#pylab.imshow(np.concatenate(fluid.density.data[...,0], axis=1), origin='lower', cmap='magma')\n", - "fig, axes = pylab.subplots(1, 4, figsize=(20, 4))\n", - "for i in range(inflow.shape.nbatch):\n", - " axes[i].imshow(smoke.values.nbatch[i].numpy('y,x'), origin='lower', cmap='magma')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**TODO** add labels...\n", - "\n", - "Now we see four simulations, (0) to (3). This final density of simulation (0), with the curved plume on the far left, will be our **reference state**, while the initial velocity of the other three will be modified in the optimization procedure below." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "rdSTbMoaS0Uz" - }, - "source": [ - "## Differentiation\n", - "\n", - "The simulation we just computed was using purely (non-differentiable) operations from numpy.\n", - "To enable differentiability, we need to build a TensorFlow graph that computes this result. \n", - "\n", - "This can be achieved by changing the import statement to [`from phi.tf.flow import *`](https://tum-pbs.github.io/PhiFlow/phi/tf/flow.html) (or [`phi.torch.flow`](https://tum-pbs.github.io/PhiFlow/phi/torch/flow.html) for PyTorch).\n", - "Tensors created after this import will be allocated using TensorFlow and operations on these will be executed with TensorFlow.\n", - "Note that this will make use of a GPU through CUDA if your TensorFlow version supports it, and potentially custom CUDA operators of phiflow (if you dont have those compiled you might see a warning below, which you can safely ignore)." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": { - "id": "mphMP0sYIOz-" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Could not load resample cuda libraries: CUDA binaries not found at /Users/thuerey/Dropbox/mbaDevelSelected/phiflow-v2/phi/tf/cuda/build/resample.so. Run \"python setup.py tf_cuda\" to compile them\n" - ] - } - ], - "source": [ - "from phi.tf.flow import * # Causes deprecation warnings with TF 1.15\n", - "import pylab\n", - "# ### session = Session(None) # Used to run the TensorFlow graph" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "3mpyowRYUSS4" - }, - "source": [ - "Now that we have imported `phi.tf.flow`, let's set up the simulation just like before. But now, the initial velocity state that we start from will be a variable quantity that we can modify. Then we can optimize these initial velocities so that all simulations arrive at a final state that is similar to the first simulation from the previous example. I.e., the state shown in the left-most image above.\n", - "\n", - "This is a fairly tough task: we're producing diffent dynamics by changing the boundary conditions (the marker inflow position), and an optimizer should now find a single initial velocity state, that gives the same state as simulation `0` above at $t=20$. Thus, after 20 steps the simulation should reproduce a different set of boundary conditions from the velocity state. It would be much easier to simply change the position of the marker inflow to arrive at this goal, but -- to make things a bit more difficult and interesting here -- the inflow is _not_ a degree of freedom. The optimizer can only change the velocity $\\mathbf{u}$ at time $t=0$.\n", - "\n", - "To achieve this, we'll run the full chain of steps for the simulation over time in TensorFlow, and track the gradients for the initial velocity state at $t=0$ to be modified in the optimization.\n", - "\n", - "**REMOVE we create a TensorFlow variable for the velocity at t=0.\n", - "It is initialized with zeros (like with the NumPy simulation above) and can later be used as a target for optimization.**" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "id": "NlJMJikaHOL6" - }, - "outputs": [], - "source": [ - "domain = Domain(x=32, y=40, boundaries=CLOSED, bounds=Box[0:32, 0:40])\n", - "sphere_centers = [(10.,5.), (12.,5.), (19.,5.), (16.,5.)]\n", - "inflow_location = math.tensor(sphere_centers, names='inflow_loc,vector', convert=True)\n", - "inflow = domain.grid(Sphere(center=inflow_location, radius=3)) * 0.6\n", - "\n", - "# world = World()\n", - "# fluid = world.add(Fluid(Domain([40, 32], boundaries=CLOSED), buoyancy_factor=0.05, batch_size=4), physics=IncompressibleFlow())\n", - "# world.add(Inflow(Sphere(center=centers, radius=3), rate=0.2));\n", - "# fluid.velocity = variable(fluid.velocity) # create TensorFlow variable\n", - "# initial_state = fluid.state # Remember the state at t=0 for later visualization\n", - "# session.initialize_variables()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can verify that tensors are now backed by TensorFlow with the following `print` statement." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "tensorflow.python.framework.ops.EagerTensor" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "type(inflow.values.native())" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "vSdGIEzCgq5-" - }, - "source": [ - "Note that tensors created with NumPy will keep on using NumPy/SciPy operations unless a TensorFlow tensor is also passed to the same operation.\n", - "\n", - "**CONTINUE**\n", - "\n", - "The simulation now contains variables in the initial state.\n", - "Since all later states depend on the value of the variable, the `step` method cannot directly compute concrete state values.\n", - "Instead, `world.step` will extend the TensorFlow graph by the operations needed to perform the step.\n", - "\n", - "To execute the graph with actual data, we can use `session.run`, just like with regular TensorFlow 1.x. While `run` would usually be used to infer predictions from a learning model, it now executes the graph of simulation steps." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "id": "wSrIezfWHjcQ" - }, - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'world' is not defined", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mworld\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mpylab\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimshow\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconcatenate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfluid\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdensity\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m...\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0maxis\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0morigin\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'lower'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcmap\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'magma'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mNameError\u001b[0m: name 'world' is not defined" - ] - } - ], - "source": [ - "world.step()\n", - "pylab.imshow(np.concatenate(session.run(fluid.density).data[...,0], axis=1), origin='lower', cmap='magma')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "iJc6UdYHhtOH" - }, - "source": [ - "Let's build a graph for the full simulation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "b9xHtdDQRrjL" - }, - "outputs": [], - "source": [ - "for step in range(20):\n", - " print('Building graph for time step %d' % step)\n", - " world.step(dt=1.5)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "2VQ92g2rs6wM" - }, - "source": [ - "When calling `session.run` now, the full simulation is evaluated using TensorFlow operations.\n", - "This will take advantage of your GPU, if available.\n", - "If you compile ΦFlow with [CUDA support](https://github.com/tum-pbs/PhiFlow/blob/master/documentation/Installation_Instructions.md), the TensorFlow graph will also use optimized operators for efficient simulation and training runs.\n", - "\n", - "The `session.run()` call of the following code block will now retrieve the final fluid density, and for that it actually needs to process all 20 simulation steps of the TF graph that we just constructed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "TA6Ibs-mXsTc" - }, - "outputs": [], - "source": [ - "print('Computing frames...')\n", - "original_state = np.concatenate(session.run(fluid.density).data[...,0], axis=1)\n", - "pylab.imshow(original_state, origin='lower', cmap='magma')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "IClfRMfoyGUa" - }, - "source": [ - "Next, we define the *loss* function. This is the value we want to decrease via optimization.\n", - "For this example, we want the marker densities of all final simulation states to match the left-most one, called `target`, in terms of an $L^2$ norm.\n", - "\n", - "For the optimizer, we choose again gradient descent for this example." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "7KPpyIwjYETi" - }, - "outputs": [], - "source": [ - "target = session.run(fluid.density).data[0,...]\n", - "loss = math.l2_loss(fluid.density.data[1:,...] - target)\n", - "optim = tf.train.GradientDescentOptimizer(learning_rate=0.01).minimize(loss)\n", - "session.initialize_variables()\n", - "\n", - "print('Initial loss: %f' % session.run(loss))" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "AALD66-N0U5F" - }, - "source": [ - "With the loss and optimizer set up, all that's left is to run the actual optimization." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "pvvF6xqmaRLX" - }, - "outputs": [], - "source": [ - "for optim_step in range(100):\n", - " _, loss_value = session.run([optim, loss])\n", - " if optim_step<3 or optim_step%10==0: \n", - " print('Optimization step %d, loss %f %s' % (optim_step, loss_value, '' if optim_step else '. The first step sets up the adjoint graph.'))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The loss should have gone down significantly, from above 60 to below 40, and now we can visualize how well the reconstruction for the last frame turned out for simulations 1 to 3." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "SQBtCmhZaYYj" - }, - "outputs": [], - "source": [ - "optimized_state = np.concatenate(session.run(fluid.density).data[...,0], axis=1)\n", - "pylab.title(\"Original state at t=20 (top), and optimized version (bottom):\")\n", - "pylab.imshow( np.concatenate([optimized_state, original_state],axis=0), origin='lower', cmap='magma')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "HP7aDQfpKifp" - }, - "source": [ - "Naturally, the image on the left is the same (this is simulation (0), our reference), and the other three simulations now exhibit a noticeable curved shape towards the left. This is especially visible for the far right simulation (3), which was moving straight up in its original state, and now has an off-center plume shape that tries to match the reference on the left. \n", - "\n", - "Note that this simulation (like both others) needs to \"work\" with a fixed inflow, hence it cannot simply \"produce\" marker density out of the blue to match the target. Also it needs to take into account how the non-linear model equations change the state of the system over the course of 20 time steps. So the optimization goal is quite tough, and it is not possible to exactly satisfy the constraints to match simulation (0) in this scenario. This is exactly what makes it an interesting test case, though.\n", - "\n", - "Now we can also have a look at the now-optimized initial velocity field. Here we see the y-components (from `data[...,0]`)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "i7ZahlUudex8" - }, - "outputs": [], - "source": [ - "optimized_velocity_field = session.run(initial_state.velocity).at_centers()\n", - "\n", - "pylab.title('Initial y-velocity (optimized)')\n", - "pylab.imshow(np.concatenate(optimized_velocity_field.data[...,0], axis=1), origin='lower')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This nicely shows how the initial perturbation of the velocity field grows for the larger inflow deviations towards the right:\n", - "\n", - "For comlpeteness, here are the x-components:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "Pqw5BDxmdkut" - }, - "outputs": [], - "source": [ - "pylab.title('Initial x-velocity (optimized)')\n", - "pylab.imshow(np.concatenate(optimized_velocity_field.data[...,1], axis=1), origin='lower')" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ooqVxCPM8PXl" - }, - "source": [ - "This example illustrates how the differentiable physics approach can easily be extended towards significanlty more complex PDEs. Above, we've optimized for a mini-batch of 20 steps of a full Navier-Stokes solver.\n", - "\n", - "This is a powerful basis to bring NNs into the picture! As you might have noticed, our degrees of freedom were still a regular grid, and we've solved a single inverse problem. There was more than one case to solve, but nonetheless no question of generalization and no neural network structure." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "Differentiable Physics with Fluid Simulations.ipynb", - "provenance": [] - }, - "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.8.5" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/intro-teaser.ipynb b/intro-teaser.ipynb index 8e3f299..029b793 100644 --- a/intro-teaser.ipynb +++ b/intro-teaser.ipynb @@ -65,7 +65,7 @@ "id": "latest-amino", "metadata": {}, "source": [ - "To illustrate these two approaches, we consider the following simplified setting: Given the function $\\mathcal P: y\\to y^2$ for $y$ in the inverval $[0,1]$, find the unknown function $f$ such that $\\mathcal P(f(x)) = x$ for all $x$ in $[0,1]$. Note: to make things a bit more interesting, we're using $y^2$ here instead of the more common $x^2$ parabola, and the _discretization_ is simply given by representing the $x$ and $y$ via floating point numbers in the computer for this simple case.\n", + "To illustrate these two approaches, we consider the following simplified setting: Given the function $\\mathcal P: y\\to y^2$ for $y$ in the intverval $[0,1]$, find the unknown function $f$ such that $\\mathcal P(f(x)) = x$ for all $x$ in $[0,1]$. Note: to make things a bit more interesting, we're using $y^2$ here instead of the more common $x^2$ parabola, and the _discretization_ is simply given by representing the $x$ and $y$ via floating point numbers in the computer for this simple case.\n", "\n", "We know possible solutions for $f$ are the positive or negative square root function (for completeness: piecewise combinations would also be possible).\n", "We can try to solve this by using a neural network to approximate the inverse mapping $f$.\n", @@ -184,9 +184,9 @@ "id": "governmental-mixture", "metadata": {}, "source": [ - "As both model and data set are very small, the training converges very quickly, but if we inspect the predictions of the network, we can see that it nowhere near the solution we were hoping to find: it averages between the data points on both sides of the x-axis and therefore, fails to find satisfying solutions to our above problem.\n", + "As both model and data set are very small, the training converges very quickly, but if we inspect the predictions of the network, we can see that it is nowhere near the solution we were hoping to find: it averages between the data points on both sides of the x-axis and therefore, fails to find satisfying solutions to our above problem.\n", "\n", - "The following plots nicely highlights this: it shows the data in blue, and the supervised solution in red. " + "The following plots nicely highlights this: it shows the data in light gray, and the supervised solution in red. " ] }, { @@ -373,7 +373,7 @@ "\n", "- We're still only getting one side of the curve! This is to be expected, because we're representing the solutions with a deterministic function. Hence we can only represent a single mode. Interestingly, whether it's the top or bottom mode is determined by the random initialization of the weights in $f$ - run the example a couple of time to see this effect in action. To capture multiple modes we'd need to extend the model to capture the full distribution of the outputs and parametrize it with additional dimensions.\n", "\n", - "- The region with $x$ near zero is typically still off in this example. The model primarily learns a linear approximation of one half of the parabola here. This is primarily caused by the weak neural network: it is very small and shallow. (Give it a try - it's very easy to fix this in the `model_dp` defition.)\n" + "- The region with $x$ near zero is typically still off in this example. The model primarily learns a linear approximation of one half of the parabola here. This is primarily caused by the weak neural network: it is very small and shallow. (Give it a try - it's very easy to fix this in the `model_dp` definition.)\n" ] }, { @@ -385,7 +385,7 @@ "\n", "It's a very simple example, but it very clearly shows a failure case for supervised learning. While it might seem very artificial on first sight, many practical PDEs exhibit a variety of these modes, and it's often not clear where (and how many) exist in the solution manifold we're interested in. Using supervised learning is very dangerous in such cases - we might simply and unknowingly _blur_ out these different modes.\n", "\n", - "A good and obvious example are bifurcations in fluid flows - the smoke rising above a candle will start out straight, and then, due to tiny perturbations in its motion, start oscillating in a random direction. The images below illustrate this case via _numerical perturbations_: the perfectly symmetric setup will start turning left or right, depending on how the approximation errors build up. Similarly, we'll have different modes in all our numerical solutions, and typically it's important to recover them, rather than averaging them out. Hence, we'll show how to leverage training via _differentiable physics_ in the following chapters for more practical and complex cases.\n", + "Good and obvious example are bifurcations in fluid flows - the smoke rising above a candle will start out straight, and then, due to tiny perturbations in its motion, start oscillating in a random direction. The images below illustrate this case via _numerical perturbations_: the perfectly symmetric setup will start turning left or right, depending on how the approximation errors build up. Similarly, we'll have different modes in all our numerical solutions, and typically it's important to recover them, rather than averaging them out. Hence, we'll show how to leverage training via _differentiable physics_ in the following chapters for more practical and complex cases.\n", "\n", "```{figure} resources/intro-fluid-bifurcation.jpg\n", "---\n", @@ -407,7 +407,7 @@ "\n", "For the simple DP example above:\n", "\n", - "- This notebook is itentionally using a very simple setup. Change the training setup and NN above to obtain a higher-quality solution such as the green one shown in the very first image at the top. \n", + "- This notebook is intentionally using a very simple setup. Change the training setup and NN above to obtain a higher-quality solution such as the green one shown in the very first image at the top. \n", "\n", "- Or try extending the setup to a 2D case, i.e. a paraboloid. Given the function $\\mathcal P:(y_1,y_2)\\to y_1^2+y_2^2$, find an inverse function $f$ such that $\\mathcal P(f(x)) = x$ for all $x$ in $[0,1]$.\n", "\n", diff --git a/overview-burgers-forw.ipynb b/overview-burgers-forw.ipynb index 2a8dd3b..cf7eef3 100644 --- a/overview-burgers-forw.ipynb +++ b/overview-burgers-forw.ipynb @@ -8,14 +8,14 @@ "\n", "This chapter will give an introduction for how to run _forward_, i.e., regular simulations starting with a given initial state and approximating a later state numerically, with ΦFlow. The ΦFlow framework provides a set of differentiable building blocks that directly interface with deep learning frameworks, and before going for deeper and more complicated integrations, this notebook (and the next one), will show how regular simulations can be done with ΦFlow. Later on, we can use very similar code to couple them with neural networks.\n", "\n", - "The main repository for ΦFlow (in the following \"phiflow\") is https://github.com/tum-pbs/PhiFlow, and additional API documenation and examples can be found at https://tum-pbs.github.io/PhiFlow/.\n", + "The main repository for ΦFlow (in the following \"phiflow\") is https://github.com/tum-pbs/PhiFlow, and additional API documentation and examples can be found at https://tum-pbs.github.io/PhiFlow/.\n", "\n", "For this jupyter notebook (and all following ones), you can find a _\"[run in colab]\"_ link at the end of the first paragraph. This link will load the latest version from the PBDL github repo in a colab notebook that you can execute on the spot: \n", "[[run in colab]](https://colab.research.google.com/github/tum-pbs/pbdl-book/blob/main/overview-burgers-forw.ipynb)\n", "\n", "## Model\n", "\n", - "As phyical model we'll use Burgers equation.\n", + "As physical model we'll use Burgers equation.\n", "This equation is a very simple, yet non-linear and non-trivial, model equation that can lead to interesting shock formations. Hence, it's a very good starting point for experiments, and it's 1D version (from equation {eq}`model-burgers1d`) is given by:\n", "\n", "$$\n", @@ -270,7 +270,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This concludes a first simulation in phiflow. It's not overly complex, but because of that it's a good starting point for evaluating and comparing different physics-based deep learning approaches in the next chatper. But before that, we'll target a more complex simulation type in the next section." + "This concludes a first simulation in phiflow. It's not overly complex, but because of that it's a good starting point for evaluating and comparing different physics-based deep learning approaches in the next chapter. But before that, we'll target a more complex simulation type in the next section." ] }, { diff --git a/overview-ns-forw.ipynb b/overview-ns-forw.ipynb index bf7796a..2b16278 100644 --- a/overview-ns-forw.ipynb +++ b/overview-ns-forw.ipynb @@ -8,7 +8,7 @@ "source": [ "# Navier-Stokes Forward Simulation\n", "\n", - "Now let's target a somewhat more complex example: a fluid simulations based on the Navier-Stokes equations. This is still very simple with ΦFlow (phiflow), as differentiable operators for all steps exist. The Navier-Stokes equations (in their incompressible form) introduce an additional pressure field $p$, and a constraint for conservation of mass, as introduced in equation {eq}`model-boussinesq2d`. We're also moving a marker field, denoted by $d$ here, with the flow. It indicates regions of higher temperature, and excerts a force via with a bouyancy factor $\\xi$:\n", + "Now let's target a somewhat more complex example: a fluid simulation based on the Navier-Stokes equations. This is still very simple with ΦFlow (phiflow), as differentiable operators for all steps exist. The Navier-Stokes equations (in their incompressible form) introduce an additional pressure field $p$, and a constraint for conservation of mass, as introduced in equation {eq}`model-boussinesq2d`. We're also moving a marker field, denoted by $d$ here, with the flow. It indicates regions of higher temperature, and exerts a force via a buouyancy factor $\\xi$:\n", "\n", "$$\\begin{aligned}\n", " \\frac{\\partial \\mathbf{u}}{\\partial{t}} + \\mathbf{u} \\cdot \\nabla \\mathbf{u} &= - \\frac{\\Delta t}{\\rho} \\nabla p + \\nu \\nabla\\cdot \\nabla \\mathbf{u} + (0,1)^T \\xi d\n", @@ -18,7 +18,7 @@ "\\end{aligned}$$\n", "\n", "\n", - "Here $\\mathbf{g}$ collects the forcing terms. Below we'll use a simple buoyancy model. We'll solve this PDE on a closed domain with Dirchlet boundary conditions $\\mathbf{u}=0$ for the velocity, and Neumann boundaries $\\frac{\\partial p}{\\partial x}=0$ for pressure, on a domain $\\Omega$ with a physical size of $100 \\times 80$ units. \n", + "Here $\\mathbf{g}$ collects the forcing terms. Below we'll use a simple buoyancy model. We'll solve this PDE on a closed domain with Dirichlet boundary conditions $\\mathbf{u}=0$ for the velocity, and Neumann boundaries $\\frac{\\partial p}{\\partial x}=0$ for pressure, on a domain $\\Omega$ with a physical size of $100 \\times 80$ units. \n", "[[run in colab]](https://colab.research.google.com/github/tum-pbs/pbdl-book/blob/main/overview-ns-forw.ipynb)\n", "\n" ] @@ -47,7 +47,7 @@ "#!pip install --upgrade --quiet git+https://github.com/tum-pbs/PhiFlow@develop\n", "#!pip install --upgrade --quiet phiflow \n", "\n", - "from phi.flow import * # The Dash GUI is not supported on Google Colab, ignore the warning\n", + "from phi.flow import * # The Dash GUI is not supported on Google colab, ignore the warning\n", "import pylab" ] }, @@ -495,7 +495,7 @@ "source": [ "## Next steps\n", "\n", - "You could create a variety of nice fluid simulations based on this setup. E.g., try chaning the spatial resolution, the buoyancy factors, and the overall length of the simulation run." + "You could create a variety of nice fluid simulations based on this setup. E.g., try changing the spatial resolution, the buoyancy factors, and the overall length of the simulation run." ] } ], diff --git a/overview.md b/overview.md index 170c651..380e30c 100644 --- a/overview.md +++ b/overview.md @@ -81,7 +81,7 @@ what can be done with numerical methods: e.g., in scenarios where a solver targets cases from a certain well-defined problem domain repeatedly, it can make a lot of sense to once invest significant resources to train -an neural network that supports the repeated solves. Based on the +a neural network that supports the repeated solves. Based on the domain-specific specialization of this network, such a hybrid could vastly outperform traditional, generic solvers. And despite the many open questions, first publications have demonstrated diff --git a/supervised-airfoils.ipynb b/supervised-airfoils.ipynb index aa9c4b5..2d954bd 100644 --- a/supervised-airfoils.ipynb +++ b/supervised-airfoils.ipynb @@ -646,7 +646,7 @@ "\n", "Now let's look at actual test samples: In this case we'll use new airfoil shapes as out-of-distribution (OOD) data. These are shapes that the network never saw in any training samples, and hence it tells us a bit about how well the model generalizes to unseen inputs (the validation data wouldn't suffice to draw conclusions about generalization).\n", "\n", - "We'll use the samme visualization as before, and as indicated by the Bernoulli equation, especially the _pressure_ in the first column is a challenging quantity for the network. Due to it's cubic scaling w.r.t. the input freestream velocity and localized peaks, it is the toughest quantity to infer for the network.\n", + "We'll use the same visualization as before, and as indicated by the Bernoulli equation, especially the _pressure_ in the first column is a challenging quantity for the network. Due to it's cubic scaling w.r.t. the input freestream velocity and localized peaks, it is the toughest quantity to infer for the network.\n", "\n", "The cell below first downloads a smaller archive with these test data samples, and then runs them through the network. The evaluation loop also computes the accumulated L1 error such that we can quantify how well the network does on the test samples." ] @@ -781,7 +781,7 @@ "id": "vhH-rUZ-JMTX" }, "source": [ - "## Nex steps\n", + "## Next steps\n", "\n", "There are many obvious things to try here (see the suggestions below), e.g. longer training, larger data sets, larger networks etc. \n", "\n", diff --git a/supervised-discuss.md b/supervised-discuss.md index 7b05016..f9fb2e9 100644 --- a/supervised-discuss.md +++ b/supervised-discuss.md @@ -30,7 +30,7 @@ To summarize the scattered comments of the previous sections, here's a set of "g - Always start with a 1-sample overfitting test. - Check how many trainable parameters your network has. -- Slowly increase the amount of trianing data (and potentially network parameters and depth). +- Slowly increase the amount of training data (and potentially network parameters and depth). - Adjust hyperparameters (especially the learning rate). - Then introduce other components such as differentiable solvers or adversarial training.