diff --git a/Euler_approximate_solvers.ipynb b/Euler_approximate_solvers.ipynb index e43dd599..aa87a538 100644 --- a/Euler_approximate_solvers.ipynb +++ b/Euler_approximate_solvers.ipynb @@ -21,7 +21,7 @@ "editable": true }, "source": [ - "In the [last notebook](Euler_equations.ipynb) we studied the Euler equations of inviscid, compressible fluid flow -- including the Riemann problem and its solution. As we saw, the exact solution of the Riemann problem is computationally expensive, since it requires solving a set of nonlinear algebraic equations. In the context of finite volume methods, the detailed structure of the Riemann solution is almost immediately discarded -- only its impact on the neighboring cell averages is used. So it makes sense to consider whether we can approximate the solution with less computation. In this notebook, we investigate approximate solvers for the Euler equations." + "In the [Part I](Euler_equations.ipynb) we studied the Riemann problem for Euler equations of inviscid, compressible fluid flow . As we saw, the exact solution of the Riemann problem is computationally expensive, since it requires solving a set of nonlinear algebraic equations. In the context of finite volume methods, the detailed structure of the Riemann solution is almost immediately discarded -- only its impact on the neighboring cell averages is used. So it makes sense to consider whether we can approximate the solution with less computation. In this chapter, we investigate approximate solvers for the Euler equations." ] }, { diff --git a/Index.ipynb b/Index.ipynb index 39366219..5398a126 100644 --- a/Index.ipynb +++ b/Index.ipynb @@ -23,6 +23,8 @@ " \n", "## Part II: Approximate solvers\n", "\n", + " 1. [Euler approximate solvers](Euler_approximate_solvers.ipynb)\n", + "\n", "## Part III: Riemann problems with spatially-varying flux\n", " 1. Advection\n", " 2. Acoustics\n", @@ -35,10 +37,12 @@ " \n", "## Part V: Non-classical problems\n", " 1. [Nonconvex flux for a scalar problem](Nonconvex_Scalar_Osher_Solution.ipynb)\n", + " 2. [Pressureless flow](Pressureless_flow.ipynb)\n", " \n", "## Part VI: Multidimensional systems\n", " 1. [Acoustics](http://nbviewer.jupyter.org/github/maojrs/ipynotebooks/blob/master/acoustics_riemann.ipynb)\n", - " 2. [Elasticity](http://nbviewer.jupyter.org/github/maojrs/ipynotebooks/blob/master/elasticity_riemann.ipynb)" + " 2. [Elasticity](http://nbviewer.jupyter.org/github/maojrs/ipynotebooks/blob/master/elasticity_riemann.ipynb)\n", + " 3. [The Kitchen Sink: shallow water in cylindrical coordinates](Kitchen_sink_problem.ipynb)" ] } ], diff --git a/Kitchen_sink_problem.ipynb b/Kitchen_sink_problem.ipynb index a9504d53..e140c065 100644 --- a/Kitchen_sink_problem.ipynb +++ b/Kitchen_sink_problem.ipynb @@ -19,7 +19,7 @@ "from clawpack import riemann\n", "from clawpack.visclaw.ianimate import ianimate\n", "import matplotlib\n", - "matplotlib.rcParams['font.size'] = 12\n", + "plt.style.use('seaborn-talk')\n", "from IPython.display import HTML" ] }, @@ -745,17 +745,6 @@ "plt.xlim(r[0],r[-1]);" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, - "outputs": [], - "source": [] - }, { "cell_type": "markdown", "metadata": { diff --git a/Pressureless_flow.ipynb b/Pressureless_flow.ipynb new file mode 100644 index 00000000..f37c39e4 --- /dev/null +++ b/Pressureless_flow.ipynb @@ -0,0 +1,373 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from exact_solvers import shallow_water\n", + "from collections import namedtuple\n", + "from utils import riemann_tools\n", + "from ipywidgets import interact, widgets\n", + "State = namedtuple('State', shallow_water.conserved_variables)\n", + "Primitive_State = namedtuple('PrimState', shallow_water.primitive_variables)\n", + "plt.style.use('seaborn-talk')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Shallow water in low-gravity\n", + "Recall the shallow water equations:\n", + "\n", + "\\begin{align}\n", + " h_t + (hu)_x & = 0 \\\\\n", + " (hu)_t + \\left(hu^2 + \\frac{1}{2}gh^2\\right)_x & = 0.\n", + "\\end{align}\n", + "\n", + "These are very similar to the *isothermal flow* equations of gas dynamics:\n", + "\n", + "\\begin{align}\n", + " \\rho_t + (\\rho u)_x & = 0 \\\\\n", + " (\\rho u)_t + \\left(\\rho u^2 + a^2 \\rho \\right)_x & = 0\n", + "\\end{align}\n", + "\n", + "Indeed, if we identify the shallow water depth $h$ with the isothermal gas density $\\rho$, then these systems differ only in the second term of the momentum flux. In both systems this term represents the effect of pressure; in the first system it causes water to flow from regions of greater depth to lower depth, while in the second system it causes gas to flow from regions of higher density to lower density.\n", + "\n", + "In this chapter we investigate what happens when the pressure tends to zero; this corresponds to the limit $g\\to 0$ for shallow water and the limit $a \\to 0$ for isothermal flow.\n", + "\n", + "First, let's see what happens to the integral curves and Hugoniot loci as $g \\to 0$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def connect_states(h_l=1.,u_l=-1.,h_r=1.,u_r=1.,logg=0.,plot_unphysical=True):\n", + " g = 10.**logg\n", + " q_l = np.array([h_l,h_l*u_l])\n", + " q_r = np.array([h_r,h_r*u_r])\n", + " fig, ax = plt.subplots(1,2,figsize=(14,6))\n", + " shallow_water.phase_plane_curves(q_l[0], q_l[1], 'qleft', g, wave_family=1,ax=ax[0],plot_unphysical=plot_unphysical)\n", + " shallow_water.phase_plane_curves(q_r[0], q_r[1], 'qright', g, wave_family=2,ax=ax[0],plot_unphysical=plot_unphysical)\n", + " shallow_water.phase_plane_curves(q_l[0], q_l[1], 'qleft', g, wave_family=1,y_axis='hu',ax=ax[1],plot_unphysical=plot_unphysical)\n", + " shallow_water.phase_plane_curves(q_r[0], q_r[1], 'qright', g, wave_family=2,y_axis='hu',ax=ax[1],plot_unphysical=plot_unphysical)\n", + " ax[0].set_title('h-u plane'); ax[1].set_title('h-hu plane'); ax[0].set_xlim(0,3); ax[1].set_xlim(0,3);\n", + " ax[0].set_ylim(-10,10); ax[1].set_ylim(-10,10); plt.tight_layout(); plt.show()\n", + "interact(connect_states,\n", + " h_l=widgets.FloatSlider(min=0.001,max=2,value=1),\n", + " u_l=widgets.FloatSlider(min=-5,max=5,value=-1),\n", + " h_r=widgets.FloatSlider(min=0.001,max=2,value=1),\n", + " u_r=widgets.FloatSlider(min=-5,max=5,value=1),\n", + " logg=widgets.FloatSlider(value=0,min=-5,max=2,description='$\\log_{10}(g)$'));" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def c1(q, xi, g=1.):\n", + " \"Characteristic speed for shallow water 1-waves.\"\n", + " h = q[0]\n", + " if h > 0:\n", + " u = q[1]/q[0]\n", + " return u - np.sqrt(g*h)\n", + " else:\n", + " return 0\n", + "\n", + "def c2(q, xi, g=1.):\n", + " \"Characteristic speed for shallow water 2-waves.\"\n", + " h = q[0]\n", + " if h > 0:\n", + " u = q[1]/q[0]\n", + " return u + np.sqrt(g*h)\n", + " else:\n", + " return 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "def make_plot_function(q_l,q_r,force_waves=None,extra_lines=None):\n", + " def plot_function(t,logg,plot_1_chars=False,plot_2_chars=False):\n", + " g = 10**logg\n", + " states, speeds, reval, wave_types = shallow_water.exact_riemann_solution(q_l,q_r,g,force_waves=force_waves)\n", + " ax = riemann_tools.plot_riemann(states,speeds,reval,wave_types,t=t,t_pointer=0,\n", + " extra_axes=True,variable_names=shallow_water.primitive_variables,\n", + " fill=[0],derived_variables=shallow_water.cons_to_prim);\n", + " if plot_1_chars:\n", + " riemann_tools.plot_characteristics(reval,c1,(g,g),axes=ax[0],extra_lines=extra_lines)\n", + " if plot_2_chars:\n", + " riemann_tools.plot_characteristics(reval,c2,(g,g),axes=ax[0],extra_lines=extra_lines)\n", + " shallow_water.phase_plane_plot(q_l,q_r,g,ax=ax[3],force_waves=force_waves,y_axis='u')\n", + " plt.show()\n", + " return plot_function\n", + "\n", + "def plot_riemann_SW(q_l,q_r,force_waves=None,extra_lines=None):\n", + " plot_function = make_plot_function(q_l,q_r,force_waves,extra_lines)\n", + " interact(plot_function, t=widgets.FloatSlider(value=0.1,min=0,max=.9),\n", + " plot_1_chars=widgets.Checkbox(description='1-characteristics',value=False),\n", + " plot_2_chars=widgets.Checkbox(description='2-characteristics'),\n", + " logg=widgets.IntSlider(value=0,min=-10,max=2,description='$\\log_{10}(g)$'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice the following behaviors as $g \\to 0$:\n", + "- The integral curves and hugoniot loci become parallel.\n", + "- All four curves become horizontal lines in the $h-u$ plane." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What happens to the hyperbolic structure of this system as $g \\to 0$? Recall that the flux jacobian is\n", + "\n", + "\\begin{align}\n", + "f'(q) & = \\begin{pmatrix} 0 & 1 \\\\ -(q_2/q_1)^2 + g q_1 & 2 q_2/q_1 \\end{pmatrix} \n", + " = \\begin{pmatrix} 0 & 1 \\\\ -u^2 + g h & 2 u \\end{pmatrix},\n", + "\\end{align}\n", + "\n", + "with eigenvalues\n", + "\n", + "\\begin{align}\n", + " \\lambda_1 & = u - \\sqrt{gh} & \\lambda_2 & = u + \\sqrt{gh},\n", + "\\end{align}\n", + "\n", + "and corresponding eigenvectors\n", + "\n", + "\\begin{align}\n", + " r_1 & = \\begin{bmatrix} 1 \\\\ u-\\sqrt{gh} \\end{bmatrix} &\n", + " r_2 & = \\begin{bmatrix} 1 \\\\ u+\\sqrt{gh} \\end{bmatrix}.\n", + "\\end{align}\n", + "\n", + "As $g \\to 0$, the eigenvalues both approach a single value of $\\lambda_0 = u$ and the eigenvectors both approach\n", + "\n", + "\\begin{align}\n", + " r_0 & = \\begin{bmatrix} 1 \\\\ u \\end{bmatrix}.\n", + "\\end{align}\n", + "\n", + "Thus for $g=0$ there is a single eigenvalue $\\lambda_0=u$ with algebraic multiplicity 2 but geometric multiplicity 1. The flux Jacobian is defective and the system is no longer hyperbolic. The integral curves are everywhere parallel to the eigenvector $r_0$, but this eigenvector points in a direction of constant $u$ and depends only on $u$, so the integral curves in the $h-hu$ plane are straight lines of constant $u$, all passing through the origin. \n", + "\n", + "What about the Hugoniot loci? Recall from the shallow water chapter that a shock with a jump in the depth of $\\alpha = h-h_*$ must have a jump in the momentum of\n", + "\n", + "\\begin{align}\n", + " h u & = h_* u_* + \\alpha \\left[u_* \\pm \\sqrt{gh_* \\left(1+\\frac{\\alpha}{h_*}\\right)\\left(1+\\frac{\\alpha}{2h_*}\\right)}\\right]\n", + "\\end{align}\n", + "\n", + "Setting $g=0$ we obtain $hu = h_* u_* + (h-h_*)u_*$, so that $u=u^*$. Thus the Hugoniot loci are the same as the integral curves and are lines of constant $u$.\n", + "\n", + "In the Riemann problem, if the left and right states have the same initial velocity, then we can still connect them as $g \\to 0$; in the limit, everything just moves along at that constant velocity. Here's an example." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "q_l = State(Depth = 3.,\n", + " Momentum = 1.)\n", + "q_r = State(Depth = 1.,\n", + " Momentum = 1./3)\n", + "\n", + "plot_riemann_SW(q_l,q_r)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now suppose we have a Riemann problem whose initial states have different velocities. The only way to connect them is through a pair of rarefactions with a dry state in between. Recall from our earlier treatment of the shallow water equations that a middle dry state occurred if $u_l + 2 \\sqrt{gh_l} < u_r-2\\sqrt{gh_r}$; for $g=0$ this reduces simply to the condition $u_l < u_r$.\n", + "Here's an example with $u_l < u_r$; use the second slider to see what happens as $g \\to 0$.\n", + "\n", + "Also, turn on the plots of the two characteristic families and notice how they become parallel as $g \\to 0$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "q_l = State(Depth = 3.,\n", + " Momentum = -1.)\n", + "q_r = State(Depth = 1.,\n", + " Momentum = 2.)\n", + "\n", + "plot_riemann_SW(q_l,q_r)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What happens if $u_l \\ge u_r$, so that the double-rarefaction solution is unphysical? The answer can be found by considering what happens when $g$ is small but nonzero. Here's an example to play with." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "q_l = State(Depth = 3.,\n", + " Momentum = 1.)\n", + "q_r = State(Depth = 1.,\n", + " Momentum = 0.1)\n", + "\n", + "plot_riemann_SW(q_l,q_r)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Keep a close eye on the scale of the x-axis in the phase plane plot, and notice again that s $g \\to 0$ the hugoniot loci become nearly parallel (horizontal in the $h-u$ plane). This means that their intersection occurs at a very large value of $h$, so the middle state depth goes to $\\infty$ as $g\\to 0$. Meanwhile, the speed of both shocks approaches a single value, so the region occupied by the middle state gets ever narrower. In the limit, the solution for the depth involves a delta function." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To make this even clearer, here's an example with uniform initial depth and both flows directed inward, very similar to the first example we considered in the initial chapter on shallow water." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "q_l = State(Depth = 1.,\n", + " Momentum = 2.)\n", + "q_r = State(Depth = 1.,\n", + " Momentum = -1.)\n", + "\n", + "plot_riemann_SW(q_l,q_r)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What's going on here, physically, when $g$ is very small? At the interface, water is colliding. With the gravitational pressure term present, this leads to the formation of outgoing shock waves that redistribute water away from the interface. But without that term, there is no force to equilibrate the depth and each parcel of water just flows with its initial velocity, until eventually it collides with water of a different velocity. Water from both sides accumulates at the interface, leading to a delta function.\n", + "\n", + "If you drag the time slider for different fixed values of $g$ in the last example, you'll see that since $g$ is still nonzero there are two shock waves and the near-delta-function region expands with time, but the rate of this expansion is smaller for smaller values of $g$. For very small values, the expansion cannot be resolved on the scale of the plots above. Indeed, the width of the line plotted there significantly exaggerates the width of this region when $g$ is very small.\n", + "\n", + "There are two import details we haven't yet explained: first, the interface is moving; second the delta function is growing in time. Movement of the interface is necessary in order to conserve momentum; since the water from the left is arriving with a different speed than that from the right, if the interface were stationary then momentum would not be conserved. Growth of the delta function is of course necessary in order to conserve mass.\n", + "\n", + "Let $h_\\delta$ denote the mass of the delta function for depth. How must $h_\\delta$ grow in order to conserve mass? Let $\\hat{u}$ denote the velocity of the interface, with $u_l \\ge \\hat{u} \\ge u_r$. Then over a unit time interval, a quantity $h_l(u_l-\\hat{u})$ of water arrives from the left, while $h_r(\\hat{u}-u_r)$ arrives from the right. Thus we must have\n", + "\n", + "$$\n", + " h_\\delta(t) = t \\left( h_l u_l - h_r u_r + \\hat{u}(h_r-h_l) \\right).\n", + "$$\n", + "\n", + "At what speed must the water in the delta function move in order to conserve momentum? The rate of momentum flowing into the delta function from the left is $h_l u_l (u_l-\\hat{u})$ and the rate of momentum flowing into the delta function from the right is $h_r u_r (\\hat{u}-u_r)$, so we must have\n", + "\n", + "$$\n", + " h_\\delta(t) \\hat{u} = t \\left( h_l u_l^2 - h_r u_r^2 + \\hat{u}(h_r u_r - h_l u_l \\right).\n", + "$$\n", + "\n", + "Combining these two conservation conditions, we find the speed of the interface:\n", + "\n", + "$$\n", + " \\hat{u} = \\frac{\\sqrt{h_l}u_l + \\sqrt{h_r}{u_r}}{\\sqrt{h_l}+\\sqrt{h_r}}\n", + "$$\n", + "\n", + "and the rate of growth of the delta function:\n", + "\n", + "$$\n", + " h_\\delta(t) = t \\sqrt{h_l h_r}(u_l - u_r).\n", + "$$\n", + "\n", + "Note that for any non-zero value of $g$, the length of the interval occupied by the middle state grows in time while its amplitude is constant. In the limit $g\\to 0$ the interval becomes infinitesimal so the amplitude must change to conserve mass. Notice also that this change in amplitude implies that the Riemann solution is no longer a similarity solution when $g=0$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exercise\n", + "In the example below, the initial velocity is zero for both states. Observe that multiplying the value of $g$ by a factor $\\alpha$ is equivalent to rescaling all the velocities by $\\sqrt{\\alpha}$. Prove that this scaling must hold for any Riemann solution of this system when $u_l = u_r = 0$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "q_l = State(Depth = 3.,\n", + " Momentum = 0.)\n", + "q_r = State(Depth = 1.,\n", + " Momentum = 0.)\n", + "\n", + "plot_riemann_SW(q_l,q_r)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/README.md b/README.md index be43c713..c855533b 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ Parentheticals indicate concepts introduced for the first time. 1. [Acoustics](https://github.com/clawpack/riemann_book/blob/master/Acoustics.ipynb) (eigenvalue analysis, characteristics, similarity solutions) 2. [Traffic flow](https://github.com/clawpack/riemann_book/blob/master/Traffic_flow.ipynb) (shocks, rarefactions, conservation, jump conditions) 3. Burgers' (weak solutions) -4. [Shallow water](https://github.com/clawpack/riemann_book/blob/master/Shallow_tracer.ipynb) (jump conditions for a nonlinear system; Riemann invariants, integral curves, Hugoniot Loci) (see also [this](http://nbviewer.jupyter.org/url/faculty.washington.edu/rjl/notebooks/shallow/SW_riemann_tester.ipynb) and [this](http://nbviewer.jupyter.org/gist/rjleveque/8994740)) +4. [Shallow water](https://github.com/clawpack/riemann_book/blob/master/Shallow_water.ipynb) (jump conditions for a nonlinear system; Riemann invariants, integral curves, Hugoniot Loci) (see also [this](http://nbviewer.jupyter.org/url/faculty.washington.edu/rjl/notebooks/shallow/SW_riemann_tester.ipynb) and [this](http://nbviewer.jupyter.org/gist/rjleveque/8994740)) 5. How to solve the Riemann problem exactly -- go in depth into SW solver, including Newton iteration to find root of piecewise function -5. Shallow water with a tracer (contact waves) +5. [Shallow water with a tracer](https://github.com/clawpack/riemann_book/blob/master/Shallow_tracer.ipynb) (contact waves) 5. [Euler equations](https://github.com/clawpack/riemann_book/blob/master/Euler_equations.ipynb) **Part II: Approximate solvers** diff --git a/Shallow_water.ipynb b/Shallow_water.ipynb index 4ef94515..84041eb0 100644 --- a/Shallow_water.ipynb +++ b/Shallow_water.ipynb @@ -532,7 +532,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "## Comparison of integral curves and Hugoniot loci\n", "You may have noticed that the integral curves look very similar to the Hugoniot loci, especially when plotted in the $h-hu$ plane. Below we plot the curve from each of these families that passes through a specified point. This divergence is less noticeable in the $h-hu$ plane since all of the curves approach the origin." @@ -542,7 +545,9 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false + "collapsed": false, + "deletable": true, + "editable": true }, "outputs": [], "source": [ @@ -561,7 +566,7 @@ " plt.show()\n", " \n", "interact(compare_curves,\n", - " wave_family=widgets.RadioButtons(options=[1,2]),\n", + " wave_family=widgets.RadioButtons(options=[1,2],description='Wave family:'),\n", " y_axis=widgets.RadioButtons(options=['u','hu'],description='Vertical axis:'),\n", " h0=widgets.FloatSlider(min=1.e-1,max=3.,value=1.,description='$h_*$'),\n", " u0=widgets.FloatSlider(min=-3,max=3,description='$u_*$'));" @@ -569,7 +574,10 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "deletable": true, + "editable": true + }, "source": [ "Near the point of intersection, the curves are very close; indeed, they must be tangent at this point since their direction is parallel to the corresponding eigenvector there. Far from this point they diverge; for small depths they must diverge greatly, since the Hugoniot locus never reaches $h=0$ at any finite depth." ] @@ -706,7 +714,7 @@ "editable": true }, "source": [ - "Plot the 2-characteristics and notice that they cross each other within the 2-rarefaction. This rarefaction is not physical and should be replaced with a shock; the corresponding part of the integral curve is hence shown as a dashed line." + "Plot the 2-characteristics and notice that they cross each other within the 2-rarefaction. This means that the solution we constructed is triple-valued and nonsensical as a solution to this one-dimensional conservation law, and so this portion of the solution is omitted in the plots of depth and momentum. In this case a rarefaction wave is not physical and should be replaced with a shock; the corresponding part of the integral curve is hence shown as a dashed line." ] }, { @@ -792,7 +800,7 @@ " shallow_water.phase_plane_curves(q_r[0], q_r[1], 'qright', wave_family=2,ax=ax[0])\n", " shallow_water.phase_plane_curves(q_l[0], q_l[1], 'qleft', wave_family=1,y_axis='hu',ax=ax[1])\n", " shallow_water.phase_plane_curves(q_r[0], q_r[1], 'qright', wave_family=2,y_axis='hu',ax=ax[1])\n", - " ax[0].set_title('h-u plane'); ax[1].set_title('h-hu plane')\n", + " ax[0].set_title('h-u plane'); ax[1].set_title('h-hu plane'); ax[0].set_xlim(0,3); ax[1].set_xlim(0,3);\n", " ax[0].set_ylim(-10,10); ax[1].set_ylim(-10,10); plt.tight_layout(); plt.show()\n", "interact(connect_states,\n", " h_l=widgets.FloatSlider(min=0.001,max=2,value=1),\n", @@ -810,6 +818,8 @@ "source": [ "You should find that by making the initial states flow sufficiently fast away from each other, there is no intersection in the $h-u$ plane. In the $h-hu$ plane, the integral curves always intersect at the origin. The reason for this ambiguity is that, for zero depth it isn't meaningful to assign a velocity. Thus in the $h-u$ plane we could think of the entire $u$-axis as being part of every integral curve. That means that we can always connect the left and right states via an intermediate state with depth $h=0$ (a dry state). \n", "\n", + "Since the 1-integral curve through $q_l$ reaches $h=0$ at $u = u_l + 2 \\sqrt{gh_l}$ and the 2-integral curve reaches $h=0$ at $u=u_r-2\\sqrt{gh_r}$, we see that a dry middle state occurs if and only if $u_l + 2 \\sqrt{gh_l} < u_r-2\\sqrt{gh_r}$.\n", + "\n", "It is clear in this case that the solution must consist of two centered rarefaction waves, connecting the left and right states to the middle dry state. These centered rarefactions still have the structure derived above; to complete the solution we only need to know the range of centered characteristics included in each rarefaction. This is most conveniently determined by considering the $h-u$ plane. The 1-integral curve passing through $q_l$ is given by $u = u_l + 2(\\sqrt{gh_l}-\\sqrt{gh_r})$; it reaches a dry state $h=0$ at $u=u_l + 2\\sqrt{gh_l}$. Similarly, the 2-integral curve passing through $q_r$ reaches a dry state $h=0$ at $u=u_r - 2\\sqrt{gh_r}$. Thus the dry state solution is given by\n", "\n", "\\begin{align}\n", @@ -944,7 +954,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.13" + "version": "2.7.12" } }, "nbformat": 4, diff --git a/exact_solvers/shallow_water.py b/exact_solvers/shallow_water.py index d0f289b8..2fe0aa1c 100644 --- a/exact_solvers/shallow_water.py +++ b/exact_solvers/shallow_water.py @@ -13,13 +13,14 @@ def primitive_to_conservative(h, u): hu = h*u return h, hu - def conservative_to_primitive(h, hu): assert np.all(h>=0) # We should instead check that hu is zero everywhere that h is u = hu/pospart(h) return h, u +def cons_to_prim(q): + return conservative_to_primitive(*q) def exact_riemann_solution(q_l, q_r, grav=1., force_waves=None, primitive_inputs=False): """Return the exact solution to the Riemann problem with initial states q_l, q_r. @@ -173,18 +174,29 @@ def raref2(xi): speeds[1] = (ws[2],ws[3]) def reval(xi): + """ + Function that evaluates the Riemann solution for arbitrary xi = x/t. + Sets the solution to nan in an over-turning rarefaction wave + for illustration purposes of this non-physical solution. + """ rar1 = raref1(xi) rar2 = raref2(xi) h_out = (xi<=ws[0])*h_l + \ (xi>ws[0])*(xi<=ws[1])*rar1[0] + \ + (xi>ws[1])*(xi<=ws[0])*1e9 + \ (xi>ws[1])*(xi<=ws[2])*h_m + \ (xi>ws[2])*(xi<=ws[3])*rar2[0] + \ + (xi>ws[3])*(xi<=ws[2])*1e9 + \ (xi>ws[3])*h_r + h_out[h_out>1e8] = np.nan hu_out = (xi<=ws[0])*hu_l + \ (xi>ws[0])*(xi<=ws[1])*rar1[1] + \ + (xi>ws[1])*(xi<=ws[0])*1e9 + \ (xi>ws[1])*(xi<=ws[2])*hu_m + \ (xi>ws[2])*(xi<=ws[3])*rar2[1] + \ + (xi>ws[3])*(xi<=ws[2])*1e9 + \ (xi>ws[3])*hu_r + hu_out[hu_out>1e8] = np.nan return h_out, hu_out return states, speeds, reval, wave_types @@ -217,17 +229,18 @@ def hugoniot_locus(h, hstar, hustar, wave_family, g=1., y_axis='u'): d = np.sqrt(g*hstar*(1 + alpha/hstar)*(1 + alpha/(2*hstar))) if wave_family == 1: if y_axis == 'u': - return (hustar + alpha*(ustar - d))/h + return (hustar + alpha*(ustar - d))/pospart(h) else: return hustar + alpha*(ustar - d) else: if y_axis == 'u': - return (hustar + alpha*(ustar + d))/h + return (hustar + alpha*(ustar + d))/pospart(h) else: return hustar + alpha*(ustar + d) -def phase_plane_curves(hstar, hustar, state, wave_family='both', y_axis='u', ax=None): +def phase_plane_curves(hstar, hustar, state, g=1., wave_family='both', y_axis='u', ax=None, + plot_unphysical=False): """ Plot the curves of points in the h - u or h-hu phase plane that can be connected to (hstar,hustar). @@ -241,37 +254,37 @@ def phase_plane_curves(hstar, hustar, state, wave_family='both', y_axis='u', ax= h = np.linspace(0, hstar, 200) if wave_family in [1,'both']: - if state == 'qleft': - u = integral_curve(h, hstar, hustar, 1, y_axis=y_axis) + if state == 'qleft' or plot_unphysical: + u = integral_curve(h, hstar, hustar, 1, g, y_axis=y_axis) ax.plot(h,u,'b', label='1-rarefactions') - else: - u = hugoniot_locus(h, hstar, hustar, 1, y_axis=y_axis) + if state == 'qright' or plot_unphysical: + u = hugoniot_locus(h, hstar, hustar, 1, g, y_axis=y_axis) ax.plot(h,u,'--r', label='1-shocks') if wave_family in [2,'both']: - if state == 'qleft': - u = hugoniot_locus(h, hstar, hustar, 2, y_axis=y_axis) + if state == 'qleft' or plot_unphysical: + u = hugoniot_locus(h, hstar, hustar, 2, g, y_axis=y_axis) ax.plot(h,u,'--r', label='2-shocks') - else: - u = integral_curve(h, hstar, hustar, 2, y_axis=y_axis) + if state == 'qright' or plot_unphysical: + u = integral_curve(h, hstar, hustar, 2, g, y_axis=y_axis) ax.plot(h,u,'b', label='2-rarefactions') h = np.linspace(hstar, 3, 200) if wave_family in [1,'both']: - if state == 'qright': - u = integral_curve(h, hstar, hustar, 1, y_axis=y_axis) + if state == 'qright' or plot_unphysical: + u = integral_curve(h, hstar, hustar, 1, g, y_axis=y_axis) ax.plot(h,u,'--b', label='1-rarefactions') - else: - u = hugoniot_locus(h, hstar, hustar, 1, y_axis=y_axis) + if state == 'qleft' or plot_unphysical: + u = hugoniot_locus(h, hstar, hustar, 1, g, y_axis=y_axis) ax.plot(h,u,'r', label='1-shocks') if wave_family in [2,'both']: - if state == 'qright': - u = hugoniot_locus(h, hstar, hustar, 2, y_axis=y_axis) + if state == 'qright' or plot_unphysical: + u = hugoniot_locus(h, hstar, hustar, 2, g, y_axis=y_axis) ax.plot(h,u,'r', label='2-shocks') - else: - u = integral_curve(h, hstar, hustar, 2, y_axis=y_axis) + if state == 'qleft' or plot_unphysical: + u = integral_curve(h, hstar, hustar, 2, g, y_axis=y_axis) ax.plot(h,u,'--b', label='2-rarefactions') # plot and label the point (hstar, hustar) @@ -315,11 +328,11 @@ def phase_plane_plot(q_l, q_r, g=1., ax=None, force_waves=None, y_axis='u'): (dry_velocity_l+dry_velocity_r) xmax, xmin = max(x), min(x) - ymax, ymin = max(y), min(y) - dx, dy = xmax - xmin, ymax - ymin + ymax = max(abs(y)) + dx = xmax - xmin ymax = max(abs(y)) ax.set_xlim(0, xmax + 0.5*dx) - ax.set_ylim(-ymax - 0.5*dy, ymax + 0.5*dy) + ax.set_ylim(-1.5*ymax, 1.5*ymax) ax.set_xlabel('Depth (h)') if y_axis == 'u': ax.set_ylabel('Velocity (u)') @@ -359,7 +372,7 @@ def phase_plane_plot(q_l, q_r, g=1., ax=None, force_waves=None, y_axis='u'): ax.plot(xp,yp,'ok',markersize=10) # Label states for i,label in enumerate(('Left', 'Middle', 'Right')): - ax.text(x[i] + 0.025*dx,y[i] + 0.025*dy,label) + ax.text(x[i] + 0.025*dx,y[i] + 0.025*ymax,label) def plot_hugoniot_loci(plot_1=True,plot_2=False,y_axis='hu'): h = np.linspace(0.001,3,100) diff --git a/utils/riemann_tools.py b/utils/riemann_tools.py index 49c24161..8cb235b6 100644 --- a/utils/riemann_tools.py +++ b/utils/riemann_tools.py @@ -288,8 +288,8 @@ def plot_riemann(states, s, riemann_eval, wave_types=None, t=0.1, ax=None, for i in range(num_vars): ax[i+1].set_xlim((-1,1)) - qmax = q_sample[i][:].max() - qmin = q_sample[i][:].min() + qmax = max(np.nanmax(q_sample[i][:]), np.nanmax(states[i,:])) + qmin = min(np.nanmin(q_sample[i][:]), np.nanmin(states[i,:])) qdiff = qmax - qmin ax[i+1].set_xlim(-xmax,xmax) ax[i+1].set_ylim((qmin-0.1*qdiff,qmax+0.1*qdiff)) @@ -299,6 +299,19 @@ def plot_riemann(states, s, riemann_eval, wave_types=None, t=0.1, ax=None, ax[i+1].set_ylabel(variable_names[i]) x = np.linspace(-xmax,xmax,1000) + # Make sure we have a point between each pair of waves + # Important e.g. for nearly-pressureless gas + wavespeeds = [] + for speed in s: + from numbers import Number + if isinstance(speed, Number): + wavespeeds.append(speed) + else: + wavespeeds += speed + wavespeeds = np.array(wavespeeds) + xm = 0.5*(wavespeeds[1:]+wavespeeds[:-1])*t + iloc = np.searchsorted(x,xm) + x = np.insert(x, iloc, xm) if t == 0: q = riemann_eval(x/1e-10) @@ -309,7 +322,10 @@ def plot_riemann(states, s, riemann_eval, wave_types=None, t=0.1, ax=None, q = derived_variables(q) for i in range(num_vars): - ax[i+1].plot(x,q[i][:],'-k',lw=2) + if color == 'multi': + ax[i+1].plot(x,q[i][:],'-k',lw=2) + else: + ax[i+1].plot(x,q[i][:],'-',color=color,lw=2) if i in fill: ax[i+1].fill_between(x,q[i][:],color='b') ax[i+1].set_ybound(lower=0)