diff --git a/book/_config.yml b/book/_config.yml index 4273941..6a3e5a7 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -3,11 +3,22 @@ author: Caspar Jungbacker logo: images/TUDelft_logo_rgb.png execute: - execute_notebooks: auto + execute_notebooks: 'off' + +launch_buttons: + thebe: true sphinx: extra_extensions: - sphinx_inline_tabs - jupyterlite_sphinx + - sphinx.ext.imgconverter + - download_link_replacer + - jupyterbook_patches config: jupyterlite_config: jupyterlite-config.json + html_js_files: + - https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js + thebe_config: + use_thebe_lite: true + exclude_patterns: ["**/_*.yml", "**/*.md", "**/*.ipynb"] \ No newline at end of file diff --git a/book/_toc.yml b/book/_toc.yml index 805543d..c4ba6c7 100644 --- a/book/_toc.yml +++ b/book/_toc.yml @@ -23,20 +23,38 @@ parts: sections: - file: workflows/git-merge-conflict - file: workflows/gitlab + - file: workflows/github - caption: Features chapters: - file: basic-features/overview + sections: + - file: basic-features/recipes.md - file: features/overview sections: - file: features/hypothesis - file: features/remove_from_publish - - file: features/recipes.md - file: features/grasple.md + - file: features/custom_toc.md + - file: features/custom_download.md + - file: features/h5p.md + - file: features/live_code.md + sections: + - file: features/widgets.ipynb + - file: features/exercise_checking.ipynb - file: features/jupyterlite/jupyterlite sections: - file: features/jupyterlite/configuration - file: features/jupyterlite/including-jupyterlite - - file: features/h5p.md + + - caption: Examples chapters: - - file: examples/overview \ No newline at end of file + - file: examples/overview + - file: examples/h5p_examples.md + - file: examples/theory_questions.md + - file: examples/interactive_templates.md + sections: + - file: examples/example_gumbel.ipynb + - file: examples/example_quiz_interactive.ipynb + - file: examples/coding_theory_widgets.ipynb + - file: examples/parametric_questions.ipynb \ No newline at end of file diff --git a/book/features/recipes.md b/book/basic-features/recipes.md similarity index 100% rename from book/features/recipes.md rename to book/basic-features/recipes.md diff --git a/book/examples/Unconstrained_optimization_stripped.ipynb b/book/examples/Unconstrained_optimization_stripped.ipynb new file mode 100644 index 0000000..6ff49e9 --- /dev/null +++ b/book/examples/Unconstrained_optimization_stripped.ipynb @@ -0,0 +1,540 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Unconstrained optimization\n", + "\n", + "In this chapter, we'll cover how to apply `scipy.optimize.minimize` to unconstrained optimization problems. As a reminder, unconstrained optimization considers:\n", + "\n", + "$$\\mathop {\\min }\\limits_x f\\left( x \\right)$$\n", + "with $x$ the design variable of length $n$ and $f$ the objective function.\n", + "\n", + "## Method\n", + "In this course, we're making use of the function `scipy.optimize.minimize`. The documentation of this function is available here: \n", + "https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html. In this course we'll cover only the relevant parts.\n", + "\n", + "For unconstrained optimization we need to run at least `scipy.optimize.minimize(fun, x0, ...)` with:\n", + "- `fun`, the objective function $f(x)$ to be minimized. `fun` is a callable. The `scipy.optmize.minimize` function takes care of defining and inputing our design variable $x$.\n", + "- `x0`, the initial guess for our design variable $x$. It needs to be a `ndarray` with length $n$\n", + "\n", + "The function `scipy.optimize.minimize` outputs an object `scipy.optimize.OptimizeResult`. with:\n", + "- `scipy.optimize.OptimizeResult.x` the optimized solution of the design variable `x`. It is a `ndarray` with length $n$\n", + "- `scipy.optimize.OptimizeResult.success`, a indication whether or not the optimizer was executed succesfully. It is a `bool`, indicating `True` or `False`\n", + "- `scipy.optimize.OptimizeResult.message`, a message describing the cause of termination of the optimizatino algorithm. It is a `str`.\n", + "- `scipy.optimize.OptimizeResult.fun`, the values of the optimized objective function $f$. It is a `int` or `float`\n", + "- `scipy.optimize.OptimizeResult.nit`, the number of iteration performed by the optimizer. It is a `int`\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 1\n", + "\n", + "### Problem\n", + "It is desired to determine the number of bathymetry maps $n$ of a local area that should be produced to maximize the profit of a company. The total cost of production and distribution is €$75$ per unit $n$. The revenues are proportional to the number of units multiplied by its price: $Revenues = n \\cdot Price $\n", + "\n", + "The demand depends on the price ($Price = 150 - 0.01n^2$), as shown in the graph:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import numpy as np\n", + "import matplotlib.pylab as plt\n", + "n = np.linspace(0,120,100)\n", + "price = 150 - 0.01 * n**2\n", + "plt.plot(n,price)\n", + "plt.xlabel('$n$ (-)')\n", + "plt.ylabel('$Price$ (€)')\n", + "plt.ylim(0,160)\n", + "plt.xlim(0,120)\n", + "ax = plt.gca()\n", + "ax.grid(True, which='both')\n", + "ax.spines['left'].set_position('zero')\n", + "ax.spines['right'].set_color('none')\n", + "ax.yaxis.tick_left()\n", + "ax.spines['bottom'].set_position('zero')\n", + "ax.spines['top'].set_color('none')\n", + "ax.xaxis.tick_bottom()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The profit can be estimated as the revenues minus the total costs." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Model\n", + "The function for the profit can be found by combining the relations in the problem statement. However, this is the profit which should be maximized. To turn this into a minimization problem, the profit can be multiplied with $-1$. The final model of this problem results in:\n", + "\n", + "$$ \\mathop {\\min }\\limits_n \\left(75n - \\left( 150 - 0.01n^2 \\right) n \\right) $$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Method\n", + "\n", + "This model is described using `scipy.optimize.minimize` according to the standard structure in this course.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Importing libraries\n", + "For this problem, we'll use all three packages `scipy`, `numpy`, `matplotlib`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import scipy as sp \n", + "import numpy as np\n", + "import matplotlib.pylab as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Defining the variables\n", + "There are very few variables in this problem. In fact, the only variable we have to specify is the initial guess for the optimization algorithm. The objective function will be treated later.\n", + "The length of $n$ doesn't have to be specified." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "n0 = 20" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Defining the objective function\n", + "In the objective function, the formula given in the model description can be inserted. Or, each individual step can be calculated on a seperate line. Again, note that the profit is multiplied with $-1$ to maximize the profit in the minimization formulation.\n", + "This results in:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def negprofit(n):\n", + " price = 150 - 0.01 * n**2\n", + " revenues = price * n\n", + " totalcost = 75 * n\n", + " profit = revenues - totalcost\n", + " return -profit" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Solving the problem\n", + "Now, the problem can be solved. The result is stored in the variables `result` which is printed." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " message: Optimization terminated successfully.\n", + " success: True\n", + " status: 0\n", + " fun: -2499.9999999998727\n", + " x: [ 5.000e+01]\n", + " nit: 8\n", + " jac: [ 0.000e+00]\n", + " hess_inv: [[ 3.503e-01]]\n", + " nfev: 22\n", + " njev: 11\n" + ] + } + ], + "source": [ + "result = sp.optimize.minimize(negprofit,n0)\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Postprocessing\n", + "As this case is only one-dimensional and the potential range of values is limited, we can easily check this answer by an exhaustive search, evaluating all possible values for $n$. The plot below shows the $\\text{negative profit}$ for $0" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "n_range = np.linspace(0,100,100)\n", + "negprofit_result = negprofit(n_range)\n", + "plt.plot(n_range,negprofit_result)\n", + "plt.plot(result.x,result.fun,'o');\n", + "plt.xlabel('$n$')\n", + "plt.ylabel('Negative profit');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 2\n", + "\n", + "### Problem\n", + "8 data points are collected where $X_i$ is the explanatory variable and $Y_i$ the response variable. It is believed that the data follows the trend $Y = aX^b+c$ where $a$, $b$ and $c$ are the parameters to be determined to fit the model.\n", + "\n", + "| $X_i$ | $Y_i$ |\n", + "| :----- | :----- |\n", + "| 0.97 | 0.97 |\n", + "| 0 | 0.06 |\n", + "| 0.5 | 0.7 |\n", + "| 0.85 | 0.74 |\n", + "| 0.7 | 0.2 |\n", + "| 0.19 | 0.34 |\n", + "| 0.41 | 0.29 |\n", + "| 0.78 | 0.94 |" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "image/png": "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esLqzr/zCRMm6MiRIzpx4kRoW11dnZxOpzIzMyM6rw26c8y/+OILOZ1t3zpdLpek/511QM/qsffQLi1xhjHmf3/GuGbNGlNbW2vmz59v+vTpYz744ANjjDELFy40t9xyS2j/1j+pW7BggamtrTVr1qzhz9K7qKvH/PnnnzcpKSlmxYoVprGxMfT47LPPYvUrJJyuHvPv4q+0uq6rx7y5udlkZmaaX/ziF2bfvn1m+/btJjs728ybNy9Wv0LC6eoxf+aZZ0xKSopZuXKlef/9983OnTtNXl6eGTNmTKx+hYTT3NxsampqTE1NjZFkysvLTU1NTeijACL1HkrwdNOKFSvM0KFDTWpqqhk9erTZvn176GuzZs0yV155ZZv933jjDfOjH/3IpKammgsuuMCsWrUqyhMnvq4c8yuvvNJIaveYNWtW9AdPYF39d/5tBE/3dPWY79+/31xzzTWmd+/eJjMz05SUlJgvvvgiylMntq4e84qKCnPZZZeZ3r17m0GDBpmbb77ZHD58OMpTJ65//OMf3/vf50i9hzqM4RwcAACwG2t4AACA9QgeAABgPYIHAABYj+ABAADWI3gAAID1CB4AAGA9ggcAAFiP4AEAANYjeAAAgPUIHgAJLxgM6tJLL9Vvf/vbNttfe+01paamasOGDTGaDEC8IHgAJDyn06nS0lKtWrVKn376qSTpv//9r2688Ub94Q9/0I033hjjCQHEGvfSAmCFU6dOafjw4Zo1a5bmzp2rcePG6ac//alWrFgR69EAxAGCB4A1Vq9erd/97ncaMmSILrjgAm3atEkulyvWYwGIAwQPAGucOHFCAwcO1MUXX6y33npLffr0afP1v//973r77be1YMGCGE0IIFYIHgDWmD17tiorK3XOOefo4MGDSk9Pj/VIAOIEi5YBWOHee+/V3/72N7311ls6deqU1qxZ026fyZMna//+/TGYDkCsETwAEt7TTz+tRx55RK+88opGjRql+fPna+nSpfr666/b7FdfX6/s7OwYTQkglggeAAnt1Vdf1a9//Ws999xzGjdunCTpjjvukN/v15///OfQfj6fT2effbZSUlJiNSqAGCJ4ACSs6upq3XjjjVq6dKl+9rOfhba73W7dcccd+uMf/6hAICBJeueddzRy5MhYjQogxli0DCAprF69Wp988olKS0tjPQqAGOAMD4CksG/fPuXk5MR6DAAxwhkeAABgPc7wAAAA6xE8AADAegQPAACwHsEDAACsR/AAAADrETwAAMB6BA8AALAewQMAAKxH8AAAAOsRPAAAwHoEDwAAsB7BAwAArPf/PM1lxqCxnMcAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "xi = np.array([0.97,0,0.5,0.85,0.7,0.19,0.41,0.78])\n", + "yi = np.array([0.97,0.06,0.7,0.74,0.2,0.34,0.29,0.94])\n", + "plt.xlabel('$X_i$')\n", + "plt.ylabel('$Y_i$')\n", + "plt.plot(xi,yi,'x');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Examples of the source of this data can be:\n", + " - Stress of a pre-stressed concrete specimen based on the deformation when applied an axial force\n", + " - Permeability-coefficient for compacted sand based on the grain size\n", + " - Job performance based on the number of working hours\n", + " - etc.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Model\n", + "The problem is modelled as a minimization of the squared error, where error is the difference between $a X_i^b+C$ and $Y_i$\n", + "$$\\mathop {\\min }\\limits_{a,b,c} \\sum\\limits_{i = 1}^8 {{{\\left( {a{X_i}^b + c - {Y_i}} \\right)}^2}} $$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The plot below shows the data points and the total error squared." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "029c3407d1ac46e7a9765bca5443248d", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "interactive(children=(FloatSlider(value=2.0, description='a', max=5.0, step=0.2), FloatSlider(value=2.0, descr…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from ipywidgets import widgets, interact\n", + "x_range = np.linspace(0.0001,1,100)\n", + "def func(a,b,c):\n", + " fig, ax = plt.subplots(1, 1)\n", + " ax.plot(xi,yi,'x')\n", + " ax.plot(x_range,a * x_range**b + c);\n", + " ax.set_xlim([-0,1])\n", + " ax.set_ylim([-0,1])\n", + " y_est = a * xi ** b + c\n", + " error = np.sum((yi-y_est)**2)\n", + " title = 'Total error squared $=$'+str(round(error,4))\n", + " ax.set_title(title)\n", + " for i in range(8):\n", + " ax.annotate(\"\",xy=[xi[i],yi[i]],xytext=[xi[i],max(min(a * xi[i] ** b + c,1),0)],arrowprops=dict(arrowstyle= '<->'))\n", + " return\n", + " plt.show()\n", + "\n", + "interact(func,\n", + " a = widgets.FloatSlider(min=0, max=5, value=2, step=0.2, description=\"a\"),\n", + " b = widgets.FloatSlider(min=0, max=5, value=2, step=0.2, description=\"b\"),\n", + " c = widgets.FloatSlider(min=0, max=1, value=0.5, step=0.05, description=\"c\"));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Method\n", + "\n", + "Again, this model is described using `scipy.optimize.minimize` according to the [standard structure in this course](./Python_start_3.ipynb)\n", + "\n", + "#### Importing libraries\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "import scipy as sp\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Defining the variables\n", + "In this problem, there are 3 design variables: $a$, $b$ and $c$. In `scipy.optimize`, these variables are part of one array. You don't have to specify this variable beforehand, but the optimization algorithm will define the number of variables on your other input. Furthermore, input data is given which is required to specify explicitly. Additionally, an initial guess is required for the otimization algorithm." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "xi = np.array([0.97, 0 , 0.5, 0.85, 0.7, 0.19, 0.41, 0.78])\n", + "yi = np.array([0.97, 0.06, 0.7, 0.74, 0.2, 0.34, 0.29, 0.94])\n", + "abc0 = np.array([4., 2., 0.5]) #initial guess" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Objective function\n", + "The objective function was alerady defined. Because `a`, `b`, `c` and `xi` are all numpy arrays, we can simply use `*`, `**` and `+` to deal with the multiplication, exponent and summation of those arrays. `np.sum` is used to sum up all components of the array which is the result of `(yi-y_est)**2`" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [], + "source": [ + "def squarederror(abc):\n", + " a = abc[0]\n", + " b = abc[1]\n", + " c = abc[2]\n", + " y_est = a * xi ** b + c\n", + " error = np.sum((yi-y_est)**2)\n", + " return error" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Solve the problem\n", + "Now we can solve the actual problem" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " message: Optimization terminated successfully.\n", + " success: True\n", + " status: 0\n", + " fun: 0.3342203980898386\n", + " x: [ 8.028e-01 1.231e+00 1.233e-01]\n", + " nit: 54\n", + " jac: [-2.332e-06 2.570e-06 -3.166e-06]\n", + " hess_inv: [[ 6.233e-01 -3.674e-01 -3.660e-01]\n", + " [-3.674e-01 1.485e+01 2.251e+00]\n", + " [-3.660e-01 2.251e+00 5.564e-01]]\n", + " nfev: 276\n", + " njev: 69\n" + ] + } + ], + "source": [ + "result = sp.optimize.minimize(squarederror,abc0)\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Postprocessing\n", + "As this problem involves three design variables, a plot of all possible design variables with the objective function is not possible because it would require four dimensions. However, we can plot the data which the obtained solution to see how it looks like" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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73kuKiriqquuok2bNmRnZ7PHHntw//33V1Pa5FXe8/zoo4+y//77U7duXZo1a8Y555zD6tWrqyltcnrjjTc44YQTaN68OWlpaTz11FM/+55q/ywMVG7//Oc/g1q1agX33HNPMG/evODSSy8N6tWrF3zxxRelvv6zzz4L6tatG1x66aXBvHnzgnvuuSeoVatWMGnSpGpOnlzKe54vvfTS4Oabbw7+85//BPPnzw+GDx8e1KpVK/jvf/9bzcmTT3nPdbFvv/022H333YM+ffoE+++/f/WETWIVOc8nnnhi0KVLlyAvLy9YtGhR8O677wZvvfVWNaZOPuU9z9OnTw/S09OD22+/Pfjss8+C6dOnB/vuu2/Qv3//ak6eXKZMmRJcddVVwb///e8ACCZPnvyTr4/is9CSUwGHHHJIMGjQoK2O7b333sGVV15Z6uuvuOKKYO+9997q2AUXXBB07dq1yjKmgvKe59Lss88+wYgRIyo7Wsqp6LkeMGBAcPXVVwfXXXedJacMynueX3jhhSA3NzdYvXp1dcRLGeU9z3/729+C3XfffatjY8aMCVq2bFllGVNNWUpOFJ+FXq4qp02bNjFr1iz69Omz1fE+ffowY8aMUt/z9ttvb/P6Y445hpkzZ7J58+Yqy5rMKnKefywej1NYWEijRo2qImLKqOi5fuCBB1i4cCHXXXddVUdMCRU5z8888wydO3fmlltuoUWLFrRv357LLruMDRs2VEfkpFSR89y9e3eWLl3KlClTCIKAr776ikmTJnHcccdVR+QaI4rPwhq9QWdFrFq1ilgsRtOmTbc63rRpU1asWFHqe1asWFHq67ds2cKqVato1qxZleVNVhU5zz926623sm7dOk477bSqiJgyKnKuFyxYwJVXXsn06dPJzPSfkbKoyHn+7LPPePPNN6lduzaTJ09m1apVXHjhhaxZs8Z1OdtRkfPcvXt3Hn30UQYMGMDGjRvZsmULJ554InfccUd1RK4xovgsdCangtLS0rYaB0GwzbGfe31px7W18p7nYo8//jjXX389EydOZJdddqmqeCmlrOc6FotxxhlnMGLECNq3b19d8VJGef5Ox+Nx0tLSePTRRznkkEM49thjGTVqFA8++KCzOT+jPOd53rx5DB48mGuvvZZZs2bx4osvsmjRIgYNGlQdUWuU6v4s9P+ClVPjxo3JyMjY5v8RrFy5cpuGWmzXXXct9fWZmZnsvPPOVZY1mVXkPBebOHEi5557Lk888QRHH310VcZMCeU914WFhcycOZPZs2dz8cUXA+GHcRAEZGZm8vLLL3PUUUdVS/ZkUpG/082aNaNFixbk5uaWHOvQoQNBELB06VLatWtXpZmTUUXO88iRI+nRoweXX345APvttx/16tWjZ8+e3Hjjjc62V5IoPgudySmnrKwsOnXqRF5e3lbH8/Ly6N69e6nv6dat2zavf/nll+ncuTO1atWqsqzJrCLnGcIZnLPPPpvHHnvM6+llVN5znZOTwwcffMCcOXNKvgYNGsRee+3FnDlz6NKlS3VFTyoV+Tvdo0cPli1bxtq1a0uOzZ8/n/T0dFq2bFmleZNVRc7z+vXrSU/f+uMwIyMD+H6mQTsuks/CKlvSnMKKb0+87777gnnz5gVDhgwJ6tWrF3z++edBEATBlVdeGZx55pklry++bW7o0KHBvHnzgvvuu89byMugvOf5scceCzIzM4Nx48YFy5cvL/n69ttvo/oVkkZ5z/WPeXdV2ZT3PBcWFgYtW7YMTjnllGDu3LnBtGnTgnbt2gXnnXdeVL9CUijveX7ggQeCzMzMYPz48cHChQuDN998M+jcuXNwyCGHRPUrJIXCwsJg9uzZwezZswMgGDVqVDB79uySW/UT4bPQklNB48aNC9q0aRNkZWUFBx10UDBt2rSS7w0cODA4/PDDt3r966+/Hhx44IFBVlZWsNtuuwUTJkyo5sTJqTzn+fDDDw+Abb4GDhxY/cGTUHn/Tv+QJafsynueP/roo+Doo48O6tSpE7Rs2TIYNmxYsH79+mpOnXzKe57HjBkT7LPPPkGdOnWCZs2aBb/+9a+DpUuXVnPq5PLaa6/95L+5ifBZmBYEzsVJkqTU45ocSZKUkiw5kiQpJVlyJElSSrLkSJKklGTJkSRJKcmSI0mSUpIlR5IkpSRLjiRJSkmWHEmSlJIsOZKSXjweZ++99+aPf/zjVsdfeuklsrKyeOKJJyJKJilKlhxJSS89PZ3hw4czYcIEvvnmGwDef/99Tj31VG666SZOPfXUiBNKioJ7V0lKCVu2bKF9+/YMHDiQc889l65du9KvXz/GjRsXdTRJEbHkSEoZd911F3/6059o0aIFu+22G5MnTyYjIyPqWJIiYsmRlDLWrl1LkyZN2HPPPXnnnXeoV6/eVt9/5ZVX+OCDDxg6dGhECSVVJ0uOpJRx9tlnM3HiRBo2bMiiRYuoXbt21JEkRciFx5JSwjXXXMPzzz/PO++8w5YtW7jvvvu2eU3fvn356KOPIkgnKQqWHElJ79577+XWW2/l2WefZf/992fIkCHccsstbN68eavXLViwgHbt2kWUUlJ1s+RISmovvPACF110EY888ghdu3YF4JJLLqGgoICHH3645HX5+fnUr1+fzMzMqKJKqmaWHElJa9asWZx66qnccsstnHzyySXHc3JyuOSSS/jrX/9KLBYD4MMPP2TfffeNKqqkCLjwWFKNcNddd7FmzRqGDx8edRRJ1cSZHEk1wty5c+nYsWPUMSRVI2dyJElSSnImR5IkpSRLjiRJSkmWHEmSlJIsOZIkKSVZciRJUkqy5EiSpJRkyZEkSSnJkiNJklKSJUeSJKUkS44kSUpJlhxJkpSSLDmSJCkl/T9bxwpX7Qf61AAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "x_range = np.linspace(0,1,100)\n", + "plt.plot(xi,yi,'x');\n", + "plt.plot(x_range,result.x[0] * x_range ** result.x[1] + result.x[2])\n", + "plt.xlabel('$X_i$')\n", + "plt.ylabel('$Y_i$');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To solve uncontrained optimisation problem like:\n", + "\n", + "$$\n", + " \\mathop {\\min }\\limits_x f\\left( x \\right)\n", + "$$\n", + "\n", + "with $x$ the design variable of length $n$, you can make use of `scipy.optimize.minimize(fun, x0, ...)` which outputs `scipy.optimize.OptimizeResult` with the problem's solution, some characteristics of the problem and the performance of the optimization algorithm." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.17" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/examples/coding_theory_widgets.ipynb b/book/examples/coding_theory_widgets.ipynb new file mode 100644 index 0000000..81c8255 --- /dev/null +++ b/book/examples/coding_theory_widgets.ipynb @@ -0,0 +1,333 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Combining coding theory with interactive questions and potential live coding\n", + "\n", + "This page shows an example of how to combine the introduction of a certain coding implementation with interactive questions. Automatic executation of the thebe live coding has been enables and the download button has been replaced by a custom notebook file." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{custom_download_link} ./Unconstrained_optimization_stripped.ipynb\n", + ":replace_default: \"True\"\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example unconstrained optimization\n", + "\n", + "In this chapter, we'll cover how to apply `scipy.optimize.minimize` to unconstrained optimization problems. As a reminder, unconstrained optimization considers:\n", + "\n", + "```{math}\n", + ":label: unconstrained_optimizatino\n", + "\n", + "\\mathop {\\min }\\limits_x f\\left( x \\right)\n", + "```\n", + "with $x$ the design variable of length $n$ and $f$ the objective function.\n", + "\n", + "\n", + "### Method\n", + "In this course, we're making use of the function `scipy.optimize.minimize`. The documentation of this function is available here: \n", + "https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html. In this course we'll cover only the relevant parts.\n", + "\n", + "For unconstrained optimization we need to run at least `scipy.optimize.minimize(fun, x0, ...)` with:\n", + "- `fun`, the objective function $f(x)$ to be minimized. `fun` is a callable. The `scipy.optmize.minimize` function takes care of defining and inputing our design variable $x$.\n", + "- `x0`, the initial guess for our design variable $x$. It needs to be a `ndarray` with length $n$\n", + "\n", + "The function `scipy.optimize.minimize` outputs an object `scipy.optimize.OptimizeResult`. with:\n", + "- `scipy.optimize.OptimizeResult.x` the optimized solution of the design variable `x`. It is a `ndarray` with length $n$\n", + "- `scipy.optimize.OptimizeResult.success`, a indication whether or not the optimizer was executed succesfully. It is a `bool`, indicating `True` or `False`\n", + "- `scipy.optimize.OptimizeResult.message`, a message describing the cause of termination of the optimizatino algorithm. It is a `str`.\n", + "- `scipy.optimize.OptimizeResult.fun`, the values of the optimized objective function $f$. It is a `int` or `float`\n", + "- `scipy.optimize.OptimizeResult.nit`, the number of iteration performed by the optimizer. It is a `int`\n", + "\n", + ":::{card}\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example 1\n", + "\n", + "#### Problem\n", + "It is desired to determine the number of bathymetry maps $n$ of a local area that should be produced to maximize the profit of a company. The total cost of production and distribution is €$75$ per unit $n$. The revenues are proportional to the number of units multiplied by its price: $Revenues = n \\cdot Price $\n", + "\n", + "The demand depends on the price ($Price = 150 - 0.01n^2$), as shown in the graph:\n", + "\n", + "```{figure} ./coding_theory_widgets_images/output.png\n", + "---\n", + "width: 600px\n", + "name: demand with respect to pice\n", + "---\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The profit can be estimated as the revenues minus the total costs." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Model\n", + "The function for the profit can be found by combining the relations in the problem statement. However, this is the profit which should be maximized. To turn this into a minimization problem, the profit can be multiplied with $-1$. The final model of this problem results in:\n", + "\n", + "```{math}\n", + ":label: unconstrained_optimization_problem\n", + "\n", + "\\mathop {\\min }\\limits_n \\left(75n - \\left( 150 - 0.01n^2 \\right) n \\right)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::{card}\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Method\n", + "\n", + "This model is described using `scipy.optimize.minimize`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Importing libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "tags": [ + "auto-execute-page", + "thebe-init" + ] + }, + "outputs": [], + "source": [ + "import scipy as sp \n", + "import numpy as np\n", + "import matplotlib.pylab as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Defining the variables\n", + "There are very few variables in this problem. In fact, the only variable we have to specify is the initial guess for the optimization algorithm. The objective function will be treated later.\n", + "The length of $n$ doesn't have to be specified." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "tags": [ + "thebe-init" + ] + }, + "outputs": [], + "source": [ + "n0 = 20" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::{card}\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Defining the objective function\n", + "In the objective function, the formula given in the model description can be inserted. Or, each individual step can be calculated on a seperate line. Again, note that the profit is multiplied with $-1$ to maximize the profit in the minimization formulation.\n", + "This results in:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "tags": [ + "thebe-init" + ] + }, + "outputs": [], + "source": [ + "def negprofit(n):\n", + " price = 150 - 0.01 * n**2\n", + " revenues = price * n\n", + " totalcost = 75 * n\n", + " profit = revenues - totalcost\n", + " return -profit" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Solving the problem\n", + "Now, the problem can be solved. The result is stored in the variables `result` which is printed." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "tags": [ + "thebe-init" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " message: Optimization terminated successfully.\n", + " success: True\n", + " status: 0\n", + " fun: -2499.9999999998727\n", + " x: [ 5.000e+01]\n", + " nit: 8\n", + " jac: [ 0.000e+00]\n", + " hess_inv: [[ 3.503e-01]]\n", + " nfev: 22\n", + " njev: 11\n" + ] + } + ], + "source": [ + "result = sp.optimize.minimize(negprofit,n0)\n", + "print(result)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::{card}\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Postprocessing\n", + "As this case is only one-dimensional and the potential range of values is limited, we can easily check this answer by an exhaustive search, evaluating all possible values for $n$. The plot below shows the $\\text{negative profit}$ for $0" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "n_range = np.linspace(0,100,100)\n", + "negprofit_result = negprofit(n_range)\n", + "plt.plot(n_range,negprofit_result)\n", + "plt.plot(result.x,result.fun,'o');\n", + "plt.xlabel('$n$')\n", + "plt.ylabel('Negative profit');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::{card}\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise\n", + "\n", + ":::{card}\n", + "Adapt the code to answer the following questions.\n", + "\n", + "\n", + "\n", + "\n", + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/examples/coding_theory_widgets_images/output.png b/book/examples/coding_theory_widgets_images/output.png new file mode 100644 index 0000000..eafe77b Binary files /dev/null and b/book/examples/coding_theory_widgets_images/output.png differ diff --git a/book/examples/example_gumbel.ipynb b/book/examples/example_gumbel.ipynb new file mode 100644 index 0000000..9095c4c --- /dev/null +++ b/book/examples/example_gumbel.ipynb @@ -0,0 +1,259 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "# Programming assignment\n", + "\n", + "This page shows how you can convert the classical Jupyter Notebook assignment to a jupyter book format with live coding. A widget has been implemented to check the answers, of which the data is included in a separate python file. The page automatically loads and a custom download page has been added\n", + "\n", + "```{custom_download_link} ./example_gumbel_stripped.ipynb\n", + ":replace_default: \"True\"\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Gumbel Distribution Exercise\n", + "\n", + "Imagine you are concerned with the concentration of an airborne contaminant, $X$, measured in ppm. A previous benchmark study estimates that there is a 10% and 1% chance, respectively, of exceeding 4 and 10 ppm, respectively. You have been asked by the regulatory agency to estimate the contaminant concentration with 0.1% probability of being exceeded. Prior studies suggest that a Gumbel distribution can be used to model contaminant concentration, given by the CDF:\n", + "\n", + "$$\n", + "F_X(x) = \\exp{\\Bigg( -\\exp{\\Big(-\\frac{x-\\mu}{\\beta}\\Big)}\\Bigg)}\n", + "$$\n", + "\n", + "Using the cell blocks below as a guide (and also to check your analysis): Task 1) find the parameters of a Gumbel distribution that matches the information provided above, then, Tasks 2-3) use it to estimate the concentration with exceedance probability 0.1%.\n", + "\n", + "To complete this assignment, you can use `numpy`, `matplotlib` and from the `math` library, `log` and `e` (these are imported for you when you inialize the notebook)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "import micropip\n", + "await micropip.install(\"ipywidgets\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "from math import log, e\n", + "import numpy as np\n", + "from example_gumbel import check_example" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Task 0:** fill in the appropriate fitting points:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "auto-execute-page" + ] + }, + "outputs": [], + "source": [ + "x_1 = _\n", + "p_1 = _\n", + "x_2 = _\n", + "p_2 = _" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Task 1:** derive the distribution parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def gumbel_2_points(x_1, p_1, x_2, p_2): \n", + " \"\"\"Compute Gumbel distribution parameters from two points of the CDF.\n", + " Arguments:\n", + " x_1 (float): point one\n", + " p_1 (float): cumulative probability for point 1\n", + " x_2 (float): point two\n", + " p_2 (float): cumulative probability for point 2\n", + " \"\"\"\n", + "\n", + " # YOUR CODE GOES HERE #\n", + " beta = _\n", + " mu = _\n", + " #######################\n", + "\n", + " return mu, beta" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The cells below will print your parameter values and create a plot to help confirm you have the right implementation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "def plot_distribution(mu, beta,\n", + " x_1, p_1, x_2, p_2):\n", + " plt.title(\"Gumbel Distribution, $1-F_X(x)$\")\n", + " plt.xlabel(\"Contaminant Concentration, $X$ [ppm]\")\n", + " plt.ylabel(\"Exceedance Probability [--]\")\n", + " plt.grid(color='black', linestyle='-', linewidth=0.3)\n", + " x_axis = np.arange(0, 20, 0.1)\n", + " plt.plot(x_axis, np.vectorize(gumbel_distribution)(x_axis), linewidth=2)\n", + " plt.plot(x_1, 1 - p_1, 'ro')\n", + " plt.annotate(\"Point 1\", (x_1 + 0.4, 1 - p_1 + 0.02))\n", + " plt.plot(x_2, 1 - p_2, 'ro')\n", + " plt.annotate(\"Point 2\", (x_2 + 0.4, 1 - p_2 + 0.001))\n", + " plt.yscale(\"log\") \n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mu, beta = gumbel_2_points(x_1, p_1, x_2, p_2)\n", + "\n", + "print(f\"Your mu: {mu:0.5f}\\nYour beta: {beta:0.5f}\")\n", + "\n", + "gumbel_distribution = lambda x: 1 - e**(-e**(-(x - mu)/beta))\n", + "\n", + "plot_distribution(mu, beta, x_1, p_1, x_2, p_2)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Task 2:** write a function to solve for the random variable value (it will be tested for you with the Check answer button, along with the distribution parameters)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def find_x_with_probability_p(p): \n", + " \"\"\" Compute point in the gumbel distribution for which the CDF is p\n", + " Use the variables mu and beta defined above!\n", + " Hint: they have been defined globally, so you don't need to \n", + " include them as arguments.\n", + " \"\"\"\n", + " # YOUR CODE GOES HERE #\n", + " x = _\n", + " #######################\n", + "\n", + " return x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Task 3:** use the function `find_x_with_probability_p` to evaluate the random variable value with exceedance probabiltiy of 0.001." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# YOUR CODE GOES HERE #\n", + "x = _\n", + "#######################\n", + "\n", + "print(f\"The value of x with probability of exceedance 0.001 os {x:0.5f}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "check_example(globals())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/examples/example_gumbel.py b/book/examples/example_gumbel.py new file mode 100644 index 0000000..a2c1337 --- /dev/null +++ b/book/examples/example_gumbel.py @@ -0,0 +1,61 @@ +from grading import check +from math import isclose + +tolerance = 0.001 + +# Using rel_tol is more reliable in the face of aliasing, but to fit the instructions give we use abs_total +check_float = lambda a, b: isclose(a, b, rel_tol=0, abs_tol=tolerance) + +# We have to pass in the globals object since we need to have access to changes as well +@check +def check_example(glob): + mu, beta = glob["mu"], glob["beta"] + if (check_float(mu, -1.7461520011511888) and check_float(beta, 2.553428469837263)): + print(f"Part 1: you got the parameters right, well done! (checked with tolerance {tolerance})") + else: + print(f"Part 1: your parameters are incorrect. (checked with tolerance {tolerance})") + print(f" Other parts won't be graded until these are fixed. ") + return + + function = glob["find_x_with_probability_p"] + + test_inputs = [(1 - 0.001, 15.891029744351862), (1 - 0.2, 2.0838374640878863), (1 - 0.9, -3.875794191615308)] + failed = [] + + for x, out in test_inputs: + result = function(x) + if not check_float(result, out): + failed.append((x, out, result)) + + if len(failed) == 0: + print(f"Part 2: Well done, your inverse function is correct! (checked with tolerance {tolerance})") + else: + print(f"Part 2: Your function failed some tests. Keep in mind the tolerance is {tolerance}") + print(" --> Failed inputs:") + for case in failed: + print(f"{case[0]} gave {case[2]}, expected {case[1]}") + print(f" Other parts won't be graded until these are fixed.") + + x = glob["x"] + if check_float(x, 15.891029744351862): + print(f"Part 3: you got the value of x right, well done! (checked with tolerance {tolerance})") + else: + print(f"Part 3: your value of x is incorrect. (checked with tolerance {tolerance})") + return +""" +Task 0: +x_1 = 4 +p_1 = 1 - .1 +x_2 = 10 +p_2 = 1 - .01 + +Task 1: + beta = -(x_2 - x_1) / log(log(p_2) / log(p_1)) + mu = x_1 + beta * log(-log(p_1)) + +Task 2: +x = mu - beta * log(-log(p)) + +Task 3: +x = find_x_with_probability_p(1 - 0.001) +""" \ No newline at end of file diff --git a/book/examples/example_gumbel_stripped.ipynb b/book/examples/example_gumbel_stripped.ipynb new file mode 100644 index 0000000..e79657a --- /dev/null +++ b/book/examples/example_gumbel_stripped.ipynb @@ -0,0 +1,203 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "## Gumbel Distribution Exercise\n", + "\n", + "Imagine you are concerned with the concentration of an airborne contaminant, $X$, measured in ppm. A previous benchmark study estimates that there is a 10% and 1% chance, respectively, of exceeding 4 and 10 ppm, respectively. You have been asked by the regulatory agency to estimate the contaminant concentration with 0.1% probability of being exceeded. Prior studies suggest that a Gumbel distribution can be used to model contaminant concentration, given by the CDF:\n", + "\n", + "$$\n", + "F_X(x) = \\exp{\\Bigg( -\\exp{\\Big(-\\frac{x-\\mu}{\\beta}\\Big)}\\Bigg)}\n", + "$$\n", + "\n", + "Using the cell blocks below as a guide (and also to check your analysis): Task 1) find the parameters of a Gumbel distribution that matches the information provided above, then, Tasks 2-3) use it to estimate the concentration with exceedance probability 0.1%.\n", + "\n", + "To complete this assignment, you can use `numpy`, `matplotlib` and from the `math` library, `log` and `e`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "from math import log, e\n", + "import numpy as np" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Task 0:** fill in the appropriate fitting points:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "x_1 = _\n", + "p_1 = _\n", + "x_2 = _\n", + "p_2 = _" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Task 1:** derive the distribution parameters:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def gumbel_2_points(x_1, p_1, x_2, p_2): \n", + " \"\"\"Compute Gumbel distribution parameters from two points of the CDF.\n", + " Arguments:\n", + " x_1 (float): point one\n", + " p_1 (float): cumulative probability for point 1\n", + " x_2 (float): point two\n", + " p_2 (float): cumulative probability for point 2\n", + " \"\"\"\n", + "\n", + " # YOUR CODE GOES HERE #\n", + " beta = _\n", + " mu = _\n", + " #######################\n", + "\n", + " return mu, beta" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The cells below will print your parameter values and create a plot to help confirm you have the right implementation." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def plot_distribution(mu, beta,\n", + " x_1, p_1, x_2, p_2):\n", + " plt.title(\"Gumbel Distribution, $1-F_X(x)$\")\n", + " plt.xlabel(\"Contaminant Concentration, $X$ [ppm]\")\n", + " plt.ylabel(\"Exceedance Probability [--]\")\n", + " plt.grid(color='black', linestyle='-', linewidth=0.3)\n", + " x_axis = np.arange(0, 20, 0.1)\n", + " plt.plot(x_axis, np.vectorize(gumbel_distribution)(x_axis), linewidth=2)\n", + " plt.plot(x_1, 1 - p_1, 'ro')\n", + " plt.annotate(\"Point 1\", (x_1 + 0.4, 1 - p_1 + 0.02))\n", + " plt.plot(x_2, 1 - p_2, 'ro')\n", + " plt.annotate(\"Point 2\", (x_2 + 0.4, 1 - p_2 + 0.001))\n", + " plt.yscale(\"log\") \n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "mu, beta = gumbel_2_points(x_1, p_1, x_2, p_2)\n", + "\n", + "print(f\"Your mu: {mu:0.5f}\\nYour beta: {beta:0.5f}\")\n", + "\n", + "gumbel_distribution = lambda x: 1 - e**(-e**(-(x - mu)/beta))\n", + "\n", + "plot_distribution(mu, beta, x_1, p_1, x_2, p_2)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Task 2:** write a function to solve for the random variable value (it will be tested for you with the Check answer button, along with the distribution parameters)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def find_x_with_probability_p(p): \n", + " \"\"\" Compute point in the gumbel distribution for which the CDF is p\n", + " Use the variables mu and beta defined above!\n", + " Hint: they have been defined globally, so you don't need to \n", + " include them as arguments.\n", + " \"\"\"\n", + " # YOUR CODE GOES HERE #\n", + " x = _\n", + " #######################\n", + "\n", + " return x" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Task 3:** use the function `find_x_with_probability_p` to evaluate the random variable value with exceedance probabiltiy of 0.001." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# YOUR CODE GOES HERE #\n", + "x = _\n", + "#######################\n", + "\n", + "print(f\"The value of x with probability of exceedance 0.001 os {x:0.5f}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.13" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/examples/example_quiz_interactive.ipynb b/book/examples/example_quiz_interactive.ipynb new file mode 100644 index 0000000..3cc70d9 --- /dev/null +++ b/book/examples/example_quiz_interactive.ipynb @@ -0,0 +1,359 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Combining theory with interactive questions and extensive coding assignment\n", + "This page shows an example of how to combine theory, quizzes and code on one page. With headings and boxes, the interactive parts can be indicated (for now only if part of one cell). Automatic execution has been activated and a custom ipynb.file has replaced the download button.\n", + "\n", + "```{custom_download_link} ./example_quiz_interactive_stripped.ipynb\n", + ":replace_default: \"True\"\n", + "```\n", + "\n", + "\n", + "## Weighted least-squares estimation\n", + "In ordinary least-squares estimation, we assume that all observations are equally important. In many cases this is not realistic, as observations may be obtained by different measurement systems, or under different circumstances. We want our methodology for least-squares estimation to be able to take this into account.\n", + "\n", + "We achieve this goal by introducing a weight matrix in the minimization problem:\n", + "\n", + "$$\n", + "\\underset{\\mathrm{x}}{\\min} {\\mathrm{(y-Ax)^T W(y-Ax)}}\n", + "$$\n", + "\n", + "In the unweighted least-squares approach, we arrive at the normal equation by pre-multiplying both sides of $\\mathrm{y=Ax}$ with the transpose of the design matrix $\\mathrm{A^T}$:\n", + "\n", + "$$ \n", + "\\mathrm{y=Ax} \\; \\rightarrow \\; \\mathrm{A^T\\; y = A^T\\; A x }\n", + "$$\n", + "\n", + "In the weighted least-squares approach, we now need to add weight matrix $W$ to this pre-multiplication factor, i.e., $ \\mathrm{A^T W}$, to obtain the normal equation:\n", + "\n", + "$$ \n", + "\\mathrm{y=Ax} \\; \\rightarrow \\; \\mathrm{A^T W \\; y = A^TW \\; A x}\n", + "$$\n", + "\n", + "The normal matrix is now defined as $\\mathrm{N=A^T W A}$. From this, assuming that the normal matrix $N$ is invertible (non-singular) we find the weighted least-squares estimate $ \\mathrm{\\hat{x}} $, \n", + "\n", + "$$ \n", + "\\begin{align*}\n", + "\\mathrm{\\hat{x}} &= \\mathrm{(A^T W A)^{-1} A^T W y} \\\\\n", + " &= \\arg \\underset{\\mathrm{x}}{\\min} {\\mathrm{(y-Ax)^T W(y-Ax)}}\n", + "\\end{align*}\n", + "$$\n", + "\n", + "We also find the derived estimate $ \\mathrm{\\hat{y}} $ and $ \\mathrm{\\hat{\\epsilon}} $:\n", + "\n", + "$$\n", + "\\mathrm{\\hat{y} = A \\hat{x} = A (A^T W A )^{-1} A^T W y}\n", + "$$\n", + "\n", + "$$\n", + " \\mathrm{\\hat{\\epsilon} = y - \\hat{y}= y - A \\hat{x} = y-A (A^T W A )^{-1} A^T W y = (I- A(A^T W A )^{-1} A^T W) y}\n", + "$$\n", + "\n", + "### Quiz question\n", + ":::{card}\n", + "\n", + ":::\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "### Video\n", + "```{eval-rst}\n", + ".. raw:: html\n", + "\n", + " \n", + "```\n", + "\n", + "### Discussion on the weight matrix\n", + "The weight matrix $\\mathrm{W}$ expresses the (relative) weights between the observations. It is always a square matrix. The size of the weight matrix depends on the number of observations, $m$. The size of the weight matrix is $m\\times m$. \n", + "\n", + "If it is a unit matrix ($\\mathrm{W=I}$), this implies that all observations have equal weight. Note that in this case the equations are equal to the ordinary least-squares solution. \n", + "\n", + "If it is a diagonal matrix, with different values on the diagonal, this implies that entries with a higher value correspond to the observations which are considered to be of more importance. If the weight matrix has non-zero elements on the off-diagonal positions, this implies that (some) observations are correlated.\n", + "\n", + "### Weighted least-squares estimator: properties\n", + "\n", + "Until now, we have looked at the weighted least-squares solution of a single *realization* of the observations, where generally we assume that it is a realization of the *random* observable vector $Y$, since measurements are affected by random errors. As such it follows the the weighted least-squares *estimator* is given by:\n", + "\n", + "$$\n", + "\\hat{X} = \\mathrm{(A^T W A )^{-1} A^T W} Y\n", + "$$\n", + "\n", + "This estimator has two important properties: it is *linear* and *unbiased*.\n", + "\n", + "The linearity property is due to the fact that $\\hat{X}$ is a linear function of the observables $Y$.\n", + "\n", + "The unbiased property means that the expectation of $\\hat{X}$ is equal to the true (but unknown) $\\mathrm{x}$. This can be shown as follows:\n", + "\n", + "$$\n", + "\\mathbb{E}(\\hat{X}) = \\mathrm{(A^T W A )^{-1} A^T W} \\mathbb{E}(Y) = \\mathrm{(A^T W A )^{-1} A^T W Ax = x}\n", + "$$\n", + "\n", + "This a very desirable property. It applies that if we would repeat the measurements many times to obtain a new estimate, the *average of the estimated* values would be equal to the truy values." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise\n", + "\n", + ":::{card}\n", + "\n", + "You have a time series of 8 measurements and fit a model assuming a linear trend (constant velocity).\n", + "\n", + "Times of observations, observed values and number of observations are given as the variables `t`, `y` and `m`, as well as `numpy` and `matplotlib.pyplot` abbreviated as `np` and `plt`\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "# this will print all float arrays with 3 decimal places\n", + "import numpy as np\n", + "float_formatter = \"{:.2f}\".format\n", + "np.set_printoptions(formatter={'float_kind':float_formatter})\n", + "\n", + "import micropip\n", + "await micropip.install(\"ipywidgets\")\n", + "import ipywidgets as widgets\n", + "from IPython.display import display\n", + "import operator\n", + "\n", + "def check_answer(variable_name, expected, comparison = operator.eq):\n", + " output = widgets.Output()\n", + " button = widgets.Button(description=\"Check answer\")\n", + " def _inner_check(button):\n", + " with output:\n", + " if comparison(globals()[variable_name], expected):\n", + " output.outputs = [{'name': 'stdout', 'text': 'Correct!', 'output_type': 'stream'}]\n", + " else:\n", + " output.outputs = [{'name': 'stdout', 'text': 'Incorrect!', 'output_type': 'stream'}]\n", + " button.on_click(_inner_check)\n", + " display(button, output)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "# times of observation [months]\n", + "t = np.arange(8)\n", + "\n", + "# observed heights [m]\n", + "y = [1.39, 1.26, 1.48, 4.03, 5.89, 5.14, 6.47, 7.64]\n", + "\n", + "# number of observations\n", + "m = len(t)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::{card}\n", + "Fill in the correct $\\mathrm{A}$-matrix.\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "auto-execute-page" + ] + }, + "outputs": [], + "source": [ + "# design matrix\n", + "A = np.column_stack((np.ones(m), t))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "check_answer(\"A\", np.column_stack((np.ones(m), t)), np.array_equiv)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::{card}\n", + "Define the weight matrix for $\\mathrm{W}=I_m$\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "W_1 = 1 #added to have initial input" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Weight matrix for case 1\n", + "W_1 = ?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "check_answer(\"W_1\",np.eye(m), np.array_equiv)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::{card}\n", + "Define the weight matrix with the weight of first 3 observations is five times as large as the rest\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "W_2 = 1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Weight matrix for case 2\n", + "W_2 = ?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "# Weight matrix for case 2\n", + "w=5\n", + "W_2_ans = np.eye(m)\n", + "W_2_ans[0,0] = w\n", + "W_2_ans[1,1] = w\n", + "W_2_ans[2,2] = w\n", + "check_answer(\"W_2\",W_2_ans, np.array_equiv)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::{card}\n", + "Define the weight matrix with the weight of 4th and 5th observation is five times as large as the rest\n", + ":::" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Weight matrix for case 3\n", + "W_3 = ?" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/examples/example_quiz_interactive_stripped.ipynb b/book/examples/example_quiz_interactive_stripped.ipynb new file mode 100644 index 0000000..bcd12b9 --- /dev/null +++ b/book/examples/example_quiz_interactive_stripped.ipynb @@ -0,0 +1,221 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Weighted least-squares estimation\n", + "In ordinary least-squares estimation, we assume that all observations are equally important. In many cases this is not realistic, as observations may be obtained by different measurement systems, or under different circumstances. We want our methodology for least-squares estimation to be able to take this into account.\n", + "\n", + "We achieve this goal by introducing a weight matrix in the minimization problem:\n", + "\n", + "$$\n", + "\\underset{\\mathrm{x}}{\\min} {\\mathrm{(y-Ax)^T W(y-Ax)}}\n", + "$$\n", + "\n", + "In the unweighted least-squares approach, we arrive at the normal equation by pre-multiplying both sides of $\\mathrm{y=Ax}$ with the transpose of the design matrix $\\mathrm{A^T}$:\n", + "\n", + "$$ \n", + "\\mathrm{y=Ax} \\; \\rightarrow \\; \\mathrm{A^T\\; y = A^T\\; A x }\n", + "$$\n", + "\n", + "In the weighted least-squares approach, we now need to add weight matrix $W$ to this pre-multiplication factor, i.e., $ \\mathrm{A^T W}$, to obtain the normal equation:\n", + "\n", + "$$ \n", + "\\mathrm{y=Ax} \\; \\rightarrow \\; \\mathrm{A^T W \\; y = A^TW \\; A x}\n", + "$$\n", + "\n", + "The normal matrix is now defined as $\\mathrm{N=A^T W A}$. From this, assuming that the normal matrix $N$ is invertible (non-singular) we find the weighted least-squares estimate $ \\mathrm{\\hat{x}} $, \n", + "\n", + "$$ \n", + "\\begin{align*}\n", + "\\mathrm{\\hat{x}} &= \\mathrm{(A^T W A)^{-1} A^T W y} \\\\\n", + " &= \\arg \\underset{\\mathrm{x}}{\\min} {\\mathrm{(y-Ax)^T W(y-Ax)}}\n", + "\\end{align*}\n", + "$$\n", + "\n", + "We also find the derived estimate $ \\mathrm{\\hat{y}} $ and $ \\mathrm{\\hat{\\epsilon}} $:\n", + "\n", + "$$\n", + "\\mathrm{\\hat{y} = A \\hat{x} = A (A^T W A )^{-1} A^T W y}\n", + "$$\n", + "\n", + "$$\n", + " \\mathrm{\\hat{\\epsilon} = y - \\hat{y}= y - A \\hat{x} = y-A (A^T W A )^{-1} A^T W y = (I- A(A^T W A )^{-1} A^T W) y}\n", + "$$\n", + "\n", + "### Quiz question\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "### Video\n", + "\n", + "\n", + "### Discussion on the weight matrix\n", + "The weight matrix $\\mathrm{W}$ expresses the (relative) weights between the observations. It is always a square matrix. The size of the weight matrix depends on the number of observations, $m$. The size of the weight matrix is $m\\times m$. \n", + "\n", + "If it is a unit matrix ($\\mathrm{W=I}$), this implies that all observations have equal weight. Note that in this case the equations are equal to the ordinary least-squares solution. \n", + "\n", + "If it is a diagonal matrix, with different values on the diagonal, this implies that entries with a higher value correspond to the observations which are considered to be of more importance. If the weight matrix has non-zero elements on the off-diagonal positions, this implies that (some) observations are correlated.\n", + "\n", + "### Weighted least-squares estimator: properties\n", + "\n", + "Until now, we have looked at the weighted least-squares solution of a single *realization* of the observations, where generally we assume that it is a realization of the *random* observable vector $Y$, since measurements are affected by random errors. As such it follows the the weighted least-squares *estimator* is given by:\n", + "\n", + "$$\n", + "\\hat{X} = \\mathrm{(A^T W A )^{-1} A^T W} Y\n", + "$$\n", + "\n", + "This estimator has two important properties: it is *linear* and *unbiased*.\n", + "\n", + "The linearity property is due to the fact that $\\hat{X}$ is a linear function of the observables $Y$.\n", + "\n", + "The unbiased property means that the expectation of $\\hat{X}$ is equal to the true (but unknown) $\\mathrm{x}$. This can be shown as follows:\n", + "\n", + "$$\n", + "\\mathbb{E}(\\hat{X}) = \\mathrm{(A^T W A )^{-1} A^T W} \\mathbb{E}(Y) = \\mathrm{(A^T W A )^{-1} A^T W Ax = x}\n", + "$$\n", + "\n", + "This a very desirable property. It applies that if we would repeat the measurements many times to obtain a new estimate, the *average of the estimated* values would be equal to the truy values." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise\n", + "\n", + "You have a time series of 8 measurements and fit a model assuming a linear trend (constant velocity)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# times of observation [months]\n", + "t = np.arange(8)\n", + "\n", + "# observed heights [m]\n", + "y = [1.39, 1.26, 1.48, 4.03, 5.89, 5.14, 6.47, 7.64]\n", + "\n", + "# number of observations\n", + "m = len(t)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Fill in the correct $\\mathrm{A}$-matrix." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# design matrix\n", + "A = np.column_stack((np.ones(m), t))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Define the weight matrix for $\\mathrm{W}=I_m$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Weight matrix for case 1\n", + "W_1 = ?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Define the weight matrix with the weight of first 3 observations is five times as large as the rest" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Weight matrix for case 2\n", + "W_2 = ?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Define the weight matrix with the weight of 4th and 5th observation is five times as large as the rest" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Weight matrix for case 3\n", + "W_3 = ?" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/examples/h5p_examples.md b/book/examples/h5p_examples.md new file mode 100644 index 0000000..9fddae4 --- /dev/null +++ b/book/examples/h5p_examples.md @@ -0,0 +1,23 @@ +# H5p + +This page shows some examples of H5p questions. + +Some best-practices on the use of H5p: + - To startup your H5p-account, first login to H5p via Brightspace (follow instructions as under [https://www.tudelft.nl/teaching-support/educational-tools/h5p](https://www.tudelft.nl/teaching-support/educational-tools/h5p)). After that, direct login is possible via [tudelft.h5p.com](tudelft.h5p.com) + - Place your H5p-elements in a shared folder in H5p. + - Disable the display options "Toolbar Below Content" and "Display author's name to public (anonymous users) (only relevant when content's status is set to Public )" for each + +## Complex fill in the blanks +This can be used to evaluate numerical answers: + + +## Dialog cards +This can be used to ask open questions: + + +## Question set +This can be used to create a set of Multiple Choice, Drag and Drop, Fill in the blanks, True/False questions and Image choice questions: + + +## True/False Question + \ No newline at end of file diff --git a/book/examples/interactive_templates.md b/book/examples/interactive_templates.md new file mode 100644 index 0000000..266b10c --- /dev/null +++ b/book/examples/interactive_templates.md @@ -0,0 +1,16 @@ +# Templates for interactive coding / non-coding elements + +These pages show examples of how to use interactive coding / non-coding elements in a Jupyter Book. + +You can make use of: +- Admonitions +- JupyterQuiz +- H5p +- Grasple-exercises +- Interactive widgets +- Live coding exercise checking with widgets +- Any combination of the above + +The following templates are given: +```{tableofcontents} +``` \ No newline at end of file diff --git a/book/examples/overview.md b/book/examples/overview.md index 7de0ba3..baf8847 100644 --- a/book/examples/overview.md +++ b/book/examples/overview.md @@ -1,3 +1,12 @@ # Overview -This part will include examples for jupyter book pages with features \ No newline at end of file +This chapter includes examples for jupyter book pages with features. Here is an overview of the different examples: + +- H5p: examples of H5p questions +- Combining theory with interactive questions +- Templates for interactive coding / non-coding elements: + - Programming assignment: Example of how to convert a Jupyter notebook to Jupyter Book with live coding + - Combining theory with interactive questions and extensive coding assignment: Example how to combine theory, quizzes and code on one page + - Combining theory with interactive questions: Example how to combine theory with questions, so there's no coding involved. + - Parametric question: Example for an equation to be checked on equivalence with the correct answer + diff --git a/book/examples/parametric_questions.ipynb b/book/examples/parametric_questions.ipynb new file mode 100644 index 0000000..d61f7cd --- /dev/null +++ b/book/examples/parametric_questions.ipynb @@ -0,0 +1,318 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parametric question\n", + "\n", + "On this page an example is given for parametric questions, in which an equation is checked on equivalence with the correct answer." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "import sympy as sp\n", + "import numpy as np\n", + "from sympy import pi, latex\n", + "from sympy.printing.mathml import mathml\n", + "\n", + "import operator\n", + "import ipywidgets as widgets\n", + "from IPython.display import display, Latex, display_jpeg, Math, Markdown\n", + "sp.init_printing(use_latex=True) \n", + "\n", + "check_equation = lambda eq1, eq2: sp.simplify(eq1-eq2) == 0\n", + "\n", + "def check_answer(variable_name, expected, comparison = operator.eq):\n", + " output = widgets.Output()\n", + " button = widgets.Button(description=\"Check answer\")\n", + " def _inner_check(button):\n", + " with output:\n", + " if comparison(globals()[variable_name], expected):\n", + " output.outputs = [{'name': 'stdout', 'text': 'Correct!',\n", + " 'output_type': 'stream'}]\n", + " else:\n", + " output.outputs = [{'name': 'stdout', 'text': 'Incorrect!',\n", + " 'output_type': 'stream'}]\n", + " button.on_click(_inner_check)\n", + " display(button, output)\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercises on Taylor expansion\n", + "\n", + "This page shows some exercises on calculation Taylor expansion. If you reload this page, you'll get new values.\n", + "\n", + "### Exercise 1\n", + "\n", + "Calculate the taylor series expension of:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true, + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "x, y = sp.symbols('x, y')\n", + "a_1 = sp.Integer(np.random.randint(2,6))\n", + "b_1 = sp.Integer(np.random.randint(-10,10))\n", + "c_1 = sp.Integer(np.random.randint(-5,5))\n", + "eq1_original = a_1 * x**2 + b_1*x\n", + "eq1_correct = sp.series(eq1_original,x,c_1)\n", + "eq1_answer = 0\n", + "display(eq1_original)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "around:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "display(sp.Eq(x,c_1))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Fill in your answer and run the cell before clicking 'Check answer'. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "auto-execute-page", + "disable-download-page" + ] + }, + "outputs": [], + "source": [ + "eq1_answer = " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "check_answer(\"eq1_answer\",eq1_correct, check_equation)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 2\n", + "\n", + "Calculate the taylor series expension of:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "a_2 = sp.Integer(np.random.randint(1,7))\n", + "c_2 = sp.Integer(np.random.randint(-5,5))\n", + "eq2_original = a_2*sp.tan(x)\n", + "display(eq2_original)\n", + "eq2_correct = sp.series(eq2_original,x,c_2*sp.pi,3).removeO()\n", + "#display(eq2_correct)\n", + "eq2_answer = 0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "around:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "display(sp.Eq(x,c_2*sp.pi))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "discard any $O(x^3)$ terms.\n", + "\n", + "Fill in your answer and run the cell before clicking 'Check answer'. Furthermore, use `pi` for $\\pi$:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "eq2_answer = " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "check_answer(\"eq2_answer\",eq2_correct, check_equation)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Exercise 3\n", + "\n", + "Calculate the taylor series expension of:\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "a_3 = sp.Integer(np.random.randint(1,10))\n", + "c_3 = sp.Integer(np.random.randint(-1,1))\n", + "eq3_original = a_3 / (1 - x)\n", + "display(eq3_original)\n", + "eq3_correct = sp.series(eq3_original,x,c_3,3).removeO()\n", + "#display(eq3_correct)\n", + "eq3_answer = 0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "around:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "display(sp.Eq(x,c_3))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "discard any $O(x^3)$ terms.\n", + "\n", + "Fill in your answer and run the cell before clicking 'Check answer':" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "eq3_answer =" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "check_answer(\"eq3_answer\",eq3_correct, check_equation)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/examples/theory_questions.md b/book/examples/theory_questions.md new file mode 100644 index 0000000..5f6aeba --- /dev/null +++ b/book/examples/theory_questions.md @@ -0,0 +1,58 @@ +# Combining theory with interactive questions + +This page shows you how to combine theory with questions, so there's no coding involved. + +```{index} Truss structures +``` +## Truss structures + +Truss structures are structures assembled of straight elements connected in nodes. These elements partly create triangles, which are of importance for its deformability. Of course, the structures you'll design shouldn't deform, so it's crucial to understand this topic. + +### Examples truss structures +Examples of these structures are commonly found in rail bridges like the one below: + +```{figure} theory_questions_images/pic0.jpg +:width: 400 +``` +Furthermore, these structures are commonly found in building as well, both invisible and visible: + +```{figure} theory_questions_images/pic1.jpg +:width: 400 +``` + +### Modelling truss structures +Truss structures are modelled as rigid bars (so elements which cannot deform) connected by hinges (so elements can rotate with respect to one each other). In our model, hinges are indicate with a circle, and bars with a line. For example, the structure you've seen in the second example (with two diagonal bars removed) is modelled as follows: + +```{figure} theory_questions_images/Truss1.svg +``` + +Now that you've been introduced to truss structures, answer the following question: + +:::{card} Test Yourself + +::: + +### Mechanisms +A mechanism is a structure which can deform as a whole, while the individual elements don't deform. This deformation doesn't have to be large. An example of a mechanism is a rectangular truss structure, it can deform as shown below: +```{figure} theory_questions_images/rectangle.svg +``` +A triangle cannot deform: +```{figure} theory_questions_images/triangle.svg +``` +For structures which consists of partly triangles, partly quadrilaterals, it is not directly clear whether the structure can deform. To identify whether the truss structure is a mechanism, you can try to move each node with respect to the nodes with which it is connected: + +```{figure} theory_questions_images/gifje.gif +:width: 300px +``` +An example of a truss structure of partly triangles, partly quadrilaterals, which is not a mechanism is the following: +```{figure} theory_questions_images/structure3.svg +``` +Now that you've been introduced to mechanisms, answer the following questions: + +:::{card} Test Yourself + +::: + +:::{card} Test Yourself + +::: \ No newline at end of file diff --git a/book/examples/theory_questions_images/Truss1.svg b/book/examples/theory_questions_images/Truss1.svg new file mode 100644 index 0000000..e9b2179 --- /dev/null +++ b/book/examples/theory_questions_images/Truss1.svg @@ -0,0 +1,135 @@ + + + + + + + + + + Pagina-1 + + + Vakwerk, 1 pt.16 + + + + Vakwerk, 1 pt.17 + + + + Vakwerk, 1 pt.18 + + + + Vakwerk, 1 pt.19 + + + + Vakwerk, 1 pt.20 + + + + Vakwerk, 1 pt.14 + + + + Vakwerk, 1 pt.15 + + + + Vakwerk, 1 pt.12 + + + + Vakwerk, 1 pt.13 + + + + Vakwerk, 1 pt + + + + Vakwerk, 1 pt.2 + + + + Scharnier + + + + Scharnier.4 + + + + Scharnier.5 + + + + Scharnier.6 + + + + Scharnier.7 + + + + Scharnier.8 + + + + Scharnier.9 + + + + Scharnier.10 + + + + Blad.21 + + + + Blad.22 + + + + Blad.23 + + + + Blad.24 + + + + Blad.25 + hinges + + + + hinges + + Blad.26 + bars + + + + bars + + diff --git a/book/examples/theory_questions_images/gifje.gif b/book/examples/theory_questions_images/gifje.gif new file mode 100644 index 0000000..20a4d57 Binary files /dev/null and b/book/examples/theory_questions_images/gifje.gif differ diff --git a/book/examples/theory_questions_images/pic0.jpg b/book/examples/theory_questions_images/pic0.jpg new file mode 100644 index 0000000..7f2f938 Binary files /dev/null and b/book/examples/theory_questions_images/pic0.jpg differ diff --git a/book/examples/theory_questions_images/pic1.jpg b/book/examples/theory_questions_images/pic1.jpg new file mode 100644 index 0000000..64c9cb8 Binary files /dev/null and b/book/examples/theory_questions_images/pic1.jpg differ diff --git a/book/examples/theory_questions_images/rectangle.svg b/book/examples/theory_questions_images/rectangle.svg new file mode 100644 index 0000000..bbda50f --- /dev/null +++ b/book/examples/theory_questions_images/rectangle.svg @@ -0,0 +1,77 @@ + + + + + + + + + + Pagina-1 + + + Vakwerk, 1 pt.1301 + + + + Vakwerk, 1 pt.1302 + + + + Vakwerk, 1 pt.1308 + + + + Vakwerk, 1 pt.1309 + + + + Scharnier.1305 + + + + Scharnier.1304 + + + + Scharnier.1307 + + + + Scharnier.1306 + + + + Blad.1310 + + + + Blad.1311 + + + + Vakwerk, 1 pt.1312 + + + + Scharnier.1313 + + + + Scharnier.1314 + + + + diff --git a/book/examples/theory_questions_images/structure3.svg b/book/examples/theory_questions_images/structure3.svg new file mode 100644 index 0000000..9e3a2e6 --- /dev/null +++ b/book/examples/theory_questions_images/structure3.svg @@ -0,0 +1,118 @@ + + + + + + + + + + Pagina-1 + + + Vakwerk, 1 pt.1318 + + + + Vakwerk, 1 pt.1323 + + + + Vakwerk, 1 pt.1324 + + + + Vakwerk, 1 pt.1325 + + + + Vakwerk, 1 pt.1334 + + + + Vakwerk, 1 pt.1335 + + + + Vakwerk, 1 pt.1336 + + + + Vakwerk, 1 pt.1337 + + + + Vakwerk, 1 pt.1338 + + + + Vakwerk, 1 pt.1339 + + + + Vakwerk, 1 pt.1340 + + + + Vakwerk, 1 pt.1341 + + + + Vakwerk, 1 pt.1342 + + + + Vakwerk, 1 pt.1343 + + + + Vakwerk, 1 pt.1344 + + + + Scharnier.1329 + + + + Scharnier.1330 + + + + Scharnier.1326 + + + + Scharnier.1328 + + + + Scharnier.1331 + + + + Scharnier.1332 + + + + Scharnier.1345 + + + + Scharnier.1327 + + + + Scharnier.1333 + + + + diff --git a/book/examples/theory_questions_images/triangle.svg b/book/examples/theory_questions_images/triangle.svg new file mode 100644 index 0000000..ff8d4b5 --- /dev/null +++ b/book/examples/theory_questions_images/triangle.svg @@ -0,0 +1,46 @@ + + + + + + + + + + Pagina-1 + + + Vakwerk, 1 pt.1294 + + + + Vakwerk, 1 pt.1295 + + + + Vakwerk, 1 pt.1296 + + + + Scharnier.1297 + + + + Scharnier.1298 + + + + Scharnier.1299 + + + + diff --git a/book/features/custom_download.md b/book/features/custom_download.md new file mode 100644 index 0000000..1b88e2e --- /dev/null +++ b/book/features/custom_download.md @@ -0,0 +1,39 @@ +# Page download options + +You can control the download option of the book in two ways: + - Disabling downloading using cell tags `disable-download-page` + - Add / replace download link with custom link using `spinhx-download-link-replacer` + +## Disabling download +If you add `disable-download-page` as a cell tag to a cell in a python notebook, the download button ({fa}`download`) will disappear from the topright corner. The cell tag can be added to any code cell in the notebook. This function might be handy if your page includes code which you don't want the students to see. Be aware that this also removes the option to download a PDF of the page. + +## Add/replace download link +The download link {fa}`download` --> {fa}`file`{guilabel}`.ipynb` can be replaced by using the following code in any markdown / notebook file: +````md +```{custom_download_link} +:text: "Custom text" +:replace_default: "True" +``` +```` + +An explanation of the options of this command is given on https://gitlab.tudelft.nl/mude/sphinx-download-link-replacer. + +A potential application of this functionality is when creating a page in which students have to do some coding. Downloading the page allows the student to save their work and work with their local environment. However, the original source file might include code (jupyterbook formatting, widgets, answers) which is not necessary for students. You can make a copy of the notebook file without these elements and replace the {fa}`file`{guilabel}`.ipynb` link to this custom notebook file. Furthermore, you can add any additional data as an additional {fa}`file`{guilabel}`.zip` file. + + +### Installation instructions +To make use of this functionality, you have to install the `download_link_replace` package by running the following line in your command line: +``` +pip install git+https://gitlab.tudelft.nl/mude/sphinx-download-link-replacer +``` + +Furthermore, you should add the following line to `requirements.txt` +``` +download_link_replacer @ git+https://gitlab.tudelft.nl/mude/sphinx-download-link-replacer +``` +and to `_config.yml`: +``` +sphinx: + extra_extensions: + - download_link_replacer +``` \ No newline at end of file diff --git a/book/features/custom_toc.md b/book/features/custom_toc.md new file mode 100644 index 0000000..005c970 --- /dev/null +++ b/book/features/custom_toc.md @@ -0,0 +1,22 @@ +# Exclude parts of book from published book + +A new feature is added to enable testing of all interactive features on the web server (i.e., in the draft book) while preventing students from seeing it. Pages in the draft book (`main` branch) can be manually stripped out of the table of contents when a merge request from `main` to `publish` is made using a tag `REMOVE-FROM-PUBLISH`. Pages marked with this feature are still visible in the draft book and books build from source on personal computers. The tag is applied as follows: + +``` +format: jb-book +root: intro + +parts: + - caption: ... + chapters: + - file: ... + ... +# START REMOVE-FROM-PUBLISH + - file: files_to_remove +# END REMOVE-FROM-PUBLISH +``` +There is no limit to the number of stripped sections, they can be sequential and indentation does not matter. The action is implemented in shell script `build-book.sh` and is executed at run time (see line in script [here](https://gitlab.tudelft.nl/mude/book/-/blob/main/build-book.sh#L13)). + +```{note} +The `REMOVE-FROM-PUBLISH` feature was developed to retain testing of all interactive features on the web server and we recommended incorporating this in all books that rely on these features. It avoids problems associated with building the book on various platforms and virtual environments, rather than the actualy webserver where the book will be deployed. +``` \ No newline at end of file diff --git a/book/features/exercise_checking.ipynb b/book/features/exercise_checking.ipynb new file mode 100644 index 0000000..83445c7 --- /dev/null +++ b/book/features/exercise_checking.ipynb @@ -0,0 +1,120 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Exercise checking using check-answer button\n", + "\n", + "Using the python-enabled interactivity, exercise checking can be intuitively incorporated using a widgets which checks the value of certain variables with respect to the answer. These examples show different approaches to achieving this goal. \n", + "\n", + "A function has been made to check the value of a variable. As input values it takes the variable name, the expected value (correct answer) and how these values should be compared. You need to add this function to each page, including the imports, and add the `thebe-remove-input-init` tag to the cell.\n", + "```python\n", + "import micropip\n", + "await micropip.install(\"ipywidgets\")\n", + "import ipywidgets as widgets\n", + "from IPython.display import display\n", + "import operator\n", + "\n", + "def check_answer(variable_name, expected, comparison = operator.eq):\n", + " output = widgets.Output()\n", + " button = widgets.Button(description=\"Check answer\")\n", + " def _inner_check(button):\n", + " with output:\n", + " if comparison(globals()[variable_name], expected):\n", + " output.outputs = [{'name': 'stdout', 'text': 'Correct!', 'output_type': 'stream'}]\n", + " else:\n", + " output.outputs = [{'name': 'stdout', 'text': 'Incorrect!', 'output_type': 'stream'}]\n", + " button.on_click(_inner_check)\n", + " display(button, output)\n", + "```\n", + "\n", + "To check a float value, you could use `math.isclose` as a function. For checking the equivalence of arrays, you might consider `numpy.array_equiv`\n", + "\n", + "You can choose to include the correct answers in the same notebook. If students download the page and you didn't specify a custom url, they can find the correct answers. You can also place the correct answers in a seperate .py file which you import in the notebook.\n", + "\n", + "## Example" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "import micropip\n", + "await micropip.install(\"ipywidgets\")\n", + "import ipywidgets as widgets\n", + "from IPython.display import display\n", + "import operator\n", + "\n", + "def check_answer(variable_name, expected, comparison = operator.eq):\n", + " output = widgets.Output()\n", + " button = widgets.Button(description=\"Check answer\")\n", + " def _inner_check(button):\n", + " with output:\n", + " if comparison(globals()[variable_name], expected):\n", + " output.outputs = [{'name': 'stdout', 'text': 'Correct!', 'output_type': 'stream'}]\n", + " else:\n", + " output.outputs = [{'name': 'stdout', 'text': 'Incorrect!', 'output_type': 'stream'}]\n", + " button.on_click(_inner_check)\n", + " display(button, output)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-init", + "auto-execute-page" + ] + }, + "outputs": [], + "source": [ + "# This example has the user type in the answer as a Python variable, but they need to run the cell to update the answer checked!\n", + "pi = 3.14" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "import math\n", + "check_answer(\"pi\", 3.14, math.isclose)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/features/grasple.md b/book/features/grasple.md index 59102ac..6cb2359 100644 --- a/book/features/grasple.md +++ b/book/features/grasple.md @@ -1,4 +1,4 @@ -# Embedding Grasple question +# Grasple questions [Grasple](https://app.grasple.com/) gives you an embed code for each exercise which looks like this, which can be added cirectly to your markdown-file. ```html diff --git a/book/features/jupyterlite/including-jupyterlite.md b/book/features/jupyterlite/including-jupyterlite.md index 6e3f569..f462822 100644 --- a/book/features/jupyterlite/including-jupyterlite.md +++ b/book/features/jupyterlite/including-jupyterlite.md @@ -1,4 +1,4 @@ -# Adding `JupyterLite` to a page +# How to add `JupyterLite` to a page There are three different `JupyterLite` directives that you can include in your web page: diff --git a/book/features/jupyterlite/interactive-wave.ipynb b/book/features/jupyterlite/interactive-wave.ipynb deleted file mode 100644 index c3be4a8..0000000 --- a/book/features/jupyterlite/interactive-wave.ipynb +++ /dev/null @@ -1,91 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "%gui asyncio\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from ipywidgets import interact" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "4570ae9da644449a87c3fea9c4d7f919", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "interactive(children=(FloatSlider(value=3.0, description='omega', max=5.0, min=1.0), Output()), _dom_classes=(…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x = np.linspace(0, 2*np.pi, 100)\n", - "fig, ax = plt.subplots()\n", - "line, = ax.plot(x, np.sin(x))\n", - "\n", - "def plot_sine_wave(omega):\n", - " y = np.sin(omega*x)\n", - " line.set_ydata(y)\n", - " fig.canvas.draw_idle()\n", - "\n", - "interact(plot_sine_wave, omega=(1, 5, 0.1))" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.13" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/book/features/jupyterlite/jupyterlite-gallery.md b/book/features/jupyterlite/jupyterlite-gallery.md deleted file mode 100644 index 0a6b0e5..0000000 --- a/book/features/jupyterlite/jupyterlite-gallery.md +++ /dev/null @@ -1,11 +0,0 @@ -# Gallery - -On this page, you can find some example usage of `JupyterLite`. The reader is invited to contribute to this gallery with their own interesting examples. - -## Interactive plot - -```{eval-rst} -.. retrolite:: interactive-wave.ipynb - :width: 100% - :height: 600px -``` \ No newline at end of file diff --git a/book/features/jupyterlite/jupyterlite.md b/book/features/jupyterlite/jupyterlite.md index c26432d..6ac2e11 100644 --- a/book/features/jupyterlite/jupyterlite.md +++ b/book/features/jupyterlite/jupyterlite.md @@ -1,4 +1,4 @@ -# Interactive elements with JupyterLite-Sphinx +# JupyterLite-Sphinx: Interactive elements By popular demand: how to include interactive pieces of code in your Jupyter Book. We can do this by using the [JupyterLite](https://github.com/jupyterlite) app, which allows you to run a Jupyter Lab session and execute Python code entirely in-browser. Jupyterlite can be embedded in a Jupyter Book with the [`JupyterLite-Sphinx`](https://github.com/jupyterlite/jupyterlite-sphinx) extension. diff --git a/book/features/live_code.md b/book/features/live_code.md new file mode 100644 index 0000000..dc66157 --- /dev/null +++ b/book/features/live_code.md @@ -0,0 +1,48 @@ +# Python-enabled interactivity + +Our book has been enable to run Python code live in the browser (thanks Max!). This page contains some installation instructions and the other sections show how to use this functionality to create interactive figures and feedback on code + +## Setting up Python live coding +To set up the Python live coding you need to add our [own sphinx-thebe extension](https://gitlab.tudelft.nl/interactivetextbooks-citg/extensions/sphinx-thebe) to your book. This extensions doesn't rely on a 3rd party like Binder and it supports local python execution and custom features. Therefore, you need to add some lines to `requirements.txt` and `_config.yml` + +For `requirements.txt` add the following lines: +``` +--extra-index-url https://gitlab.tudelft.nl/api/v4/projects/11239/packages/pypi/simple +sphinx-thebe == 0.2.1000 +``` +This will download the correct version of the sphinx extension when the book is build on the server (which loads the required packages from `requirements.txt`) + +Afterwards, this sphinx extension needs to be enabled in your book. This can be done by adding the following lines to `_config.yml`: + +``` +launch_buttons: + thebe: true + +sphinx: + html_js_files: + - https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js + thebe_config: + use_thebe_lite: true + exclude_patterns: ["**/_*.yml", "**/*.md", "**/*.ipynb"] +``` + +By `launch_buttons` you initiate the {fa}`rocket` --> {guilabel}`Live Code` on the top right corner of every page generated from a `.ipynb` file. The `html_js_files` link calls for some required javascript files. `use_thebe_lite` makes sure you initiate our adapted sphinx thebe extensions. The `exclude_patterns` makes sure you import all files so that they can be accessed from your code, except for the files matching the patterns shown. + +## Instructions: local build + +To test the fuctionality, you have to run a local server, otherwise the interactivity doens't work. So, you cannot just open the `index.html` file any more. This can be done with the following steps: +1. build the book by running ` jupyter book build `. If you are using Windows you should use the Git Bash terminal. Set this as your default in VS Code, then open it using "New Terminal." If you have not already done so, you will need to allow Git Bash to use your Python libraries by running the command `conda init bash`. +2. Set up a server by running `python -m http.server 8000`. +3. Open the book by going to [http://localhost:8000/](http://localhost:8000/) in your browser. You may need to reload the page once the rebuild is complete. You can do this on Chrome by right-clicking somewhere on the page, select "Inspect", open the "Network" tab, then reload with `CTRL+R`. Or try `ctrl`+`F5` +4. Go to any page that is created from a Jupyter notebook + +If you push to main, you can test the interactivity on the book-draft website as well. + +## Custom cell tags +With the extended functionality of live code, additional cell tags have been developed to be added to cells in any .ipynb file, next to the [existing tags] of the jupyter book (https://jupyterbook.org/en/stable/content/metadata.html): +- `disable-execution-cell` disables the ability to execute the cell when thebe is activated. This might be useful if you notebook file includes interactivity for only a part of the coding cells +- `disable-execution-page` disables the ability to execute the entire page. This might be useful if you use python code for creating a page, but the code is not part of the actual content of the book. +- `auto-execute-page` automatically starts the thebe live coding functionality whenever the page is opened. This is particularly useful if you're using widgets on a page. At this moment, this tags doesn't work in combination with `thebe-remove-input-init` +- `thebe-init` directly starts running this cell as soon as the thebe live coding functionality starts. +- `thebe-remove-input-init`, as `thebe-init`, starts the cell directly as soon as the thebe live coding functionality starts. However, the input is not shown to the students (in static and interactive mode). The original `remove-input` tag will not work as it deletes the entire cell, so it can never be executed. + diff --git a/book/features/overview.md b/book/features/overview.md index 51fb44a..66cb316 100644 --- a/book/features/overview.md +++ b/book/features/overview.md @@ -1,14 +1,25 @@ # CEG-book Features -```{warning} Under Construction! - -This page will provide a list of some commonly used book features, and sub-pages will provide additional information, specifically: +This chapter provides a list of commonly used book features together with sub-pages that provide additional information on: - the purpose - how to install them - how to use them - where to find the source code -``` +Overview of features covered: +- Interaction on the website: Adding comments and feedback to content on the website +- Grasple Questions: Explanation how to embed Grasple questions in the book. +- Adding interactive h5p elements: Embedding interactive HTML-blocks in the book +- Python-enabled interactivity: Run Python code live in the browser + - Widgets + - Exercise checking using check-answer button +- JupyterLite-Sphinx: Including interactive pieces of content in the book. + - Configuration + - How to add `JupyterLite` to a page +- Exclude parts of book from published book +- Page download options + +To see examples for these features, go to the Example chapter. The {guilabel}`books-CEG` label indicates a feature developed by us. diff --git a/book/features/pd/1.png b/book/features/pd/1.png new file mode 100644 index 0000000..5227771 Binary files /dev/null and b/book/features/pd/1.png differ diff --git a/book/features/pd/FN-simple.PNG b/book/features/pd/FN-simple.PNG new file mode 100644 index 0000000..7e8eea1 Binary files /dev/null and b/book/features/pd/FN-simple.PNG differ diff --git a/book/features/pd/combined_risk_criteria.png b/book/features/pd/combined_risk_criteria.png new file mode 100644 index 0000000..bb9f78e Binary files /dev/null and b/book/features/pd/combined_risk_criteria.png differ diff --git a/book/features/pd/decision-tree-blank.png b/book/features/pd/decision-tree-blank.png new file mode 100644 index 0000000..cd88fd6 Binary files /dev/null and b/book/features/pd/decision-tree-blank.png differ diff --git a/book/features/pd/decision-tree-example.png b/book/features/pd/decision-tree-example.png new file mode 100644 index 0000000..f7cf16c Binary files /dev/null and b/book/features/pd/decision-tree-example.png differ diff --git a/book/features/pd/decision-tree-filled.png b/book/features/pd/decision-tree-filled.png new file mode 100644 index 0000000..96f45af Binary files /dev/null and b/book/features/pd/decision-tree-filled.png differ diff --git a/book/features/pd/design_1_rv.svg b/book/features/pd/design_1_rv.svg new file mode 100644 index 0000000..bc9fd29 --- /dev/null +++ b/book/features/pd/design_1_rv.svg @@ -0,0 +1,1789 @@ + + + + + + + + 2023-01-08T11:58:13.384461 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/design_2_rv_correct.svg b/book/features/pd/design_2_rv_correct.svg new file mode 100644 index 0000000..65b606f --- /dev/null +++ b/book/features/pd/design_2_rv_correct.svg @@ -0,0 +1,4713 @@ + + + + + + + + 2023-01-08T11:58:19.506423 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/design_2_rv_error_1.svg b/book/features/pd/design_2_rv_error_1.svg new file mode 100644 index 0000000..499e389 --- /dev/null +++ b/book/features/pd/design_2_rv_error_1.svg @@ -0,0 +1,1137 @@ + + + + + + + + 2023-01-08T11:58:13.587795 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/design_2_rv_error_2a.svg b/book/features/pd/design_2_rv_error_2a.svg new file mode 100644 index 0000000..4de95bf --- /dev/null +++ b/book/features/pd/design_2_rv_error_2a.svg @@ -0,0 +1,1128 @@ + + + + + + + + 2023-01-08T11:58:14.020152 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/design_2_rv_error_2b.svg b/book/features/pd/design_2_rv_error_2b.svg new file mode 100644 index 0000000..9c55c4a --- /dev/null +++ b/book/features/pd/design_2_rv_error_2b.svg @@ -0,0 +1,1175 @@ + + + + + + + + 2023-01-08T11:58:14.607877 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/design_2_rv_error_3.svg b/book/features/pd/design_2_rv_error_3.svg new file mode 100644 index 0000000..45a109c --- /dev/null +++ b/book/features/pd/design_2_rv_error_3.svg @@ -0,0 +1,727 @@ + + + + + + + + 2023-01-08T11:58:16.440884 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/dike_heightening_schematic.png b/book/features/pd/dike_heightening_schematic.png new file mode 100644 index 0000000..1520bc3 Binary files /dev/null and b/book/features/pd/dike_heightening_schematic.png differ diff --git a/book/features/pd/dike_rings.jpg b/book/features/pd/dike_rings.jpg new file mode 100644 index 0000000..235c24a Binary files /dev/null and b/book/features/pd/dike_rings.jpg differ diff --git a/book/features/pd/economic_opt.svg b/book/features/pd/economic_opt.svg new file mode 100644 index 0000000..21403f2 --- /dev/null +++ b/book/features/pd/economic_opt.svg @@ -0,0 +1,349 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/ex_FN_curve_step_01_py.svg b/book/features/pd/ex_FN_curve_step_01_py.svg new file mode 100644 index 0000000..3a47dd5 --- /dev/null +++ b/book/features/pd/ex_FN_curve_step_01_py.svg @@ -0,0 +1,936 @@ + + + + + + + + 2022-12-19T21:15:04.881036 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/ex_FN_curve_step_02_py.svg b/book/features/pd/ex_FN_curve_step_02_py.svg new file mode 100644 index 0000000..97eece8 --- /dev/null +++ b/book/features/pd/ex_FN_curve_step_02_py.svg @@ -0,0 +1,1048 @@ + + + + + + + + 2022-12-19T21:15:05.264451 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/ex_FN_curve_step_03_py.svg b/book/features/pd/ex_FN_curve_step_03_py.svg new file mode 100644 index 0000000..a3d71a2 --- /dev/null +++ b/book/features/pd/ex_FN_curve_step_03_py.svg @@ -0,0 +1,822 @@ + + + + + + + + 2022-12-19T21:15:05.437199 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/example-river-excavation-2.png b/book/features/pd/example-river-excavation-2.png new file mode 100644 index 0000000..bf9da6e Binary files /dev/null and b/book/features/pd/example-river-excavation-2.png differ diff --git a/book/features/pd/example-river-excavation-3.PNG b/book/features/pd/example-river-excavation-3.PNG new file mode 100644 index 0000000..70a57e8 Binary files /dev/null and b/book/features/pd/example-river-excavation-3.PNG differ diff --git a/book/features/pd/example-river-excavation.png b/book/features/pd/example-river-excavation.png new file mode 100644 index 0000000..ddffa35 Binary files /dev/null and b/book/features/pd/example-river-excavation.png differ diff --git a/book/features/pd/exercise-FN-curve.png b/book/features/pd/exercise-FN-curve.png new file mode 100644 index 0000000..841146c Binary files /dev/null and b/book/features/pd/exercise-FN-curve.png differ diff --git a/book/features/pd/exercise-dam-fn-limit-line.png b/book/features/pd/exercise-dam-fn-limit-line.png new file mode 100644 index 0000000..bde9712 Binary files /dev/null and b/book/features/pd/exercise-dam-fn-limit-line.png differ diff --git a/book/features/pd/exercise-decision-tree.png b/book/features/pd/exercise-decision-tree.png new file mode 100644 index 0000000..75bbfaa Binary files /dev/null and b/book/features/pd/exercise-decision-tree.png differ diff --git a/book/features/pd/exercise-optimization.png b/book/features/pd/exercise-optimization.png new file mode 100644 index 0000000..b31e233 Binary files /dev/null and b/book/features/pd/exercise-optimization.png differ diff --git a/book/features/pd/exercise-sample-exam-bay.png b/book/features/pd/exercise-sample-exam-bay.png new file mode 100644 index 0000000..9283059 Binary files /dev/null and b/book/features/pd/exercise-sample-exam-bay.png differ diff --git a/book/features/pd/exercise-sample-exam-failure-domain.png b/book/features/pd/exercise-sample-exam-failure-domain.png new file mode 100644 index 0000000..8f37e8d Binary files /dev/null and b/book/features/pd/exercise-sample-exam-failure-domain.png differ diff --git a/book/features/pd/exercise-sample-exam-limit-line.png b/book/features/pd/exercise-sample-exam-limit-line.png new file mode 100644 index 0000000..97bb772 Binary files /dev/null and b/book/features/pd/exercise-sample-exam-limit-line.png differ diff --git a/book/features/pd/exercise-sample-exam-mortality.png b/book/features/pd/exercise-sample-exam-mortality.png new file mode 100644 index 0000000..7fe10aa Binary files /dev/null and b/book/features/pd/exercise-sample-exam-mortality.png differ diff --git a/book/features/pd/exercise_sample_IR.png b/book/features/pd/exercise_sample_IR.png new file mode 100644 index 0000000..047a895 Binary files /dev/null and b/book/features/pd/exercise_sample_IR.png differ diff --git a/book/features/pd/exercise_sample_opt.png b/book/features/pd/exercise_sample_opt.png new file mode 100644 index 0000000..2caeda7 Binary files /dev/null and b/book/features/pd/exercise_sample_opt.png differ diff --git a/book/features/pd/fn_limit.png b/book/features/pd/fn_limit.png new file mode 100644 index 0000000..e8cdf58 Binary files /dev/null and b/book/features/pd/fn_limit.png differ diff --git a/book/features/pd/fn_limit_eq.png b/book/features/pd/fn_limit_eq.png new file mode 100644 index 0000000..f0ed1b0 Binary files /dev/null and b/book/features/pd/fn_limit_eq.png differ diff --git a/book/features/pd/periodic_investments.png b/book/features/pd/periodic_investments.png new file mode 100644 index 0000000..1812b79 Binary files /dev/null and b/book/features/pd/periodic_investments.png differ diff --git a/book/features/pd/present_value_py.svg b/book/features/pd/present_value_py.svg new file mode 100644 index 0000000..e254b81 --- /dev/null +++ b/book/features/pd/present_value_py.svg @@ -0,0 +1,1089 @@ + + + + + + + + 2022-12-21T21:38:21.876407 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/rating_curve.svg b/book/features/pd/rating_curve.svg new file mode 100644 index 0000000..79564c7 --- /dev/null +++ b/book/features/pd/rating_curve.svg @@ -0,0 +1,1073 @@ + + + + + + + + 2023-01-08T11:58:12.535731 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/risk-curve-baecher.PNG b/book/features/pd/risk-curve-baecher.PNG new file mode 100644 index 0000000..560f4c7 Binary files /dev/null and b/book/features/pd/risk-curve-baecher.PNG differ diff --git a/book/features/pd/risk-hazard.svg b/book/features/pd/risk-hazard.svg new file mode 100644 index 0000000..7d2aa08 --- /dev/null +++ b/book/features/pd/risk-hazard.svg @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/risk-steps.PNG b/book/features/pd/risk-steps.PNG new file mode 100644 index 0000000..0a29f92 Binary files /dev/null and b/book/features/pd/risk-steps.PNG differ diff --git a/book/features/pd/risk_contour.png b/book/features/pd/risk_contour.png new file mode 100644 index 0000000..4f12983 Binary files /dev/null and b/book/features/pd/risk_contour.png differ diff --git a/book/features/pd/risk_matrix.png b/book/features/pd/risk_matrix.png new file mode 100644 index 0000000..6791816 Binary files /dev/null and b/book/features/pd/risk_matrix.png differ diff --git a/book/features/pd/risk_types_py.svg b/book/features/pd/risk_types_py.svg new file mode 100644 index 0000000..264ca51 --- /dev/null +++ b/book/features/pd/risk_types_py.svg @@ -0,0 +1,1823 @@ + + + + + + + + 2022-12-21T22:08:56.838936 + image/svg+xml + + + Matplotlib v3.5.2, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/pd/simple-city.png b/book/features/pd/simple-city.png new file mode 100644 index 0000000..6cbfd7e Binary files /dev/null and b/book/features/pd/simple-city.png differ diff --git a/book/features/pd/system-corr-coeff.PNG b/book/features/pd/system-corr-coeff.PNG new file mode 100644 index 0000000..4009a11 Binary files /dev/null and b/book/features/pd/system-corr-coeff.PNG differ diff --git a/book/features/pd/system_lightbulbs.png b/book/features/pd/system_lightbulbs.png new file mode 100644 index 0000000..66f5c50 Binary files /dev/null and b/book/features/pd/system_lightbulbs.png differ diff --git a/book/features/pd/system_series_parallel.png b/book/features/pd/system_series_parallel.png new file mode 100644 index 0000000..c94edac Binary files /dev/null and b/book/features/pd/system_series_parallel.png differ diff --git a/book/features/pd/system_series_parallel_ex.png b/book/features/pd/system_series_parallel_ex.png new file mode 100644 index 0000000..a285cc5 Binary files /dev/null and b/book/features/pd/system_series_parallel_ex.png differ diff --git a/book/features/pd/temp.svg b/book/features/pd/temp.svg new file mode 100644 index 0000000..96d11d9 --- /dev/null +++ b/book/features/pd/temp.svg @@ -0,0 +1,1054 @@ + + + + +Created by potrace 1.8, written by Peter Selinger 2001-2007 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/book/features/remove_from_publish.md b/book/features/remove_from_publish.md deleted file mode 100644 index 71f6bf5..0000000 --- a/book/features/remove_from_publish.md +++ /dev/null @@ -1,4 +0,0 @@ -(remove_from_publish)= -# Remove From Publish - -Describe me. \ No newline at end of file diff --git a/book/features/widgets.ipynb b/book/features/widgets.ipynb new file mode 100644 index 0000000..369c08b --- /dev/null +++ b/book/features/widgets.ipynb @@ -0,0 +1,192 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Widgets\n", + "\n", + "If you publish widgets in a jupyter book they might (depending on your implementation) require an active python kernel for the output to be interactive. Using the [thebe-integration](live_code.md), this is possible. Note: You can circumvent the use of a kernel by using packages which don't need such a kernel, for example the [non-kernel-based widgets using Plotly](interactive_element)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Please note:\n", + "- You might want to hide the output before the thebe has been activated.\n", + "- You can hide the code by adding a custom-made tag: `thebe-remove-input-init`.\n", + "- You can automatically start the thebe functionality when you add `auto-execute-page` to a cell. \n", + "\n", + "## Example\n", + "The code below shows an example of the strain energy in a structure and work done by a force for different trial functions. Because of the actual python code, the plot 'refreshes' upon updating a widgets. This can be avoided by updating the current figure instead of plotting a new figure. Remember, first press {fa}`rocket` --> {guilabel}`Live Code` and then wait until all cells are executed:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init", + "auto-execute-page" + ] + }, + "outputs": [], + "source": [ + "import sympy as sp\n", + "F = 10\n", + "EI = 20e3\n", + "L = 3\n", + "\n", + "# Polynomial trial function\n", + "x, C1, C2, C3, C4, F, u0= sp.symbols('x, C1, C2, C3, C4, F, u0')\n", + "w = C1*x**3+C2*x**2+C3*x + C4\n", + "eq1 = sp.Eq(w.subs(x,0),0)\n", + "eq2 = sp.Eq(w.diff(x).subs(x,0),0)\n", + "eq3 = sp.Eq((-w.diff(x,2)*EI).subs(x,L),0)\n", + "eq4 = sp.Eq(w.subs(x,L),u0)\n", + "sol = sp.solve((eq1, eq2,eq3,eq4),(C1,C2,C3,C4))\n", + "w_sol = sp.nsimplify(w.subs(sol))\n", + "w_numpy_polynomial = sp.lambdify((u0,x),w_sol)\n", + "\n", + "M_sol_polynomial = sp.nsimplify(EI * w_sol.diff(x,2))\n", + "\n", + "Ev_polynomial = sp.integrate(sp.nsimplify(M_sol_polynomial**2/(EI*2)),(x,0,3))\n", + "\n", + "Ev_numpy_polynomial = sp.lambdify(u0,Ev_polynomial)\n", + "\n", + "V_polynomial = Ev_polynomial - 10 * u0\n", + "V_numpy_polynomial = sp.lambdify(u0,V_polynomial)\n", + "\n", + "#cosine trial function\n", + "w_sol = u0*sp.sin(x/12*2*sp.pi-sp.pi/2)+u0\n", + "w_numpy_sin = sp.lambdify((u0,x),w_sol)\n", + "\n", + "M_sol_sin = sp.nsimplify(EI * w_sol.diff(x,2))\n", + "\n", + "Ev_sin = sp.integrate(sp.nsimplify(M_sol_sin**2/(EI*2)),(x,0,3))\n", + "\n", + "Ev_numpy_sin = sp.lambdify(u0,Ev_sin)\n", + "\n", + "V_sin = Ev_sin - 10 * u0\n", + "V_numpy_sin = sp.lambdify(u0,V_sin)\n", + "\n", + "#sin trial function\n", + "w_sol = 2*u0*(sp.cosh(x/sp.pi))-2*u0\n", + "w_numpy_cosh = sp.lambdify((u0,x),w_sol)\n", + "\n", + "M_sol_cosh = sp.nsimplify(EI * w_sol.diff(x,2))\n", + "\n", + "Ev_cosh = sp.integrate(sp.nsimplify(M_sol_cosh**2/(EI*2)),(x,0,3))\n", + "\n", + "Ev_numpy_cosh = sp.lambdify(u0,Ev_cosh)\n", + "\n", + "V_cosh = Ev_cosh - 10 * u0\n", + "V_numpy_cosh = sp.lambdify(u0,V_cosh)\n", + "\n", + "import matplotlib.pylab as plt\n", + "import numpy as np\n", + "from ipywidgets import widgets, interact\n", + "\n", + "def func(u,trial_function):\n", + " fig, axs = plt.subplots(2, 2, figsize=(10, 6))\n", + "\n", + " x = np.linspace(0,3,100)\n", + " if trial_function == 'Polynomial':\n", + " w_numpy = w_numpy_polynomial\n", + " Ev_numpy = Ev_numpy_polynomial\n", + " V_numpy = V_numpy_polynomial\n", + " elif trial_function == 'cos':\n", + " w_numpy = w_numpy_sin\n", + " Ev_numpy = Ev_numpy_sin\n", + " V_numpy = V_numpy_sin\n", + " elif trial_function == 'cosh':\n", + " w_numpy = w_numpy_cosh\n", + " Ev_numpy = Ev_numpy_cosh\n", + " V_numpy = V_numpy_cosh\n", + " axs[0,0].plot(x,w_numpy(u,x))\n", + " axs[0,0].set_xlim([-0.2,3.2])\n", + " axs[0,0].set_ylim([-0.003,0.01])\n", + " axs[0,0].invert_yaxis()\n", + " axs[0,0].annotate(text='', xy=(3,w_numpy(u,3)), xytext=(3,0), arrowprops=dict(arrowstyle='fancy'))\n", + " axs[0,0].text(3.1,w_numpy(u,3),'$u_0$')\n", + " axs[0,0].axis('off')\n", + " axs[0,1].axis('off')\n", + " axs[0,1].set_xlim([-0.2,3.2])\n", + " axs[0,1].set_ylim([-0.003,0.01])\n", + " axs[0,1].annotate(text='', xy=(1.5,w_numpy(u,3)), xytext=(1.5,w_numpy(u,3)-0.003), arrowprops=dict(arrowstyle='simple'))\n", + " axs[0,1].invert_yaxis()\n", + " x_axis= ['$E_v$','$A_F$','$E_v - A_F$']\n", + " y_axis = [Ev_numpy(u),w_numpy(u,3)*10,Ev_numpy(u)-w_numpy(u,3)*10]\n", + " axs[1,0].bar(x_axis,y_axis,color=('blue','green','orange'))\n", + " axs[1,0].set_ylim([-0.03,0.1])\n", + " axs[1,0].set_yticklabels([])\n", + " axs[1,0].set_yticks([])\n", + " \n", + " u_range=np.linspace(-0.0015,0.01,100)\n", + " axs[1,1].plot(u_range,Ev_numpy(u_range),label='$E_v$',color='blue')\n", + " axs[1,1].plot(u_range,w_numpy(u_range,3)*10,label='$F_A$',color='green')\n", + " axs[1,1].plot(u_range,V_numpy(u_range),label='$E_v - A_F$',color='orange')\n", + " axs[1,1].legend()\n", + " axs[1,1].plot(u,Ev_numpy(u),'o',color='blue')\n", + " axs[1,1].plot(u,w_numpy(u,3)*10,'o',color='green')\n", + " axs[1,1].plot(u,Ev_numpy(u)-w_numpy(u,3)*10,'o',color='orange')\n", + " axs[1,1].set_ylim([-0.03,0.1])\n", + " axs[1,1].set_xlim([-0.0015,0.01])\n", + " axs[1,1].set_xlabel('$u_0$')\n", + " axs[1,1].set_xticks([])\n", + " axs[1,1].set_xticklabels([])\n", + " axs[1,1].set_yticks([])\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The graph will appear below in interactive mode:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [ + "thebe-remove-input-init" + ] + }, + "outputs": [], + "source": [ + "interact(func, u = widgets.FloatSlider(min=-0.001, max=0.01, value=0, step=0.0002, description=\"Displacement u_0\",readout_format='.4f',style= {'description_width': '180px'},layout = {'width': '400px'}),\n", + " trial_function = widgets.ToggleButtons(options=['Polynomial', 'cos', 'cosh']));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/images/image-1.png b/book/images/image-1.png new file mode 100644 index 0000000..6364dcf Binary files /dev/null and b/book/images/image-1.png differ diff --git a/book/images/image-2.png b/book/images/image-2.png new file mode 100644 index 0000000..b981a19 Binary files /dev/null and b/book/images/image-2.png differ diff --git a/book/images/image-3.png b/book/images/image-3.png new file mode 100644 index 0000000..9b5a9c3 Binary files /dev/null and b/book/images/image-3.png differ diff --git a/book/images/image-4.png b/book/images/image-4.png new file mode 100644 index 0000000..7961d8e Binary files /dev/null and b/book/images/image-4.png differ diff --git a/book/images/image-5.png b/book/images/image-5.png new file mode 100644 index 0000000..7961d8e Binary files /dev/null and b/book/images/image-5.png differ diff --git a/book/images/image-6.png b/book/images/image-6.png new file mode 100644 index 0000000..419e9e7 Binary files /dev/null and b/book/images/image-6.png differ diff --git a/book/images/image-7.png b/book/images/image-7.png new file mode 100644 index 0000000..f6b167d Binary files /dev/null and b/book/images/image-7.png differ diff --git a/book/images/image-8.png b/book/images/image-8.png new file mode 100644 index 0000000..fdbf3b7 Binary files /dev/null and b/book/images/image-8.png differ diff --git a/book/images/image-9.png b/book/images/image-9.png new file mode 100644 index 0000000..1673249 Binary files /dev/null and b/book/images/image-9.png differ diff --git a/book/images/image.png b/book/images/image.png new file mode 100644 index 0000000..63fd66a Binary files /dev/null and b/book/images/image.png differ diff --git a/book/workflows/github.md b/book/workflows/github.md new file mode 100644 index 0000000..196a6e6 --- /dev/null +++ b/book/workflows/github.md @@ -0,0 +1,34 @@ +# GitHub template + +To get started making your Jupyter Book with our functionalities, fork our template book https://github.com/TeachBooks/template : + +![alt text](../images/image.png) + +Fill in a repository name, this name will be used in the future url of your book: + +![alt text](../images/image-1.png) + +Set up the build of your website using GitHub pages by selecting under `Settings` - `Pages` - `Build and deployment` - `Source` - `GitHub Actions`: + +![alt text](../images/image-2.png) + +Enable the predefined workflow to publish your book under `Actions` - `I understand my workflows, go ahead an enable them`: + +![alt text](../images/image-3.png) + +Edit a file under `Code` - `book` - A file of your choice - `Edit in place`, in the example below `book/intro.md` is edited: + +![alt text](../images/image-5.png) + +Make your edit and `Commit changes` to `main` branch: + +![alt text](../images/image-6.png) +![alt text](../images/image-7.png) + +Wait a few minutes for the website to be deployed, if you're curious, have a look at the progress under `Actions` - `All wokflows`: + +![alt text](../images/image-8.png) + +When the workflow has finished, visit your build book at https://.github.io/ (case sensitive). For our example it is: https://dummystudent-Tom.github.io/My_own_book: + +![alt text](../images/image-9.png) \ No newline at end of file diff --git a/book/workflows/overview.md b/book/workflows/overview.md index d77d16d..b202ace 100644 --- a/book/workflows/overview.md +++ b/book/workflows/overview.md @@ -2,4 +2,6 @@ ```{warning} Under Construction! -This page will provide a workflow how to collaborate in Git and GitLab for different uses types. Every team might have a different workflow but this workflow is shown as an example and is used in the MUDE course in CEG. \ No newline at end of file +This page will provide a workflow how to collaborate in Git and GitLab for different uses types. Every team might have a different workflow but this workflow is shown as an example and is used in the MUDE course in CEG. + +An alternative is to use Github and Github pages to publish your book. \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2c544df..a3dc6e5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,10 @@ jupyter-book sphinx-inline-tabs jupyterlite-xeus-python -jupyterlite-sphinx \ No newline at end of file +jupyterlite-sphinx + +--extra-index-url https://gitlab.tudelft.nl/api/v4/projects/11239/packages/pypi/simple +sphinx-thebe == 0.2.1000 +--extra-index-url https://gitlab.tudelft.nl/api/v4/projects/17983/packages/pypi/simple +jupyterbook_patches == 0.1.2 +download_link_replacer @ git+https://gitlab.tudelft.nl/mude/sphinx-download-link-replacer@dlr-0.1.2