{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Step 3 : generate $Q_\\Delta$ coefficients\n", "\n", "📜 _We denote by_ $Q_\\Delta$ _the approximation of a given_ $Q$ _matrix related to any_ $Q$_-coefficients (from collocation, RK, ...) :_\n", "\n", "$$\n", "\\begin{array}\n", " {c|c}\n", " \\tau & Q \\\\\n", " \\hline\n", " & w^\\top\n", "\\end{array}\n", "$$\n", "\n", "There is **two approaches** to generate $Q_\\Delta$ approximations, from which you can choose in function of your needs and preferences." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use a generic function\n", "\n", "The quick easy way, simply import :" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from qmat import genQDeltaCoeffs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then considering, e.g, the following collocation method :" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from qmat.qcoeff.collocation import Collocation\n", "coll = Collocation.getInstance() # use default parameters : 4 LEGENDRE RADAU-RIGHT nodes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "we can generate the $Q_\\Delta$ matrix from a Backward Euler discretization between the nodes (as in the [original SDC method](https://link.springer.com/article/10.1023/A:1022338906936)) :" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.08858796 0. 0. 0. ]\n", " [0.08858796 0.3208789 0. 0. ]\n", " [0.08858796 0.3208789 0.3781926 0. ]\n", " [0.08858796 0.3208789 0.3781926 0.21234054]]\n" ] } ], "source": [ "QDelta = genQDeltaCoeffs(\"BE\", nodes=coll.nodes)\n", "print(QDelta)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... or get the LU approximation from [[Weiser, 2015]](https://doi.org/10.1007/s10543-014-0540-y) :" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.11299948 0. 0. 0. ]\n", " [0.234384 0.29050213 0. 0. ]\n", " [0.21668178 0.48341808 0.30825766 0. ]\n", " [0.22046221 0.46683684 0.44141588 0.11764706]]\n" ] } ], "source": [ "QDelta = genQDeltaCoeffs(\"LU\", Q=coll.Q)\n", "print(QDelta)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... or the diagonal approximation from [[van der Houwen & Sommeijer, 1991]](https://doi.org/10.1137/0912054) :" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.32049937 0. 0. 0. ]\n", " [0. 0.08915379 0. 0. ]\n", " [0. 0. 0.18173956 0. ]\n", " [0. 0. 0. 0.2333628 ]]\n" ] } ], "source": [ "QDelta = genQDeltaCoeffs(\"VDHS\", nNodes=coll.nNodes, nodeType=coll.nodeType, quadType=coll.quadType)\n", "print(QDelta)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "... or even some magical diagonal coefficients obtained with a black box optimizer that does not exists anymore from [[Speck, 2021]](https://zenodo.org/records/5775971): " ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.31987868 0. 0. 0. ]\n", " [0. 0.08887606 0. 0. ]\n", " [0. 0. 0.18123663 0. ]\n", " [0. 0. 0. 0.23273925]]\n" ] } ], "source": [ "QDelta = genQDeltaCoeffs(\"MIN3\", nNodes=coll.nNodes, nodeType=coll.nodeType, quadType=coll.quadType)\n", "print(QDelta)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> 🔍 ... and yes, the `MIN3` coefficients are almost the same as those from `VDHS`, even if both where obtained independently ! \n", "> But who really _never ever_ re-invented the wheel in research 😉\n", "\n", "Note that depending on the requested approximation, different arguments may be required for the $Q_\\Delta$-generator called in the background.\n", "If not provided, a descriptive exception is raised, for instance : " ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "TypeError: TimeStepping.__init__() missing 1 required positional argument: 'nodes'\n" ] } ], "source": [ "try:\n", " QDelta = genQDeltaCoeffs(\"BE\")\n", "except Exception as e:\n", " print(f\"{e.__class__.__name__}: {e}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To allow more generic call, it is possible to give keyword arguments that will be used only for some $Q_\\Delta$ matrices :" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "for qdType in [\"BE\", \"LU\", \"VDHS\", \"MIN3\"]:\n", " QDelta = genQDeltaCoeffs(qdType,\n", " Q=coll.Q, nodes=coll.nodes, nNodes=coll.nNodes, nodeType=coll.nodeType, quadType=coll.quadType)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And to **make it more simple**, it is also possible to only give a `QGenerator` object as `qGen` keyword argument only, \n", "and `qmat` will extract all required arguments from it :" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "for qdType in [\"BE\", \"LU\", \"VDHS\", \"MIN3\"]:\n", " QDelta = genQDeltaCoeffs(qdType, qGen=coll)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> 🔔 As for $Q$-generators, different unique aliases exists for each $Q_\\Delta$-generators. For instance :" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "QDelta = genQDeltaCoeffs(\"IE\", nodes=coll.nodes) # equivalent to BE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Don't hesitate to look at the [API documentation](../api/qmat/qdelta/index.html) for a complete list of available generators ..." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use generator objects\n", "\n", "As for $Q$-generators, you can also retrieve the $Q_\\Delta$-generators with their classes, using one of the following approaches :\n", "\n", "- import the generator directly from its submodule" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "from qmat.qdelta.timestepping import BE\n", "approx = BE(nodes=coll.nodes)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- retrieve it with one of its aliases from the `QDELTA_GENERATORS` dictionary" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "from qmat import QDELTA_GENERATORS\n", "Generator = QDELTA_GENERATORS[\"IE\"]\n", "approx = Generator(nodes=coll.nodes)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In both case, you'll instantiate an object that provides $Q_\\Delta$ matrix through a `getQDelta` method :" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.08858796 0. 0. 0. ]\n", " [0.08858796 0.3208789 0. 0. ]\n", " [0.08858796 0.3208789 0.3781926 0. ]\n", " [0.08858796 0.3208789 0.3781926 0.21234054]]\n" ] } ], "source": [ "print(approx.getQDelta())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> ⚠️ While the $Q_\\Delta$ generators do have a `QDelta` attribute, it is usually zero-initialized before calling the `getQDelta` method !\n", "\n", "The reason behind the use of a method here (and not a property) is that some approximations can vary depending on a given iteration number, _e.g_ :" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Approximation for k=1 :\n", "[[0.08858796 0. 0. 0. ]\n", " [0. 0.40946686 0. 0. ]\n", " [0. 0. 0.78765946 0. ]\n", " [0. 0. 0. 1. ]]\n", "Approximation for k=2 :\n", "[[0.04429398 0. 0. 0. ]\n", " [0. 0.20473343 0. 0. ]\n", " [0. 0. 0.39382973 0. ]\n", " [0. 0. 0. 0.5 ]]\n" ] } ], "source": [ "approx = QDELTA_GENERATORS[\"MIN-SR-FLEX\"](nNodes=coll.nNodes, nodeType=coll.nodeType, quadType=coll.quadType)\n", "\n", "print(\"Approximation for k=1 :\")\n", "print(approx.getQDelta(k=1))\n", "print(\"Approximation for k=2 :\")\n", "print(approx.getQDelta(k=2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However this corresponds to some very specific methods, in practice most of the time any $k$ argument given to the `getQDelta` method will be ignored.\n", "Finally, note that all $Q_\\Delta$-generator objects have a `genCoeffs` method, which per default provides the same result as the `getQDelta` method, but has some more functionalities (covered in a later tutorial).\n", "\n", "> 🔔 As for the `genQDeltaCoeffs` function before, you can also use the `qGen` keyword argument for generic calls that will take only the name of the `QDeltaGenerator` as parameter.\n", "\n", "Now, you can use those $Q$-coefficients and $Q_\\Delta$ approximations to [build a Spectral Deferred Correction type time-stepper ...](./04_sdc.ipynb)" ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 2 }