Failed to display Jupyter Widget of type interactive
.
\n", 413 | " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", 414 | " that the widgets JavaScript is still loading. If this message persists, it\n", 415 | " likely means that the widgets JavaScript library is either not installed or\n", 416 | " not enabled. See the Jupyter\n", 417 | " Widgets Documentation for setup instructions.\n", 418 | "
\n", 419 | "\n", 420 | " If you're reading this message in another frontend (for example, a static\n", 421 | " rendering on GitHub or NBViewer),\n", 422 | " it may mean that your frontend doesn't currently support widgets.\n", 423 | "
\n" 424 | ], 425 | "text/plain": [ 426 | "interactive(children=(FloatSlider(value=1.0, description='hl', max=10.0, min=0.1), FloatSlider(value=0.0, description='ul', max=1.0, min=-1.0), Output()), _dom_classes=('widget-interact',))" 427 | ] 428 | }, 429 | "metadata": {}, 430 | "output_type": "display_data" 431 | } 432 | ], 433 | "source": [ 434 | "from ipywidgets import interactive, FloatSlider\n", 435 | "\n", 436 | "interactive(plot_sw_rarefaction, \n", 437 | " hl = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 438 | " ul = FloatSlider(min = -1.0, max = 1.0, value = 0.0))" 439 | ] 440 | }, 441 | { 442 | "cell_type": "markdown", 443 | "metadata": {}, 444 | "source": [ 445 | "There is a problem with this: we haven't checked if the states on the curve can be *physically* connected to this point. That is, we haven't checked how the characteristic speed changes along the curve.\n", 446 | "\n", 447 | "Here it is obvious: we know that the characteristics must spread across the rarefaction, so $\\lambda$ must increase, and as $\\xi = \\lambda$ we must have the characteristic coordinate increasing." 448 | ] 449 | }, 450 | { 451 | "cell_type": "code", 452 | "execution_count": 5, 453 | "metadata": {}, 454 | "outputs": [], 455 | "source": [ 456 | "def plot_sw_rarefaction_physical(hl, ul):\n", 457 | " \"Plot the rarefaction curve through the state (hl, ul)\"\n", 458 | " \n", 459 | " xil = ul - np.sqrt(hl)\n", 460 | " xi_physical = np.linspace(xil, xi_max)\n", 461 | " xi_unphysical = np.linspace(xi_min, xil)\n", 462 | " h_physical = ((xil - xi_physical) / 3.0 + np.sqrt(hl))**2\n", 463 | " u_physical = 2.0 * (xi_physical - xil) / 3.0 + ul\n", 464 | " h_unphysical = ((xil - xi_unphysical) / 3.0 + np.sqrt(hl))**2\n", 465 | " u_unphysical = 2.0 * (xi_unphysical - xil) / 3.0 + ul\n", 466 | " \n", 467 | " \n", 468 | " fig = plt.figure(figsize=(12,8))\n", 469 | " ax = fig.add_subplot(111)\n", 470 | " ax.plot(hl, ul, 'rx', markersize = 16, markeredgewidth = 3)\n", 471 | " ax.plot(h_physical, u_physical, 'k-', linewidth = 2, label=\"Physical\")\n", 472 | " ax.plot(h_unphysical, u_unphysical, 'k--', linewidth = 2, label=\"Unphysical\")\n", 473 | " ax.set_xlabel(r\"$h$\")\n", 474 | " ax.set_ylabel(r\"$u$\")\n", 475 | " dh = h_max - h_min\n", 476 | " du = u_max - u_min\n", 477 | " ax.set_xbound(h_min - 0.1 * dh, h_max + 0.1 * dh)\n", 478 | " ax.set_ybound(u_min - 0.1 * du, u_max + 0.1 * du)\n", 479 | " ax.legend()\n", 480 | " fig.tight_layout()" 481 | ] 482 | }, 483 | { 484 | "cell_type": "code", 485 | "execution_count": 6, 486 | "metadata": {}, 487 | "outputs": [ 488 | { 489 | "data": { 490 | "application/vnd.jupyter.widget-view+json": { 491 | "model_id": "ee72c0ff03984da2bd2de728435f057a", 492 | "version_major": 2, 493 | "version_minor": 0 494 | }, 495 | "text/html": [ 496 | "Failed to display Jupyter Widget of type interactive
.
\n", 498 | " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", 499 | " that the widgets JavaScript is still loading. If this message persists, it\n", 500 | " likely means that the widgets JavaScript library is either not installed or\n", 501 | " not enabled. See the Jupyter\n", 502 | " Widgets Documentation for setup instructions.\n", 503 | "
\n", 504 | "\n", 505 | " If you're reading this message in another frontend (for example, a static\n", 506 | " rendering on GitHub or NBViewer),\n", 507 | " it may mean that your frontend doesn't currently support widgets.\n", 508 | "
\n" 509 | ], 510 | "text/plain": [ 511 | "interactive(children=(FloatSlider(value=1.0, description='hl', max=10.0, min=0.1), FloatSlider(value=0.0, description='ul', max=1.0, min=-1.0), Output()), _dom_classes=('widget-interact',))" 512 | ] 513 | }, 514 | "metadata": {}, 515 | "output_type": "display_data" 516 | } 517 | ], 518 | "source": [ 519 | "interactive(plot_sw_rarefaction_physical, \n", 520 | " hl = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 521 | " ul = FloatSlider(min = -1.0, max = 1.0, value = 0.0))" 522 | ] 523 | }, 524 | { 525 | "cell_type": "markdown", 526 | "metadata": {}, 527 | "source": [ 528 | "We see that along the physical part of the rarefaction curve the height $h$ decreases." 529 | ] 530 | }, 531 | { 532 | "cell_type": "markdown", 533 | "metadata": {}, 534 | "source": [ 535 | "Instead of writing the solution in terms of the similarity coordinate $\\xi$ we can instead write the solution in terms of any other single parameter. It is useful to write it in terms of the height, which can be done simply by re-arranging the equations giving $u$ and $h$ in terms of $\\xi$. So, a state with height $h_m$ to the right of the state $(h_l, u_l)$ can be connected across a rarefaction if\n", 536 | "$$\n", 537 | " u_m = u_l + 2 \\left( \\sqrt{h_l} - \\sqrt{h_m} \\right).\n", 538 | "$$" 539 | ] 540 | }, 541 | { 542 | "cell_type": "markdown", 543 | "metadata": {}, 544 | "source": [ 545 | "In this form we will look at the characteristic curves and the behaviour in state space to cross-check." 546 | ] 547 | }, 548 | { 549 | "cell_type": "code", 550 | "execution_count": 7, 551 | "metadata": {}, 552 | "outputs": [], 553 | "source": [ 554 | "def plot_sw_rarefaction_physical_characteristics(hl, ul, hm):\n", 555 | " \"Plot the rarefaction curve through the state (hl, ul) finishing at (hm, um)\"\n", 556 | " \n", 557 | " um = ul + 2.0 * (np.sqrt(hl) - np.sqrt(hm))\n", 558 | " \n", 559 | " h_maximum = np.max([h_max, hl, hm])\n", 560 | " h_minimum = np.min([h_min, hl, hm])\n", 561 | " u_maximum = np.max([u_max, ul, um])\n", 562 | " u_minimum = np.min([u_min, ul, um])\n", 563 | " dh = h_maximum - h_minimum\n", 564 | " du = u_maximum - u_minimum\n", 565 | " xi_min = u_minimum - np.sqrt(h_maximum)\n", 566 | " xi_max = u_maximum - np.sqrt(h_minimum)\n", 567 | " \n", 568 | " xil = ul - np.sqrt(hl)\n", 569 | " xim = um - np.sqrt(hm)\n", 570 | " xi_physical = np.linspace(xil, xi_max)\n", 571 | " xi_unphysical = np.linspace(xi_min, xil)\n", 572 | " h_physical = ((xil - xi_physical) / 3.0 + np.sqrt(hl))**2\n", 573 | " u_physical = 2.0 * (xi_physical - xil) / 3.0 + ul\n", 574 | " h_unphysical = ((xil - xi_unphysical) / 3.0 + np.sqrt(hl))**2\n", 575 | " u_unphysical = 2.0 * (xi_unphysical - xil) / 3.0 + ul\n", 576 | " \n", 577 | " \n", 578 | " fig = plt.figure(figsize=(12,8))\n", 579 | " ax1 = fig.add_subplot(121)\n", 580 | " ax1.plot(hl, ul, 'rx', markersize = 16, markeredgewidth = 3, label=r\"$(h_l, u_l)$\")\n", 581 | " ax1.plot(hm, um, 'b+', markersize = 16, markeredgewidth = 3, label=r\"$(h_m, u_m)$\")\n", 582 | " ax1.plot(h_physical, u_physical, 'k-', linewidth = 2, label=\"Physical\")\n", 583 | " ax1.plot(h_unphysical, u_unphysical, 'k--', linewidth = 2, label=\"Unphysical\")\n", 584 | " ax1.set_xlabel(r\"$h$\")\n", 585 | " ax1.set_ylabel(r\"$u$\")\n", 586 | " ax1.set_xbound(h_minimum - 0.1 * dh, h_maximum + 0.1 * dh)\n", 587 | " ax1.set_ybound(u_minimum - 0.1 * du, u_maximum + 0.1 * du)\n", 588 | " ax1.legend()\n", 589 | " \n", 590 | " ax2 = fig.add_subplot(122)\n", 591 | " left_edge = np.min([-1.0, -1.0 - xil])\n", 592 | " right_edge = np.max([1.0, 1.0 - xim])\n", 593 | " x_start_points_l = np.linspace(left_edge, 0.0, 20)\n", 594 | " x_start_points_r = np.linspace(0.0, right_edge, 20)\n", 595 | " x_end_points_l = x_start_points_l + xil\n", 596 | " x_end_points_r = x_start_points_r + xim\n", 597 | " \n", 598 | " for xs, xe in zip(x_start_points_l, x_end_points_l):\n", 599 | " ax2.plot([xs, xe], [0.0, 1.0], 'b-')\n", 600 | " for xs, xe in zip(x_start_points_r, x_end_points_r):\n", 601 | " ax2.plot([xs, xe], [0.0, 1.0], 'g-')\n", 602 | " \n", 603 | " # Rarefaction wave\n", 604 | " if (xim > xil):\n", 605 | " xi = np.linspace(xil, xim, 11)\n", 606 | " x_end_rarefaction = xi\n", 607 | " for xe in x_end_rarefaction:\n", 608 | " ax2.plot([0.0, xe], [0.0, 1.0], 'r--')\n", 609 | " else:\n", 610 | " x_fill = [x_end_points_l[-1], x_start_points_l[-1], x_end_points_r[0]]\n", 611 | " t_fill = [1.0, 0.0, 1.0]\n", 612 | " ax2.fill_between(x_fill, t_fill, 1.0, facecolor = 'red', alpha = 0.5)\n", 613 | " \n", 614 | " ax2.set_xbound(-1.0, 1.0)\n", 615 | " ax2.set_ybound(0.0, 1.0)\n", 616 | " ax2.set_xlabel(r\"$x$\")\n", 617 | " ax2.set_ylabel(r\"$t$\")\n", 618 | " fig.tight_layout()" 619 | ] 620 | }, 621 | { 622 | "cell_type": "code", 623 | "execution_count": 8, 624 | "metadata": {}, 625 | "outputs": [ 626 | { 627 | "data": { 628 | "application/vnd.jupyter.widget-view+json": { 629 | "model_id": "f7624fe13abb44eabcf433104b4b4f2f", 630 | "version_major": 2, 631 | "version_minor": 0 632 | }, 633 | "text/html": [ 634 | "Failed to display Jupyter Widget of type interactive
.
\n", 636 | " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", 637 | " that the widgets JavaScript is still loading. If this message persists, it\n", 638 | " likely means that the widgets JavaScript library is either not installed or\n", 639 | " not enabled. See the Jupyter\n", 640 | " Widgets Documentation for setup instructions.\n", 641 | "
\n", 642 | "\n", 643 | " If you're reading this message in another frontend (for example, a static\n", 644 | " rendering on GitHub or NBViewer),\n", 645 | " it may mean that your frontend doesn't currently support widgets.\n", 646 | "
\n" 647 | ], 648 | "text/plain": [ 649 | "interactive(children=(FloatSlider(value=1.0, description='hl', max=10.0, min=0.1), FloatSlider(value=0.0, description='ul', max=1.0, min=-1.0), FloatSlider(value=0.5, description='hm', max=10.0, min=0.1), Output()), _dom_classes=('widget-interact',))" 650 | ] 651 | }, 652 | "metadata": {}, 653 | "output_type": "display_data" 654 | } 655 | ], 656 | "source": [ 657 | "interactive(plot_sw_rarefaction_physical_characteristics, \n", 658 | " hl = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 659 | " ul = FloatSlider(min = -1.0, max = 1.0, value = 0.0), \n", 660 | " hm = FloatSlider(min = 0.1, max = 10.0, value = 0.5))" 661 | ] 662 | }, 663 | { 664 | "cell_type": "markdown", 665 | "metadata": {}, 666 | "source": [ 667 | "We clearly see that only if $h_m < h_l$ do the characteristics spread as they should for a rarefaction. This is, in fact, already given by results above: we showed that $\\partial_{\\xi} h \\propto -\\sqrt{h}$. As the height $h$ is positive, this means that as $\\xi$ increase across the rarefaction, the height must decrease." 668 | ] 669 | }, 670 | { 671 | "cell_type": "markdown", 672 | "metadata": {}, 673 | "source": [ 674 | "## All rarefaction solution" 675 | ] 676 | }, 677 | { 678 | "cell_type": "markdown", 679 | "metadata": {}, 680 | "source": [ 681 | "The above exercise assumed we knew the left state and found all right states connecting it by a rarefaction. Now we assume we know both left *and* right states, and assume they connect to a central state, *both* along rarefactions." 682 | ] 683 | }, 684 | { 685 | "cell_type": "markdown", 686 | "metadata": {}, 687 | "source": [ 688 | "First, we need to find which states will connect to the right state across a rarefaction." 689 | ] 690 | }, 691 | { 692 | "cell_type": "markdown", 693 | "metadata": {}, 694 | "source": [ 695 | "### Exercise" 696 | ] 697 | }, 698 | { 699 | "cell_type": "markdown", 700 | "metadata": {}, 701 | "source": [ 702 | "Repeat the above calculations for states connecting to a known right state. That is, show that, given the right state $(h_r, u_r)$, the left state that connects to it across a rarefaction satisfies\n", 703 | "$$\n", 704 | " \\begin{pmatrix} h \\\\ u \\end{pmatrix} = \\begin{pmatrix} \\left( -\\frac{\\xi_r - \\xi}{3} + \\sqrt{h_r} \\right)^2 \\\\ \\frac{2}{3} (\\xi - \\xi_r) + u_r \\end{pmatrix}. \n", 705 | "$$\n", 706 | "or equivalently, given $h_m$, that\n", 707 | "$$\n", 708 | " u_m = u_r - 2 \\left( \\sqrt{h_r} - \\sqrt{h_m} \\right).\n", 709 | "$$\n", 710 | "Also check that $h$ decreases across the rarefaction, so for a physical solution $h_m < h_r$." 711 | ] 712 | }, 713 | { 714 | "cell_type": "markdown", 715 | "metadata": {}, 716 | "source": [ 717 | "Then we can plot the curve of all states that can be connected to $(h_l, u_l)$ across a left rarefaction, and the curve of all states that can be connected to $(h_r, u_r)$ across a right rarefaction. *If* they intersect along the *physical* part of the curve, then we have the solution to the Riemann problem. Clearly this only occurs if $h_m < h_l$ *and* $h_m < h_r$." 718 | ] 719 | }, 720 | { 721 | "cell_type": "markdown", 722 | "metadata": {}, 723 | "source": [ 724 | "In this case (and note that this is a special case!) we can solve it analytically. We note that, using our *assumption* that both curves are rarefactions, we have that\n", 725 | "$$\n", 726 | "\\begin{align}\n", 727 | " u_m & = u_l + 2 \\left( \\sqrt{h_l} - \\sqrt{h_m} \\right) \\\\\n", 728 | " & = u_r - 2 \\left( \\sqrt{h_r} - \\sqrt{h_m} \\right)\n", 729 | "\\end{align}\n", 730 | "$$\n", 731 | "Therefore we have\n", 732 | "$$\n", 733 | " h_m = \\frac{1}{16} \\left( u_l - u_r + 2 \\left( \\sqrt{h_l} + \\sqrt{h_r} \\right) \\right)^2.\n", 734 | "$$" 735 | ] 736 | }, 737 | { 738 | "cell_type": "code", 739 | "execution_count": 9, 740 | "metadata": {}, 741 | "outputs": [], 742 | "source": [ 743 | "def plot_sw_all_rarefaction(hl, ul, hr, ur):\n", 744 | " \"Plot the all rarefaction solution curve for states (hl, ul) and (hr, ur)\"\n", 745 | " \n", 746 | " hm = (ul - ur + 2.0 * (np.sqrt(hl) + np.sqrt(hr)))**2 / 16.0\n", 747 | " um = ul + 2.0 * (np.sqrt(hl) - np.sqrt(hm))\n", 748 | " \n", 749 | " h_maximum = np.max([h_max, hl, hr, hm])\n", 750 | " h_minimum = np.min([h_min, hl, hr, hm])\n", 751 | " u_maximum = np.max([u_max, ul, ur, um])\n", 752 | " u_minimum = np.min([u_min, ul, ur, um])\n", 753 | " dh = h_maximum - h_minimum\n", 754 | " du = u_maximum - u_minimum\n", 755 | " xil_min = u_minimum - np.sqrt(h_maximum)\n", 756 | " xil_max = u_maximum - np.sqrt(h_minimum)\n", 757 | " xir_min = u_minimum + np.sqrt(h_minimum)\n", 758 | " xir_max = u_maximum + np.sqrt(h_maximum)\n", 759 | " \n", 760 | " xil = ul - np.sqrt(hl)\n", 761 | " xilm = um - np.sqrt(hm)\n", 762 | " xil_physical = np.linspace(xil, xil_max)\n", 763 | " xil_unphysical = np.linspace(xil_min, xil)\n", 764 | " hl_physical = ((xil - xil_physical) / 3.0 + np.sqrt(hl))**2\n", 765 | " ul_physical = 2.0 * (xil_physical - xil) / 3.0 + ul\n", 766 | " hl_unphysical = ((xil - xil_unphysical) / 3.0 + np.sqrt(hl))**2\n", 767 | " ul_unphysical = 2.0 * (xil_unphysical - xil) / 3.0 + ul\n", 768 | " \n", 769 | " xir = ur + np.sqrt(hr)\n", 770 | " xirm = um + np.sqrt(hm)\n", 771 | " xir_unphysical = np.linspace(xir, xir_max)\n", 772 | " xir_physical = np.linspace(xir_min, xir)\n", 773 | " hr_physical = (-(xir - xir_physical) / 3.0 + np.sqrt(hr))**2\n", 774 | " ur_physical = 2.0 * (xir_physical - xir) / 3.0 + ur\n", 775 | " hr_unphysical = (-(xir - xir_unphysical) / 3.0 + np.sqrt(hr))**2\n", 776 | " ur_unphysical = 2.0 * (xir_unphysical - xir) / 3.0 + ur\n", 777 | " \n", 778 | " fig = plt.figure(figsize=(12,8))\n", 779 | " ax1 = fig.add_subplot(111)\n", 780 | " if (hm < np.min([hl, hr])):\n", 781 | " ax1.plot(hm, um, 'b+', markersize = 16, markeredgewidth = 3, \n", 782 | " label=r\"$(h_m, u_m)$, physical solution\")\n", 783 | " else:\n", 784 | " ax1.plot(hm, um, 'b+', markersize = 16, markeredgewidth = 3, \n", 785 | " label=r\"$(h_m, u_m)$, not physical solution\")\n", 786 | " ax1.plot(hl, ul, 'rx', markersize = 16, markeredgewidth = 3, label=r\"$(h_l, u_l)$\")\n", 787 | " ax1.plot(hr, ur, 'go', markersize = 16, markeredgewidth = 3, label=r\"$(h_r, u_r)$\")\n", 788 | " ax1.plot(hl_physical, ul_physical, 'k-', linewidth = 2, label=\"Physical (left)\")\n", 789 | " ax1.plot(hl_unphysical, ul_unphysical, 'k--', linewidth = 2, label=\"Unphysical (left)\")\n", 790 | " ax1.plot(hr_physical, ur_physical, 'c-', linewidth = 2, label=\"Physical (right)\")\n", 791 | " ax1.plot(hr_unphysical, ur_unphysical, 'c--', linewidth = 2, label=\"Unphysical (right)\")\n", 792 | " ax1.set_xlabel(r\"$h$\")\n", 793 | " ax1.set_ylabel(r\"$u$\")\n", 794 | " ax1.set_xbound(h_minimum - 0.1 * dh, h_maximum + 0.1 * dh)\n", 795 | " ax1.set_ybound(u_minimum - 0.1 * du, u_maximum + 0.1 * du)\n", 796 | " ax1.legend()\n", 797 | " \n", 798 | " fig.tight_layout()" 799 | ] 800 | }, 801 | { 802 | "cell_type": "code", 803 | "execution_count": 10, 804 | "metadata": {}, 805 | "outputs": [ 806 | { 807 | "data": { 808 | "application/vnd.jupyter.widget-view+json": { 809 | "model_id": "36712c27d2a84211a9e2d7bccb645b21", 810 | "version_major": 2, 811 | "version_minor": 0 812 | }, 813 | "text/html": [ 814 | "Failed to display Jupyter Widget of type interactive
.
\n", 816 | " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", 817 | " that the widgets JavaScript is still loading. If this message persists, it\n", 818 | " likely means that the widgets JavaScript library is either not installed or\n", 819 | " not enabled. See the Jupyter\n", 820 | " Widgets Documentation for setup instructions.\n", 821 | "
\n", 822 | "\n", 823 | " If you're reading this message in another frontend (for example, a static\n", 824 | " rendering on GitHub or NBViewer),\n", 825 | " it may mean that your frontend doesn't currently support widgets.\n", 826 | "
\n" 827 | ], 828 | "text/plain": [ 829 | "interactive(children=(FloatSlider(value=1.0, description='hl', max=10.0, min=0.1), FloatSlider(value=-0.5, description='ul', max=1.0, min=-1.0), FloatSlider(value=1.0, description='hr', max=10.0, min=0.1), FloatSlider(value=0.5, description='ur', max=1.0, min=-1.0), Output()), _dom_classes=('widget-interact',))" 830 | ] 831 | }, 832 | "metadata": {}, 833 | "output_type": "display_data" 834 | } 835 | ], 836 | "source": [ 837 | "interactive(plot_sw_all_rarefaction, \n", 838 | " hl = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 839 | " ul = FloatSlider(min = -1.0, max = 1.0, value = -0.5), \n", 840 | " hr = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 841 | " ur = FloatSlider(min = -1.0, max = 1.0, value = 0.5))" 842 | ] 843 | }, 844 | { 845 | "cell_type": "markdown", 846 | "metadata": {}, 847 | "source": [ 848 | "Given the central state and the relation along rarefaction curves, we can then construct the characteristics and the solution in terms of the similarity coordinate (which, given a time $t$, gives the solution as a function of $x$)." 849 | ] 850 | }, 851 | { 852 | "cell_type": "code", 853 | "execution_count": 11, 854 | "metadata": {}, 855 | "outputs": [], 856 | "source": [ 857 | "def plot_sw_all_rarefaction_solution(hl, ul, hr, ur):\n", 858 | " \"Plot the all rarefaction solution curve for states (hl, ul) and (hr, ur)\"\n", 859 | " \n", 860 | " hm = (ul - ur + 2.0 * (np.sqrt(hl) + np.sqrt(hr)))**2 / 16.0\n", 861 | " um = ul + 2.0 * (np.sqrt(hl) - np.sqrt(hm))\n", 862 | " \n", 863 | " xi1l = ul - np.sqrt(hl)\n", 864 | " xi1m = um - np.sqrt(hm)\n", 865 | " xi1r = ur - np.sqrt(hr)\n", 866 | " hl_raref = np.linspace(hl, hm, 20)\n", 867 | " ul_raref = ul + 2.0 * (np.sqrt(hl) - np.sqrt(hl_raref))\n", 868 | " xil_raref = ul_raref - np.sqrt(hl_raref)\n", 869 | " \n", 870 | " xi2r = ur + np.sqrt(hr)\n", 871 | " xi2m = um + np.sqrt(hm)\n", 872 | " xi2l = ul + np.sqrt(hl)\n", 873 | " hr_raref = np.linspace(hm, hr)\n", 874 | " ur_raref = ur - 2.0 * (np.sqrt(hr) - np.sqrt(hr_raref))\n", 875 | " xir_raref = ur_raref + np.sqrt(hr_raref)\n", 876 | " \n", 877 | " xi_min = np.min([-1.0, xi1l, xi1m, xi2r, xi2m])\n", 878 | " xi_max = np.max([1.0, xi1l, xi1m, xi2r, xi2m])\n", 879 | " d_xi = xi_max - xi_min\n", 880 | " h_max = np.max([hl, hr, hm])\n", 881 | " h_min = np.min([hl, hr, hm])\n", 882 | " d_h = h_max - h_min\n", 883 | " u_max = np.max([ul, ur, um])\n", 884 | " u_min = np.min([ul, ur, um])\n", 885 | " d_u = u_max - u_min\n", 886 | " \n", 887 | " xi = np.array([xi_min - 0.1 * d_xi, xi1l])\n", 888 | " h = np.array([hl, hl])\n", 889 | " u = np.array([ul, ul])\n", 890 | " xi = np.append(xi, xil_raref)\n", 891 | " h = np.append(h, hl_raref)\n", 892 | " u = np.append(u, ul_raref)\n", 893 | " xi = np.append(xi, [xi1m, xi2m])\n", 894 | " h = np.append(h, [hm, hm])\n", 895 | " u = np.append(u, [um, um])\n", 896 | " xi = np.append(xi, xir_raref)\n", 897 | " h = np.append(h, hr_raref)\n", 898 | " u = np.append(u, ur_raref)\n", 899 | " xi = np.append(xi, [xi2r, xi_max + 0.1 * d_xi])\n", 900 | " h = np.append(h, [hr, hr])\n", 901 | " u = np.append(u, [ur, ur])\n", 902 | " \n", 903 | " fig = plt.figure(figsize=(12,8))\n", 904 | " ax1 = fig.add_subplot(221)\n", 905 | " if (hm < np.min([hl, hr])):\n", 906 | " ax1.plot(xi, h, 'b-', label = \"Physical solution\")\n", 907 | " else:\n", 908 | " ax1.plot(xi, h, 'r--', label = \"Unphysical solution\")\n", 909 | " ax1.set_ybound(h_min - 0.1 * d_h, h_max + 0.1 * d_h)\n", 910 | " ax1.set_xlabel(r\"$\\xi$\")\n", 911 | " ax1.set_ylabel(r\"$h$\")\n", 912 | " ax1.legend()\n", 913 | " ax2 = fig.add_subplot(222)\n", 914 | " if (hm < np.min([hl, hr])):\n", 915 | " ax2.plot(xi, u, 'b-', label = \"Physical solution\")\n", 916 | " else:\n", 917 | " ax2.plot(xi, u, 'r--', label = \"Unphysical solution\")\n", 918 | " ax2.set_ybound(u_min - 0.1 * d_u, u_max + 0.1 * d_u)\n", 919 | " ax2.set_xlabel(r\"$\\xi$\")\n", 920 | " ax2.set_ylabel(r\"$u$\")\n", 921 | " ax2.legend()\n", 922 | " \n", 923 | " ax3 = fig.add_subplot(223)\n", 924 | " left_end = np.min([-1.0, 1.1*xi1l])\n", 925 | " right_end = np.max([1.0, 1.1*xi2r])\n", 926 | " left_edge = left_end - xi1l\n", 927 | " right_edge = right_end - xi1r\n", 928 | " x1_start_points_l = np.linspace(np.min([left_edge, left_end]), 0.0, 20)\n", 929 | " x1_start_points_r = np.linspace(0.0, np.max([right_edge, right_end]), 20)\n", 930 | " x1_end_points_l = x1_start_points_l + xi1l\n", 931 | " t1_end_points_r = np.ones_like(x1_start_points_r)\n", 932 | " \n", 933 | " # Look for intersections\n", 934 | " t1_end_points_r = np.minimum(t1_end_points_r, x1_start_points_r / (xi2r - xi1r))\n", 935 | " x1_end_points_r = x1_start_points_r + xi1r * t1_end_points_r\n", 936 | " # Note: here we are cheating, and using the characteristic speed of the middle state, \n", 937 | " # ignoring howo it varies across the rarefaction\n", 938 | " x1_final_points_r = x1_end_points_r + (1.0 - t1_end_points_r) * xi1m\n", 939 | " \n", 940 | " for xs, xe in zip(x1_start_points_l, x1_end_points_l):\n", 941 | " ax3.plot([xs, xe], [0.0, 1.0], 'b-')\n", 942 | " for xs, xe, te in zip(x1_start_points_r, x1_end_points_r, t1_end_points_r):\n", 943 | " ax3.plot([xs, xe], [0.0, te], 'g-')\n", 944 | " for xs, xe, ts in zip(x1_end_points_r, x1_final_points_r, t1_end_points_r):\n", 945 | " ax3.plot([xs, xe], [ts, 1.0], 'g-')\n", 946 | " \n", 947 | " # Highlight the edges of both rarefactions\n", 948 | " ax3.plot([0.0, xi1l], [0.0, 1.0], 'r-', linewidth=2)\n", 949 | " ax3.plot([0.0, xi1m], [0.0, 1.0], 'r-', linewidth=2)\n", 950 | " ax3.plot([0.0, xi2m], [0.0, 1.0], 'r-', linewidth=2)\n", 951 | " ax3.plot([0.0, xi2r], [0.0, 1.0], 'r-', linewidth=2)\n", 952 | " \n", 953 | " # Rarefaction wave\n", 954 | " if (xi1l < xi1m):\n", 955 | " xi = np.linspace(xi1l, xi1m, 11)\n", 956 | " x_end_rarefaction = xi\n", 957 | " for xe in x_end_rarefaction:\n", 958 | " ax3.plot([0.0, xe], [0.0, 1.0], 'r--')\n", 959 | " else:\n", 960 | " x_fill = [xi1l, 0.0, xi1m]\n", 961 | " t_fill = [1.0, 0.0, 1.0]\n", 962 | " ax3.fill_between(x_fill, t_fill, 1.0, facecolor = 'red', alpha = 0.5)\n", 963 | " \n", 964 | " ax3.set_xlabel(r\"$x$\")\n", 965 | " ax3.set_ylabel(r\"$t$\")\n", 966 | " ax3.set_title(\"1-characteristics\")\n", 967 | " ax3.set_xbound(left_end, right_end)\n", 968 | " \n", 969 | " ax4 = fig.add_subplot(224)\n", 970 | " left_end = np.min([-1.0, 1.1*xi1l])\n", 971 | " right_end = np.max([1.0, 1.1*xi2r])\n", 972 | " left_edge = left_end - xi2l\n", 973 | " right_edge = right_end - xi2r\n", 974 | " x2_start_points_l = np.linspace(np.min([left_edge, left_end]), 0.0, 20)\n", 975 | " x2_start_points_r = np.linspace(0.0, np.max([right_edge, right_end]), 20)\n", 976 | " x2_end_points_r = x2_start_points_r + xi2r\n", 977 | " t2_end_points_l = np.ones_like(x2_start_points_l)\n", 978 | " \n", 979 | " # Look for intersections\n", 980 | " t2_end_points_l = np.minimum(t2_end_points_l, x2_start_points_l / (xi1l - xi2r))\n", 981 | " x2_end_points_l = x2_start_points_l + xi2r * t2_end_points_l\n", 982 | " # Note: here we are cheating, and using the characteristic speed of the middle state, \n", 983 | " # ignoring howo it varies across the rarefaction\n", 984 | " x2_final_points_l = x2_end_points_l + (1.0 - t2_end_points_l) * xi2m\n", 985 | " \n", 986 | " for xs, xe in zip(x2_start_points_r, x2_end_points_r):\n", 987 | " ax4.plot([xs, xe], [0.0, 1.0], 'g-')\n", 988 | " for xs, xe, te in zip(x2_start_points_l, x2_end_points_l, t2_end_points_l):\n", 989 | " ax4.plot([xs, xe], [0.0, te], 'b-')\n", 990 | " for xs, xe, ts in zip(x2_end_points_l, x2_final_points_l, t2_end_points_l):\n", 991 | " ax4.plot([xs, xe], [ts, 1.0], 'b-')\n", 992 | " \n", 993 | " # Highlight the edges of both rarefactions\n", 994 | " ax4.plot([0.0, xi1l], [0.0, 1.0], 'r-', linewidth=2)\n", 995 | " ax4.plot([0.0, xi1m], [0.0, 1.0], 'r-', linewidth=2)\n", 996 | " ax4.plot([0.0, xi2m], [0.0, 1.0], 'r-', linewidth=2)\n", 997 | " ax4.plot([0.0, xi2r], [0.0, 1.0], 'r-', linewidth=2)\n", 998 | " \n", 999 | " # Rarefaction wave\n", 1000 | " if (xi2r > xi2m):\n", 1001 | " xi = np.linspace(xi2m, xi2r, 11)\n", 1002 | " x_end_rarefaction = xi\n", 1003 | " for xe in x_end_rarefaction:\n", 1004 | " ax4.plot([0.0, xe], [0.0, 1.0], 'r--')\n", 1005 | " else:\n", 1006 | " x_fill = [xi2m, 0.0, xi2r]\n", 1007 | " t_fill = [1.0, 0.0, 1.0]\n", 1008 | " ax4.fill_between(x_fill, t_fill, 1.0, facecolor = 'red', alpha = 0.5)\n", 1009 | " \n", 1010 | " ax4.set_xlabel(r\"$x$\")\n", 1011 | " ax4.set_ylabel(r\"$t$\")\n", 1012 | " ax4.set_title(\"2-characteristics\")\n", 1013 | " ax4.set_xbound(left_end, right_end)\n", 1014 | " \n", 1015 | " fig.tight_layout()" 1016 | ] 1017 | }, 1018 | { 1019 | "cell_type": "code", 1020 | "execution_count": 12, 1021 | "metadata": {}, 1022 | "outputs": [ 1023 | { 1024 | "data": { 1025 | "application/vnd.jupyter.widget-view+json": { 1026 | "model_id": "839f9bd0806e4e1d9ab0622808b8eec3", 1027 | "version_major": 2, 1028 | "version_minor": 0 1029 | }, 1030 | "text/html": [ 1031 | "Failed to display Jupyter Widget of type interactive
.
\n", 1033 | " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", 1034 | " that the widgets JavaScript is still loading. If this message persists, it\n", 1035 | " likely means that the widgets JavaScript library is either not installed or\n", 1036 | " not enabled. See the Jupyter\n", 1037 | " Widgets Documentation for setup instructions.\n", 1038 | "
\n", 1039 | "\n", 1040 | " If you're reading this message in another frontend (for example, a static\n", 1041 | " rendering on GitHub or NBViewer),\n", 1042 | " it may mean that your frontend doesn't currently support widgets.\n", 1043 | "
\n" 1044 | ], 1045 | "text/plain": [ 1046 | "interactive(children=(FloatSlider(value=1.0, description='hl', max=10.0, min=0.1), FloatSlider(value=-0.5, description='ul', max=1.0, min=-1.0), FloatSlider(value=1.0, description='hr', max=10.0, min=0.1), FloatSlider(value=0.5, description='ur', max=1.0, min=-1.0), Output()), _dom_classes=('widget-interact',))" 1047 | ] 1048 | }, 1049 | "metadata": {}, 1050 | "output_type": "display_data" 1051 | } 1052 | ], 1053 | "source": [ 1054 | "interactive(plot_sw_all_rarefaction_solution, \n", 1055 | " hl = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 1056 | " ul = FloatSlider(min = -1.0, max = 1.0, value = -0.5), \n", 1057 | " hr = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 1058 | " ur = FloatSlider(min = -1.0, max = 1.0, value = 0.5))" 1059 | ] 1060 | }, 1061 | { 1062 | "cell_type": "markdown", 1063 | "metadata": {}, 1064 | "source": [ 1065 | "## Shocks" 1066 | ] 1067 | }, 1068 | { 1069 | "cell_type": "markdown", 1070 | "metadata": {}, 1071 | "source": [ 1072 | "We note that the [general theory](Lesson_Theory.ipynb) tells us that across a shock the Rankine-Hugoniot conditions\n", 1073 | "$$\n", 1074 | " V_s \\left[ {\\bf q} \\right] = \\left[ {\\bf f}({\\bf q}) \\right]\n", 1075 | "$$\n", 1076 | "must be satisfied." 1077 | ] 1078 | }, 1079 | { 1080 | "cell_type": "markdown", 1081 | "metadata": {}, 1082 | "source": [ 1083 | "For the shallow water equations we will start, as with the rarefaction case, by assuming we know the left state ${\\bf q}_l = (h_l, u_l)$, and work out which states ${\\bf q}_m$ can be connected to it across a shock. \n", 1084 | "\n", 1085 | "Note here that the procedure is *identical* for the right state as the direction does not matter. However, there will be multiple solutions, and checking which is physically correct does require checking whether the left or the right state is known" 1086 | ] 1087 | }, 1088 | { 1089 | "cell_type": "markdown", 1090 | "metadata": {}, 1091 | "source": [ 1092 | "Writing out the conditions in full we see that\n", 1093 | "$$\n", 1094 | "\\begin{align}\n", 1095 | " V_s \\left( h_m - h_l \\right) & = h_m u_m - h_l u_l \\\\\n", 1096 | " V_s \\left( h_m u_m - h_l u_l \\right) & = h_m u_m^2 + \\tfrac{1}{2} h_m^2 - h_l u_l^2 - \\tfrac{1}{2} h_l^2\n", 1097 | "\\end{align}\n", 1098 | "$$" 1099 | ] 1100 | }, 1101 | { 1102 | "cell_type": "markdown", 1103 | "metadata": {}, 1104 | "source": [ 1105 | "Eliminating the shock speed $V_s$ gives, using the second equation,\n", 1106 | "$$\n", 1107 | " u_m^2 - (2 u_l) u_m + \\left[ u_l^2 - \\tfrac{1}{2} \\left( h_l - h_m \\right) \\left( \\frac{h_l}{h_m} - \\frac{h_m}{h_l} \\right) \\right] = 0.\n", 1108 | "$$\n", 1109 | "This has the solutions (assuming that $h_m$ is known!)\n", 1110 | "$$\n", 1111 | " u_m = u_l \\pm \\sqrt{\\tfrac{1}{2} \\left( h_l - h_m \\right) \\left( \\frac{h_l}{h_m} - \\frac{h_m}{h_l} \\right)}.\n", 1112 | "$$" 1113 | ] 1114 | }, 1115 | { 1116 | "cell_type": "markdown", 1117 | "metadata": {}, 1118 | "source": [ 1119 | "We can again use the Rankine-Hugoniot relations to find the shock speed.\n", 1120 | "$$\n", 1121 | " V_s = u_l \\pm \\frac{h_m}{h_m - h_l} \\sqrt{\\tfrac{1}{2} \\left( h_l - h_m \\right) \\left( \\frac{h_l}{h_m} - \\frac{h_m}{h_l} \\right)}.\n", 1122 | "$$" 1123 | ] 1124 | }, 1125 | { 1126 | "cell_type": "markdown", 1127 | "metadata": {}, 1128 | "source": [ 1129 | "We should at this point find which sign is appropriate. Comparing the shock speeds against the characteristic speed will show that\n", 1130 | "\n", 1131 | "* we need $h_m > h_l$ for the wave to be a shock, and\n", 1132 | "* we take the negative sign if connected to a left state, and the positive if connected to a right state.\n", 1133 | "\n", 1134 | "However, we can see this by plotting the *Hugoniot locus*: the curve of all states that can be connected to $(h_l, u_l)$ across a shock." 1135 | ] 1136 | }, 1137 | { 1138 | "cell_type": "code", 1139 | "execution_count": 13, 1140 | "metadata": {}, 1141 | "outputs": [], 1142 | "source": [ 1143 | "def plot_sw_shock_physical(hl, ul):\n", 1144 | " \"Plot the shock curve through the state (hl, ul)\"\n", 1145 | " \n", 1146 | " h = np.linspace(h_min, h_max, 500)\n", 1147 | " u_negative = ul - np.sqrt(0.5 * (hl - h) * (hl / h - h / hl))\n", 1148 | " u_positive = ul + np.sqrt(0.5 * (hl - h) * (hl / h - h / hl))\n", 1149 | " \n", 1150 | " vs_negative = ul - h / (h - hl) * np.sqrt(0.5 * (hl - h) * (hl / h - h / hl))\n", 1151 | " vs_positive = ul + h / (h - hl) * np.sqrt(0.5 * (hl - h) * (hl / h - h / hl))\n", 1152 | " \n", 1153 | " xi1_negative = u_negative - np.sqrt(h) \n", 1154 | " xi1_positive = u_positive - np.sqrt(h)\n", 1155 | " xi2_negative = u_negative + np.sqrt(h) \n", 1156 | " xi2_positive = u_positive + np.sqrt(h)\n", 1157 | " \n", 1158 | " xi1_l = ul - np.sqrt(hl)\n", 1159 | " xi2_l = ul + np.sqrt(hl)\n", 1160 | " \n", 1161 | " h1_physical = h[np.logical_and(xi1_negative <= vs_negative, xi1_l >= vs_negative)]\n", 1162 | " u1_physical = u_negative[np.logical_and(xi1_negative <= vs_negative, xi1_l >= vs_negative)]\n", 1163 | " h2_physical = h[np.logical_and(xi2_positive >= vs_positive, xi2_l <= vs_positive)]\n", 1164 | " u2_physical = u_positive[np.logical_and(xi2_positive >= vs_positive, xi2_l <= vs_positive)]\n", 1165 | " h1_unphysical = h[np.logical_or(xi1_negative >= vs_negative, xi1_l <= vs_negative)]\n", 1166 | " u1_unphysical = u_negative[np.logical_or(xi1_negative >= vs_negative, xi1_l <= vs_negative)]\n", 1167 | " h2_unphysical = h[np.logical_or(xi2_positive <= vs_positive, xi2_l >= vs_positive)]\n", 1168 | " u2_unphysical = u_positive[np.logical_or(xi2_positive <= vs_positive, xi2_l >= vs_positive)]\n", 1169 | " \n", 1170 | " fig = plt.figure(figsize=(12,8))\n", 1171 | " ax = fig.add_subplot(111)\n", 1172 | " ax.plot(hl, ul, 'rx', markersize = 16, markeredgewidth = 3)\n", 1173 | " ax.plot(h1_physical, u1_physical, 'b-', linewidth = 2, \n", 1174 | " label=\"Physical, 1-shock\")\n", 1175 | " ax.plot(h1_unphysical, u1_unphysical, 'b--', linewidth = 2, \n", 1176 | " label=\"Unphysical, 1-shock\")\n", 1177 | " ax.plot(h2_physical, u2_physical, 'g-', linewidth = 2, \n", 1178 | " label=\"Physical, 2-shock\")\n", 1179 | " ax.plot(h2_unphysical, u2_unphysical, 'g--', linewidth = 2, \n", 1180 | " label=\"Unphysical, 2-shock\")\n", 1181 | " ax.plot(h[::5], u_negative[::5], 'co', markersize = 12, markeredgewidth = 2, alpha = 0.3,\n", 1182 | " label=\"Negative branch\")\n", 1183 | " ax.plot(h[::5], u_positive[::5], 'ro', markersize = 12, markeredgewidth = 2, alpha = 0.3,\n", 1184 | " label=\"Positive branch\")\n", 1185 | " ax.set_xlabel(r\"$h$\")\n", 1186 | " ax.set_ylabel(r\"$u$\")\n", 1187 | " dh = h_max - h_min\n", 1188 | " du = u_max - u_min\n", 1189 | " ax.set_xbound(h_min, h_max)\n", 1190 | " ax.set_ybound(u_min, u_max)\n", 1191 | " ax.legend()\n", 1192 | " fig.tight_layout()" 1193 | ] 1194 | }, 1195 | { 1196 | "cell_type": "code", 1197 | "execution_count": 14, 1198 | "metadata": {}, 1199 | "outputs": [ 1200 | { 1201 | "data": { 1202 | "application/vnd.jupyter.widget-view+json": { 1203 | "model_id": "9d5b8698f1f74b47abc98feff828adff", 1204 | "version_major": 2, 1205 | "version_minor": 0 1206 | }, 1207 | "text/html": [ 1208 | "Failed to display Jupyter Widget of type interactive
.
\n", 1210 | " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", 1211 | " that the widgets JavaScript is still loading. If this message persists, it\n", 1212 | " likely means that the widgets JavaScript library is either not installed or\n", 1213 | " not enabled. See the Jupyter\n", 1214 | " Widgets Documentation for setup instructions.\n", 1215 | "
\n", 1216 | "\n", 1217 | " If you're reading this message in another frontend (for example, a static\n", 1218 | " rendering on GitHub or NBViewer),\n", 1219 | " it may mean that your frontend doesn't currently support widgets.\n", 1220 | "
\n" 1221 | ], 1222 | "text/plain": [ 1223 | "interactive(children=(FloatSlider(value=1.0, description='hl', max=10.0, min=0.1), FloatSlider(value=0.0, description='ul', max=1.0, min=-1.0), Output()), _dom_classes=('widget-interact',))" 1224 | ] 1225 | }, 1226 | "metadata": {}, 1227 | "output_type": "display_data" 1228 | } 1229 | ], 1230 | "source": [ 1231 | "interactive(plot_sw_shock_physical, \n", 1232 | " hl = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 1233 | " ul = FloatSlider(min = -1.0, max = 1.0, value = 0.0))" 1234 | ] 1235 | }, 1236 | { 1237 | "cell_type": "markdown", 1238 | "metadata": {}, 1239 | "source": [ 1240 | "We see from these results, as claimed above, that\n", 1241 | "\n", 1242 | "* we need $h_m > h_l$ (or $h_m > h_r$) for the wave to be a shock, and\n", 1243 | "* we take the negative sign if connected to a left state, and the positive if connected to a right state." 1244 | ] 1245 | }, 1246 | { 1247 | "cell_type": "markdown", 1248 | "metadata": {}, 1249 | "source": [ 1250 | "## All shock solution" 1251 | ] 1252 | }, 1253 | { 1254 | "cell_type": "markdown", 1255 | "metadata": {}, 1256 | "source": [ 1257 | "When we assumed the solution contained two rarefactions it was possible to write the full solution in closed form. If we assume the solution contains two shocks then it is not possible to do this. However, it is straightforward to find the solution numerically. " 1258 | ] 1259 | }, 1260 | { 1261 | "cell_type": "markdown", 1262 | "metadata": {}, 1263 | "source": [ 1264 | "We assume the left state ${\\bf w}_l = (h_l, u_l)$ and the right state ${\\bf w}_r = (h_r, u_r)$ are known, and that they both connect to the central state ${\\bf w}_m = (h_m, u_m)$ through shocks. We know that\n", 1265 | "$$\n", 1266 | "\\begin{align}\n", 1267 | " u_m & = u_l - \\sqrt{\\tfrac{1}{2} \\left( h_l - h_m \\right) \\left( \\frac{h_l}{h_m} - \\frac{h_m}{h_l} \\right)}, \\\\\n", 1268 | " u_m & = u_r + \\sqrt{\\tfrac{1}{2} \\left( h_r - h_m \\right) \\left( \\frac{h_r}{h_m} - \\frac{h_m}{h_r} \\right)}.\n", 1269 | "\\end{align}\n", 1270 | "$$\n", 1271 | "We schematically write these equations as\n", 1272 | "$$\n", 1273 | "\\begin{align}\n", 1274 | " u_m & = \\phi_l \\left( h_m; {\\bf w}_l \\right), \\\\\n", 1275 | " u_m & = \\phi_r \\left( h_m; {\\bf w}_r \\right),\n", 1276 | "\\end{align}\n", 1277 | "$$\n", 1278 | "to indicate that the velocity in the central state, $u_m$, can be written as a function of the single unknown $h_m$ and known data.\n", 1279 | "\n", 1280 | "We immediately see that $h_m$ is a root of the nonlinear equation\n", 1281 | "$$\n", 1282 | " \\phi \\left( h_m; {\\bf w}_l, {\\bf w}_r \\right) = \\phi_l \\left( h_m; {\\bf w}_l \\right) - \\phi_r \\left( h_m; {\\bf w}_r \\right) = 0.\n", 1283 | "$$\n", 1284 | "\n", 1285 | "Finding the roots of scalar nonlinear equations is a standard problem in numerical methods, with methods such as bisection, Newton-Raphson and more being well-known. `scipy` provides a number of standard algorithms - here we will use the recommended `brentq` method.\n", 1286 | "\n", 1287 | "Note that as soon as we have numerically determined $h_m$ then either formula above gives $u_m$, and the shock speeds follow." 1288 | ] 1289 | }, 1290 | { 1291 | "cell_type": "code", 1292 | "execution_count": 15, 1293 | "metadata": {}, 1294 | "outputs": [], 1295 | "source": [ 1296 | "def plot_sw_all_shock(hl, ul, hr, ur):\n", 1297 | " \"Plot the all shock solution curve for states (hl, ul) and (hr, ur)\"\n", 1298 | " \n", 1299 | " from scipy.optimize import brentq\n", 1300 | " \n", 1301 | " def phi(hstar):\n", 1302 | " \"Function defining the root\"\n", 1303 | " \n", 1304 | " phi_l = ul - np.sqrt(0.5 * (hl - hstar) * (hl / hstar - hstar / hl))\n", 1305 | " phi_r = ur + np.sqrt(0.5 * (hr - hstar) * (hr / hstar - hstar / hr))\n", 1306 | " \n", 1307 | " return phi_l - phi_r\n", 1308 | " \n", 1309 | " # There is a solution only in the physical case. \n", 1310 | " physical_solution = True\n", 1311 | " try:\n", 1312 | " hm = brentq(phi, np.max([hl, hr]), 10.0 * h_max)\n", 1313 | " except ValueError:\n", 1314 | " physical_solution = False\n", 1315 | " hm = hl\n", 1316 | " um = ul - np.sqrt(0.5 * (hl - hm) * (hl / hm - hm / hl))\n", 1317 | " \n", 1318 | " h = np.linspace(h_min, h_max, 500)\n", 1319 | " u_negative = ul - np.sqrt(0.5 * (hl - h) * (hl / h - h / hl))\n", 1320 | " u_positive = ur + np.sqrt(0.5 * (hr - h) * (hr / h - h / hr))\n", 1321 | " \n", 1322 | " h_maximum = np.max([h_max, hl, hr, hm])\n", 1323 | " h_minimum = np.min([h_min, hl, hr, hm])\n", 1324 | " u_maximum = np.max([u_max, ul, ur, um])\n", 1325 | " u_minimum = np.min([u_min, ul, ur, um])\n", 1326 | " dh = h_maximum - h_minimum\n", 1327 | " du = u_maximum - u_minimum\n", 1328 | " xil_min = u_minimum - np.sqrt(h_maximum)\n", 1329 | " xil_max = u_maximum - np.sqrt(h_minimum)\n", 1330 | " xir_min = u_minimum + np.sqrt(h_minimum)\n", 1331 | " xir_max = u_maximum + np.sqrt(h_maximum)\n", 1332 | " \n", 1333 | " vs_negative = ul - h / (h - hl) * np.sqrt(0.5 * (hl - h) * (hl / h - h / hl))\n", 1334 | " vs_positive = ur + h / (h - hr) * np.sqrt(0.5 * (hr - h) * (hr / h - h / hr))\n", 1335 | " \n", 1336 | " xi1_negative = u_negative - np.sqrt(h) \n", 1337 | " xi1_positive = u_positive - np.sqrt(h)\n", 1338 | " xi2_negative = u_negative + np.sqrt(h) \n", 1339 | " xi2_positive = u_positive + np.sqrt(h)\n", 1340 | " \n", 1341 | " xi1_l = ul - np.sqrt(hl)\n", 1342 | " xi2_r = ur + np.sqrt(hr)\n", 1343 | " \n", 1344 | " h1_physical = h[np.logical_and(xi1_negative <= vs_negative, xi1_l >= vs_negative)]\n", 1345 | " u1_physical = u_negative[np.logical_and(xi1_negative <= vs_negative, xi1_l >= vs_negative)]\n", 1346 | " h2_physical = h[np.logical_and(xi2_positive >= vs_positive, xi2_r <= vs_positive)]\n", 1347 | " u2_physical = u_positive[np.logical_and(xi2_positive >= vs_positive, xi2_r <= vs_positive)]\n", 1348 | " h1_unphysical = h[np.logical_or(xi1_negative >= vs_negative, xi1_l <= vs_negative)]\n", 1349 | " u1_unphysical = u_negative[np.logical_or(xi1_negative >= vs_negative, xi1_l <= vs_negative)]\n", 1350 | " h2_unphysical = h[np.logical_or(xi2_positive <= vs_positive, xi2_r >= vs_positive)]\n", 1351 | " u2_unphysical = u_positive[np.logical_or(xi2_positive <= vs_positive, xi2_r >= vs_positive)]\n", 1352 | " \n", 1353 | " fig = plt.figure(figsize=(12,8))\n", 1354 | " ax = fig.add_subplot(111)\n", 1355 | " ax.plot(hl, ul, 'rx', markersize = 16, markeredgewidth = 3, label = r\"${\\bf w}_l$\")\n", 1356 | " ax.plot(hr, ur, 'r+', markersize = 16, markeredgewidth = 3, label = r\"${\\bf w}_r$\")\n", 1357 | " if physical_solution:\n", 1358 | " ax.plot(hm, um, 'ro', markersize = 16, markeredgewidth = 3, label = r\"${\\bf w}_m$\")\n", 1359 | " ax.plot(h1_physical, u1_physical, 'b-', linewidth = 2, \n", 1360 | " label=\"Physical, 1-shock\")\n", 1361 | " ax.plot(h1_unphysical, u1_unphysical, 'b--', linewidth = 2, \n", 1362 | " label=\"Unphysical, 1-shock\")\n", 1363 | " ax.plot(h2_physical, u2_physical, 'g-', linewidth = 2, \n", 1364 | " label=\"Physical, 2-shock\")\n", 1365 | " ax.plot(h2_unphysical, u2_unphysical, 'g--', linewidth = 2, \n", 1366 | " label=\"Unphysical, 2-shock\")\n", 1367 | " ax.set_xlabel(r\"$h$\")\n", 1368 | " ax.set_ylabel(r\"$u$\")\n", 1369 | " ax.set_xbound(h_minimum - 0.1 * dh, h_maximum + 0.1 * dh)\n", 1370 | " ax.set_ybound(u_minimum - 0.1 * du, u_maximum + 0.1 * du)\n", 1371 | " ax.legend()\n", 1372 | " \n", 1373 | " fig.tight_layout()" 1374 | ] 1375 | }, 1376 | { 1377 | "cell_type": "code", 1378 | "execution_count": 16, 1379 | "metadata": {}, 1380 | "outputs": [ 1381 | { 1382 | "data": { 1383 | "application/vnd.jupyter.widget-view+json": { 1384 | "model_id": "0304279e49bc4e8181530c6e6d4469c5", 1385 | "version_major": 2, 1386 | "version_minor": 0 1387 | }, 1388 | "text/html": [ 1389 | "Failed to display Jupyter Widget of type interactive
.
\n", 1391 | " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", 1392 | " that the widgets JavaScript is still loading. If this message persists, it\n", 1393 | " likely means that the widgets JavaScript library is either not installed or\n", 1394 | " not enabled. See the Jupyter\n", 1395 | " Widgets Documentation for setup instructions.\n", 1396 | "
\n", 1397 | "\n", 1398 | " If you're reading this message in another frontend (for example, a static\n", 1399 | " rendering on GitHub or NBViewer),\n", 1400 | " it may mean that your frontend doesn't currently support widgets.\n", 1401 | "
\n" 1402 | ], 1403 | "text/plain": [ 1404 | "interactive(children=(FloatSlider(value=1.0, description='hl', max=10.0, min=0.1), FloatSlider(value=0.2, description='ul', max=1.0, min=-1.0), FloatSlider(value=1.0, description='hr', max=10.0, min=0.1), FloatSlider(value=-0.2, description='ur', max=1.0, min=-1.0), Output()), _dom_classes=('widget-interact',))" 1405 | ] 1406 | }, 1407 | "metadata": {}, 1408 | "output_type": "display_data" 1409 | } 1410 | ], 1411 | "source": [ 1412 | "interactive(plot_sw_all_shock, \n", 1413 | " hl = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 1414 | " ul = FloatSlider(min = -1.0, max = 1.0, value = 0.2), \n", 1415 | " hr = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 1416 | " ur = FloatSlider(min = -1.0, max = 1.0, value = -0.2))" 1417 | ] 1418 | }, 1419 | { 1420 | "cell_type": "markdown", 1421 | "metadata": {}, 1422 | "source": [ 1423 | "Finally, we can plot the solution in physical space." 1424 | ] 1425 | }, 1426 | { 1427 | "cell_type": "code", 1428 | "execution_count": 17, 1429 | "metadata": {}, 1430 | "outputs": [], 1431 | "source": [ 1432 | "def plot_sw_all_shock_solution(hl, ul, hr, ur):\n", 1433 | " \"Plot the all shock solution for states (hl, ul) and (hr, ur)\"\n", 1434 | " \n", 1435 | " from scipy.optimize import brentq\n", 1436 | " \n", 1437 | " def phi(hstar):\n", 1438 | " \"Function defining the root\"\n", 1439 | " \n", 1440 | " phi_l = ul - np.sqrt(0.5 * (hl - hstar) * (hl / hstar - hstar / hl))\n", 1441 | " phi_r = ur + np.sqrt(0.5 * (hr - hstar) * (hr / hstar - hstar / hr))\n", 1442 | " \n", 1443 | " return phi_l - phi_r\n", 1444 | " \n", 1445 | " # There is a solution only in the physical case. \n", 1446 | " physical_solution = True\n", 1447 | " try:\n", 1448 | " hm = brentq(phi, np.max([hl, hr]), 10.0 * h_max)\n", 1449 | " except ValueError:\n", 1450 | " physical_solution = False\n", 1451 | " hm = hl\n", 1452 | " um = ul - np.sqrt(0.5 * (hl - hm) * (hl / hm - hm / hl))\n", 1453 | " \n", 1454 | " xi1l = ul - np.sqrt(hl)\n", 1455 | " xi1m = um - np.sqrt(hm)\n", 1456 | " xi1r = ur - np.sqrt(hr)\n", 1457 | " if physical_solution:\n", 1458 | " vsl = ul - hm / (hm - hl) * np.sqrt(0.5 * (hl - hm) * (hl / hm - hm / hl))\n", 1459 | " else:\n", 1460 | " vsl = xi1l\n", 1461 | " \n", 1462 | " xi2r = ur + np.sqrt(hr)\n", 1463 | " xi2m = um + np.sqrt(hm)\n", 1464 | " xi2l = ul + np.sqrt(hl)\n", 1465 | " if physical_solution:\n", 1466 | " vsr = ur + hm / (hm - hr) * np.sqrt(0.5 * (hr - hm) * (hr / hm - hm / hr))\n", 1467 | " else:\n", 1468 | " vsr = xi2r\n", 1469 | " \n", 1470 | " xi_min = np.min([-1.0, xi1l, xi1m, xi2r, xi2m])\n", 1471 | " xi_max = np.max([1.0, xi1l, xi1m, xi2r, xi2m])\n", 1472 | " d_xi = xi_max - xi_min\n", 1473 | " h_maximum = np.max([hl, hr, hm])\n", 1474 | " h_minimum = np.min([hl, hr, hm])\n", 1475 | " d_h = h_maximum - h_minimum\n", 1476 | " u_maximum = np.max([ul, ur, um])\n", 1477 | " u_minimum = np.min([ul, ur, um])\n", 1478 | " d_u = u_maximum - u_minimum\n", 1479 | " \n", 1480 | " xi = np.array([xi_min - 0.1 * d_xi, vsl, vsl, vsr, vsr, xi_max + 0.1 * d_xi])\n", 1481 | " h = np.array([hl, hl, hm, hm, hr, hr])\n", 1482 | " u = np.array([ul, ul, um, um, ur, ur])\n", 1483 | " \n", 1484 | " fig = plt.figure(figsize=(12,8))\n", 1485 | " ax1 = fig.add_subplot(221)\n", 1486 | " if (hm > np.max([hl, hr])):\n", 1487 | " ax1.plot(xi, h, 'b-', label = \"Physical solution\")\n", 1488 | " else:\n", 1489 | " ax1.plot(xi, h, 'r--', label = \"Unphysical solution\")\n", 1490 | " ax1.set_ybound(h_minimum - 0.1 * d_h, h_maximum + 0.1 * d_h)\n", 1491 | " ax1.set_xlabel(r\"$\\xi$\")\n", 1492 | " ax1.set_ylabel(r\"$h$\")\n", 1493 | " ax1.legend()\n", 1494 | " ax2 = fig.add_subplot(222)\n", 1495 | " if (hm > np.max([hl, hr])):\n", 1496 | " ax2.plot(xi, u, 'b-', label = \"Physical solution\")\n", 1497 | " else:\n", 1498 | " ax2.plot(xi, u, 'r--', label = \"Unphysical solution\")\n", 1499 | " ax2.set_ybound(u_minimum - 0.1 * d_u, u_maximum + 0.1 * d_u)\n", 1500 | " ax2.set_xlabel(r\"$\\xi$\")\n", 1501 | " ax2.set_ylabel(r\"$u$\")\n", 1502 | " ax2.legend()\n", 1503 | " \n", 1504 | " ax3 = fig.add_subplot(223)\n", 1505 | " left_end = np.min([-1.0, 1.1*xi1l])\n", 1506 | " right_end = np.max([1.0, 1.1*xi2r])\n", 1507 | " left_edge = left_end - xi1l\n", 1508 | " right_edge = right_end - xi1r\n", 1509 | " x1_start_points_l = np.linspace(np.min([left_edge, left_end]), 0.0, 20)\n", 1510 | " x1_start_points_r = np.linspace(0.0, np.max([right_edge, right_end]), 20)\n", 1511 | " t1_end_points_l = np.ones_like(x1_start_points_l)\n", 1512 | " t1_end_points_r = np.ones_like(x1_start_points_r)\n", 1513 | " \n", 1514 | " # Look for intersections\n", 1515 | " t1_end_points_l = np.minimum(t1_end_points_l, x1_start_points_l / (vsl - xi1l))\n", 1516 | " x1_end_points_l = x1_start_points_l + xi1l * t1_end_points_l\n", 1517 | " t1_end_points_r = np.minimum(t1_end_points_r, x1_start_points_r / (vsr - xi1r))\n", 1518 | " x1_end_points_r = x1_start_points_r + xi1r * t1_end_points_r\n", 1519 | " # Note: here we are cheating, and using the characteristic speed of the middle state, \n", 1520 | " # ignoring how it varies across the rarefaction\n", 1521 | " t1_final_points_r = np.ones_like(x1_start_points_r)\n", 1522 | " t1_final_points_r = np.minimum(t1_final_points_r, \n", 1523 | " (x1_end_points_r - t1_end_points_r * xi1m) / (vsl - xi1m))\n", 1524 | " x1_final_points_r = x1_end_points_r + (t1_final_points_r - t1_end_points_r) * xi1m\n", 1525 | " \n", 1526 | " for xs, xe, te in zip(x1_start_points_l, x1_end_points_l, t1_end_points_l):\n", 1527 | " ax3.plot([xs, xe], [0.0, te], 'b-')\n", 1528 | " for xs, xe, te in zip(x1_start_points_r, x1_end_points_r, t1_end_points_r):\n", 1529 | " ax3.plot([xs, xe], [0.0, te], 'g-')\n", 1530 | " for xs, xe, ts, te in zip(x1_end_points_r, x1_final_points_r, t1_end_points_r, \n", 1531 | " t1_final_points_r):\n", 1532 | " ax3.plot([xs, xe], [ts, te], 'g-')\n", 1533 | " \n", 1534 | " # Highlight the shocks\n", 1535 | " ax3.plot([0.0, vsl], [0.0, 1.0], 'r-', linewidth=2)\n", 1536 | " ax3.plot([0.0, vsr], [0.0, 1.0], 'r-', linewidth=2)\n", 1537 | " \n", 1538 | " # Unphysical shock\n", 1539 | " if not physical_solution:\n", 1540 | " x_fill = []\n", 1541 | " if xi1l < xi1m:\n", 1542 | " x_fill = [xi1l, 0.0, xi1m]\n", 1543 | " elif xi1l < vsl:\n", 1544 | " x_fill = [xi1l, 0.0, vsl]\n", 1545 | " elif vsl < xi1m:\n", 1546 | " x_fill = [vsl, 0.0, xi1m]\n", 1547 | " if len(x_fill) > 0:\n", 1548 | " t_fill = [1.0, 0.0, 1.0]\n", 1549 | " ax3.fill_between(x_fill, t_fill, 1.0, facecolor = 'red', alpha = 0.5)\n", 1550 | " \n", 1551 | " x_fill = []\n", 1552 | " if xi2r > xi2m:\n", 1553 | " x_fill = [xi2m, 0.0, xi2r]\n", 1554 | " elif xi2m < vsr:\n", 1555 | " x_fill = [xi2m, 0.0, vsr]\n", 1556 | " elif vsr < xi2r:\n", 1557 | " x_fill = [vsr, 0.0, xi2r]\n", 1558 | " if len(x_fill) > 0:\n", 1559 | " t_fill = [1.0, 0.0, 1.0]\n", 1560 | " ax3.fill_between(x_fill, t_fill, 1.0, facecolor = 'red', alpha = 0.5)\n", 1561 | " \n", 1562 | " ax3.set_xlabel(r\"$x$\")\n", 1563 | " ax3.set_ylabel(r\"$t$\")\n", 1564 | " ax3.set_title(\"1-characteristics\")\n", 1565 | " ax3.set_xbound(left_end, right_end)\n", 1566 | " \n", 1567 | " ax4 = fig.add_subplot(224)\n", 1568 | " left_end = np.min([-1.0, 1.1*xi1l])\n", 1569 | " right_end = np.max([1.0, 1.1*xi2r])\n", 1570 | " left_edge = left_end - xi2l\n", 1571 | " right_edge = right_end - xi2r\n", 1572 | " x2_start_points_l = np.linspace(np.min([left_edge, left_end]), 0.0, 20)\n", 1573 | " x2_start_points_r = np.linspace(0.0, np.max([right_edge, right_end]), 20)\n", 1574 | " x2_end_points_r = x2_start_points_r + xi2r\n", 1575 | " t2_end_points_l = np.ones_like(x2_start_points_l)\n", 1576 | " t2_end_points_r = np.ones_like(x2_start_points_r)\n", 1577 | " \n", 1578 | " # Look for intersections\n", 1579 | " t2_end_points_r = np.minimum(t2_end_points_r, x2_start_points_r / (vsr - xi2r))\n", 1580 | " x2_end_points_r = x2_start_points_r + xi2r * t2_end_points_r\n", 1581 | " t2_end_points_l = np.minimum(t2_end_points_l, x2_start_points_l / (vsl - xi2l))\n", 1582 | " x2_end_points_l = x2_start_points_l + xi2l * t2_end_points_l\n", 1583 | " # Note: here we are cheating, and using the characteristic speed of the middle state, \n", 1584 | " # ignoring how it varies across the rarefaction\n", 1585 | " t2_final_points_l = np.ones_like(x2_start_points_l)\n", 1586 | " t2_final_points_l = np.minimum(t2_final_points_l, \n", 1587 | " (x2_end_points_l - t2_end_points_l * xi2m) / (vsr - xi2m))\n", 1588 | " x2_final_points_l = x2_end_points_l + (t2_final_points_l - t2_end_points_l) * xi2m\n", 1589 | " \n", 1590 | " for xs, xe, te in zip(x2_start_points_r, x2_end_points_r, t2_end_points_r):\n", 1591 | " ax4.plot([xs, xe], [0.0, te], 'b-')\n", 1592 | " for xs, xe, te in zip(x2_start_points_l, x2_end_points_l, t2_end_points_l):\n", 1593 | " ax4.plot([xs, xe], [0.0, te], 'g-')\n", 1594 | " for xs, xe, ts, te in zip(x2_end_points_l, x2_final_points_l, t2_end_points_l, \n", 1595 | " t2_final_points_l):\n", 1596 | " ax4.plot([xs, xe], [ts, te], 'g-')\n", 1597 | " \n", 1598 | " # Highlight the shocks\n", 1599 | " ax4.plot([0.0, vsl], [0.0, 1.0], 'r-', linewidth=2)\n", 1600 | " ax4.plot([0.0, vsr], [0.0, 1.0], 'r-', linewidth=2)\n", 1601 | " \n", 1602 | " # Unphysical shock\n", 1603 | " if not physical_solution:\n", 1604 | " x_fill = []\n", 1605 | " if xi1l < xi1m:\n", 1606 | " x_fill = [xi1l, 0.0, xi1m]\n", 1607 | " elif xi1l < vsl:\n", 1608 | " x_fill = [xi1l, 0.0, vsl]\n", 1609 | " elif vsl < xi1m:\n", 1610 | " x_fill = [vsl, 0.0, xi1m]\n", 1611 | " if len(x_fill) > 0:\n", 1612 | " t_fill = [1.0, 0.0, 1.0]\n", 1613 | " ax4.fill_between(x_fill, t_fill, 1.0, facecolor = 'red', alpha = 0.5)\n", 1614 | " \n", 1615 | " x_fill = []\n", 1616 | " if xi2r > xi2m:\n", 1617 | " x_fill = [xi2m, 0.0, xi2r]\n", 1618 | " elif xi2m < vsr:\n", 1619 | " x_fill = [xi2m, 0.0, vsr]\n", 1620 | " elif vsr < xi2r:\n", 1621 | " x_fill = [vsr, 0.0, xi2r]\n", 1622 | " if len(x_fill) > 0:\n", 1623 | " t_fill = [1.0, 0.0, 1.0]\n", 1624 | " ax4.fill_between(x_fill, t_fill, 1.0, facecolor = 'red', alpha = 0.5)\n", 1625 | " \n", 1626 | " ax4.set_xlabel(r\"$x$\")\n", 1627 | " ax4.set_ylabel(r\"$t$\")\n", 1628 | " ax4.set_title(\"2-characteristics\")\n", 1629 | " ax4.set_xbound(left_end, right_end)\n", 1630 | " \n", 1631 | " fig.tight_layout()" 1632 | ] 1633 | }, 1634 | { 1635 | "cell_type": "code", 1636 | "execution_count": 18, 1637 | "metadata": {}, 1638 | "outputs": [ 1639 | { 1640 | "data": { 1641 | "application/vnd.jupyter.widget-view+json": { 1642 | "model_id": "134b60c42f554224ab328d7a48805c53", 1643 | "version_major": 2, 1644 | "version_minor": 0 1645 | }, 1646 | "text/html": [ 1647 | "Failed to display Jupyter Widget of type interactive
.
\n", 1649 | " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", 1650 | " that the widgets JavaScript is still loading. If this message persists, it\n", 1651 | " likely means that the widgets JavaScript library is either not installed or\n", 1652 | " not enabled. See the Jupyter\n", 1653 | " Widgets Documentation for setup instructions.\n", 1654 | "
\n", 1655 | "\n", 1656 | " If you're reading this message in another frontend (for example, a static\n", 1657 | " rendering on GitHub or NBViewer),\n", 1658 | " it may mean that your frontend doesn't currently support widgets.\n", 1659 | "
\n" 1660 | ], 1661 | "text/plain": [ 1662 | "interactive(children=(FloatSlider(value=1.0, description='hl', max=10.0, min=0.1), FloatSlider(value=0.2, description='ul', max=1.0, min=-1.0), FloatSlider(value=1.0, description='hr', max=10.0, min=0.1), FloatSlider(value=-0.2, description='ur', max=1.0, min=-1.0), Output()), _dom_classes=('widget-interact',))" 1663 | ] 1664 | }, 1665 | "metadata": {}, 1666 | "output_type": "display_data" 1667 | } 1668 | ], 1669 | "source": [ 1670 | "interactive(plot_sw_all_shock_solution, \n", 1671 | " hl = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 1672 | " ul = FloatSlider(min = -1.0, max = 1.0, value = 0.2), \n", 1673 | " hr = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 1674 | " ur = FloatSlider(min = -1.0, max = 1.0, value = -0.2))" 1675 | ] 1676 | }, 1677 | { 1678 | "cell_type": "markdown", 1679 | "metadata": {}, 1680 | "source": [ 1681 | "## Full solution" 1682 | ] 1683 | }, 1684 | { 1685 | "cell_type": "markdown", 1686 | "metadata": {}, 1687 | "source": [ 1688 | "The all shock solution illustrates how the full solution can be obtained. We know that \n", 1689 | "\n", 1690 | "1. the central state ${\\bf w}_m$ will be connected to the known states ${\\bf w}_{l, r}$ across waves that are either shocks or rarefactions,\n", 1691 | "2. if $h_m > h_{l, r}$ then the wave will be a shock, otherwise it will be a rarefaction, and\n", 1692 | "3. given $h_m$ and the known data, we can compute $u_m$ for either a shock or a rarefaction.\n", 1693 | "\n", 1694 | "So, using the results above, we can find the full solution to the Riemann problem by solving the nonlinear algebraic root-finding problem\n", 1695 | "$$\n", 1696 | " \\Phi \\left( h_m ; {\\bf w}_l, {\\bf w}_r \\right) = 0,\n", 1697 | "$$\n", 1698 | "where\n", 1699 | "$$\n", 1700 | " \\Phi \\left( h_m ; {\\bf w}_l, {\\bf w}_r \\right) = \\Phi_l \\left( h_m ; {\\bf w}_l \\right) - \\Phi_r \\left( h_m ; {\\bf w}_r \\right),\n", 1701 | "$$\n", 1702 | "and\n", 1703 | "$$\n", 1704 | "\\begin{align}\n", 1705 | " \\Phi_l & = u_m \\left( h_m ; {\\bf w}_l \\right) & \\Phi_r & = u_m \\left( h_m ; {\\bf w}_r \\right) \\\\\n", 1706 | " & = \\begin{cases} u_l + 2 \\left( \\sqrt{h_l} - \\sqrt{h_m} \\right) & h_l > h_m \\\\ u_l - \\sqrt{\\tfrac{1}{2} \\left( h_l - h_m \\right) \\left( \\frac{h_l}{h_m} - \\frac{h_m}{h_l} \\right)} & h_l < h_m \\end{cases} & & = \\begin{cases} u_r - 2 \\left( \\sqrt{h_r} - \\sqrt{h_m} \\right) & h_r > h_m \\\\ u_r + \\sqrt{\\tfrac{1}{2} \\left( h_r - h_m \\right) \\left( \\frac{h_r}{h_m} - \\frac{h_m}{h_r} \\right)} & h_r < h_m \\end{cases}.\n", 1707 | "\\end{align}\n", 1708 | "$$" 1709 | ] 1710 | }, 1711 | { 1712 | "cell_type": "code", 1713 | "execution_count": 19, 1714 | "metadata": {}, 1715 | "outputs": [], 1716 | "source": [ 1717 | "def plot_sw_Riemann_curves(hl, ul, hr, ur):\n", 1718 | " \"Plot the solution curves for states (hl, ul) and (hr, ur)\"\n", 1719 | " \n", 1720 | " from scipy.optimize import brentq\n", 1721 | " \n", 1722 | " def phi(hstar):\n", 1723 | " \"Function defining the root\"\n", 1724 | " \n", 1725 | " if hl < hstar:\n", 1726 | " phi_l = ul - np.sqrt(0.5 * (hl - hstar) * (hl / hstar - hstar / hl))\n", 1727 | " else:\n", 1728 | " phi_l = ul + 2.0 * (np.sqrt(hl) - np.sqrt(hstar))\n", 1729 | " if hr < hstar:\n", 1730 | " phi_r = ur + np.sqrt(0.5 * (hr - hstar) * (hr / hstar - hstar / hr))\n", 1731 | " else:\n", 1732 | " phi_r = ur - 2.0 * (np.sqrt(hr) - np.sqrt(hstar))\n", 1733 | " \n", 1734 | " return phi_l - phi_r\n", 1735 | " \n", 1736 | " hm = brentq(phi, 0.1 * h_min, 10.0 * h_max)\n", 1737 | " if hl < hm:\n", 1738 | " um = ul - np.sqrt(0.5 * (hl - hm) * (hl / hm - hm / hl))\n", 1739 | " else:\n", 1740 | " um = ul + 2.0 * (np.sqrt(hl) - np.sqrt(hm))\n", 1741 | " \n", 1742 | " h_maximum = np.max([h_max, hl, hr, hm])\n", 1743 | " h_minimum = np.min([h_min, hl, hr, hm])\n", 1744 | " u_maximum = np.max([u_max, ul, ur, um])\n", 1745 | " u_minimum = np.min([u_min, ul, ur, um])\n", 1746 | " dh = h_maximum - h_minimum\n", 1747 | " du = u_maximum - u_minimum\n", 1748 | " \n", 1749 | " # Now plot the rarefaction and shock curves as appropriate\n", 1750 | " # Here we only plot the physical pieces.\n", 1751 | " \n", 1752 | " h1_shock = np.linspace(hl, h_max)\n", 1753 | " u1_shock = ul - np.sqrt(0.5 * (hl - h1_shock) * (hl / h1_shock - h1_shock / hl))\n", 1754 | " h2_shock = np.linspace(hr, h_max)\n", 1755 | " u2_shock = ur + np.sqrt(0.5 * (hr - h2_shock) * (hr / h2_shock - h2_shock / hr))\n", 1756 | " \n", 1757 | " h1_rarefaction = np.linspace(h_min, hl)\n", 1758 | " u1_rarefaction = ul + 2.0 * (np.sqrt(hl) - np.sqrt(h1_rarefaction))\n", 1759 | " h2_rarefaction = np.linspace(h_min, hr)\n", 1760 | " u2_rarefaction = ur - 2.0 * (np.sqrt(hr) - np.sqrt(h2_rarefaction))\n", 1761 | " \n", 1762 | " fig = plt.figure(figsize=(12,8))\n", 1763 | " ax = fig.add_subplot(111)\n", 1764 | " ax.plot(hl, ul, 'rx', markersize = 16, markeredgewidth = 3, label = r\"${\\bf w}_l$\")\n", 1765 | " ax.plot(hr, ur, 'r+', markersize = 16, markeredgewidth = 3, label = r\"${\\bf w}_r$\")\n", 1766 | " ax.plot(hm, um, 'ro', markersize = 16, markeredgewidth = 3, label = r\"${\\bf w}_m$\")\n", 1767 | " ax.plot(h1_shock, u1_shock, 'b-', linewidth = 2, \n", 1768 | " label=\"1-shock\")\n", 1769 | " ax.plot(h1_rarefaction, u1_rarefaction, 'b-.', linewidth = 2, \n", 1770 | " label=\"1-rarefaction\")\n", 1771 | " ax.plot(h2_shock, u2_shock, 'g-', linewidth = 2, \n", 1772 | " label=\"2-shock\")\n", 1773 | " ax.plot(h2_rarefaction, u2_rarefaction, 'g-.', linewidth = 2, \n", 1774 | " label=\"2-rarefaction\")\n", 1775 | " ax.set_xlabel(r\"$h$\")\n", 1776 | " ax.set_ylabel(r\"$u$\")\n", 1777 | " ax.set_xbound(h_minimum - 0.1 * dh, h_maximum + 0.1 * dh)\n", 1778 | " ax.set_ybound(u_minimum - 0.1 * du, u_maximum + 0.1 * du)\n", 1779 | " ax.legend()\n", 1780 | " \n", 1781 | " fig.tight_layout()" 1782 | ] 1783 | }, 1784 | { 1785 | "cell_type": "code", 1786 | "execution_count": 20, 1787 | "metadata": {}, 1788 | "outputs": [ 1789 | { 1790 | "data": { 1791 | "application/vnd.jupyter.widget-view+json": { 1792 | "model_id": "1c183cd1cc1043fcb9148ee6e2f78523", 1793 | "version_major": 2, 1794 | "version_minor": 0 1795 | }, 1796 | "text/html": [ 1797 | "Failed to display Jupyter Widget of type interactive
.
\n", 1799 | " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", 1800 | " that the widgets JavaScript is still loading. If this message persists, it\n", 1801 | " likely means that the widgets JavaScript library is either not installed or\n", 1802 | " not enabled. See the Jupyter\n", 1803 | " Widgets Documentation for setup instructions.\n", 1804 | "
\n", 1805 | "\n", 1806 | " If you're reading this message in another frontend (for example, a static\n", 1807 | " rendering on GitHub or NBViewer),\n", 1808 | " it may mean that your frontend doesn't currently support widgets.\n", 1809 | "
\n" 1810 | ], 1811 | "text/plain": [ 1812 | "interactive(children=(FloatSlider(value=1.0, description='hl', max=10.0, min=0.1), FloatSlider(value=0.2, description='ul', max=1.0, min=-1.0), FloatSlider(value=1.0, description='hr', max=10.0, min=0.1), FloatSlider(value=-0.2, description='ur', max=1.0, min=-1.0), Output()), _dom_classes=('widget-interact',))" 1813 | ] 1814 | }, 1815 | "metadata": {}, 1816 | "output_type": "display_data" 1817 | } 1818 | ], 1819 | "source": [ 1820 | "interactive(plot_sw_Riemann_curves, \n", 1821 | " hl = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 1822 | " ul = FloatSlider(min = -1.0, max = 1.0, value = 0.2), \n", 1823 | " hr = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 1824 | " ur = FloatSlider(min = -1.0, max = 1.0, value = -0.2))" 1825 | ] 1826 | }, 1827 | { 1828 | "cell_type": "markdown", 1829 | "metadata": {}, 1830 | "source": [ 1831 | "Finally, we can plot the solution in physical space." 1832 | ] 1833 | }, 1834 | { 1835 | "cell_type": "code", 1836 | "execution_count": 21, 1837 | "metadata": {}, 1838 | "outputs": [], 1839 | "source": [ 1840 | "def plot_sw_Riemann_solution(hl, ul, hr, ur):\n", 1841 | " \"Plot the Riemann problem solution for states (hl, ul) and (hr, ur)\"\n", 1842 | " \n", 1843 | " from scipy.optimize import brentq\n", 1844 | " \n", 1845 | " def phi(hstar):\n", 1846 | " \"Function defining the root\"\n", 1847 | " \n", 1848 | " if hl < hstar:\n", 1849 | " phi_l = ul - np.sqrt(0.5 * (hl - hstar) * (hl / hstar - hstar / hl))\n", 1850 | " else:\n", 1851 | " phi_l = ul + 2.0 * (np.sqrt(hl) - np.sqrt(hstar))\n", 1852 | " if hr < hstar:\n", 1853 | " phi_r = ur + np.sqrt(0.5 * (hr - hstar) * (hr / hstar - hstar / hr))\n", 1854 | " else:\n", 1855 | " phi_r = ur - 2.0 * (np.sqrt(hr) - np.sqrt(hstar))\n", 1856 | " \n", 1857 | " return phi_l - phi_r\n", 1858 | " \n", 1859 | " left_raref = False\n", 1860 | " left_shock = False\n", 1861 | " right_raref = False\n", 1862 | " right_shock = False\n", 1863 | " \n", 1864 | " hm = brentq(phi, 0.1 * h_min, 10.0 * h_max)\n", 1865 | " if hl < hm:\n", 1866 | " um = ul - np.sqrt(0.5 * (hl - hm) * (hl / hm - hm / hl))\n", 1867 | " else:\n", 1868 | " um = ul + 2.0 * (np.sqrt(hl) - np.sqrt(hm))\n", 1869 | " \n", 1870 | " h_maximum = np.max([h_max, hl, hr, hm])\n", 1871 | " h_minimum = np.min([h_min, hl, hr, hm])\n", 1872 | " u_maximum = np.max([u_max, ul, ur, um])\n", 1873 | " u_minimum = np.min([u_min, ul, ur, um])\n", 1874 | " dh = h_maximum - h_minimum\n", 1875 | " du = u_maximum - u_minimum\n", 1876 | " \n", 1877 | " xi1l = ul - np.sqrt(hl)\n", 1878 | " xi1m = um - np.sqrt(hm)\n", 1879 | " xi1r = ur - np.sqrt(hr)\n", 1880 | " if hm > hl:\n", 1881 | " left_shock = True\n", 1882 | " vsl = ul - hm / (hm - hl) * np.sqrt(0.5 * (hl - hm) * (hl / hm - hm / hl))\n", 1883 | " else:\n", 1884 | " left_raref = True\n", 1885 | " hl_raref = np.linspace(hl, hm, 20)\n", 1886 | " ul_raref = ul + 2.0 * (np.sqrt(hl) - np.sqrt(hl_raref))\n", 1887 | " xil_raref = ul_raref - np.sqrt(hl_raref)\n", 1888 | " \n", 1889 | " xi2r = ur + np.sqrt(hr)\n", 1890 | " xi2m = um + np.sqrt(hm)\n", 1891 | " xi2l = ul + np.sqrt(hl)\n", 1892 | " if hm > hr:\n", 1893 | " right_shock = True\n", 1894 | " vsr = ur + hm / (hm - hr) * np.sqrt(0.5 * (hr - hm) * (hr / hm - hm / hr))\n", 1895 | " else:\n", 1896 | " right_raref = True\n", 1897 | " hr_raref = np.linspace(hm, hr)\n", 1898 | " ur_raref = ur - 2.0 * (np.sqrt(hr) - np.sqrt(hr_raref))\n", 1899 | " xir_raref = ur_raref + np.sqrt(hr_raref)\n", 1900 | " \n", 1901 | " xi_min = np.min([-1.0, xi1l, xi1m, xi2r, xi2m])\n", 1902 | " xi_max = np.max([1.0, xi1l, xi1m, xi2r, xi2m])\n", 1903 | " d_xi = xi_max - xi_min\n", 1904 | " h_maximum = np.max([hl, hr, hm])\n", 1905 | " h_minimum = np.min([hl, hr, hm])\n", 1906 | " d_h = h_maximum - h_minimum\n", 1907 | " u_maximum = np.max([ul, ur, um])\n", 1908 | " u_minimum = np.min([ul, ur, um])\n", 1909 | " d_u = u_maximum - u_minimum\n", 1910 | " \n", 1911 | " xi = np.array([xi_min - 0.1 * d_xi])\n", 1912 | " h = np.array([hl])\n", 1913 | " u = np.array([ul])\n", 1914 | " if left_shock:\n", 1915 | " xi = np.append(xi, [vsl, vsl])\n", 1916 | " h = np.append(h, [hl, hm])\n", 1917 | " u = np.append(u, [ul, um])\n", 1918 | " else:\n", 1919 | " xi = np.append(xi, xil_raref)\n", 1920 | " h = np.append(h, hl_raref)\n", 1921 | " u = np.append(u, ul_raref)\n", 1922 | " if right_shock:\n", 1923 | " xi = np.append(xi, [vsr, vsr])\n", 1924 | " h = np.append(h, [hm, hr])\n", 1925 | " u = np.append(u, [um, ur])\n", 1926 | " else:\n", 1927 | " xi = np.append(xi, xir_raref)\n", 1928 | " h = np.append(h, hr_raref)\n", 1929 | " u = np.append(u, ur_raref)\n", 1930 | " xi = np.append(xi, [xi_max + 0.1 * d_xi])\n", 1931 | " h = np.append(h, [hr])\n", 1932 | " u = np.append(u, [ur])\n", 1933 | " \n", 1934 | " fig = plt.figure(figsize=(12,8))\n", 1935 | " ax1 = fig.add_subplot(221)\n", 1936 | " ax1.plot(xi, h, 'b-', label = \"True solution\")\n", 1937 | " ax1.set_ybound(h_minimum - 0.1 * d_h, h_maximum + 0.1 * d_h)\n", 1938 | " ax1.set_xlabel(r\"$\\xi$\")\n", 1939 | " ax1.set_ylabel(r\"$h$\")\n", 1940 | " ax1.legend()\n", 1941 | " ax2 = fig.add_subplot(222)\n", 1942 | " ax2.plot(xi, u, 'b-', label = \"True solution\")\n", 1943 | " ax2.set_ybound(u_minimum - 0.1 * d_u, u_maximum + 0.1 * d_u)\n", 1944 | " ax2.set_xlabel(r\"$\\xi$\")\n", 1945 | " ax2.set_ylabel(r\"$u$\")\n", 1946 | " ax2.legend()\n", 1947 | " \n", 1948 | " ax3 = fig.add_subplot(223)\n", 1949 | " left_end = np.min([-1.0, 1.1*xi1l])\n", 1950 | " right_end = np.max([1.0, 1.1*xi2r])\n", 1951 | " left_edge = left_end - xi1l\n", 1952 | " right_edge = right_end - xi1r\n", 1953 | " x1_start_points_l = np.linspace(np.min([left_edge, left_end]), 0.0, 20)\n", 1954 | " x1_start_points_r = np.linspace(0.0, np.max([right_edge, right_end]), 20)\n", 1955 | " t1_end_points_l = np.ones_like(x1_start_points_l)\n", 1956 | " t1_end_points_r = np.ones_like(x1_start_points_r)\n", 1957 | " \n", 1958 | " # Look for intersections\n", 1959 | " if left_shock:\n", 1960 | " t1_end_points_l = np.minimum(t1_end_points_l, x1_start_points_l / (vsl - xi1l))\n", 1961 | " x1_end_points_l = x1_start_points_l + xi1l * t1_end_points_l\n", 1962 | " if right_shock:\n", 1963 | " t1_end_points_r = np.minimum(t1_end_points_r, x1_start_points_r / (vsr - xi1r))\n", 1964 | " else:\n", 1965 | " t1_end_points_r = np.minimum(t1_end_points_r, x1_start_points_r / (xi2r - xi1r))\n", 1966 | " x1_end_points_r = x1_start_points_r + xi1r * t1_end_points_r\n", 1967 | " # Note: here we are cheating, and using the characteristic speed of the middle state, \n", 1968 | " # ignoring how it varies across the rarefaction\n", 1969 | " t1_final_points_r = np.ones_like(x1_start_points_r)\n", 1970 | " if left_shock:\n", 1971 | " t1_final_points_r = np.minimum(t1_final_points_r, \n", 1972 | " (x1_end_points_r - t1_end_points_r * xi1m) / \n", 1973 | " (vsl - xi1m))\n", 1974 | " x1_final_points_r = x1_end_points_r + (t1_final_points_r - t1_end_points_r) * xi1m\n", 1975 | " \n", 1976 | " for xs, xe, te in zip(x1_start_points_l, x1_end_points_l, t1_end_points_l):\n", 1977 | " ax3.plot([xs, xe], [0.0, te], 'b-')\n", 1978 | " for xs, xe, te in zip(x1_start_points_r, x1_end_points_r, t1_end_points_r):\n", 1979 | " ax3.plot([xs, xe], [0.0, te], 'g-')\n", 1980 | " for xs, xe, ts, te in zip(x1_end_points_r, x1_final_points_r, t1_end_points_r, \n", 1981 | " t1_final_points_r):\n", 1982 | " ax3.plot([xs, xe], [ts, te], 'g-')\n", 1983 | " \n", 1984 | " # Highlight the waves\n", 1985 | " if left_shock:\n", 1986 | " ax3.plot([0.0, vsl], [0.0, 1.0], 'r-', linewidth=2)\n", 1987 | " else:\n", 1988 | " ax3.plot([0.0, xi1l], [0.0, 1.0], 'r-', linewidth=2)\n", 1989 | " ax3.plot([0.0, xi1m], [0.0, 1.0], 'r-', linewidth=2)\n", 1990 | " xi = np.linspace(xi1l, xi1m, 11)\n", 1991 | " x_end_rarefaction = xi\n", 1992 | " for xe in x_end_rarefaction:\n", 1993 | " ax3.plot([0.0, xe], [0.0, 1.0], 'r--')\n", 1994 | " if right_shock:\n", 1995 | " ax3.plot([0.0, vsr], [0.0, 1.0], 'r-', linewidth=2)\n", 1996 | " else:\n", 1997 | " ax3.plot([0.0, xi2m], [0.0, 1.0], 'r-', linewidth=2)\n", 1998 | " ax3.plot([0.0, xi2r], [0.0, 1.0], 'r-', linewidth=2)\n", 1999 | " \n", 2000 | " ax3.set_xlabel(r\"$x$\")\n", 2001 | " ax3.set_ylabel(r\"$t$\")\n", 2002 | " ax3.set_title(\"1-characteristics\")\n", 2003 | " ax3.set_xbound(left_end, right_end)\n", 2004 | " \n", 2005 | " ax4 = fig.add_subplot(224)\n", 2006 | " left_end = np.min([-1.0, 1.1*xi1l])\n", 2007 | " right_end = np.max([1.0, 1.1*xi2r])\n", 2008 | " left_edge = left_end - xi2l\n", 2009 | " right_edge = right_end - xi2r\n", 2010 | " x2_start_points_l = np.linspace(np.min([left_edge, left_end]), 0.0, 20)\n", 2011 | " x2_start_points_r = np.linspace(0.0, np.max([right_edge, right_end]), 20)\n", 2012 | " x2_end_points_r = x2_start_points_r + xi2r\n", 2013 | " t2_end_points_l = np.ones_like(x2_start_points_l)\n", 2014 | " t2_end_points_r = np.ones_like(x2_start_points_r)\n", 2015 | " \n", 2016 | " # Look for intersections\n", 2017 | " if right_shock:\n", 2018 | " t2_end_points_r = np.minimum(t2_end_points_r, x2_start_points_r / (vsr - xi2r))\n", 2019 | " x2_end_points_r = x2_start_points_r + xi2r * t2_end_points_r\n", 2020 | " if left_shock:\n", 2021 | " t2_end_points_l = np.minimum(t2_end_points_l, x2_start_points_l / (vsl - xi2l))\n", 2022 | " else:\n", 2023 | " t2_end_points_l = np.minimum(t2_end_points_l, x2_start_points_l / (xi1l - xi2l))\n", 2024 | " x2_end_points_l = x2_start_points_l + xi2l * t2_end_points_l\n", 2025 | " # Note: here we are cheating, and using the characteristic speed of the middle state, \n", 2026 | " # ignoring how it varies across the rarefaction\n", 2027 | " t2_final_points_l = np.ones_like(x2_start_points_l)\n", 2028 | " if right_shock:\n", 2029 | " t2_final_points_l = np.minimum(t2_final_points_l, \n", 2030 | " (x2_end_points_l - t2_end_points_l * xi2m) / \n", 2031 | " (vsr - xi2m))\n", 2032 | " x2_final_points_l = x2_end_points_l + (t2_final_points_l - t2_end_points_l) * xi2m\n", 2033 | " \n", 2034 | " for xs, xe, te in zip(x2_start_points_r, x2_end_points_r, t2_end_points_r):\n", 2035 | " ax4.plot([xs, xe], [0.0, te], 'b-')\n", 2036 | " for xs, xe, te in zip(x2_start_points_l, x2_end_points_l, t2_end_points_l):\n", 2037 | " ax4.plot([xs, xe], [0.0, te], 'g-')\n", 2038 | " for xs, xe, ts, te in zip(x2_end_points_l, x2_final_points_l, t2_end_points_l, \n", 2039 | " t2_final_points_l):\n", 2040 | " ax4.plot([xs, xe], [ts, te], 'g-')\n", 2041 | " \n", 2042 | " # Highlight the waves\n", 2043 | " if left_shock:\n", 2044 | " ax4.plot([0.0, vsl], [0.0, 1.0], 'r-', linewidth=2)\n", 2045 | " else:\n", 2046 | " ax4.plot([0.0, xi1l], [0.0, 1.0], 'r-', linewidth=2)\n", 2047 | " ax4.plot([0.0, xi1m], [0.0, 1.0], 'r-', linewidth=2)\n", 2048 | " if right_shock:\n", 2049 | " ax4.plot([0.0, vsr], [0.0, 1.0], 'r-', linewidth=2)\n", 2050 | " else:\n", 2051 | " ax4.plot([0.0, xi2m], [0.0, 1.0], 'r-', linewidth=2)\n", 2052 | " ax4.plot([0.0, xi2r], [0.0, 1.0], 'r-', linewidth=2)\n", 2053 | " xi = np.linspace(xi2m, xi2r, 11)\n", 2054 | " x_end_rarefaction = xi\n", 2055 | " for xe in x_end_rarefaction:\n", 2056 | " ax4.plot([0.0, xe], [0.0, 1.0], 'r--')\n", 2057 | " \n", 2058 | " ax4.set_xlabel(r\"$x$\")\n", 2059 | " ax4.set_ylabel(r\"$t$\")\n", 2060 | " ax4.set_title(\"2-characteristics\")\n", 2061 | " ax4.set_xbound(left_end, right_end)\n", 2062 | " \n", 2063 | " fig.tight_layout()" 2064 | ] 2065 | }, 2066 | { 2067 | "cell_type": "code", 2068 | "execution_count": 22, 2069 | "metadata": {}, 2070 | "outputs": [ 2071 | { 2072 | "data": { 2073 | "application/vnd.jupyter.widget-view+json": { 2074 | "model_id": "29ed701b74d14c70a6acb95f4dd2e7a3", 2075 | "version_major": 2, 2076 | "version_minor": 0 2077 | }, 2078 | "text/html": [ 2079 | "Failed to display Jupyter Widget of type interactive
.
\n", 2081 | " If you're reading this message in the Jupyter Notebook or JupyterLab Notebook, it may mean\n", 2082 | " that the widgets JavaScript is still loading. If this message persists, it\n", 2083 | " likely means that the widgets JavaScript library is either not installed or\n", 2084 | " not enabled. See the Jupyter\n", 2085 | " Widgets Documentation for setup instructions.\n", 2086 | "
\n", 2087 | "\n", 2088 | " If you're reading this message in another frontend (for example, a static\n", 2089 | " rendering on GitHub or NBViewer),\n", 2090 | " it may mean that your frontend doesn't currently support widgets.\n", 2091 | "
\n" 2092 | ], 2093 | "text/plain": [ 2094 | "interactive(children=(FloatSlider(value=1.0, description='hl', max=10.0, min=0.1), FloatSlider(value=0.2, description='ul', max=1.0, min=-1.0), FloatSlider(value=1.0, description='hr', max=10.0, min=0.1), FloatSlider(value=-0.2, description='ur', max=1.0, min=-1.0), Output()), _dom_classes=('widget-interact',))" 2095 | ] 2096 | }, 2097 | "metadata": {}, 2098 | "output_type": "display_data" 2099 | } 2100 | ], 2101 | "source": [ 2102 | "interactive(plot_sw_Riemann_solution, \n", 2103 | " hl = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 2104 | " ul = FloatSlider(min = -1.0, max = 1.0, value = 0.2), \n", 2105 | " hr = FloatSlider(min = 0.1, max = 10.0, value = 1.0), \n", 2106 | " ur = FloatSlider(min = -1.0, max = 1.0, value = -0.2))" 2107 | ] 2108 | } 2109 | ], 2110 | "metadata": { 2111 | "kernelspec": { 2112 | "display_name": "Python 3", 2113 | "language": "python", 2114 | "name": "python3" 2115 | }, 2116 | "language_info": { 2117 | "codemirror_mode": { 2118 | "name": "ipython", 2119 | "version": 3 2120 | }, 2121 | "file_extension": ".py", 2122 | "mimetype": "text/x-python", 2123 | "name": "python", 2124 | "nbconvert_exporter": "python", 2125 | "pygments_lexer": "ipython3", 2126 | "version": "3.6.4" 2127 | } 2128 | }, 2129 | "nbformat": 4, 2130 | "nbformat_minor": 1 2131 | } 2132 | -------------------------------------------------------------------------------- /Lesson_05_Approximate_Solvers.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "text/html": [ 11 | "\n", 12 | "\n", 13 | "\n", 14 | "\n", 15 | "\n", 16 | "\n", 17 | "\n", 138 | "\n", 139 | "\n" 156 | ], 157 | "text/plain": [ 158 | "