diff --git a/coastalcodebook/assignments/Chris/Initialize/Week_3_Initialize.ipynb b/coastalcodebook/assignments/Chris/Initialize/Week_3_Initialize.ipynb deleted file mode 100644 index 7cef0c6..0000000 --- a/coastalcodebook/assignments/Chris/Initialize/Week_3_Initialize.ipynb +++ /dev/null @@ -1,1370 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "from IPython.display import display, HTML\n", - "from ipywidgets import interact\n", - "import matplotlib.animation as animation\n", - "\n", - "from matplotlib.ticker import MultipleLocator\n", - "from matplotlib.animation import FuncAnimation\n", - "\n", - "from random import shuffle\n", - "from random import uniform\n", - "\n", - "import IPython\n", - "\n", - "print('Packages succesfully loaded')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### General settings" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# change the width of the cells on screen \n", - "from IPython.display import display, HTML\n", - "display(HTML(\"\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Basic functions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def dispersion(k,h): # calculate omega\n", - " return (9.81*k*np.tanh(k*h))**0.5" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length(T,h):\n", - " L = 9.81*T**2/(2*np.pi)\n", - " L_all = [L]\n", - " \n", - " for i in range(1500):\n", - " L = 9.81*T**2/(2*np.pi)*np.tanh(2*np.pi*h/L)\n", - " L_all.append(L)\n", - " \n", - " if np.abs(L_all[-1] - L_all[-2]) < 0.0005:\n", - " break\n", - " \n", - " return round(L,13)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def general_text_widget(text):\n", - " widget_text = widgets.Label(value= text)\n", - " widget_text.layout.margin = '-8px 0 0 0'# remove distance above and below, -4.5 is to default value\n", - " display(widget_text) " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "general_margin = (\"-8px 0 0 0\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def simple_text_widget(text):\n", - " widget_text = widgets.Label(value= text)\n", - " display(widget_text) " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def Numerical_question_body(Question, units, answer):\n", - " \n", - " # makes the widgets\n", - " question_widget = widgets.Label(value= Question)\n", - " unit_widget = widgets.Label(value= 'Answer:', layout=widgets.Layout(width='50px'))\n", - " num_widget = widgets.FloatText(value=0, disabled=False, step = 0.01, layout=widgets.Layout(width='80px'))\n", - " widget_unit = widgets.Label(value= units, layout=widgets.Layout(width='30px'))\n", - "\n", - " submit_button = widgets.Button(description='Check',)\n", - "\n", - " output_widget = widgets.Text(value= '', placeholder='', description='Feedback:', disabled=False, layout=widgets.Layout(width='500px'))\n", - " #output_widget.add_class('Broad_widget')\n", - "\n", - " HBox1 = widgets.HBox([unit_widget, num_widget, widget_unit, output_widget])\n", - " #HBox.layout.justify_content = 'flex-start' # dont adjust the layout on the window \n", - "\n", - " #place all the horizontal boxes in one vertical box, and display it.\n", - " quiz_widget = widgets.VBox([question_widget] + [HBox1] + [submit_button])\n", - " quiz_widget.layout.flex = 'none'\n", - "\n", - " display(quiz_widget)\n", - "\n", - " def check_answers(button, answer = answer):\n", - "\n", - " # get value from widget and check if this corresponds with the answer\n", - " response = num_widget.value\n", - "\n", - " if response == answer: # if the answer is correct\n", - " output_widget.value = str('Good job, this is correct')\n", - " else: # the answer is wrong\n", - " output_widget.value = str('Incorrect, the answer should be ' + str(answer) + str(units) + ', please try again.')\n", - "\n", - " submit_button.on_click(check_answers)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Q1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def Q1():\n", - "\n", - "\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - " \n", - " text_general = 'Can you asses the wave length in three different ways?'\n", - " text_general2 = 'Firstly through an iterative approach, then through tables (Appendix B, table B-3 of the book), and lastly via the formula of Fentom.'\n", - " text_general3 = 'The wave period ($T$) is ' + str(T1) + ' seconds, and the water depth ($h$) is ' + str(h1) + ' m?'\n", - " \n", - " Q1_text = 'Q1a) What is the deep water wave length?'\n", - " Q1_unit = ' m'\n", - " L = 9.81*T1**2/(2*np.pi)\n", - " Answer1 = round( L ,2)\n", - " \n", - " Q2_text = 'Q1b) If you use the dispersion relation expressed in the wave length, what is the wave length in the first iteration?'\n", - " Q2_unit = ' m'\n", - " L = 9.81*T1**2/(2*np.pi)*np.tanh(2*np.pi*h1/L)\n", - " Answer2 = round(L,2)\n", - " \n", - " Q3_text = 'Q1c) To what wave length does this iteration converge?'\n", - " Q3_unit = ' m'\n", - " Answer3 = round (wave_length(T1,h1) , 2)\n", - " \n", - " Q4_text = 'Q2a) Following table B-3 of the book, what is the ratio of h/L0? (Use 3 decimal numbers in this question)'\n", - " Q4_unit = ''\n", - " L0 = 9.81*T1**2/(2*np.pi)\n", - " Answer4 = round(h1/L0, 3)\n", - " \n", - " Q5_text = 'Q2b) What is the ratio of h/L, you can linear interpolate. (Use 3 decimal numbers in this question)'\n", - " Q5_unit = ''\n", - " \n", - " # data from table B-3, page 536, of the book CD1\n", - " h_L0_serie = np.array([0,0.002,0.004,0.006,0.008,0.01,0.015,0.02,0.025,0.03,0.035,0.04,0.045,0.05,0.055,0.06,0.065,0.07,0.075,0.08,0.085,0.09,0.095,0.1,0.11,0.12,0.13,0.14,0.15,0.16,0.17,0.18,0.19,0.2,0.21,0.22,0.23,0.24,0.25,0.26,0.27,0.28,0.29,0.30,0.31,0.32,0.33,0.34,0.35,0.36,0.37,0.38,0.39,0.4,0.41,0.42,0.43,0.44,0.45,0.46,0.47,0.48,0.49,0.5,1])\n", - " h_L_serie = np.array([0,0.0179,0.0253,0.0311,0.0360,0.0403,0.0496,0.0576,0.0648,0.0713,0.0775,0.0833,0.0888,0.0942,0.0993,0.104,0.109,0.114,0.119,0.123,0.128,0.132,0.137,0.141,0.150,0.158,0.167,0.175,0.183,0.192,0.2,0.208,0.217,0.225,0.234,0.242,0.251,0.259,0.268,0.277,0.285,0.294,0.303,0.312,0.321,0.330,0.339,0.349,0.358,0.367,0.377,0.386,0.395,0.405,0.415,0.424,0.434,0.433,0.453,0.463,0.472,0.482,0.492,0.502,1])\n", - " \n", - " # read the table\n", - " id_lower = np.where(h_L0_serie <= h1/L0)[0][-1]# get the (last) id where the value is below h/L0\n", - " h_L_lower = h_L_serie[id_lower] # use this id to get the new ratio H/L, the lower boundary\n", - " h_L_upper = h_L_serie[id_lower+1] # the upper boundary\n", - " \n", - " # linear interpolate\n", - " slope = (h_L_upper - h_L_lower)/(h_L0_serie[id_lower+1]- h_L0_serie[id_lower])\n", - " h_L = h_L_lower + slope * (h1/L0- h_L0_serie[id_lower])\n", - " \n", - " #print(h1/L0, id_lower, h_L_lower, h_L_upper, h_L)\n", - " Answer5 = round (h_L, 3)\n", - " \n", - " Q6_text = 'Q2c) While using the answer of the previous question, what is the wave length?'\n", - " Q6_unit = ' m'\n", - " Answer6 = round(h1/h_L,2)\n", - " \n", - " \n", - " Q7_text = 'Q2d) What is the value tanh(kh), you can linear interpolate. (Use 3 decimal numbers in this question)'\n", - " Q7_unit = ''\n", - " \n", - " # data from table B-3\n", - " tanh_kh_serie = np.array([0,0.112,0.158,0.193,0.222,0.248,0.302,0.347,0.386,0.420,0.452,0.48,0.507,0.531,0.554,0.575,0.595,0.614,0.632,0.649,0.665,0.681,0.695,0.681,0.695,0.709,0.735,0.759,0.780,0.8,0.818,0.835,0.85,0.864,0.877,0.888,0.899,0.909,0.918,0.926,0.933,0.940,0.946,0.952,0.957,0.961,0.965,0.969,0.972,0.975,0.978,0.980,0.983,0.984,0.986,0.988,0.989,0.990,0.991,0.992,0.993,0.994,0.995,0.995,0.996,0.996,1])\n", - " \n", - " # read the table\n", - " id_lower = np.where(h_L0_serie <= h1/L0)[0][-1]# get the (last) id where the value is below h/L0\n", - " tanh_kh_lower = tanh_kh_serie[id_lower]\n", - " tanh_kh_upper = tanh_kh_serie[id_lower+1]\n", - " \n", - " # linear interpolate\n", - " slope = (tanh_kh_upper - tanh_kh_lower)/(h_L0_serie[id_lower+1]- h_L0_serie[id_lower])\n", - " tanh_kh = tanh_kh_lower + slope * (h1/L0- h_L0_serie[id_lower])\n", - " \n", - " Answer7 = round(tanh_kh,3)\n", - " \n", - " Q8_text = 'Q3a) What is the wave number ($k$), following the explicit formula of Fentom?'\n", - " Q8_unit = ''\n", - " \n", - " def waveNumber_Fenton(T,d): # made by J.A.ArriagaGarcia (Jaime)\n", - " g=9.81\n", - " omega = 2*np.pi/T\n", - " k0 = omega*omega/g\n", - " alpha = k0*d\n", - " beta = alpha * (np.tanh(alpha))**-0.5 \n", - " k = (alpha + beta**2 * np.cosh(beta)**-2) / (np.tanh(beta)+ beta*np.cosh(beta)**-2) / d\n", - " return k\n", - " \n", - " k1 = waveNumber_Fenton(T = T1,d = h1) \n", - " Answer8 = round(k1,2)\n", - " \n", - " Q9_text = 'Q3b) What is the wave length, following the explicit formula of Fentom?'\n", - " Q9_unit = ' m'\n", - " Answer9 = round(2*np.pi/k1,2)\n", - " \n", - " # organise the questions, units, and answers \n", - " questions = [Q1_text, Q2_text, Q3_text,Q4_text, Q5_text, Q6_text, Q7_text, Q8_text, Q9_text]\n", - " units = [Q1_unit, Q2_unit, Q3_unit, Q4_unit, Q5_unit, Q6_unit, Q7_unit, Q8_unit, Q9_unit]\n", - " Answers = [Answer1, Answer2, Answer3, Answer4, Answer5, Answer6, Answer7, Answer8, Answer9]\n", - " \n", - " # make and display tekst widgets\n", - " general_text_widget(text_general)\n", - " general_text_widget(text_general2)\n", - " general_text_widget(text_general3)\n", - " \n", - " # make and display the questions\n", - " for Question, unit, answer in zip(questions,units,Answers):\n", - " Numerical_question_body(Question, unit, answer)\n", - " \n", - "#Q1()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Q2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def Q2():\n", - " \n", - " h = round(uniform(2, 4),1) # m , random value from 2 to 4 (excluding 4)\n", - " L = round(uniform(25, 30),1) # m\n", - " k = 2*np.pi/L\n", - " omega = dispersion(k,h)\n", - " \n", - " Ans0 = T = round( 2*np.pi/omega ,2)\n", - " Q0 = 'What is the wave period ($T$) for a wave with a length ($L$) of '+ str(L) + ' meter when the depth ($h$) is ' + str(h) + ' meters?'\n", - " unit0 = ' s'\n", - " \n", - " h = round(uniform(2, 4),1) # m\n", - " L = round(uniform(20, 30),1) # m\n", - " k = 2*np.pi/L\n", - " omega = dispersion(k,h)\n", - " T = 2*np.pi/omega\n", - " \n", - " Ans1 = c = round(L/T,2)\n", - " Q1 = 'What is the wave celerity ($c$) for waves with a length ($L$) of '+ str(L) + ' meter when the depth ($h$) is ' + str(h) + ' meters?'\n", - " unit1 = ' m/s'\n", - " \n", - " h = round(uniform(1, 2),1)\n", - " L = round(uniform(20, 30),1) # m\n", - " omega = dispersion(k,h)\n", - " T = 2*np.pi/omega\n", - " \n", - " Ans2 = c = round(L/T,2)\n", - " Q2 = 'What is the wave celerity ($c$) for waves with a length ($L$) of '+ str(L) + ' meter when the depth ($h$) is '+ str(h) + ' meters?'\n", - " unit2 = ' m/s'\n", - " \n", - " \n", - " h = round(uniform(1, 2),1)\n", - " T = round(uniform(5, 7),1)\n", - " L = wave_length(T,h)\n", - " Ans3 = round (L,2)\n", - " Q3 = 'What is the wave length ($L$) for a wave with a period ($T$) of ' + str(T) + ' seconds when the depth ($h$) is '+ str(h) + ' meters?'\n", - " unit3 = ' m'\n", - " \n", - " h = round(uniform(5, 7),1) # km\n", - " L = 500000# m\n", - " k = 2*np.pi/L\n", - " omega = dispersion(k,h*1000) # m to km\n", - " T = 2*np.pi/omega\n", - " Ans4 = c = round(L/T /1000*3600,2)\n", - " Q4 = 'What is the propagation speed ($c$) of a tsunami wave train with ($L$) of 500 km in the deep ocean with a depth ($h$) of ' + str(h) + ' km?'\n", - " unit4 = ' km/h'\n", - " \n", - " h = round(uniform(5, 7),1) # km\n", - " T = round(uniform(30,38), 0) # min\n", - " L = wave_length(T*60,h)\n", - " Ans5 = c = round(L/(T) /1000*60,2)\n", - " Q5 = 'What is the propagation speed of a tsunami wave train with a period ($T$) of ' + str(T) + ' minutes in the deep ocean with a depth ($h$) of ' + str(h) + ' km?'\n", - " unit5 = ' km/h'\n", - " \n", - " \n", - " h = round(uniform(0.6, 10),1)\n", - " T = round(uniform(2.5, 12),1)\n", - " L = round(wave_length(T,h),1) # releastic value for question, based on T\n", - " \n", - " k = 2 * np.pi/L\n", - " n = 0.5+k*h/np.sinh(2*k*h)\n", - " Q6 = 'What is the value of $\\\\mbox{n}$ when the water depth ($h$) is ' + str(h) + ' m, and the wave length ($L$) is ' + str(L) + ' m?' \n", - " unit6 = ''\n", - " Ans6 = round(n,2)\n", - " \n", - " \n", - " h = round(uniform(0.8, 10),1)\n", - " T = round(uniform(2.5, 12),1)\n", - " \n", - " L = wave_length(T,h)\n", - " k = 2*np.pi/L\n", - " \n", - " c = L/T\n", - " n = 0.5+k*h/np.sinh(2*k*h)\n", - " cg = n*c\n", - " \n", - " Q7 = 'What is the group velocity ($c_g$) when the water depth ($h$) is ' + str(h) + ' m, and the wave period ($T$) is ' + str(T) + ' s?' \n", - " unit7 = ' m/s'\n", - " Ans7 = round(cg,2)\n", - " \n", - " # get realistic value\n", - " h = round(uniform(1, 10),1)\n", - " T = round(uniform(4, 8),1)\n", - " L = round(wave_length(T,h),1)\n", - " \n", - " omega = dispersion(k,h)\n", - " T = 2*np.pi/omega\n", - " k = 2*np.pi/L\n", - " c = L/T\n", - " n = 0.5+k*h/np.sinh(2*k*h)\n", - " cg = n*c\n", - " \n", - " Q8 = 'At which speed does the wave energy propagate when the water depth ($h$) is ' + str(h) + ' m, and the wave length ($L$) is ' + str(L) + ' m?' \n", - " unit8 = ' m/s'\n", - " Ans8 = round(cg,2)\n", - " \n", - " # organize the questions, units, and answers\n", - " questions = [Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8]\n", - " units = [unit0, unit1, unit2, unit3, unit4, unit5, unit6, unit7, unit8]\n", - " answers = [Ans0, Ans1, Ans2, Ans3, Ans4, Ans5, Ans6, Ans7, Ans8]\n", - "\n", - " # change the order randomly\n", - " order = np.arange(0,len(questions),1)\n", - " #shuffle(order) # the randomization is disabled\n", - "\n", - " # display the questions\n", - " for i in np.array(order):\n", - " Numerical_question_body(questions[i], units[i], answers[i])\n", - "\n", - "#Q2()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Q2B" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "def par(symbol):\n", - " return \"$\\\\mbox{ $ \" + str(symbol) + \"$ }$\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def Q2B():\n", - " Options = ['Wave 1', 'Wave 2']\n", - " \n", - " text_widget1 = widgets.Label(value= 'Select the correct wave, you may assume that the other conditions are equal.')\n", - " text_widget2 = widgets.Label(value= 'The subscript of 1 and 2 relates to wave 1 and 2 respectively')\n", - " \n", - " n1 = round(uniform(0.5, 1),2)\n", - " n2 = round(uniform(0.5, 1),2)\n", - " while n2 == n1:\n", - " n2 = round(uniform(0.5, 1),2)\n", - " \n", - " # in deep water, n = 0.5\n", - " Q1 = 'Which wave is in more shallow water, with ' + par('n_1') + '= ' + str(n1) + ' and' + par('n_2') + '= ' + str(n2) + '.'\n", - " Ans1 = Options[0]\n", - " if n1 < n2:\n", - " Ans1 = Options[1]\n", - " \n", - " Q2 = 'Which wave group has the highest celerity, with' + par('n_1') + '= ' + str(n1) + ' and ' + par('n_2') + '= ' + str(n2) + '.'\n", - " Ans2 = Options[0]\n", - " if n1 > n2:\n", - " Ans2 = Options[1]\n", - " \n", - " \n", - " \n", - " L1 = round(uniform(20, 30),1)\n", - " L2 = round(uniform(20, 30),1)\n", - " while L2 == L1:\n", - " L2 = round(uniform(20, 30),1)\n", - " Q3 = 'Which wave has the highest radial frequency, with ' + par('L_1') + '= ' + str(L1) + ' m and' + par('L_2') + ' = ' + str(L2) +' m'\n", - " \n", - " # higher L, smaller omega\n", - " # so if L1 larger than L2, results in wave 2 correct\n", - " Ans3 = Options[0]\n", - " if L1 > L2:\n", - " Ans3 = Options[1]\n", - " \n", - " Q4 = 'Which wave has the highest wave period, with ' + par('L_1') + '= ' + str(L1) + ' m and ' + par('L_2') + '= ' + str(L2) +' m'\n", - " # higher L, smaller omega, higher wave period\n", - " # so if L1 larger than L2, results in wave 1 correct\n", - " Ans4 = Options[1]\n", - " if L1 > L2:\n", - " Ans4 = Options[0]\n", - " \n", - " Q5 = 'Which wave is more sensative to bottom friction, with ' +par('L_1') + ' = ' + str(L1) + ' m and ' + par('L_2') + '= ' + str(L2) + ' m'\n", - " # higher wave length, faster in shallow water, and more sensative to bottom friction\n", - " # So if L1 larger than L2, results in wave 1 correct\n", - " Ans5 = Options[1]\n", - " if L1 > L2:\n", - " Ans5 = Options[0]\n", - " \n", - " Q6 = 'Which wave has a higher celerity, with ' + par('L_1') + '= ' + str(L1) + ' m and '+ par('L_2') + ' = ' + str(L2) + ' m'\n", - " # higher L, higher celerity\n", - " # So if L1 larger than L2, results in wave 1 correct\n", - " Ans6 = Options[1]\n", - " if L1 > L2:\n", - " Ans6 = Options[0]\n", - " \n", - " \n", - " Questions= [Q1,Q2, Q3, Q4, Q5, Q6]\n", - " Answers = [Ans1,Ans2, Ans3, Ans4, Ans5, Ans6]\n", - " \n", - " #Randomize the order of questions, keep the order of answers the same.\n", - " indices = list(range(len(Questions)))\n", - " shuffle(indices)\n", - " Questions = [Questions[i] for i in indices]\n", - " Answers = [Answers[i] for i in indices]\n", - " \n", - " # Make and display the widgets\n", - " text_widget1.layout.margin = general_margin\n", - " text_widget2.layout.margin = general_margin\n", - " text_widgets = widgets.VBox([text_widget1, text_widget2])\n", - " \n", - " question_widgets = []\n", - " output_widgets = []\n", - " Hboxes = []\n", - " for Question, Answer in zip(Questions, Answers):\n", - " question_widget = widgets.Label(value= Question)\n", - " toggle_widget = widgets.ToggleButtons(options=Options, description='', disabled=False, button_style='') \n", - " output_widget = widgets.Text(value= '', placeholder='', description='Feedback:', disabled=False, layout=widgets.Layout(width='500px'))\n", - " HBox_question = widgets.HBox([question_widget])\n", - " HBox_new = widgets.HBox([toggle_widget, output_widget])\n", - " Hboxes.append(HBox_question)\n", - " Hboxes.append(HBox_new)\n", - " question_widgets.append(toggle_widget)\n", - " output_widgets.append(output_widget)\n", - " \n", - " submit_button = widgets.Button(description='Submit',)\n", - " \n", - " output_widget = widgets.Text(value= '', placeholder='', description='Final score:', disabled=False, layout=widgets.Layout(width='230px'))\n", - " HBox_check = widgets.HBox([submit_button, output_widget])\n", - " \n", - " VBox = widgets.VBox([text_widgets] + Hboxes + [HBox_check])\n", - " VBox.layout.flex = 'none'\n", - " display(VBox)\n", - " \n", - " # make a function to calculate the score and to give feedback\n", - " \n", - " def check_answers(button):\n", - " score = 0\n", - " \n", - " for i in range(len(Questions)):\n", - " answer = question_widgets[i].value\n", - " correct_answer = Answers[i]\n", - " \n", - " if answer == correct_answer:\n", - " score += 1\n", - " output_widgets[i].value = 'Correct!'\n", - " else:\n", - " output_widgets[i].value = 'Not good, try another reasoning!'\n", - "\n", - " output_widget.value = 'Your score is ' + str(score) + '/' + str(len(Questions))\n", - "\n", - " submit_button.on_click(check_answers)\n", - "\n", - " \n", - "#Q2B()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# a function to verify the answers of the questions above\n", - "def asses_L_c():\n", - " h = round(uniform(2, 15),1) # m , random value from 2 to 4 (excluding 4)\n", - "\n", - "\n", - " L_serie = np.arange(0,50)\n", - " c_serie = []\n", - " omega_serie = []\n", - " for L in L_serie:\n", - " k = 2*np.pi/L\n", - " omega = dispersion(k,h)\n", - " omega_serie.append(omega)\n", - " T = 2*np.pi/omega\n", - " c_serie.append(L/T)\n", - "\n", - " plt.plot(L_serie, omega_serie)\n", - " plt.plot(L_serie, c_serie)\n", - "#asses_L_c()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Q3" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def dispersion(k,h):\n", - " return (9.81*k*np.tanh(k*h))**0.5\n", - "\n", - "def wave_celerity(L1, L2, L3):\n", - " %matplotlib inline\n", - " # bed profile\n", - " x = np.arange(0,700+1,1) # cross-shore coordinate [m]\n", - " slope = 1. / 30. # bed slope [-]\n", - " d0 = 20. # offshore water depth [m]\n", - " zbed = -(d0 - slope * x) # bed elevation [m]\n", - " h = -zbed # still water depth [m]\n", - " h[h < 0] = 0 # no negative depths\n", - " \n", - " # w is zero when h is 0, causes a divide by zero.\n", - " x0_id = np.argwhere(h == 0)[0][0] # first location where water depth = 0\n", - " h_water = h[0:x0_id]\n", - " x_water = x[0:x0_id]\n", - " \n", - " #velocity profile\n", - " c1 = L1/(2*np.pi / dispersion(k = 2*np.pi/L1,h=h_water)) # c = L/T, T = 2 pi / omega\n", - " c2 = L2/(2*np.pi / dispersion(k = 2*np.pi/L2,h=h_water))\n", - " c3 = L3/(2*np.pi / dispersion(k = 2*np.pi/L3,h=h_water))\n", - "\n", - " plt.figure(figsize=(9, 5))\n", - " plt.plot(x, zbed, label = 'Bed', color = 'k')\n", - " plt.plot([0,x[x0_id]], [0,0], color = 'gray', label = 'Still water surface')\n", - " plt.xlabel('x [m]')\n", - " plt.ylabel('y [m]')\n", - " plt.title('cross-section')\n", - " plt.xlim(0,np.max(x))\n", - " plt.legend(loc = 'lower right')\n", - "\n", - " plt.figure(figsize=(10, 3))\n", - " plt.title('Wave celerity for 3 waves')\n", - " plt.plot(x_water, c1, label='wave 1')\n", - " plt.plot(x_water, c2, label='wave 2')\n", - " plt.plot(x_water, c3, label='wave 3')\n", - " plt.legend()\n", - " plt.xlabel('x-coordinate [m]')\n", - " plt.ylabel('Celerity')\n", - " plt.xlim(0,np.max(x));\n", - "\n", - "def Q3():\n", - " # Create interactive widgets\n", - " L1 = widgets.FloatSlider(value=5, min=0, max=50, step=0.01, description='L1 [m]')\n", - " L2 = widgets.FloatSlider(value=10, min=0, max=50, step=0.01, description='L2 [m]')\n", - " L3 = widgets.FloatSlider(value=30, min=0, max=50, step=0.01, description='L3 [m]')\n", - "\n", - " # Use the interactive function to update the plot\n", - " Plot = interact(wave_celerity, L1=L1, L2=L2, L3=L3);\n", - " display(Plot);\n", - "\n", - "#Q3()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Q4" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def Q4():\n", - " \n", - " h1 = uniform(0.5, 2)\n", - " h2 = uniform(0.5, 3.5)\n", - " h3 = uniform(0.5, 5)\n", - " h4 = uniform(2, 5)\n", - " h5 = uniform(0.3, 0.91)\n", - " \n", - " L1 = h1 / uniform(0.025, 0.05)# shallow water\n", - " L2 = h2 / uniform(0.051, 0.49) # intermediate water\n", - " L3 = h3 / uniform(0.5, 0.75) # deep water\n", - " L4 = h4 / uniform(0.025, 0.075) # shallow or intermediate water\n", - " L5 = h5 /uniform(0.25, 0.75) # intermediate water or deep water\n", - " \n", - " L_serie = [L1, L2, L3, L4, L5]\n", - " h_serie = [h1, h2, h3, h4 , h5]\n", - " \n", - " Questions = []\n", - " correct_answers_id = []\n", - " \n", - " for L,h in zip(L_serie, h_serie):\n", - " L = round(L,2)\n", - " h = round(h,2)\n", - " Questions.append('L = ' + str(L) + ' m, h = ' + str(h) + ' m')\n", - " \n", - " ratio = h/L\n", - " \n", - " if ratio <= 0.05:\n", - " correct_answers_id.append(0)\n", - " if ratio > 0.05 and ratio < 0.5:\n", - " correct_answers_id.append(1)\n", - " if ratio >= 0.5:\n", - " correct_answers_id.append(2)\n", - " \n", - " #print('For debuging, 0-based answers: ' + str(correct_answers_id))\n", - " answers = ['Shallow', 'Intermediate', 'Deep']\n", - " \n", - " text_widget = widgets.Label(value= 'Select if the wave described on the right experiences shallow, intermediate, or deep water.')\n", - " \n", - " Hboxes =[]\n", - " toggle_widgets = []\n", - " for i in range(len(Questions)):\n", - " question_widget = widgets.Label(value= Questions[i], layout=widgets.Layout(width='175px'))\n", - " #question_widget.add_class('Small_widget')\n", - " toggle_widget = widgets.ToggleButtons(options=answers, description='', disabled=False, button_style='') \n", - " HBox1 = widgets.HBox([question_widget, toggle_widget])\n", - " Hboxes.append(HBox1)\n", - " toggle_widgets.append(toggle_widget) \n", - " \n", - " shuffle(Hboxes)\n", - " \n", - " submit_button = widgets.Button(description='Submit',)\n", - " output_widget = widgets.Text(value= '', placeholder='', description='Verify:', disabled=False, layout=widgets.Layout(width='230px'))\n", - " #output_widget.add_class('Broad_widget')\n", - " \n", - " HBox_check = widgets.HBox([submit_button, output_widget])\n", - "\n", - " VBox = widgets.VBox([text_widget] + Hboxes + [HBox_check])\n", - " VBox.layout.flex = 'none'\n", - " display(VBox)\n", - " \n", - " def check_answers(button):\n", - " score = 0\n", - " \n", - " for i in range(len(Questions)):\n", - " if toggle_widgets[i].value == answers[ correct_answers_id[i]]:\n", - " score += 1\n", - " \n", - " #print(toggle_widget.value)\n", - " output_widget.value = 'Your score is ' + str(score) + '/' + str(len(Questions))\n", - " \n", - " \n", - " submit_button.on_click(check_answers)\n", - " \n", - "#Q4()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Question 4_2, sub script 2 to prevent renumbering" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#from IPython.display import display" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length(T,h):\n", - " L = 9.81*T**2/(2*np.pi)\n", - " L_all = [L]\n", - " \n", - " for i in range(1500):\n", - " L = 9.81*T**2/(2*np.pi)*np.tanh(2*np.pi*h/L)\n", - " L_all.append(L)\n", - " \n", - " if np.abs(L_all[-1] - L_all[-2]) < 0.0005:\n", - " break\n", - " \n", - " return round(L,13)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def relation_to_omega(h, T1, T2): # T1 [s] (higher omega) < T2 [s]\n", - " %matplotlib inline\n", - "\n", - " T_serie = []\n", - " L_serie = []\n", - " k_serie = []\n", - " c_serie = []\n", - " n_serie = []\n", - " cg_serie = []\n", - " omega_serie = np.linspace(2*np.pi/T2, 2*np.pi/T1, 100)\n", - " for omega in omega_serie:\n", - " T = 2*np.pi/omega\n", - " L = wave_length(T,h)\n", - " k = 2*np.pi/L\n", - " c = L/T\n", - " n = 0.5+k*h/np.sinh(2*k*h)\n", - " cg = n*c\n", - "\n", - " T_serie.append(T)\n", - " L_serie.append(L)\n", - " k_serie.append(k)\n", - " c_serie.append(c)\n", - " n_serie.append(n)\n", - " cg_serie.append(cg)\n", - "\n", - " #plt.plot(omega_serie, T_serie);\n", - " #plt.plot(omega_serie, L_serie);\n", - " #plt.plot(omega_serie, k_serie);\n", - " #plt.plot(omega_serie, c_serie);\n", - " #plt.plot(omega_serie, n_serie);\n", - " #plt.plot(omega_serie, cg_serie);\n", - " #plt.xlabel('$\\omega$ [rad/s]');\n", - " \n", - " return omega_serie, T_serie, L_serie, k_serie, c_serie, n_serie, cg_serie\n", - " \n", - "#ans = relation_to_omega(h = 5, T1 = 2, T2 = 60) # works for T1 = 2 sec, T2 = 60 sec, h = 5 m\n", - "ans = relation_to_omega(h = 6000, T1 = 1*60, T2 = 60*60) # works for T1 = 2 sec, T2 = 60 sec, h = 5 m" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def modify_y_boundaries(ax,y_data, factor = 0.6, y_min = 0):\n", - " y_ticks = ax.get_yticks().tolist()\n", - " y_ticks_step = y_ticks[1] - y_ticks[0]\n", - " \n", - " if y_min == 0:\n", - " lowest = np.min(y_data) - y_ticks_step*factor\n", - " y_min = np.min(lowest,0)\n", - " \n", - " ax.set_ylim(y_min, np.max(y_data) + y_ticks_step * factor)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def widget_values(widgets):\n", - " values = []\n", - " for widget in widgets:\n", - " values.append(widget.value)\n", - " return values" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def Q4_2():\n", - " h = np.arange(3000, 6000+500,500)\n", - " h = np.random.choice(h)\n", - "\n", - " text1 = 'We are going to analyze the characteristics of tsunami waves at a depth of ' + str(h) + ' m.'\n", - " text2 = 'It will be analyzed for waves with a period of 5 minutes (point 1), at the shallow water boundary (point 2),'\n", - " text3 = 'the deep water boundary (point 3), and for waves with a period of 60 minutes (point 4).'\n", - "\n", - " Q1 = 'What is the wave length of the tsunami wave at those 4 points?'\n", - " Q1_labels = ['$L_1$', '$L_2$', '$L_3$', '$L_4$']\n", - " Q1_unit = ' km'\n", - " T1 = 5*60\n", - " T4 = 60 * 60\n", - "\n", - " L1 = wave_length(T = T1,h=h)\n", - " L2 = h/0.05\n", - " L3 = h/0.5\n", - " L4 = wave_length(T = T4,h=h)\n", - " Q1_answers = [round(L1/1000,2),L2/1000,L3/1000,round(L4/1000,2)]\n", - "\n", - " # The 4 points of interest in this question\n", - " w1 = 2*np.pi/T1\n", - " w2 = dispersion(k = 2*np.pi/(L2),h=h)\n", - " w3 = dispersion(k = 2*np.pi/(L3),h=h)\n", - " w4 = 2*np.pi/T4 \n", - " omega_points = np.array([w1,w2,w3,w4])\n", - "\n", - " Q2 = 'What is the wave celerity ($c$) of the tsunami wave at those 4 points?'\n", - " Q2_labels = ['$c_1$', '$c_2$', '$c_3$', '$c_4$']\n", - " Q2_unit = ' m/s'\n", - " T2 = 2*np.pi/w2\n", - " T3 = 2*np.pi/w3\n", - "\n", - " c1 = L1/T1\n", - " c2 = L2/T2\n", - " c3 = L3/T3\n", - " c4 = L4/T4\n", - " Q2_answers = [round(c1,2),round(c2,2),round(c3,2),round(c4,2)]\n", - "\n", - " Q3 = 'What is the wave group velocity ($c_g$) of the tsunami at those 4 points?'\n", - " Q3_labels = ['$c_{g1}$', '$c_{g2}$', '$c_{g3}$', '$c_{g4}$']\n", - " Q3_unit = ' m/s'\n", - "\n", - " k1 = 2*np.pi/L1\n", - " k2 = 2*np.pi/L2\n", - " k3 = 2*np.pi/L3\n", - " k4 = 2*np.pi/L4\n", - "\n", - " n1 = 0.5+k1*h/np.sinh(2*k1*h)\n", - " n2 = 0.5+k2*h/np.sinh(2*k2*h)\n", - " n3 = 0.5+k3*h/np.sinh(2*k3*h)\n", - " n4 = 0.5+k4*h/np.sinh(2*k4*h)\n", - "\n", - " cg1 = c1 * n1\n", - " cg2 = c2 * n2\n", - " cg3 = c3 * n3\n", - " cg4 = c4 * n4\n", - "\n", - " Q3_answers = [round(cg1,2),round(cg2,2),round(cg3,2),round(cg4,2)]\n", - " \n", - " # store the questions in a list\n", - " Questions = [Q1,Q2,Q3]\n", - " Unit_question = [Q1_unit,Q2_unit,Q3_unit]\n", - " answer_question = [Q1_answers,Q2_answers,Q3_answers]\n", - " label_question = [Q1_labels,Q2_labels,Q3_labels]\n", - " question_id_all = [1,2,3]# only used for debugging\n", - " \n", - " # the answers for the graph\n", - " omega_serie, T_serie, L_serie, k_serie, c_serie, n_serie, cg_serie = relation_to_omega(h, 60, T4)\n", - " \n", - " # add text above graph, the introduction\n", - " general_text_widget(text1)\n", - " general_text_widget(text2)\n", - " general_text_widget(text3)\n", - "\n", - " # plot graph\n", - " %matplotlib notebook\n", - " fig, axs = plt.subplots(figsize = (9,6), sharex=True) \n", - " ax1 = plt.subplot(411)\n", - " ax2 = plt.subplot(412)\n", - " ax3 = plt.subplot(413)\n", - " ax4 = plt.subplot(414)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(bottom=0.2)# Add some extra space for the axis at the bottom\n", - " fig.subplots_adjust(left=0.15)# Add some space for the labels\n", - "\n", - " # remove ticks in upper graphs\n", - " ax1.set_xticklabels([], fontsize=0)\n", - " ax2.set_xticklabels([], fontsize=0)\n", - " ax3.set_xticklabels([], fontsize=0)\n", - "\n", - " # set labels\n", - " ax1.set_ylabel('$T$ [s]')\n", - " ax2.set_ylabel('$L$ [m]')\n", - " ax3.set_ylabel('$n$ [-]')\n", - " ax4.set_ylabel('$c + c_g$ [m/s]')\n", - " ax4.set_xlabel('$\\omega$ [rad/s]')\n", - "\n", - " # set boundaries\n", - " modify_y_boundaries(ax1, T_serie)\n", - " modify_y_boundaries(ax2, L_serie)\n", - " ##ax2.set_ylim(0,1000)\n", - " modify_y_boundaries(ax3, n_serie)\n", - " modify_y_boundaries(ax4, np.concatenate([c_serie, cg_serie]))\n", - " ax1.set_xlim(0,np.max(omega_serie))\n", - " ax2.set_xlim(0,np.max(omega_serie))\n", - " ax3.set_xlim(0,np.max(omega_serie))\n", - " ax4.set_xlim(0,np.max(omega_serie))\n", - " \n", - " # move the scaling number (1e6)\n", - " ax2.yaxis.get_offset_text().set_x(-0.05)\n", - " \n", - " def create_button_callback(question_id, input_widgets, answers, output_widgets, units):\n", - " def button_callback(b):\n", - " #print(\"verify:\", question_id, input_widgets, answers, output_widgets)# debug\n", - " #print('You are answering question: ', question_id)# debug\n", - "\n", - " score = 0\n", - " for i in range(len(answers)):\n", - " num_widget = input_widgets[i]\n", - " answer = answers[i]\n", - " response = num_widget.value\n", - "\n", - " if response == answer:\n", - " output_widgets[i].value = str(\"Good job, this is correct\")\n", - " score += 1\n", - " else:\n", - " output_widgets[i].value = str(\"Incorrect, the answer should be \" + str(answer) + str(units)+ \", please try again.\")\n", - "\n", - " response_list = widget_values(input_widgets)\n", - "\n", - " # plot lines\n", - " line1, = ax1.plot(omega_serie, T_serie, color = 'k')\n", - " modify_y_boundaries(ax1, T_serie)\n", - "\n", - " if question_id == 1:\n", - " line2, = ax2.plot(omega_serie, L_serie, color = 'k')\n", - " modify_y_boundaries(ax2, np.concatenate([L_serie, Q1_answers]))\n", - "\n", - " dots2 = ax2.scatter(omega_points, np.array(Q1_answers)*1000 , c='r', s=40)\n", - " #dots2_2 = ax2.scatter(omega_points, np.array(response_list)*1000 , c='k', s=20)\n", - "\n", - " if question_id == 2:\n", - " line3, = ax3.plot(omega_serie, n_serie, color = 'k')\n", - " modify_y_boundaries(ax3, n_serie)\n", - "\n", - " line4, = ax4.plot(omega_serie, c_serie, label = 'c', color = 'r')\n", - " dots3 = ax4.scatter(omega_points, np.array(Q2_answers), c='r', s=40)\n", - " modify_y_boundaries(ax4, np.concatenate([c_serie, cg_serie,Q2_answers]))\n", - " ax4.legend(loc = 'best')\n", - "\n", - " if question_id == 3:\n", - "\n", - " line5, = ax4.plot(omega_serie, cg_serie, label = '$c_g$', color = '#ff5d00')\n", - " dots4 = ax4.scatter(omega_points, np.array(Q3_answers), c='#ff5d00', s=40)\n", - " ax4.legend(loc = 'best')\n", - "\n", - " modify_y_boundaries(ax4, np.concatenate([c_serie, cg_serie]))\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - " submit_buttons = []\n", - " all_quiz_widgets = []\n", - " for Question, units, answers, labels, question_id in zip(Questions, Unit_question, answer_question, label_question, question_id_all):\n", - "\n", - " question_widget = widgets.Label(value=Question)\n", - "\n", - " HBoxes = []\n", - " input_widgets = []\n", - " output_widgets = []\n", - " for answer, label in zip(answers, labels):\n", - " unit_widget = widgets.Label(value=label, layout=widgets.Layout(width=\"50px\"))\n", - " num_widget = widgets.FloatText(value=0, disabled=False, step=0.01, layout=widgets.Layout(width=\"80px\"))\n", - " widget_unit = widgets.Label(value=units, layout=widgets.Layout(width=\"30px\"))\n", - " output_widget = widgets.Text(value=\"\", placeholder=\"\", description=\"Feedback:\", disabled=False, layout=widgets.Layout(width=\"500px\"))\n", - "\n", - " HBox1 = widgets.HBox([unit_widget, num_widget, widget_unit, output_widget])\n", - " HBoxes.append(HBox1)\n", - "\n", - " input_widgets.append(num_widget)\n", - " output_widgets.append(output_widget)\n", - "\n", - " submit_button = widgets.Button(description= 'check')\n", - " submit_button.on_click(create_button_callback(question_id, input_widgets, answers, output_widgets, units))\n", - " submit_buttons.append(submit_button)\n", - "\n", - " # place all the horizontal boxes in one vertical box, and display it.\n", - " quiz_widget = widgets.VBox([question_widget] + HBoxes + [submit_button])\n", - " quiz_widget.layout.flex = \"none\"\n", - " all_quiz_widgets.append(quiz_widget)\n", - "\n", - " display(quiz_widget);" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#Q4_2()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Q5" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def group_stats(k1,k2,w1,w2):\n", - " Delta_k = np.abs(k2-k1)\n", - " Delta_w = np.abs(w2-w1)\n", - " L = 2*np.pi/Delta_k\n", - " T = 2*np.pi/Delta_w\n", - " cg = Delta_w/Delta_k\n", - " return L,T, cg" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#%matplotlib notebook\n", - "#%matplotlib widget\n", - "def harmonic_components():# manual set interval between frames in secondes\n", - " %matplotlib notebook\n", - "\n", - " \n", - " # manual set interval between frames in secondes\n", - " delta_t = 0.1\n", - "\n", - " # Define parameters and widgets\n", - " a1 = widgets.FloatSlider(value=1.1, min=0, max=20, step=0.01, description='a [m]');\n", - " a2 = widgets.FloatSlider(value=1, min=0, max=20, step=0.01, description='a [m]');\n", - "\n", - " T1 = widgets.FloatSlider(value=5, min=0.01, max=25, step=0.01, description='T [s]')\n", - " T2 = widgets.FloatSlider(value=5.3, min=0.01, max=25, step=0.01, description='T [s]')\n", - "\n", - " depth = widgets.FloatText(value=7.5, min=1, max=250, step=0.01, description='h [m]')\n", - " n_waves = widgets.FloatSlider(value=3, min=0.1, max=10, step=0.1, description='$n_{waves}$')\n", - " \n", - " # Calculate initial values\n", - " L1 = widgets.FloatText(value=wave_length(T1.value,depth.value), description='L [m]', disabled=True)\n", - " L2 = widgets.FloatText(value=wave_length(T2.value,depth.value), description='L [m]', disabled=True)\n", - " \n", - " L_group, T_group, c_g = group_stats(k1 = 2*np.pi/L1.value,k2 = 2*np.pi/L2.value,w1 = 2*np.pi/T1.value,w2 = 2*np.pi/T2.value)\n", - "\n", - " # setup linear mesh (x) and duration before time (t) is reset\n", - " x_max = L_group*n_waves.value\n", - " x = np.linspace(0, x_max, 5000)\n", - " t = np.arange(0,1000,delta_t)\n", - " \n", - " # Setup widget layout (User Interface) and display\n", - " vbox1 = widgets.VBox([widgets.Label('Wave 1', layout=widgets.Layout(align_self='center')),a1, T1, L1])\n", - " vbox2 = widgets.VBox([widgets.Label('Wave 2', layout=widgets.Layout(align_self='center')),a2, T2, L2])\n", - " vbox3 = widgets.VBox([widgets.Label('General', layout=widgets.Layout(align_self='center')),depth, n_waves])\n", - "\n", - " ui = widgets.HBox([vbox1, vbox2, vbox3])\n", - " display(ui)\n", - " \n", - " # Create figure, set structure and initial layout\n", - " fig, axs = plt.subplots(figsize = (9,5), sharex=True) \n", - " ax1 = plt.subplot(411) # axs[0] rather than ax1 could also be used\n", - " ax2 = plt.subplot(412)\n", - " ax3 = plt.subplot(413)\n", - " ax4 = plt.subplot(414) \n", - " plt.tight_layout()\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(bottom=0.2)# Add some extra space for the axis at the bottom\n", - " fig.subplots_adjust(left=0.1)# Add some space for the labels\n", - " \n", - " # set grid\n", - " grid_x = MultipleLocator(base=25)\n", - " grid_y = MultipleLocator(base=5)\n", - " ax1.xaxis.set_minor_locator(grid_x)\n", - " ax1.yaxis.set_minor_locator(grid_y)\n", - " ax1.grid(which='both', linestyle='-', linewidth='0.5', color='gray', alpha=0.6)\n", - " ax2.xaxis.set_minor_locator(grid_x)\n", - " ax2.yaxis.set_minor_locator(grid_y)\n", - " ax2.grid(which='both', linestyle='-', linewidth='0.5', color='gray', alpha=0.6)\n", - " ax3.xaxis.set_minor_locator(grid_x)\n", - " ax3.yaxis.set_minor_locator(grid_y)\n", - " ax3.grid(which='both', linestyle='-', linewidth='0.5', color='gray', alpha=0.6)\n", - " ax4.xaxis.set_minor_locator(grid_x)\n", - " ax4.yaxis.set_minor_locator(grid_y)\n", - " ax4.grid(which='both', linestyle='-', linewidth='0.5', color='gray', alpha=0.6)\n", - " \n", - " # change the axis when related parameters change\n", - " def update_axis(change):\n", - " L_group, T_group, c_g = group_stats(k1 = 2*np.pi/L1.value,k2 = 2*np.pi/L2.value,w1 = 2*np.pi/T1.value,w2 = 2*np.pi/T2.value)\n", - " x_max = L_group * n_waves.value\n", - " ax1.set_xlim(0, x_max)\n", - " ax2.set_xlim(0, x_max)\n", - " ax3.set_xlim(0, x_max)\n", - " ax4.set_xlim(0, x_max)\n", - " \n", - " x = np.linspace(0, x_max, 500)\n", - " \n", - " grid_x = MultipleLocator(base=x_max/100)\n", - " ax1.xaxis.set_minor_locator(grid_x)\n", - " ax2.xaxis.set_minor_locator(grid_x)\n", - " ax3.xaxis.set_minor_locator(grid_x)\n", - " ax4.xaxis.set_minor_locator(grid_x)\n", - " \n", - " ax1.grid(which='both', linestyle='-', linewidth='0.5', color='gray', alpha=0.6)\n", - " ax2.grid(which='both', linestyle='-', linewidth='0.5', color='gray', alpha=0.6)\n", - " ax3.grid(which='both', linestyle='-', linewidth='0.5', color='gray', alpha=0.6)\n", - " ax4.grid(which='both', linestyle='-', linewidth='0.5', color='gray', alpha=0.6)\n", - " \n", - " # disable maine vertical lines\n", - " ax1.xaxis.grid(False)\n", - " ax2.xaxis.grid(False)\n", - " ax3.xaxis.grid(False)\n", - " ax4.xaxis.grid(False)\n", - " \n", - " n_waves.observe(update_axis)\n", - " \n", - " def update_y_axis(change):\n", - " # adjust the boundaries of the plot\n", - " amp = a1.value +a2.value\n", - " ax1.set_ylim(-amp*1.1, amp*1.1)\n", - " ax2.set_ylim(-amp*1.1, amp*1.1)\n", - " ax3.set_ylim(-amp*1.1, amp*1.1)\n", - " ax4.set_ylim(-amp*1.1, amp*1.1)\n", - " \n", - " a1.observe(update_y_axis)\n", - " a2.observe(update_y_axis)\n", - " \n", - " # change the values of L, T of wave components and wave group parameters when T or depth changes \n", - " def update_param(change):\n", - " L1.value = wave_length(T1.value,h = depth.value)\n", - " L2.value = wave_length(T2.value,h = depth.value)\n", - " L_group, T_group, c_g = group_stats(k1 = 2*np.pi/L1.value,k2 = 2*np.pi/L2.value,w1 = 2*np.pi/T1.value,w2 = 2*np.pi/T2.value)\n", - " \n", - " update_axis(change)\n", - " \n", - " # debug\n", - " #ax1.text(0.02, 0.8, L_group, transform=ax1.transAxes, fontsize=8, bbox={'facecolor': 'white'})\n", - " #ax1.text(0.02, 0.50, T_group, transform=ax1.transAxes, fontsize=8, bbox={'facecolor': 'white'})\n", - " #ax1.text(0.02, 0.2, c_g, transform=ax1.transAxes, fontsize=8, bbox={'facecolor': 'white'})\n", - " \n", - " T1.observe(update_param)\n", - " T2.observe(update_param)\n", - " depth.observe(update_param) \n", - " \n", - " # Initialize\n", - " update_param('None')\n", - " update_axis('None')\n", - " update_y_axis('None')\n", - " \n", - " # set labels\n", - " ax4.set_xlabel('x [m]')\n", - " \n", - " ax1.set_ylabel('$\\eta_1$')\n", - " ax2.set_ylabel('$\\eta_2$')\n", - " ax3.set_ylabel('$\\eta_1$ + $\\eta_2$')\n", - " \n", - " # remove ticks in upper graphs\n", - " ax1.set_xticklabels([], fontsize=0)\n", - " ax2.set_xticklabels([], fontsize=0)\n", - " ax3.set_xticklabels([], fontsize=0)\n", - " \n", - " # Compute initial displacement\n", - " eta = a1.value*np.sin(2*np.pi/L1.value*x) + a2.value*np.sin(2*np.pi/L2.value*x)\n", - " eta1 = a1.value*np.sin(2*np.pi/L1.value*x)\n", - " eta2 = a2.value*np.sin(2*np.pi/L2.value*x)\n", - " \n", - " k_bar = (2*np.pi/L1.value + 2*np.pi/L2.value)/2\n", - " car_wave = (a1.value + a2.value)*np.sin(k_bar*x)\n", - " \n", - " k_dif = (2*np.pi/L1.value - 2*np.pi/L2.value)# Delta k\n", - " var_amp = (a1.value + a2.value)*np.cos(-k_dif/2*x)\n", - "\n", - " # Plot initial wave\n", - " line1, = ax1.plot(x, eta1, label = '$\\u03B7_1$ (wave 1)', linewidth= 0.5, color = '#0b5394')\n", - " line2, = ax2.plot(x, eta2, label = '$\\u03B7_2$ (wave 2)', linewidth= 0.5, color = '#0b5394')#03396c\n", - " line, = ax3.plot(x, eta, label = '$\\u03B7$ (wave 1+2)', color = 'k')\n", - " line_carrier, = ax4.plot(x, car_wave, label = 'Carrier wave [m]', color = 'k')\n", - " #line_var, = ax3.plot(x, var_amp, label = 'Variable amplitude', color = 'gray')\n", - " line_var2, = ax4.plot(x, var_amp, label = 'Variable amplitude [-]', color = '#0b5394')\n", - " \n", - " # layout line\n", - " line.set_linewidth(0.75)\n", - " line_carrier.set_linewidth(0.75)\n", - " line_var2.set_linewidth(0.75)\n", - " \n", - " # make legend of axis 4\n", - " legend4 = ax4.legend(loc='lower right')\n", - " for text in legend4.get_texts():\n", - " text.set_fontsize(7) # Set individual legend item text size\n", - " \n", - " # update the graph each frame\n", - " def update2(frame):\n", - " L_group, T_group, c_g = group_stats(k1 = 2*np.pi/L1.value,k2 = 2*np.pi/L2.value,w1 = 2*np.pi/T1.value,w2 = 2*np.pi/T2.value)# <-- there should be a less computational demanding solution\n", - " x_max = L_group * n_waves.value\n", - " x = np.linspace(0, x_max, 500)\n", - " \n", - " # get current time\n", - " t = delta_t * frame\n", - "\n", - " # calculate sea surface elevation\n", - " eta = a1.value*np.sin(2*np.pi/T1.value*t-2*np.pi/L1.value*x) + a2.value*np.sin(2*np.pi/T2.value*t-2*np.pi/L2.value*x)\n", - " eta1 = a1.value*np.sin(2*np.pi/T1.value*t-2*np.pi/L1.value*x)\n", - " eta2 = a2.value*np.sin(2*np.pi/T2.value*t-2*np.pi/L2.value*x)\n", - " \n", - " omega_bar = (2*np.pi/T1.value + 2*np.pi/T2.value)/2\n", - " k_bar = (2*np.pi/L1.value + 2*np.pi/L2.value)/2\n", - " carrier = (a1.value + a2.value)*np.sin(omega_bar*t-k_bar*x)\n", - " \n", - " omega_dif = (2*np.pi/T1.value - 2*np.pi/T2.value)\n", - " k_dif = (2*np.pi/L1.value - 2*np.pi/L2.value)\n", - " var_amp = np.cos(omega_dif/2*t - k_dif/2*x)#(a1.value + a2.value)\n", - "\n", - " # adjust sea surface elevation (line) in plot\n", - " line.set_ydata(eta)\n", - " line_carrier.set_ydata(carrier)\n", - " line_var2.set_ydata(var_amp)\n", - " \n", - " line.set_xdata(x)\n", - " line_carrier.set_xdata(x)\n", - " line_var2.set_xdata(x)\n", - "\n", - " # adjust line of wave component or set thinkness to 0 (not visible) if amplitude is zero\n", - " if a1.value > 0:\n", - " line1.set_xdata(x)\n", - " line1.set_ydata(eta1)\n", - " line1.set_linewidth(0.75)\n", - " else:\n", - " line1.set_linewidth(0)\n", - "\n", - " if a2.value > 0:\n", - " line2.set_xdata(x)\n", - " line2.set_ydata(eta2)\n", - " line2.set_linewidth(0.75)\n", - " else:\n", - " line2.set_linewidth(0)\n", - "\n", - " plt.show()\n", - " \n", - "\n", - " # Create/call animation and show it\n", - " anim = FuncAnimation(fig, update2, frames=len(t), interval=delta_t*1000)\n", - " plt.show()\n", - " \n", - " return anim, a1,a2,T1,T2,L1,L2, depth, n_waves # animation is required to prevent freezing of the picture, a1 is the widget, exporting a1.value will result in not updating the widget value\n", - "\n", - "def Q5():\n", - " anim_5 = harmonic_components()\n", - " return anim_5\n", - "\n", - "#anim_5 = Q5()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [] - }, - { - "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.11.4" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/coastalcodebook/assignments/Chris/Initialize/Week_5_Initialize.ipynb b/coastalcodebook/assignments/Chris/Initialize/Week_5_Initialize.ipynb deleted file mode 100644 index aa02a96..0000000 --- a/coastalcodebook/assignments/Chris/Initialize/Week_5_Initialize.ipynb +++ /dev/null @@ -1,232 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "de4d73be", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "#from IPython.display import display, HTML\n", - "from ipywidgets import interact\n", - "import matplotlib.animation as animation\n", - "\n", - "from matplotlib.ticker import MultipleLocator\n", - "from matplotlib.animation import FuncAnimation\n", - "\n", - "from random import shuffle\n", - "from random import uniform\n", - "\n", - "import IPython\n", - "\n", - "print('Packages succesfully loaded')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b2fa1b6e", - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length(T,h):\n", - " Dif = []\n", - " L_serie = np.arange(0.1,1000,0.01) \n", - " for L in L_serie:\n", - " LHS = 2*np.pi/T\n", - " RHS = 9.81*2*np.pi/L * np.tanh(2*np.pi/L * h)\n", - " Dif.append(np.abs(LHS-RHS))\n", - " if len(Dif) > 2 and Dif[-1] > Dif[-2]:\n", - " break\n", - " \n", - " id_best = np.argmin(Dif)\n", - " L= L_serie[id_best]\n", - " #plt.plot(Dif)\n", - " \n", - " return round(L,13)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8dcbcdfd", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_phases():\n", - " \n", - " # define widgets\n", - " a1 = widgets.FloatSlider(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - " a2 = widgets.FloatSlider(value=0.5, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - " T1 = widgets.FloatSlider(value=8, min=0.01, max=250000, step=0.01, description='T [s]')\n", - " T2 = widgets.FloatSlider(value=4, min=0.01, max=250000, step=0.01, description='T [s]')\n", - "\n", - " phi_1 = widgets.FloatSlider(value=0, min=-1, max=1, step=0.01, description='$\\phi$ [2 $\\pi$ rad]')\n", - " phi_2 = widgets.FloatSlider(value=0.25, min=-1, max=1, step=0.01, description='$\\phi$ [2 $\\pi$ rad]')\n", - "\n", - " # Setup widget layout (User Interface) and display\n", - " vbox1 = widgets.VBox([widgets.Label('Wave 1', layout=widgets.Layout(align_self='center')),a1, T1, phi_1])\n", - " vbox2 = widgets.VBox([widgets.Label('Wave 2', layout=widgets.Layout(align_self='center')),a2, T2, phi_2])\n", - "\n", - " ui = widgets.HBox([vbox1, vbox2])\n", - "\n", - " def calc_eta(a1,T1,phi_1,a2,T2,phi_2):\n", - " t = np.arange(0,3*T1,0.1)\n", - "\n", - " fig, axs = plt.subplots(figsize = (9,5), sharex=True, sharey = True)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.04)\n", - " \n", - " # time based\n", - " ax1 = plt.subplot(321)\n", - " ax2 = plt.subplot(323)\n", - " ax3 = plt.subplot(325)\n", - " # space based\n", - " ax4 = plt.subplot(322)\n", - " ax5 = plt.subplot(324)\n", - " ax6 = plt.subplot(326)\n", - " \n", - " # calculate surface \n", - " eta1 = a1*np.cos(2*np.pi/T1*t-phi_1*(2*np.pi))\n", - " eta2 = a2*np.cos(2*np.pi/T2*t-phi_2*(2*np.pi))\n", - " eta = eta1+eta2\n", - " \n", - " eta_x1 = a1*np.cos(-2*np.pi/T1*t-phi_1*(2*np.pi))\n", - " eta_x2 = a2*np.cos(-2*np.pi/T2*t-phi_2*(2*np.pi))\n", - " eta_x = eta_x1 + eta_x2\n", - "\n", - " # calculate surface without phase change\n", - " eta_1_basic = a1*np.cos(2*np.pi/T1*t)\n", - " eta_2_basic = a2*np.cos(2*np.pi/T2*t)\n", - " eta_basic = eta1\n", - " \n", - " eta_1x_basic = a1*np.cos(-2*np.pi/T1*t)\n", - " eta_2x_basic = a2*np.cos(-2*np.pi/T2*t)\n", - " eta_x_basic = eta_1x_basic + eta_2x_basic\n", - " \n", - " # plot surface excluding phase change\n", - " ax1.plot(t,eta_1_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax2.plot(t,eta_2_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax3.plot(t, eta1, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " ax4.plot(t, eta_1x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax5.plot(t, eta_2x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax6.plot(t, eta_x1, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " \n", - " # plot surface including phase change\n", - " ax1.plot(t,eta1, label = '$\\eta_1$')\n", - " ax2.plot(t,eta2, label = '$\\eta_2$' )\n", - " ax3.plot(t,eta, label = '$\\eta_{1+2}$')\n", - " \n", - " ax4.plot(t,eta_x1, label = '$\\eta_1$')\n", - " ax5.plot(t,eta_x2, label = '$\\eta_2$')\n", - " ax6.plot(t,eta_x, label = '$\\eta_{1+2}$')\n", - " \n", - " # set vertical axis the same\n", - " amp = (a1+a2)*1.1\n", - " ax1.set_ylim(-amp,amp)\n", - " ax2.set_ylim(-amp,amp)\n", - " ax3.set_ylim(-amp,amp)\n", - " ax4.set_ylim(-amp,amp)\n", - " ax5.set_ylim(-amp,amp)\n", - " ax6.set_ylim(-amp,amp)\n", - " \n", - " # set horizontal axis\n", - " ax1.set_xlim(0,3*T1)\n", - " ax2.set_xlim(0,3*T1)\n", - " ax3.set_xlim(0,3*T1)\n", - " ax4.set_xlim(0,3*T1)\n", - " ax5.set_xlim(0,3*T1)\n", - " ax6.set_xlim(0,3*T1)\n", - " \n", - " # set labels\n", - " ax1.set_ylabel('$\\eta_1$ [m]')\n", - " ax2.set_ylabel('$\\eta_2$ [m]')\n", - " ax3.set_ylabel('$\\eta_{1+2}$ [m]')\n", - " \n", - " ax3.set_xlabel('t/T1 [s]')\n", - " ax6.set_xlabel('x/L1 [m]')\n", - " \n", - " # set scaled ticks\n", - " ax3.set_xticks([0, T1,T1*2, T1*3])\n", - " ax3.set_xticklabels([0,1,2,3])\n", - " \n", - " ax6.set_xticks([0,T1,T1*2, T1*3])\n", - " ax6.set_xticklabels([0,1,2,3])\n", - " \n", - " # plot legends \n", - " #legend1 = ax4.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " #legend2 = ax5.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " #legend3 = ax6.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " \n", - " # set title\n", - " ax1.set_title('Time based')\n", - " ax4.set_title('Space based')\n", - " \n", - " # remove x and y ticks\n", - " ax4.set_xticklabels([], fontsize=0)\n", - " ax5.set_xticklabels([], fontsize=0)\n", - " \n", - " ax4.set_yticklabels([], fontsize=0)\n", - " ax5.set_yticklabels([], fontsize=0)\n", - " ax6.set_yticklabels([], fontsize=0)\n", - "\n", - " # initialize graph\n", - " #out = calc_eta(a1.value,T1.value,phi_1.value,a2.value,T2.value,phi_2.value)\n", - " \n", - " #update graph\n", - " out = widgets.interactive_output(calc_eta, {'a1': a1,'T1':T1, 'phi_1': phi_1, 'a2': a2,'T2':T2, 'phi_2': phi_2})\n", - " \n", - "\n", - " \n", - " # display outcomes\n", - " display(ui, out)\n", - "\n", - "#plot_phases()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e1d49617", - "metadata": {}, - "outputs": [], - "source": [ - "def Q1():\n", - " plot_phases() " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3c2526b5", - "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.11.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/coastalcodebook/assignments/Chris/Topic/Bounded_waves.ipynb b/coastalcodebook/assignments/Chris/Topic/Bounded_waves.ipynb deleted file mode 100644 index 250f826..0000000 --- a/coastalcodebook/assignments/Chris/Topic/Bounded_waves.ipynb +++ /dev/null @@ -1,232 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "850a2a5e", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from matplotlib.animation import FuncAnimation\n", - "import ipywidgets as widgets\n", - "from ipywidgets import interact" - ] - }, - { - "cell_type": "markdown", - "id": "5117cd50", - "metadata": {}, - "source": [ - "https://ipywidgets.readthedocs.io/en/7.6.4/examples/Using%20Interact.html" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a59e6f8f", - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length(T,h):\n", - " Dif = []\n", - " L_serie = np.arange(0.1,1000,0.01) \n", - " for L in L_serie:\n", - " LHS = 2*np.pi/T\n", - " RHS = 9.81*2*np.pi/L * np.tanh(2*np.pi/L * h)\n", - " Dif.append(np.abs(LHS-RHS))\n", - " if len(Dif) > 2 and Dif[-1] > Dif[-2]:\n", - " break\n", - " \n", - " id_best = np.argmin(Dif)\n", - " L= L_serie[id_best]\n", - " #plt.plot(Dif)\n", - " \n", - " return round(L,13)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "54d7c37d", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_phases():\n", - " \n", - " # define widgets\n", - " a1 = widgets.FloatSlider(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - " a2 = widgets.FloatSlider(value=0.5, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - " T1 = widgets.FloatSlider(value=8, min=0.01, max=250000, step=0.01, description='T [s]')\n", - " T2 = widgets.FloatSlider(value=4, min=0.01, max=250000, step=0.01, description='T [s]')\n", - "\n", - " phi_1 = widgets.FloatSlider(value=0, min=-1, max=1, step=0.01, description='$\\phi$ *2 $\\pi$ [rad]')\n", - " phi_2 = widgets.FloatSlider(value=0.25, min=-1, max=1, step=0.01, description='$\\phi$ *2 $\\pi$ [rad]')\n", - "\n", - " # Setup widget layout (User Interface) and display\n", - " vbox1 = widgets.VBox([widgets.Label('Wave 1', layout=widgets.Layout(align_self='center')),a1, T1, phi_1])\n", - " vbox2 = widgets.VBox([widgets.Label('Wave 2', layout=widgets.Layout(align_self='center')),a2, T2, phi_2])\n", - "\n", - " ui = widgets.HBox([vbox1, vbox2])\n", - "\n", - " def calc_eta(a1,T1,phi_1,a2,T2,phi_2):\n", - " t = np.arange(0,3*T1,0.1)\n", - "\n", - " fig, axs = plt.subplots(figsize = (9,5), sharex=True, sharey = True)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.04)\n", - " \n", - " # time based\n", - " ax1 = plt.subplot(321)\n", - " ax2 = plt.subplot(323)\n", - " ax3 = plt.subplot(325)\n", - " # space based\n", - " ax4 = plt.subplot(322)\n", - " ax5 = plt.subplot(324)\n", - " ax6 = plt.subplot(326)\n", - " \n", - " # calculate surface \n", - " eta1 = a1*np.cos(2*np.pi/T1*t-phi_1*(2*np.pi))\n", - " eta2 = a2*np.cos(2*np.pi/T2*t-phi_2*(2*np.pi))\n", - " eta = eta1+eta2\n", - " \n", - " eta_x1 = a1*np.cos(-2*np.pi/T1*t-phi_1*(2*np.pi))\n", - " eta_x2 = a2*np.cos(-2*np.pi/T2*t-phi_2*(2*np.pi))\n", - " eta_x = eta_x1 + eta_x2\n", - "\n", - " # calculate surface without phase change\n", - " eta_1_basic = a1*np.cos(2*np.pi/T1*t)\n", - " eta_2_basic = a2*np.cos(2*np.pi/T2*t)\n", - " eta_basic = eta1\n", - " \n", - " eta_1x_basic = a1*np.cos(-2*np.pi/T1*t)\n", - " eta_2x_basic = a2*np.cos(-2*np.pi/T2*t)\n", - " eta_x_basic = eta_1x_basic + eta_2x_basic\n", - " \n", - " # plot surface excluding phase change\n", - " ax1.plot(t,eta_1_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax2.plot(t,eta_2_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax3.plot(t, eta1, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " ax4.plot(t, eta_1x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax5.plot(t, eta_2x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax6.plot(t, eta_x1, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " \n", - " # plot surface including phase change\n", - " ax1.plot(t,eta1, label = '$\\eta_1$')\n", - " ax2.plot(t,eta2, label = '$\\eta_2$' )\n", - " ax3.plot(t,eta, label = '$\\eta_{1+2}$')\n", - " \n", - " ax4.plot(t,eta_x1, label = '$\\eta_1$')\n", - " ax5.plot(t,eta_x2, label = '$\\eta_2$')\n", - " ax6.plot(t,eta_x, label = '$\\eta_{1+2}$')\n", - " \n", - " # set vertical axis the same\n", - " amp = (a1+a2)*1.1\n", - " ax1.set_ylim(-amp,amp)\n", - " ax2.set_ylim(-amp,amp)\n", - " ax3.set_ylim(-amp,amp)\n", - " ax4.set_ylim(-amp,amp)\n", - " ax5.set_ylim(-amp,amp)\n", - " ax6.set_ylim(-amp,amp)\n", - " \n", - " # set horizontal axis\n", - " ax1.set_xlim(0,3*T1)\n", - " ax2.set_xlim(0,3*T1)\n", - " ax3.set_xlim(0,3*T1)\n", - " ax4.set_xlim(0,3*T1)\n", - " ax5.set_xlim(0,3*T1)\n", - " ax6.set_xlim(0,3*T1)\n", - " \n", - " # set labels\n", - " ax1.set_ylabel('$\\eta_1$ [m]')\n", - " ax2.set_ylabel('$\\eta_2$ [m]')\n", - " ax3.set_ylabel('$\\eta_{1+2}$ [m]')\n", - " \n", - " ax3.set_xlabel('t/T1 [s]')\n", - " ax6.set_xlabel('x/L1 [m]')\n", - " \n", - " # set scaled ticks\n", - " ax3.set_xticks([0, T1,T1*2, T1*3])\n", - " ax3.set_xticklabels([0,1,2,3])\n", - " \n", - " ax6.set_xticks([0,T1,T1*2, T1*3])\n", - " ax6.set_xticklabels([0,1,2,3])\n", - " \n", - " # plot legends \n", - " legend1 = ax4.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend2 = ax5.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend3 = ax6.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " \n", - " # set title\n", - " ax1.set_title('Time based')\n", - " ax4.set_title('Space based')\n", - " \n", - " # remove x and y ticks\n", - " ax4.set_xticklabels([], fontsize=0)\n", - " ax5.set_xticklabels([], fontsize=0)\n", - " \n", - " ax4.set_yticklabels([], fontsize=0)\n", - " ax5.set_yticklabels([], fontsize=0)\n", - " ax6.set_yticklabels([], fontsize=0)\n", - "\n", - " # initialize graph\n", - " #out = calc_eta(a1.value,T1.value,phi_1.value,a2.value,T2.value,phi_2.value)\n", - " \n", - " #update graph\n", - " out = widgets.interactive_output(calc_eta, {'a1': a1,'T1':T1, 'phi_1': phi_1, 'a2': a2,'T2':T2, 'phi_2': phi_2})\n", - " \n", - "\n", - " \n", - " # display outcomes\n", - " display(ui, out)\n", - "\n", - "plot_phases()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bf7ba0a7", - "metadata": {}, - "outputs": [], - "source": [ - "var = 9\n", - "del(var)\n", - "\n", - "if 'var' not in locals():\n", - " print('var exist')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "86c7dff0", - "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.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/coastalcodebook/assignments/Chris/Week 3.ipynb b/coastalcodebook/assignments/Chris/Week 3.ipynb deleted file mode 100644 index 1645de4..0000000 --- a/coastalcodebook/assignments/Chris/Week 3.ipynb +++ /dev/null @@ -1,256 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - " \n", - "
\n", - "\n", - "Before you is the Jupiter Notebook of week 3. This notebook will be incorporated in a Jupiter Book, which has the option to hide cells, and thus hide the code. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Chapter 3: Oceanic wind waves and tides" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%run Initialize/Week_3_Initialize.ipynb" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Wind waves and tidal waves can travel great distances until they finally reach the coast. In this section we discuss the propagation of waves, both of individual waves and the behavior of a group of waves together. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 3.1) Individual wave characteristics" - ] - }, - { - "attachments": { - "afbeelding.png": { - "image/png": "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" - } - }, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "The description of waves are important, both over time at a stationary location as over space for a specific time. The spatial distribution of waves is directly visible pictures, while time series can come from measurement devices, like buoys or outputs from models at a specific site. Their signals are different, especially for irregular and/or assymetric wavefields, the paramaters are illustrated in the figure below.\n", - "\n", - "![afbeelding.png](attachment:afbeelding.png)\n", - "
A simple representation of a wave, with left the elevation of one wave over space, and on the right side a temporal serie of the same wave measured at location 2, from Coastal Dynamics (2023, figure 3.1)
\n", - " \n", - "The oscilation that the propagating waves make in the water surface ($\\eta$) can be described by the anglular frequency ($\\omega$)[rad/s] and the wavenumber (k)[rad/m], that depend on the wave period ($T$) and wave length ($L$), as in:\n", - "\n", - "\\begin{equation}\n", - "\\tag{3.1}\n", - "\\eta = a sin(\\omega t - kx)\n", - "\\end{equation}\n", - "\n", - "\\begin{equation}\n", - "\\tag{3.2}\n", - "\\omega = 2\\pi f = \\frac{2 \\pi}{T}; \\,\\ k = \\frac{2\\pi}{L}\n", - "\\end{equation}\n", - "\n", - "Each wave causes a periodic ossilation in the water surface. The corresponding deformation propagates with the wave speed ($c$), which is:\n", - "\n", - "\\begin{equation}\n", - "\\tag{3.3}\n", - "\\ c= \\frac{L}{T} = \\frac{\\omega}{k}\n", - "\\end{equation}\n", - "\n", - "In deep water is the wave speed related to the wave period and is the ossilation of water surface elevation a sinusoidal function. The propagation velocity of these waves is reduced by the bottom friction when these enter intermediate water depths. The water depth ($h$) that influences the wave propagation and shape is depended on the wave characteristics, and lead to various wave transformations, especially when the water depth becomes shallower as the wave propagate. The wave propagation can be described by the dispersion relation, as long as the waves are not in too shallow water, where the wave amplitude becomes significant compared to the water depth and a too steep wave is formed. The dispersion relation is described in formula 3.4.\n", - "\n", - "\\begin{equation}\n", - "\\tag{3.4}\n", - "\\label{eq:dispersion_relation}\n", - "\\omega = \\sqrt{(gk\\,\\tanh (kh))}\n", - "\\end{equation}\n", - "\n", - "This function is usefull in calculating the wave period or wave length if one of them and the depth are given. This gives also the possibility to analyze what happens to these waves when they propagate to shallower water." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "'''Run this cell to get the questions'''\n", - "Q1() " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Various wave characteristics can be calculated, with the wave length at hand. [requires additional tekst]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "'''Run this cell to get the questions'''\n", - "Q2()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It is also important to quantatively access the situation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "'''Run this cell to get the questions'''\n", - "Q2B()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "[this is old text, should be rewritten]\n", - "\n", - "The water depth can sometimes influence the wave propagation speed, as we have seen in the previous question. There are two type of exceptions, namely deep water and shallow water. In deep water is the depth so large that the orbital velocities of the wave do not reach the bottom and no interaction between the waves and the bed is possible. In this case is the wave celerity is only affected by the wave period. The other extreme is in shallow water, where only the water depth influences the wave celerity, and the wave period is irrevant. This can lead to waves with the same celerity (non-dispersive waves) eventhough they have different characteristics. For both of these waves hold that they should meet the requirements for shallow water conditions. In other words, although both waves are on the same location, one of them can be in shallow water while the other is not, which result in that both waves have a different celerity. \n", - "\n", - "In the interactive graph below you can check the wave celerity for 3 wave components if they have a specific wave lengths. At the offshore location is the wave celerity constant, then the wave celerity decreases in the intermediate water depth. And in one section of the graphs are the celerities for all 3 wave components equal. In this section are all the 3 waves in shallow water. \n", - "\n", - "Waves with various periods reach the coastal area. The wave speed and length is influenced by the water depth. The graph below shows the wave celerity at each location when the wave length is the specified length. Note that the wave length descreases as waves travels to the shoreline, this graph shows the wave celerity for waves with a specified length.\n", - "\n", - "[tekst above should be rewritten]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "'''Run this cell to get an interactive graph.'''\n", - "Q3()\n", - "# The layout will be improved, especially a reduction in graph size and shared axis. Week 5 will elaborate on this.\n", - "# Or, to incorporate this in the preparation/repetition section, \"In coastal waves you have learned that .... \"" - ] - }, - { - "attachments": { - "afbeelding.png": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlEAAAFCCAYAAADR1oh2AAAgAElEQVR4XuydB/gURdK427vT8zPcGRAwB8wi5ggoShDwQwygiCBmFBQMqIgCZiTpqYABBUQxJwQTiphQMWckKChGxNMzXPzuf3/f8hqHZfe3Mz092xuqn2efX9jpnpnqnu53qqqrVvjPz8VoUQmoBFQCKgGVgEpAJaASSCSBFRSiEslLD1YJqARUAioBlYBKQCUgElCI0oGgElAJqARUAioBlYBKwEECClEOQtMqKgGVgEpAJaASUAmoBBSidAyoBFQCKgGVgEpAJaAScJBAzUDUv/71L/O73/3OrLDCCg5i0ippJfB///d/5re//a3KP60gHer/v//3/8w//vEP8/vf/9785je/cWhBq6SVgI7/tBJ0r8/eKeSv87+7DNPUZP6hD5j/q7HUDET16tXLfPnllyVfRBhAgFstL148QH/729/M//zP/5iVVlpJHigmNS2lkcC///1v89e//lXkv+KKK4r8tZROAsjbyj/JPEC/UZfFX4u7BJiD7fyTRP7uZ9SaVgKsfX//+99N/fr1zaBBg8xGG21UdcKpGYhq1qyZGTp0qFlnnXVK2onnnXeeadGihdltt91qEhxYtL/44gtz0003mZYtW5p58+aZLbfc0uyxxx7GvqGUtENq7GRonz788ENz6623ms6dO5vGjRvLpKalNBIAgHh5u/rqq03v3r3NhhtuaNCKFyurrbaaueOOO+R5Ofvss81f/vKXYlX0+zwSYP75+uuvzciRI03//v3NH/7wBwOcaimNBFZeeWWzcOFCc88995ixY8ea9dZbrzQnLuFZqgKieFsrZqY76KCDzG233SYPUSnLCSecYDp27CgAEWfyLOW1leJcaJ54iK666ioB2BtvvNEce+yxZvDgwTKZqVYk215gEnvvvffMlVdeaU4++WSz++67y1u5ltJIgEX8k08+Meeee6659NJLzeabb27++c9/Fj0589S1115r3n77bQGwP//5z0Xr6AHLS4D557PPPjP9+vUTea655poKUSUcKKussor54IMPzPjx482wYcPMWmutVcKzl+ZUFQtRPBgPPPCA2Fl79uxZ1FzWoUMHIeGGDRuWRrL/Pcsxxxxj/vd//1e0UbUKUYsWLZI3QTQiL730knn44YfNnnvuqRqREoxEIGr27NnmmmuuMQD9rrvuqhBVArnbUwBRjP+BAweKOaNRo0axIeq6664z7777rhk+fLj59ttvS3jV1XMqqwnHIsAcpBBV2r7FhWDu3Llm0qRJMo7XXnvt0l5ACc5WcRCFWpuH4ZFHHjFvvvmm2Wuvvcxzzz1XVFQKUUVFlMkBvAlizjj11FPNrFmzpL9uvvlmg7miFqEyEyHX0SgQ9f7774smipcN1USVtgesJopF/OKLL06kiVKISt9XyP/zzz8XkygaPTQhas5LL9e4LShExZVUCY/jgZg4caL4dkydOlVMRY8++mhRcx4QxeKNg1spi2qiVjJLliwxXbp0EegdPXq06datmzjaqikv+5GIT9T8+fPNhAkTpA+233571QBmL/alZ7CLOC9+ffv2NRtvvHHZa6LyuUYUcpeoy42imIsFQopzTJLjco9F/l999ZX4w6IN/OMf/xgbouJeW5rhVIpzpLk+l7rRewKi5syZY8aNGyd9oJooF4l6rhP1f7rgggvMyy+/bB5//PGiD6NClOeOiNkcmihMEfiF4Ytz1113iWM5EKUlewkwoeGD891338kCAlQpvC4v9ywWM9rkw05UXiTq1asnu1NtiZ7THmu/o6/w4XnnnXdEgxI15+XWi96NbSefn2hcOGLDR662hnvg/7mF/xcaT3xHO4VkS7042mi7mzfuuI1ek5X/N998I/KPuzsv6Tldn2TuP+59uZ6j1PWivq7MNx9//LF55plnxKVAfaJK3RtFzoez5htvvBELopo2bSqqdLs7g7g5W221lenevbts+843QaS5XR5e2sUPpW3btobdgXEmjDTnLMe6PESALnLu2rWrufDCC2VR9y3vcrz3crgmu4j88MMPZtVVV10aYqIcrs3nNRQDi1zQyAceuXBTqE37/2LntBDBXANEIX8KiwxzAc9A9KddfPgfZtgpU6aIH+Fxxx0nu/NsWBB+2t9tG3Yhpq4Fl+h3XKs9b/TeeRapG13IOS4XjqLnLFbffm+vhb/zgQL/s072dYGEPS4ubNj5hXu2dZBFXICKyir3nHGvIe7YZmzEKb7PG+ecrsdEgdvKskGDBrJDD014tZWK84mKdkASiAJiMGdssMEGSycy3kx23nlnebh8D1L7Rsi22ubNm4svSq1BFHJlizdmjGnTppkxY8YIULKgaymNBBiHjDsWYV4gbJyu0pzd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UnR3nC+ZAEbIHM0S5tUZM2YIvObbYUdfPP744+boo4+WnHq33367AcDS7MbLvQ+FKF8969aOQpSb3HzVcoUo5g2eZbIL/PTTT7o7z7FDFKIcBeepmkJUAUHiKM3gxG/GFhZuHnoLVMQ3QSvVoEEDOQTNFH+vscYanronWTOhIOrtt982mMyQVSmcyzGjPvTQQ+b44483hxxyiOS8y5dsmGsC7vCDmjt3rgBU06ZNvcejUYhKNk59H60Q5VuiydpzhSjm0YULF0qQTXbXFgtRkuyqaudohaiwfa0QFVP+r776qgRmBJI233xzs+uuu5r69eub1157zeDcvOGGG8Zqia317777rmhOdt9996JvX7yl4fuDY3Sxc4SCqNNOO018kwAUnxqefALFoZw+wCR38803iwYQUyvm1Wixxw0fPtxcccUV5qyzzjLnn3++l914qomKNdRLdpBCVMlEnfdErhCFRnjcuHHm/fffNxdffLHs8NWSXAIKUcll5rOGQlRMaU6YMEFSE5x++ukGoOLB5w3qs88+k4V8t912q7MlNDQ33nijufLKK81aa60lweWaN29u/vSnPxk6IV954oknzNChQ8VMeMIJJ5hTTjmlznOEgig0PR07djT7779/5hAFTKJV6ty5s6lXr54Ey0SeuQmHOe7ll182Bx10kNlmm23MpEmTTMOGDZeDrZjdX+dhqonyIUX3NhSi3GXno6YrRKFRHj16tOx6Zl5kntOSXAIKUcll5rOGQlQBafJA41hOGIONNtpITHXEFzr55JNl8QaKPvroI/nsscceslOvrgJ0ARpordBozZo1yxxxxBHiHN21a9flquLnc+yxx5pDDz1Uomuj7i4EW7ZyKIji+jh3ixYtMoUoJgtkABD17t3bXHbZZeLMn5uyBTMBptcePXqYp59+2gDAmP1cc+MVe+AUoopJKNvvFaKylW+x1tNA1HXXXSeaeTTGClHFJJ3/e4UoN7n5qqUQVUCS7733npioiAe17bbbSoqQ77//3ixevNhceOGFZpNNNknUB/jjEODxmWeeWWqWIzQCpikc2HkQbCGMAoC13nrrCbjFLYDMTTfdJBqXUpZSQRSyAoS6d+9u5s2bJ2ENdtppp2V25SFHnMmZnDH5oSXDZwp/C6IjZ1EUorKQavw2FaLiyyqLIxWispBq/DYVouLLyteRuIvYYiGK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1IcffmjatGljPvvsM4lQvskmm6S+B01ArI7lqQdRigYUolIIz0NVV3Mep+bFi/kDCFOIcusMhSg3ufmqpRDlS5Jl0E4ciJo0aZLBEfywww4z5LxbccUVU1+5QpRCVOpBlKIBhagUwvNQVSHKgxBTNKEQlUJ4HqoqRHkQYrk0UQyi8FsixQshDR577DFzwAEHeLl0hSiFKC8DybERhShHwXmqphDlSZCOzShEOQrOUzWFKE+CLIdmikEU6RWaNGlitttuO/P444+b9dZbz+myZ82aZerXr2823XRTqV9OEEV4AdIecE3sQnQp1CfDPL5e+GrgeG//5n+5pdp8omzaiErxcUsLUYyT3/3udxKagv7GtFRpMnAZ577qKET5kqRbO64QFR33//jHP8y///1vtwuo8VoKUVU0AIpBVL9+/czIkSPNxRdfbAYOHOh05zfccIMh39Wdd95ptt56a2mDh++bb74J+hAykTApELbhL3/5i2nZsqVZddVVE18TwPT111+bZ555xmy++eZml112MZ9//rn8ve2225rGjRuLH0cU0KoNoqxvShIIBUIoAEipS1qIwqS9cOFCGTu777679Dv3bj9J7oe2GB+1tCClgSjqsoCzQ09zviUZab8e6wpRjNUFCxaYl19+Wcb9xhtvXFPj1k3ay9dSiPIlyTJopy6I+utf/yqLw08//WQIcdCsWbNEV/zll1+aSy65xIwdO9a0atXKPPDAA/LmTgE60PzwUCZZeBNdQJGDmYA5NzsP586da2655Raz/vrri2YhSUHj9Oqrr5qTTjrJHHnkkeb8888XgDr55JPNiSeeKAmb0UZVI0QBQowT7hm5XXHFFeYPf/hD0QCqTOLUQyarrLJKEnF7OTYtRK222mpm6tSppnfv3mbYsGGmdevWMo7q1asnu1dXWmmlWIsL98/zxXPAOAr1LHgRaoJGXCGKergY8OGFp1bklUC0sQ51hSjG/YMPPmguuOACc9lll5l27dotN7fFuoAaP0ghqooGQF0QBfwABkQWnzx5cqK7BiLOO+888+KLL0q9iRMnmu7duy9t4+yzz5Zo5Xvuuae8VWY9GTJpWGiyJif7Nwshuw5xmieVDcCTqzmyF04d2op+z+LHm9lxxx1njjrqKAkDMWPGDHPCCSeYnj17mj59+iw30aC9QvOwZMkSgY5KjVgORAEBBE8F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" - } - }, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The shallow water is recognizable at the left, where the celerity is only related to the water depth and thus linear. For this shallow water section holds: $tanh(kh) \\approx kh$. The deep water section is recognizable at the section where $tanh(kh) = 1$. Applying these two approximations in formula 3.4 provides $c = \\sqrt(gh)$ in shallow water and $c=\\frac{gt}{2\\pi} $ in deep water. The boundaries between shallow water and intermediate water and with intermediate water and deep water are respectively 0.05 h/L or 0.1 $\\pi$ kd and 0.5 h/L or $\\pi$ kd.
\n", - "![afbeelding.png](attachment:afbeelding.png)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "Q4()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Q4_2()\n", - "# when you check the answer, the graph is updated. When would you like to show the answers? Showing the response is also possible, but removing dots of previous attemts is still work-in-progress, but done before." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 3.2) Wave groups" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The sea surface is often influenced by multiple coexisting waves. The sea surface elevation in deep water can be described by the linear wave theory, where the various waves (components) are superpositioned." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "'''Run this cell to get an interactive graph.'''\n", - "anim5 = Q5()" - ] - }, - { - "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.11.4" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/coastalcodebook/assignments/Chris/week 5.ipynb b/coastalcodebook/assignments/Chris/week 5.ipynb deleted file mode 100644 index 95fd1b1..0000000 --- a/coastalcodebook/assignments/Chris/week 5.ipynb +++ /dev/null @@ -1,61 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "851d7f66", - "metadata": {}, - "source": [ - "# Wave phases (chapter 5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5fee2fbc", - "metadata": {}, - "outputs": [], - "source": [ - "%run Initialize/Week_5_Initialize.ipynb" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c35968f9", - "metadata": {}, - "outputs": [], - "source": [ - "Q1()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9cad33b5", - "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.11.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/cookbook/cookbook.ipynb b/cookbook/cookbook.ipynb index d346326..a34c387 100644 --- a/cookbook/cookbook.ipynb +++ b/cookbook/cookbook.ipynb @@ -45,7 +45,7 @@ "import matplotlib.pyplot as plt\n", "\n", "import panel as pn\n", - "pn.extension(\"ipywidgets\", 'katex')\n", + "pn.extension(\"ipywidgets\", 'katex', 'mathjax')\n", "import ipywidgets as ipw\n", "from matplotlib.animation import FuncAnimation\n", "from matplotlib.ticker import MultipleLocator\n", @@ -456,7 +456,7 @@ "id": "00604036-4e36-4c8a-8f69-c68dcf87f095", "metadata": {}, "source": [ - "In the code below are correct and false statements presented. Students have to select the check boxes related to correct answers. Students gain points when they choose the correct answers but lose points if they check incorrect answers. The lowest score is 0 points." + "In the code below are correct and false statements presented. Students have to select the check boxes related to correct answers. Students gain points when they choose the correct answers but lose points if they check incorrect answers. The lowest score is 0 points.
The code below has to be loaded/imported once, below this is an exemplary question." ] }, { @@ -466,16 +466,23 @@ "metadata": {}, "outputs": [], "source": [ - "def multiple_selection(correct_statements, false_statements):\n", + "def multiple_selection(question, correct_statements, false_statements):\n", " # Make an empty list to store the widgets (references), checkboxes, and true/false statements sorted.\n", " check_boxes = [] # all the boxes to click\n", " all_statements = [] # all the statements\n", + "\n", + " # question widget\n", + " question_widget = pn.widgets.StaticText(value=question)\n", + "\n", + " # For visualization, the maximum length/width of the question, with a minimum width of 200\n", + " max_length = max(len(item) for item in correct_statements + false_statements)\n", + " width_statement = max(250, max_length*7)\n", " \n", " # An empty list for visualization to store the HBoxes that contain the widgets, one statement and the corresponding checkbox\n", " all_widgets = []\n", - " \n", + "\n", " for statement in correct_statements + false_statements:\n", - " add_statement = pn.widgets.StaticText(value=statement, width=150)\n", + " add_statement = pn.widgets.StaticText(value=statement, width = width_statement)\n", " check_box_widget = pn.widgets.Checkbox(value=False, width=120)\n", " HBox1 = pn.Row(add_statement, check_box_widget)\n", " \n", @@ -495,7 +502,7 @@ " all_widgets.append(HBox2)\n", " \n", " # align all the HBoxes beneath each other (oldest below if not randomized) and display them.\n", - " quiz_widget = pn.Column(*all_widgets)\n", + " quiz_widget = pn.Column(question_widget, *all_widgets)\n", " \n", " # Check the checkbox for each statement and calculate the score.\n", " def check_answers(event):\n", @@ -518,17 +525,33 @@ " score -= 0\n", " \n", " score = np.max([score, 0])\n", - " output_widget.value = 'Your final score is: ' + str(score)\n", + " output_widget.value = f'You have: {round(score/len(correct_statements)*100,0)}% of the points'\n", " \n", " submit_button.on_click(check_answers)\n", - " display(quiz_widget)\n", - "\n", - "\n", + " return quiz_widget # This is similar to .serve(), it can also be done through: display(quiz_widget)" + ] + }, + { + "cell_type": "markdown", + "id": "1934205d-a618-4dc4-af59-fa64ce10edf3", + "metadata": {}, + "source": [ + "The function below is the question inside the notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4330a660-eb5c-4f18-b038-3c5d83d439d8", + "metadata": {}, + "outputs": [], + "source": [ "def ask_multiple_selection():\n", + " question = 'Which statements are correct/positive?'\n", " correct_statements = [\"Correct\", \"The earth is round\", \"Good\"]\n", " false_statements = [\"False\", \"The earth is a cube\"]\n", "\n", - " multiple_selection(correct_statements, false_statements)\n", + " return multiple_selection(question, correct_statements, false_statements)\n", "\n", "\n", "ask_multiple_selection()" @@ -1221,6 +1244,7 @@ " # Load the student-made function from globals\n", " function = globals()[function_name]\n", "\n", + " # Use the student made function to calculate their answer\n", " # Add the arguments to the name of the function, for eval()\n", " # + Replace the argument on the x-axis so that it can be assessed with list comprehension.\n", " # https://docs.python.org/3/library/inspect.html\n", @@ -1228,40 +1252,40 @@ " sig = signature(function)\n", " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", " function = function_name + str(sig_function)\n", - " title = function_name + str(sig)\n", - " title = title.replace('_', ' ')\n", + " student_answer = [eval(function) for par_x_axis in horizontal_axis]\n", "\n", + " # get the function for the correct answer\n", " sig = signature(correct_function) \n", " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", " correct_function2 = str(correct_function.__name__) + sig_function\n", "\n", - " # calculate the student's answer through eval()\n", - " student_answer = [eval(function) for par_x_axis in horizontal_axis]\n", - "\n", " #calculate the correct answer\n", " correct_answer = []\n", " for par_x_axis in horizontal_axis:\n", " correct_answer.append(eval(correct_function2))\n", - " # This code should work, but it does not\n", - " #correct_answer = [eval(correct_function2) for par_x_axis in horizontal_axis]\n", + " \n", + " # plot the answers\n", + " line = ax.plot(student_answer, label = 'Your answer', zorder = 1)\n", + " line2 = ax.plot(correct_answer, label = 'Correct answer', zorder = 1)\n", "\n", " # check if the answer is correct and plot it before/below lines\n", " changes = np.array(student_answer) - np.array(correct_answer)\n", " inaccuracy = np.abs(1-np.array(correct_answer)/np.array(student_answer))\n", - " \n", - " if np.max(changes) == 0:\n", - " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", - " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Perfect!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", "\n", - " if np.max(changes) != 0 and np.max(inaccuracy) < f_margin:\n", - " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", - " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Good!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", - " \n", - " # plot the answers\n", - " line = ax.plot(horizontal_axis, student_answer, label = 'Your answer')\n", - " line = ax.plot(horizontal_axis, correct_answer, label = 'Correct answer')\n", - " ax.legend()\n", + " # Plot comment if the answer is correct\n", + " y_loc = (np.nanmean(correct_answer) + np.nanmin(correct_answer))/2\n", + " x_loc = np.mean(ax.get_xticks())\n", + " if np.nanmax(changes) == 0: \n", + " text = ax.text(x_loc, y_loc, 'Perfect!', fontsize=12, color = '#1b5a00', ha='center', va='center', zorder = 0)\n", + "\n", + " if np.nanmax(changes) != 0 and np.nanmax(inaccuracy) < f_margin:\n", + " text = ax.text(x_loc, y_loc, 'Good!', fontsize=12, color = '#1b5a00', ha='center', va='center', zorder = 0)\n", + "\n", + " # set title and legend\n", + " title = function_name + str(sig)\n", + " title = title.replace('_', ' ')\n", " ax.set_title(title)\n", + " ax.legend()\n", " \n", " except:\n", " text_failed = 'Almost there, \\n your function can not be plotted, \\n please try to fix the bug'\n", @@ -1356,7 +1380,7 @@ "metadata": {}, "outputs": [], "source": [ - "a,b,c = 1,2,0.5,\n", + "a,b,c = 1,2,0.5\n", "x = 3\n", "def student_function(a,b,x,c):\n", " y = a*x + b*2 - c*x\n", @@ -1370,7 +1394,7 @@ "id": "bc12a11d-787f-42a1-927f-6d6b7de8fd96", "metadata": {}, "source": [ - "It will give positive response when the function is correct" + "It will give positive response when the function is correct, it will give perfect when the error = 0, and good when the error is within the given margin. The deviation can be due to a different number of iterations, for example." ] }, { @@ -2627,7 +2651,7 @@ "id": "97111320-94b1-4f52-b9ab-423b8900605a", "metadata": {}, "source": [ - "The function below loads the function the students make. It will plot this function if this is possible, otherwise it will give a warning. Furtheron is a generic function that can be used to make questions." + "The function below loads the function the students make. It will plot this function if this is possible, otherwise it will give a warning. Further on are two generic functions that can be used to make questions. One is made to asses functions that return a single value, and one that returns a value as an array. The example of the second generic function does not work, unfortunately. The general code works in week 3." ] }, { @@ -2726,7 +2750,15 @@ "id": "f0aee30e-15aa-461d-aba2-de45b3d44761", "metadata": {}, "source": [ - "In the cell below is the code more generalized. The list comprehension requires that the arguments are defined. This is solved by building a string of this function with arguments and then evaluating this. In this way can also additional parameters be introduced when the student is completely wrong." + "In the cell below is the code more generalized. The list comprehension requires that the arguments are defined. This is solved by building a string of this function with arguments and then evaluating this. In this way can also additional parameters be introduced when the student is completely wrong. Further more is the zorder used to improve the graph." + ] + }, + { + "cell_type": "markdown", + "id": "7a3925a1-0769-4667-bad0-7a879484dcf2", + "metadata": {}, + "source": [ + "#### Generic formula based on single point" ] }, { @@ -2748,6 +2780,7 @@ " # Load the student-made function from globals\n", " function = globals()[function_name]\n", "\n", + " # Use the student made function to calculate their answer\n", " # Add the arguments to the name of the function, for eval()\n", " # + Replace the argument on the x-axis so that it can be assessed with list comprehension.\n", " # https://docs.python.org/3/library/inspect.html\n", @@ -2755,16 +2788,13 @@ " sig = signature(function)\n", " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", " function = function_name + str(sig_function)\n", - " title = function_name + str(sig)\n", - " title = title.replace('_', ' ')\n", + " student_answer = [eval(function) for par_x_axis in horizontal_axis]\n", "\n", + " # get the function for the correct answer\n", " sig = signature(correct_function) \n", " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", " correct_function2 = str(correct_function.__name__) + sig_function\n", "\n", - " # calculate the student's answer through eval()\n", - " student_answer = [eval(function) for par_x_axis in horizontal_axis]\n", - "\n", " #calculate the correct answer\n", " correct_answer = []\n", " for par_x_axis in horizontal_axis:\n", @@ -2772,23 +2802,28 @@ " # This code should work, but it does not\n", " #correct_answer = [eval(correct_function2) for par_x_axis in horizontal_axis]\n", "\n", + " # plot the answers\n", + " line = ax.plot(student_answer, label = 'Your answer', zorder = 1)\n", + " line2 = ax.plot(correct_answer, label = 'Correct answer', zorder = 1)\n", + "\n", " # check if the answer is correct and plot it before/below lines\n", " changes = np.array(student_answer) - np.array(correct_answer)\n", " inaccuracy = np.abs(1-np.array(correct_answer)/np.array(student_answer))\n", - " \n", - " if np.max(changes) == 0:\n", - " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", - " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Perfect!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", "\n", - " if np.max(changes) != 0 and np.max(inaccuracy) < f_margin:\n", - " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", - " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Good!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", - " \n", - " # plot the answers\n", - " line = ax.plot(horizontal_axis, student_answer, label = 'Your answer')\n", - " line = ax.plot(horizontal_axis, correct_answer, label = 'Correct answer')\n", - " ax.legend()\n", + " # Plot comment if the answer is correct\n", + " y_loc = (np.nanmean(correct_answer) + np.nanmin(correct_answer))/2\n", + " x_loc = np.mean(ax.get_xticks())\n", + " if np.nanmax(changes) == 0: \n", + " text = ax.text(x_loc, y_loc, 'Perfect!', fontsize=12, color = '#1b5a00', ha='center', va='center', zorder = 0)\n", + "\n", + " if np.nanmax(changes) != 0 and np.nanmax(inaccuracy) < f_margin:\n", + " text = ax.text(x_loc, y_loc, 'Good!', fontsize=12, color = '#1b5a00', ha='center', va='center', zorder = 0)\n", + "\n", + " # set title and legend\n", + " title = function_name + str(sig)\n", + " title = title.replace('_', ' ')\n", " ax.set_title(title)\n", + " ax.legend()\n", " \n", " except:\n", " text_failed = 'Almost there, \\n your function can not be plotted, \\n please try to fix the bug'\n", @@ -2884,6 +2919,159 @@ " return y" ] }, + { + "cell_type": "markdown", + "id": "4893aca4-5848-4a9a-8acb-a38822815e3d", + "metadata": {}, + "source": [ + "#### Genereic formula based on 1D space" + ] + }, + { + "cell_type": "markdown", + "id": "444007a8-fad1-4f19-8fa7-8d7ff9770367", + "metadata": {}, + "source": [ + "The function that students make can also be such that it calculates the values for an array directly. The general function, that has to be loaded once is:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b73e58f-f436-460f-a55c-b9ad00c5b119", + "metadata": {}, + "outputs": [], + "source": [ + "def check_code_function_RANGE(fig, function_name, correct_function, f_margin = 0.001):\n", + " # The error margin (f_margin) is set at 0.1% if it is not defined\n", + "\n", + " # make the graph and show the correct answer\n", + " ax = fig.subplots()\n", + " pane = pn.pane.Matplotlib(fig, dpi=100)\n", + " \n", + " # Plot the answer if the student function is found\n", + " try:\n", + " # Load the student-made function from globals\n", + " function = globals()[function_name]\n", + "\n", + " # Add the arguments to the name of the function, for eval()\n", + " # https://docs.python.org/3/library/inspect.html\n", + " # https://peps.python.org/pep-0362/ \n", + " sig = signature(function)\n", + " student_function = function_name + str(sig)\n", + " #print(student_function)\n", + "\n", + " student_answer = eval(student_function)# This line does not work, but does in week 3.\n", + " #student_answer = x_range\n", + " #print(student_answer)\n", + " \n", + " sig = signature(correct_function) \n", + " correct_function2 = str(correct_function.__name__) + str(sig)\n", + " correct_answer = eval(correct_function2)\n", + "\n", + " # plot the answers\n", + " line = ax.plot(student_answer, label = 'Your answer', zorder = 1)\n", + " line = ax.plot(correct_answer, label = 'Correct answer', zorder = 1)\n", + "\n", + " # check if the answer is correct and plot it before/below lines\n", + " changes = np.array(student_answer) - np.array(correct_answer)\n", + " inaccuracy = np.abs(1-np.array(correct_answer)/np.array(student_answer))\n", + "\n", + " # Plot comment if the answer is correct\n", + " y_loc = (np.nanmean(correct_answer) + np.nanmin(correct_answer))/2\n", + " x_loc = np.mean(ax.get_xticks())\n", + " if np.nanmax(changes) == 0: \n", + " text = ax.text(x_loc, y_loc, 'Perfect!', fontsize=12, color = '#1b5a00', ha='center', va='center', zorder = 0)\n", + "\n", + " if np.nanmax(changes) != 0 and np.nanmax(inaccuracy) < f_margin:\n", + " text = ax.text(x_loc, y_loc, 'Good!', fontsize=12, color = '#1b5a00', ha='center', va='center', zorder = 0)\n", + "\n", + " # show legend, set title, and update figure\n", + " ax.legend()\n", + " title = student_function\n", + " title = title.replace('_', ' ')\n", + " ax.set_title(title)\n", + "\n", + " except:\n", + " text_failed = 'Almost there, \\n your function can not be plotted, \\n please try to fix the bug'\n", + " x_ticks = ax.get_xticks()\n", + " y_ticks = ax.get_yticks()\n", + " text = ax.text(np.average(x_ticks),np.average(y_ticks), text_failed, fontsize=16, color = 'r', ha='center', va='center')\n", + "\n", + " # update the graph\n", + " display(pane)" + ] + }, + { + "cell_type": "markdown", + "id": "39157cdd-0546-4948-bf01-cb973b9842a6", + "metadata": {}, + "source": [ + "Here is an example of an code, unfortunalely an error exist, the code stops when it evaluates the string describing the function. The code above works in week 3." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "205be49e-aae4-4441-8f5f-a87856dfaa29", + "metadata": {}, + "outputs": [], + "source": [ + "def Question2():\n", + " # define the name of the function that the students will make\n", + " function_name = 'student_function'\n", + "\n", + " # define the correct function\n", + " def correct_function(x_range,a,b):\n", + " x = x_range\n", + " y = a*x + b\n", + " return y\n", + " \n", + " # set the acceptable computational error (ratio)\n", + " f_margin = 0.001 # 0.001 = 0.01%\n", + "\n", + " # set the size of the figure\n", + " fig = Figure((5,2.5))\n", + "\n", + " # call the function that builds the backend.\n", + " check_code_function_RANGE(fig, function_name, correct_function, f_margin)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ae9b92d-0d04-4fae-9943-f86ad78b282f", + "metadata": {}, + "outputs": [], + "source": [ + "x_range = np.arange(0.1,10+1,1)\n", + "a = 1\n", + "b = 3\n", + "c = 5\n", + "\n", + "def student_function(x_range, a,b,c):\n", + " y = a*x_range + b*2 - c/x_range\n", + " return y\n", + "\n", + "# a plot, showing that the student function works.\n", + "#plt.plot(x_range, student_function(x_range, a,b,c))\n", + "\n", + "Question2()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c3eaac8-9b56-4bf2-9481-7dc12695b663", + "metadata": {}, + "outputs": [], + "source": [ + "'''the code stops when it evaluates the string describing the function, however, it works below. '''\n", + "\n", + "ans = eval('student_function(x_range, a, b ,c)')\n", + "print(ans)" + ] + }, { "cell_type": "markdown", "id": "5547238e-d5b9-43ab-a9e7-73a0bacb0677", diff --git a/notebooks/week_2.ipynb b/notebooks/2a_pre_knowledge_waves.ipynb similarity index 100% rename from notebooks/week_2.ipynb rename to notebooks/2a_pre_knowledge_waves.ipynb diff --git a/notebooks/2b_wave_dispersion_and_grouping.ipynb b/notebooks/2b_wave_dispersion_and_grouping.ipynb new file mode 100644 index 0000000..f228079 --- /dev/null +++ b/notebooks/2b_wave_dispersion_and_grouping.ipynb @@ -0,0 +1,1082 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a57196e5-eb53-4eab-8bb2-b11c2a313e56", + "metadata": {}, + "source": [ + "
\n", + " \n", + "
\n", + "\n", + "Before you is the Jupiter Notebook of week 3." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fcad68ab-a006-40c5-93ad-5eb0a10e41e8", + "metadata": {}, + "outputs": [], + "source": [ + "%run Initialize/Week_3_Initialize.ipynb" + ] + }, + { + "cell_type": "markdown", + "id": "673bd911-7c75-425e-8dc2-9173df6c452d", + "metadata": {}, + "source": [ + "# This document is not completely updated, week_3-Answered is the latest version. Below you can find how the coding questions are when these are not answered." + ] + }, + { + "cell_type": "markdown", + "id": "9a3b7540-f1c8-4188-9321-c162619892e7", + "metadata": {}, + "source": [ + "## 3.1) Wave and tidal information" + ] + }, + { + "cell_type": "raw", + "id": "68cd6b43-9174-4862-a334-331835a54184", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "id": "217ae4a8-cc7b-41a5-98be-d3e3e02b0b5d", + "metadata": {}, + "source": [ + "### Part 1 - section B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1d7ed5b-ccab-495d-9e35-540916da0d7d", + "metadata": {}, + "outputs": [], + "source": [ + "W3_fig_5_13()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a1bcfe0d-c962-4b5a-8fdb-e4569f7c9ade", + "metadata": {}, + "outputs": [], + "source": [ + "W3_fig_5_14()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3342b456-bb2e-4005-984f-11883b57934b", + "metadata": {}, + "outputs": [], + "source": [ + "W3_fig_5_15()\n", + "# Let op phase!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d64c2f49-8e30-4a81-9d21-a8ea3f88bcd4", + "metadata": {}, + "outputs": [], + "source": [ + "W3_fig_5_16()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43b3e750-dade-4da5-b55e-ebeca19cebad", + "metadata": {}, + "outputs": [], + "source": [ + "W3_fig_5_17()" + ] + }, + { + "cell_type": "raw", + "id": "15e07b62-2f43-4e34-a97d-2f09027c3af5", + "metadata": {}, + "source": [ + "Question: What is the phase difference between wave component 1 and 2 for skewed waves. And what for assymetric waves?" + ] + }, + { + "cell_type": "markdown", + "id": "55200145-c32c-444e-b925-9e73e173cf2a", + "metadata": {}, + "source": [ + "## 3.2) Wave transformation in the near-shore " + ] + }, + { + "cell_type": "markdown", + "id": "75e92d3c-f6b9-40c4-9fe1-3082483a6ff7", + "metadata": {}, + "source": [ + "We continue on the application of the dispersion relationship in the cross-shore, which was discussed in week 3 and the Wave course." + ] + }, + { + "cell_type": "markdown", + "id": "17205ca8-dfd6-4622-8482-c64cfd445857", + "metadata": {}, + "source": [ + "### 3.2.1) Normal indicent waves" + ] + }, + { + "cell_type": "markdown", + "id": "e5dd8576-5be2-40dd-916d-32d9d8453cfc", + "metadata": {}, + "source": [ + "In Waves you should have programmed a function to compute the wave height along the cross-shore for normal incident waves. A similar function is defined below, together with a function to calculate the wavelength. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a903c1f3-af10-46e5-bfc3-8d086a18923b", + "metadata": {}, + "outputs": [], + "source": [ + "# This function will be replaced with the one from Waves, by Jaime or otherwise in the course Ocean Waves from Marion Tissier\n", + "\n", + "def wave_length(T, h):\n", + " L = 9.81 * T**2 / (2 * np.pi)\n", + " L_all = [L]\n", + "\n", + " for i in range(1500):\n", + " L = 9.81 * T**2 / (2 * np.pi) * np.tanh(2 * np.pi * h / L)\n", + " L_all.append(L)\n", + "\n", + " # stop the iteration when the error is sufficiently small\n", + " if np.abs(L_all[-1] - L_all[-2]) < 0.0005:\n", + " break\n", + " \n", + " return round(L, 13)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b4b0fb3-dbac-40cd-b494-57a143b711b4", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_normal_indicent_waves(H0, T, d0, slope):\n", + "\n", + " # The environmental conditons\n", + " x = np.arange(0,900,.1) # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given:\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wave length\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", + " H = H0*Ksh # The wave height due to shoaling (only)\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # The wave height" + ] + }, + { + "cell_type": "markdown", + "id": "d226190a-a118-413e-87cb-8237866feeff", + "metadata": {}, + "source": [ + "The graph below shows the outcome of the calculation. Can you use it to answer the questions?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aadfa6d4-c1f3-4cd6-a67e-0fba4638c737", + "metadata": {}, + "outputs": [], + "source": [ + "W3_Q4()" + ] + }, + { + "cell_type": "markdown", + "id": "42bbaabc-6522-4f63-91ef-82616506db41", + "metadata": {}, + "source": [ + "### 3.2.2) Wave height for oblique waves" + ] + }, + { + "cell_type": "markdown", + "id": "7514bb6e-75a4-4580-aff7-6809c44fc018", + "metadata": {}, + "source": [ + "The influence of refraction should also be considered for waves that approach the shore under an angle. This process should be considered in the functions for oblique waves, in contrast to normal incident waves. Can you include the impact of refraction on the wave height in the function below by completing the code at the dots? The function W3_plot_oblique_waves() will plot the wave height calculated in W3_oblique_waves(), together with the answer. This function will give a runtime warning when the water depth (h) is 0, which is not solved to keep the code as straightforward as possible. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29643432-a621-4d60-97ff-567b4226f77f", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle = 1.5, 6, 20, 1/30, 10\n", + "x_range = np.arange(0,900,1)\n", + "\n", + "def W3_oblique_waves(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", + "\n", + " '''Complete the code here'''\n", + " \n", + " H = ... # The wave height due to shoaling (only)\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " return" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d054a0a3-e844-41cf-8f54-0a683dff6c25", + "metadata": {}, + "outputs": [], + "source": [ + "W3_plot_oblique_waves()" + ] + }, + { + "cell_type": "markdown", + "id": "978e948d-ea85-42f4-a026-156fe103c8e0", + "metadata": {}, + "source": [ + "The code below can be used to assess the influence of changing conditions on the wave height." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21a178a0-661a-4389-b5d4-c86f81da0aa2", + "metadata": {}, + "outputs": [], + "source": [ + "angle2 = 45\n", + "\n", + "H1 = W3_oblique_waves(x_range, H0 = 1.5, T = 6, d0 = 20 , slope = 1/30, angle = 0)\n", + "H2 = W3_oblique_waves(x_range, H0 = 1.5, T = 6, d0 = 20 , slope = 1/30, angle = angle2)\n", + "\n", + "plt.figure(figsize = (5,3))\n", + "plt.plot(H1, label = '0 degrees')\n", + "plt.plot(H2, label = str(angle2) + 'degrees')\n", + "plt.legend();" + ] + }, + { + "cell_type": "raw", + "id": "22db5b54-72e6-49eb-b2aa-9269724f2be7", + "metadata": {}, + "source": [ + "--> Add some reflective questions." + ] + }, + { + "cell_type": "markdown", + "id": "a6835219-6247-45d7-a269-752c309909a5", + "metadata": {}, + "source": [ + "### 3.2.3) radiation stress (Sxx) for oblique waves" + ] + }, + { + "cell_type": "markdown", + "id": "63bcf370-31d3-48da-9a0f-f59a233a071c", + "metadata": {}, + "source": [ + "Can you calculate the crossshore distribution of the radiation stress (Sxx) for oblique waves by completing the code below?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7711ede-62f4-41d7-8115-95d17fb662fe", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho = 1.5, 6, 20, 1/30, 10, 1025\n", + "x_range = np.arange(0,900,1)\n", + "\n", + "def W3_radiation_stres_Sxx(x_range, H0, T, d0, slope, angle, rho):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", + "\n", + " '''Complete the code to calculate the wave height of oblique waves here'''\n", + " \n", + " H = ... # The wave height\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " ''' Complete the code to calculate the radiations stresses (Sxx)'''\n", + "\n", + " return Sxx" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1aa6e2b-95bd-4e49-b39e-2ee263be2339", + "metadata": {}, + "outputs": [], + "source": [ + "W3_plot_radiation_stres_Sxx()" + ] + }, + { + "cell_type": "markdown", + "id": "40fa2cef-7f5d-4fe6-a90d-aa0ea9d43e0e", + "metadata": {}, + "source": [ + "### 3.2.4) Mean water level under influence of oblique waves" + ] + }, + { + "cell_type": "markdown", + "id": "8ab74425-7e60-489d-a7dd-d15004c9c6a5", + "metadata": {}, + "source": [ + "The wave-induced setup and setdown can be calculated by the radiation stress $S_{xx}$ and the depth (h) according to:\n", + "\n", + "\\begin{equation}\n", + "\\frac{d \\overline{\\eta}}{dx} = - \\frac{1}{\\rho g d} \\frac{dS_{xx}}{dx}\n", + "\\end{equation} \n", + "\n", + "It is not the radiation stress itself but its gradient what triggers a change in the mean water level.
\n", + " Following week 1.5 of the MUDE and Waves, the equation can be discretized using forward Euler: \n", + "\\begin{equation}\n", + "\\overline{\\eta}_{i+1}=\\overline{\\eta}_{i} - \\frac{S_{xx,i+1}-S_{xx,i}}{\\rho g d_i}\n", + "\\end{equation} \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1bd52489-1aa1-41fc-82bd-f5576b70fb13", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho = 1.5, 6, 20, 1/30, 10, 1025\n", + "x_range = np.arange(0,900,1)\n", + "def W3_wave_setup(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", + "\n", + " '''Complete the code to calculate the wave height of oblique waves here'''\n", + " \n", + " H = ... # The wave height\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " ''' Complete the code to calculate the radiations stresses (Sxx)'''\n", + "\n", + " ''' Adjust the code to implement oblique waves, if necessary'''\n", + " setup = np.zeros(Sxx.shape) # here we create a vector for the mean water level \n", + " for i in range(len(setup)-1):# key here is that setup[0] = 0 \n", + " setup[i+1] = setup[i] - (Sxx[i+1]-Sxx[i])/(1000*g*h[i])\n", + " \n", + " return setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b63c3983-287d-4437-80e7-997d2206a5d6", + "metadata": {}, + "outputs": [], + "source": [ + "W3_plot_wave_setup()" + ] + }, + { + "cell_type": "markdown", + "id": "bb378c1f-7267-4ab0-a15b-eb1cbee22bf3", + "metadata": {}, + "source": [ + "### 3.2.5) The shorewards directed force (Fx)" + ] + }, + { + "cell_type": "markdown", + "id": "32cd7ecc-7ee1-4883-9125-735f3cfae4e9", + "metadata": {}, + "source": [ + "We dive a bit deeper into the force in cross-shore direction that drives the wave setup (Fx). Can you calculate this force along the cross-shore for an alongshore uniform coast? For this question, you can consider using a similar approach as the setup is calculated, by using forward euler." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0bf515d7-a7e6-4bab-b108-4f258ce9f90c", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho = 1.5, 6, 20, 1/30, 10, 1025\n", + "x_range = np.arange(0,900,1)\n", + "def W3_Fx(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", + "\n", + " '''Complete the code to calculate the wave height of oblique waves here'''\n", + " H = ... # The wave height\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " ''' Complete the code to calculate the radiations stresses (Sxx)'''\n", + "\n", + " ''' Adjust the code to implement oblique waves, if necessary'''\n", + " setup = np.zeros(Sxx.shape) # here we create a vector for the mean water level \n", + " for i in range(len(setup)-1):# key here is that setup[0] = 0 \n", + " setup[i+1] = setup[i] - (Sxx[i+1]-Sxx[i])/(1000*g*h[i])\n", + "\n", + " ''' Complete the code to calculate Fx'''\n", + " Fx = np.zeros(setup.shape)\n", + " for i in range(len(Fx)-1):\n", + " Fx[i] = ... \n", + " \n", + " return Fx" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "584e6e74-6301-4607-9f4c-a1c8b6afe052", + "metadata": {}, + "outputs": [], + "source": [ + "W3_plot_Fx()" + ] + }, + { + "cell_type": "raw", + "id": "3e14b008-c2ed-472d-8c7e-0a838f7793be", + "metadata": {}, + "source": [ + "[ reflective questions to be added]" + ] + }, + { + "cell_type": "markdown", + "id": "40a688e5-8544-4fc1-9637-edf792b6c5d6", + "metadata": {}, + "source": [ + "### 3.2.6) Alongshore stresses (Syx, Syy) and forces (Fy)." + ] + }, + { + "cell_type": "markdown", + "id": "428d376f-71d5-4b4c-8fa2-6c8448cfc898", + "metadata": {}, + "source": [ + "Can you compute the radiation shear stress in y direction (Syx)?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1f3b159-8f30-43e2-9c6b-d958f4297d01", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_Syx(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " '''Complete the code to calculate the wave height of oblique waves here'''\n", + " H = ... # The wave height\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " '''finish the code here'''\n", + " \n", + " E = ... # The wave energy\n", + "\n", + " Syx = ...\n", + " \n", + " return Syx" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f1069314-a353-4440-af7d-c3ca504e6683", + "metadata": {}, + "outputs": [], + "source": [ + "W3_plot_Syx()" + ] + }, + { + "cell_type": "markdown", + "id": "c448338b-3a5e-42a1-9f1e-8e586d0ee600", + "metadata": {}, + "source": [ + "And can you compute the alongshore force (Fy) for an alongshore uniform coast?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c555c3e7-ac1e-414d-91af-0dcd8ac4b0d5", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_Fy(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " '''Complete the code to calculate the wave height of oblique waves here'''\n", + " H = ... # The wave height due to shoaling (only)\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " E = ... # The wave energy\n", + " \n", + " 'complete the code here'\n", + " \n", + " return Fy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36297ace-9346-447f-9004-40094884703d", + "metadata": {}, + "outputs": [], + "source": [ + "W3_plot_Fy()" + ] + }, + { + "cell_type": "markdown", + "id": "262420b5-5d7e-4d45-b4b8-43275aa1cb97", + "metadata": {}, + "source": [ + "### 3.2.7) Longshore current" + ] + }, + { + "cell_type": "markdown", + "id": "af022363-dffb-48da-acb8-40f5f3007d70", + "metadata": {}, + "source": [ + "Can you calculate the distribution of the longshore current, when a bottom roughness (r) of 6 cm is used in the friction factor $c_f$?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0be6cbd7-0b05-44f5-a847-b459c5b316b9", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho, r = 1.5, 6, 20, 1/30, 10, 1025, 0.06\n", + "x_range = np.arange(0,900,1)\n", + "\n", + "def W3_V(x_range, H0, T, d0, slope, angle, r):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " '''Completed the code here'''\n", + " \n", + " H = ... # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " '''Finish the code over here'''\n", + "\n", + " E = ... # The wave energy\n", + "\n", + " V = ...\n", + "\n", + " \n", + " return V" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "700763e5-8ac7-429e-b521-17358a00fb1f", + "metadata": {}, + "outputs": [], + "source": [ + "W3_plot_V()" + ] + }, + { + "cell_type": "markdown", + "id": "69c8fab3-bdad-47ab-8ec4-ed637fcca545", + "metadata": {}, + "source": [ + "### 3.2.8) Wave induced local current" + ] + }, + { + "cell_type": "markdown", + "id": "47795cf9-506e-4717-91a5-dc5ee1102ef9", + "metadata": {}, + "source": [ + "Can you calculate the amplitude of the velocity (u0) near the bed through the linear wave theory?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5550679-5d39-405f-b8e4-9e0ba12d46b8", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_u0(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " '''Completed the code here'''\n", + " \n", + " H = ... # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " '''finish the code here'''\n", + " u0 = ...\n", + " \n", + " return u0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ddce739-bd70-4ce9-9896-dbdc9b337f5f", + "metadata": {}, + "outputs": [], + "source": [ + "W3_plot_u0()" + ] + }, + { + "cell_type": "markdown", + "id": "2b3a17e1-d5de-4c6b-82c0-5a3e4a629512", + "metadata": {}, + "source": [ + "### 3.2.9) Undertow velocity under wave through" + ] + }, + { + "cell_type": "markdown", + "id": "c13311d3-e599-4d22-8d73-40252f7c215a", + "metadata": {}, + "source": [ + "In breaking waves, the mass transport towards the coast between wave crest and wave\n", + "trough may be quite large, resulting in rather large seaward-directed velocities under\n", + "the wave trough level" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6038a3d5-d4b6-47b7-b08d-1cc103bcafed", + "metadata": {}, + "outputs": [], + "source": [ + "# page 202" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36c795f3-29ae-45a0-bd68-4a8cdaedc3ad", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_u0(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " rho = 1025 # Density of water [kg/m3]\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " '''Completed the code here'''\n", + " \n", + " H = ... # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + "\n", + " ''' finish the code here'''\n", + " \n", + " alpha = 1\n", + " Er = ...\n", + " \n", + " q_non_break = ... = E/c\n", + " q_roller = ... = alpha*Er/C\n", + " q_drift = q_non_break + q_roller\n", + "\n", + " q_drift = q_drift[H == Hbreaking]\n", + "\n", + " u_trough = q_drift*np.cos(np.deg2rad(angle))/(rho*h)\n", + " \n", + " \n", + " \n", + " return u_trough" + ] + }, + { + "cell_type": "markdown", + "id": "0a564dc4-d23b-46fd-9332-f751de69299e", + "metadata": {}, + "source": [ + "### 3.2.10) The influence of wave setup" + ] + }, + { + "cell_type": "markdown", + "id": "53958f41-010d-40c5-b96c-092ba8c2c8a5", + "metadata": {}, + "source": [ + "The setup caused by waves influences the water depth. This might influence the waves as they propagate, especially in shallow water, were the setup is . The code below calculates the water depth iterative, if you complete it with the code you made before." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b379d26-2b4d-409b-8b83-c47ef9776ea7", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_wave_setup2(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range.copy() # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " setup = np.zeros(len(h)) # initial wave-induced setup\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + "\n", + " for i in range(3): # number of iterations considered\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " '''Complete the code to calculate the wave height of oblique waves here'''\n", + " \n", + " H = ... # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " ''' Complete the code to calculate the radiations stresses (Sxx)'''\n", + " Sxx = ...\n", + "\n", + " ''' Adjust the code to implement oblique waves, if necessary'''\n", + " setup = np.zeros(Sxx.shape) # here we create a vector for the mean water level \n", + " for i in range(len(setup)-1):# key here is that setup[0] = 0 \n", + " setup[i+1] = setup[i] - (Sxx[i+1]-Sxx[i])/(1000*g*h[i])\n", + "\n", + " setup_shoreline = setup[~np.isnan(setup)][-1] # get setup at the initial shoreline, which is the last non-Nan value\n", + " setup[np.isnan(setup)] = setup_shoreline # apply the setup at the shoreline on the land side of the initial shoreline\n", + "\n", + " h = -zbed + setup # the still water depth + setup\n", + " h[np.isnan(h)] = 0 # replace nan values with 0\n", + " h[h < 0] = 0 # no negative water depths are possible\n", + " \n", + " #plt.plot(h) # optionally, plot the updated water depth after each iteration\n", + " \n", + " return setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a225713-3ee1-4e8a-906e-0f2bf6c84cbb", + "metadata": {}, + "outputs": [], + "source": [ + "W3_plot_wave_setup2()" + ] + }, + { + "cell_type": "markdown", + "id": "2d3a01a8-f602-4266-8d39-0acb7b607eb6", + "metadata": {}, + "source": [ + "# Overview of content" + ] + }, + { + "cell_type": "raw", + "id": "4877cc39-712f-4539-8d3f-1eeb94cf1d36", + "metadata": {}, + "source": [ + "PART 1:\n", + "part 1 wel, figure pagian 186,187etc/. Hoeven nog niet te progermeeren, moeten wel snappen met tootlje.\n", + "\n", + "PART 2:\n", + "1.\tContinue from the notebook in week 3 on the Application of dispersion relationship in the cross-shore (could make sense to have it in the same notebook). \n", + "2.\tProvide students with the code that they should have programmed in Waves to obtain the cross-shore distribution of H for normal incident waves.\n", + "3.\tVisualize their answers\n", + "4.\tAsk students to reflect on H(x) for different variables (slope, H0, gamma, T, mean water level) by means of multiple choice or multiple selection questions. In order to answer these questions let students vary their code in step 2. and redo step 3 (or if handier with sliders). For instance: which of the following changes in parameters will increase the width of the surf zone (give multiple answer options, more than one can be correct, for instance, increase H, decrease slope)\n", + "5.\tNow change to obliquely incident waves and ask students to add the effect of refraction in the code and replot H(x). \n", + "6.\tAsk a reflective question about the effect of refraction (we will add this later)\n", + "7.\tContinue with same wave conditions or repeat. Provide students with the code that they should have programmed in Waves to obtain the cross-shore distribution of Sxx. Ask them to adjust this for obliquely incident waves. \n", + "8.\tVisualize their code\n", + "9.\tProvide students with the code that they should have programmed to obtain the cross-shore distribution of mean water level eta (discretizing forward Euler from week 1.5 MUDE) and visualize this. Ask them to:\n", + "a.\tAdjust this for obliquely incident waves. \n", + "b.\tAdd the computation and plot of Fx as an intermediate step. \n", + "10.\tAsk some reflective questions (we will add these later)\n", + "11.\tLet students add code and plot Syx and Fy (varying on the above code)\n", + "12.\tAdd reflective questions\n", + "13.\tLet students add code and plot V(x) according to Eq. 5.82\n", + "14.\tAdd reflective question\n", + "15.\tLet students add code and plot orbital velocity magnitude in the cross-shore direction \n", + "16.\tTo be detailed: provide code and plot for undertow velocity (Eq. 5.41, the latter needs an estimation of the roller energy dissipation and roller area, I will provide the equations)\n", + "--> 16 Doet Judith uitdenken (dus voor nu evne)" + ] + }, + { + "cell_type": "raw", + "id": "c9b72d80-1bf1-4693-a1ef-a152395802c1", + "metadata": {}, + "source": [ + "tot 2.7 computerlab, maak functies, daarna reflective . what is the effect of ...\n", + "refraction coeficient for refraction. refractie op golfhoogte --> wordt kleiner\n", + "3.1 gegeven, aanpassen... \n", + "8) plot tegenoverelkaar.\n", + "9) 3.3 (computerlab) eta --> pagina 215. Fx --> daarna eta uitrekenenen.\n", + "11) uit boek/slides. (voorkeur zelfde waar als slides.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5588ebbe-8d34-4bb9-8cd6-2350c0fb5b9d", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "1091162f-11da-40d7-84ed-e21911f92e6a", + "metadata": {}, + "source": [ + "# Volgende stap, week 4: Chapter 5" + ] + }, + { + "cell_type": "raw", + "id": "53970be3-e2b2-4f18-89df-14b0e7d18971", + "metadata": {}, + "source": [ + "slide 21\n", + "slide 22-> fluctuatie enegery golfgroepschaal (zelf codereren --> plot)\n", + "slide 26, sxx --> formule foto\n", + "als voor 9 januari klaar, laat weten.\n", + "\n", + "deze week status update. op 9e " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3206e466-84e8-450e-9552-eb5937bbf349", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "211e97dc-15ac-40c1-a459-e5445e8cdf26", + "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.11.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/Chris/Initialize/Old_Functions_dont_delete.ipynb b/notebooks/Chris/Initialize/Old_Functions_dont_delete.ipynb deleted file mode 100644 index 5b7055d..0000000 --- a/notebooks/Chris/Initialize/Old_Functions_dont_delete.ipynb +++ /dev/null @@ -1,246 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "5927b8b2-ef2b-4d8c-b5b4-5f1a3abaee67", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import panel as pn\n", - "pn.extension(\"ipywidgets\", 'katex', 'mathjax')\n", - "import ipywidgets as ipw\n", - "from matplotlib.animation import FuncAnimation\n", - "from matplotlib.ticker import MultipleLocator\n", - "\n", - "from random import shuffle, uniform\n", - "\n", - "print(\"Packages succesfully loaded\")" - ] - }, - { - "cell_type": "markdown", - "id": "04643ffa-ddab-4a68-a6df-cd5526f00ea0", - "metadata": {}, - "source": [ - "# From week 2" - ] - }, - { - "cell_type": "markdown", - "id": "e227d3ae-d23b-42ab-ba90-619d2821bd89", - "metadata": {}, - "source": [ - "some widgets have been removed from week 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2940cfaf-447c-46d5-9ec6-00e8c5d775bc", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q8():\n", - "\n", - " # define widgets\n", - " a1 = ipw.FloatText(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - " a2 = ipw.FloatText(value=0.5, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - " T1 = ipw.FloatText(value=8, min=0.01, max=250000, step=0.01, description='T [s]')\n", - " T2 = ipw.FloatText(value=4, min=0.01, max=250000, step=0.01, description='T [s]')\n", - "\n", - " phi_1 = ipw.FloatText(value=0, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " phi_2 = ipw.FloatText(value=0.25, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " \n", - " n_waves = ipw.FloatText(value=3, min=0.1, max=10, step=0.1, description='n_{waves}')\n", - " \n", - " depth = ipw.FloatText(value=7.5, min=1, max=250, step=0.01, description='h [m]')\n", - " xp = ipw.FloatText(value=0, min=0, step=0.1, description='x [m]')\n", - " tp = ipw.FloatText(value=0, min=1, step=0.1, description='t [s]')\n", - " \n", - " # Disabled/informative widgets\n", - " L1 = ipw.FloatText(value=wave_length(T1.value,depth.value), description='L [m]', disabled=True)\n", - " L2 = ipw.FloatText(value=wave_length(T2.value,depth.value), description='L [m]', disabled=True)\n", - " Lgroup = ipw.FloatText(value=group_stats(k1=2 * np.pi / L1.value, k2=2 * np.pi / L2.value, w1=2 * np.pi / T1.value, w2=2 * np.pi / T2.value)[0], description='L_{group} [m]', disabled=True)\n", - "\n", - " # Setup widget layout (User Interface) and display\n", - " vbox1 = ipw.VBox([ipw.Label('Wave 1', layout=ipw.Layout(align_self='center')),a1, T1, phi_1, L1])\n", - " vbox2 = ipw.VBox([ipw.Label('Wave 2', layout=ipw.Layout(align_self='center')),a2, T2, phi_2, L2])\n", - " vbox3 = ipw.VBox([ipw.Label('Wave group', layout=ipw.Layout(align_self='center')), n_waves, Lgroup])\n", - " vbox4 = ipw.VBox([ipw.Label('General', layout=ipw.Layout(align_self='center')),depth, xp, tp])\n", - " \n", - " ui = ipw.HBox([vbox1, vbox2, vbox3, vbox4])\n", - "\n", - " def calc_eta(a1,T1,phi_1, a2,T2, phi_2, L1,L2, n_waves,xp, tp, depth):\n", - " L1 = wave_length(T1,depth)\n", - " L2 = wave_length(T2,depth)\n", - " \n", - " L_group, T_group, c_g = group_stats(\n", - " k1=2 * np.pi / L1,\n", - " k2=2 * np.pi / L2,\n", - " w1=2 * np.pi / T1,\n", - " w2=2 * np.pi / T2,\n", - " )\n", - " \n", - " T = np.min([T1,T2])\n", - " L = np.max([L1, L2])\n", - " t = np.arange(0,n_waves*T_group+T/30,T/30)\n", - " x = np.arange(0,n_waves*L_group+L/30,L/30)\n", - " \n", - " fig, axs = plt.subplots(nrows = 3,ncols = 2,figsize = (9,5), sharex=False, sharey = False)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.06)\n", - " \n", - " # time based\n", - " ax1 = axs[0,0]\n", - " ax2 = axs[1,0]\n", - " ax3 = axs[2,0]\n", - " #space based\n", - " ax4 = axs[0,1]\n", - " ax5 = axs[1,1]\n", - " ax6 = axs[2,1]\n", - "\n", - " # calculate surface, including phase change\n", - " eta1_T = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp-phi_1*(2*np.pi))\n", - " eta2_T = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp-phi_2*(2*np.pi))\n", - " eta_T = eta1_T + eta2_T\n", - "\n", - " eta1_x= a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x-phi_1*(2*np.pi))\n", - " eta2_x= a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x-phi_2*(2*np.pi))\n", - " eta_x = eta1_x + eta2_x\n", - "\n", - " # calculate surface, without phase change\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - "\n", - " eta1_x_basic = a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x)\n", - " eta2_x_basic = a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x)\n", - " eta_x_basic = eta1_x_basic + eta2_x_basic\n", - "\n", - " # plot surface excluding phase change\n", - " ax1.plot(t,eta1_T_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax2.plot(t,eta2_T_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax3.plot(t, eta_T_basic, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " ax4.plot(x, eta1_x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax5.plot(x, eta2_x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax6.plot(x, eta_x_basic, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " \n", - " # plot surface including phase change\n", - " ax1.plot(t,eta1_T, label = '$\\eta_1$')\n", - " ax2.plot(t,eta2_T, label = '$\\eta_2$' )\n", - " ax3.plot(t,eta_T, label = '$\\eta_{1+2}$')\n", - " \n", - " ax4.plot(x,eta1_x, label = '$\\eta_1$')\n", - " ax5.plot(x,eta2_x, label = '$\\eta_2$')\n", - " ax6.plot(x,eta_x, label = '$\\eta_{1+2}$')\n", - "\n", - " # set vertical axis the same\n", - " amp = (a1+a2)*1.1\n", - " ax1.set_ylim(-amp,amp)\n", - " ax2.set_ylim(-amp,amp)\n", - " ax3.set_ylim(-amp,amp)\n", - " ax4.set_ylim(-amp,amp)\n", - " ax5.set_ylim(-amp,amp)\n", - " ax6.set_ylim(-amp,amp)\n", - "\n", - " # set horizontal axis\n", - " ax1.set_xlim(0,n_waves*T_group)\n", - " ax2.set_xlim(0,n_waves*T_group)\n", - " ax3.set_xlim(0,n_waves*T_group)\n", - " ax4.set_xlim(0,n_waves*L_group)\n", - " ax5.set_xlim(0,n_waves*L_group)\n", - " ax6.set_xlim(0,n_waves*L_group)\n", - "\n", - " # set labels\n", - " ax1.set_ylabel('$\\eta_1$ [m]')\n", - " ax2.set_ylabel('$\\eta_2$ [m]')\n", - " ax3.set_ylabel('$\\eta_{1+2}$ [m]')\n", - " \n", - " ax3.set_xlabel('t/T_{group}')\n", - " ax6.set_xlabel('x/L_{group}')\n", - "\n", - " # remove the lines related to the x-ticks and y-ticks\n", - " ax1.xaxis.set_visible(False)\n", - " ax2.xaxis.set_visible(False)\n", - " ax4.yaxis.set_visible(False)\n", - " ax5.yaxis.set_visible(False)\n", - " ax6.yaxis.set_visible(False)\n", - " \n", - " # set scaled ticks\n", - " if n_waves >= 1:\n", - " ax3.set_xticks(np.arange(0,n_waves//1 +1, 1)*T_group)\n", - " ax3.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - " ax6.set_xticks(np.arange(0,n_waves//1 +1, 1)*L_group)\n", - " ax6.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - "\n", - " else: # 3 times when the scale is smaller than 1\n", - " ax3.set_xticks([0,0.5*n_waves*T_group, n_waves*T_group])\n", - " ax3.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " ax6.set_xticks([0,0.5*n_waves*L_group, n_waves*L_group])\n", - " ax6.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " \n", - " # remove x and y ticks\n", - " ax4.set_xticklabels([], fontsize=0)\n", - " ax5.set_xticklabels([], fontsize=0)\n", - " \n", - " ax4.set_yticklabels([], fontsize=0)\n", - " ax5.set_yticklabels([], fontsize=0)\n", - " ax6.set_yticklabels([], fontsize=0)\n", - " \n", - " # set title\n", - " ax1.set_title('Time-based (x =' + str(xp) + ' m)')\n", - " ax4.set_title('Space-based (t =' + str(tp) + ' s)')\n", - "\n", - " # plot legends \n", - " legend1 = ax4.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend2 = ax5.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend3 = ax6.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - "\n", - " ui = ipw.HBox([vbox1, vbox2, vbox3, vbox4])\n", - "\n", - " #update graph\n", - " out = ipw.interactive_output(calc_eta, {'a1': a1,'T1':T1, 'phi_1': phi_1, 'a2': a2,'T2':T2, 'phi_2': phi_2, 'L1' : L1, 'L2': L2, 'n_waves': n_waves, 'xp':xp, 'tp': tp, 'depth':depth})\n", - "\n", - " display(ui,out)\n", - "\n", - " \n", - "\n", - "W2_Q8()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "82c01cbc-8417-4f5a-99bf-117bf8cd6eb7", - "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.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/Chris/Initialize/Week_2_Initialize.ipynb b/notebooks/Chris/Initialize/Week_2_Initialize.ipynb deleted file mode 100644 index 771e047..0000000 --- a/notebooks/Chris/Initialize/Week_2_Initialize.ipynb +++ /dev/null @@ -1,2601 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "8eb2be20-36f2-4069-9936-551c06be2740", - "metadata": {}, - "source": [ - "# Start" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c305396a-bbfb-47be-b801-9455546f77a6", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import panel as pn\n", - "pn.extension(\"ipywidgets\", 'katex', 'mathjax')\n", - "import ipywidgets as ipw\n", - "from matplotlib.animation import FuncAnimation\n", - "#from matplotlib.ticker import MultipleLocator\n", - "from matplotlib.figure import Figure\n", - "\n", - "from random import shuffle, uniform\n", - "\n", - "import sys\n", - "from inspect import signature\n", - "print(\"Packages succesfully loaded\")" - ] - }, - { - "cell_type": "markdown", - "id": "50992233-364e-4d86-9750-4684ecb87d1f", - "metadata": {}, - "source": [ - "# Functions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "40de6ed3-9192-416d-b92f-1da26cb201e7", - "metadata": {}, - "outputs": [], - "source": [ - "def limit_answer(x):\n", - " s = str(x)\n", - "\n", - " # inspired by: https://stackoverflow.com/questions/35585950/find-the-number-of-digits-after-the-decimal-point\n", - " if not '.' in s:\n", - " n_decimal = 0\n", - " else:\n", - " n_decimal = len(s) - s.index('.') - 1\n", - "\n", - " range = 5*10**-(n_decimal+1)\n", - "\n", - " return range\n", - "\n", - "def check_nummeric_answers(id, answer, unit, FB_G, FB_W, num_widget, feedback_widget, attempt):\n", - " \n", - " def button_callback(b):\n", - " attempt.value += 1\n", - " #print(\"debug question:\", id, 'attempt', attempt.value ,', response:' ,num_widget.value, ', answer:',answer)\n", - "\n", - " # the answer is within the boundaries, print positive feedback\n", - " if np.abs(answer - num_widget.value) < limit_answer(answer):\n", - " if len(FB_G) != 0:\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = 'Well done, this is correct!'\n", - "\n", - " # the answer is NOT within boundaries, provide feedback based on the number of attempts\n", - " if np.abs(answer - num_widget.value) >= limit_answer(answer):\n", - "\n", - " if attempt.value < 3 and len(FB_W) > 0:\n", - " feedback_widget.value = FB_W\n", - " \n", - " if attempt.value < 3 and len(FB_W) == 0:\n", - " feedback_widget.value = 'Oops, there seems to be a mistake'\n", - " \n", - " if attempt.value >= 3:\n", - " feedback_widget.value = 'The correct answer is ' + str(answer) + str(unit) + '.'\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - "def nummeric_question_body(questions, units, answers, FB_good, FB_wrong):\n", - " all_widgets = []\n", - " attempts = []\n", - " id = 0\n", - " for question, units, answer, Q_FB_G, Q_FB_W in zip(questions, units, answers, FB_good, FB_wrong):\n", - " id += 1\n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - " unit_widget = pn.widgets.StaticText(value=units, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " #feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=500)\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " \n", - " Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget) \n", - " quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - " all_widgets.append(quiz_widget)\n", - "\n", - " # the values for the submit button are determined at the moment these are created.\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - " attempts.append(attempt)\n", - " submit_button.on_click(check_nummeric_answers(id, answer, units, Q_FB_G, Q_FB_W, num_widget, feedback_widget, attempt))\n", - " \n", - " return all_widgets" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9530ae5d-4e8d-452b-88d5-b429b5d5f1f7", - "metadata": {}, - "outputs": [], - "source": [ - "def dispersion(k, h): # calculate omega\n", - " return (9.81 * k * np.tanh(k * h)) ** 0.5" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "beb029f3-7a91-457b-8e18-6fccbd05865c", - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length(T, h):\n", - " L = 9.81 * T**2 / (2 * np.pi)\n", - " L_all = [L]\n", - "\n", - " for i in range(1500):\n", - " L = 9.81 * T**2 / (2 * np.pi) * np.tanh(2 * np.pi * h / L)\n", - " L_all.append(L)\n", - "\n", - " if np.abs(L_all[-1] - L_all[-2]) < 0.0005:\n", - " #plt.plot(L_all)\n", - " break\n", - " \n", - " return round(L, 13)\n", - "\n", - "#wave_length(T = 3, h = 600)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c663912c-777d-4c7d-a2cc-17873a50d3f7", - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length_check(T,h):\n", - " Dif = []\n", - " L_serie = np.arange(5,1000,0.01) \n", - " for L in L_serie:\n", - " LHS = (2*np.pi/T)**2\n", - " RHS = 9.81*2*np.pi/L * np.tanh(2*np.pi/L * h)\n", - " Dif.append(np.abs(LHS-RHS))\n", - " #if len(Dif) > 2 and Dif[-1] > Dif[-2]:\n", - " # break\n", - " \n", - " id_best = np.argmin(Dif)\n", - " L= L_serie[id_best]\n", - " plt.plot(Dif)\n", - " \n", - " return round(L,13)\n", - "\n", - "#wave_length_check(T = 3,h = 600)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0a4781af-8645-4469-ad1d-0c1cd816a802", - "metadata": {}, - "outputs": [], - "source": [ - "def group_stats(k1,k2,w1,w2):\n", - " Delta_k = np.abs(k2-k1)\n", - " Delta_w = np.abs(w2-w1)\n", - " L = 2*np.pi/Delta_k\n", - " T = 2*np.pi/Delta_w\n", - " cg = Delta_w/Delta_k\n", - " return L,T, cg" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9d4cd665-bbfd-4aa9-ad0c-92b7d60886d4", - "metadata": {}, - "outputs": [], - "source": [ - "class class_variables:\n", - " def __setattr__(self, key, value):\n", - " object.__setattr__(self, key, value)\n", - "\n", - "def classify_variables(locals, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " FV = class_variables()\n", - " for key, value in {**globals(), **locals}.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2f4dc42d-b05e-4903-a37f-02b41bda56bd", - "metadata": {}, - "outputs": [], - "source": [ - "def add_local_variables_to(FV,locals, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " for key, value in locals.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def check_code_values(IV):\n", - " # The function variable (FV) is now defined as Input Variable (IV)\n", - " # The input variable and the newly defined local variable will be merged to FV\n", - "\n", - " \n", - " def get_coded_values(FV,GV):\n", - " def button_callback(b):\n", - " try:\n", - " for i, param in enumerate(FV.check_parameters):\n", - " \n", - " # get the value that students gave\n", - " response = getattr(GV, param)\n", - " \n", - " #store them in the widget\n", - " FV.all_parameter_widgets[i].value = response\n", - " \n", - " FV.debug_widget.value = ''\n", - " except:\n", - " FV.debug_widget.value = ' Careful, not all parameters are defined! '\n", - " \n", - " return button_callback\n", - "\n", - " def check_coded_values(FV,GV):\n", - " def button_callback(b):\n", - " FV.attempt.value += 1\n", - " \n", - " for i, param in enumerate(FV.check_parameters):\n", - " #response = getattr(GV, param)\n", - " response = FV.all_parameter_widgets[i].value\n", - " answer = getattr(FV, param)\n", - " \n", - " if np.abs(response - answer) < FV.f_margin * answer:\n", - " FV.all_feedback_widgets[i].value = 'Nice, this is good!'\n", - " \n", - " if np.abs(response - answer) >= FV.f_margin * answer:\n", - " if FV.attempt.value < 3:\n", - " FV.all_feedback_widgets[i].value = 'This one is incorrect, try again!'\n", - " if FV.attempt.value >= 3:\n", - " FV.all_feedback_widgets[i].value = 'This one is incorrect, the answer should be ' + str(answer) + '.'\n", - " \n", - " #print('Debug: ', param, response, answer)\n", - " \n", - " return button_callback\n", - "\n", - " all_parameter_widgets = []\n", - " info_widgets = []\n", - " all_feedback_widgets = []\n", - " for name, param in zip(IV.name_parameters, IV.check_parameters):\n", - " symbol_widget = pn.widgets.StaticText(name='', value= name, width = 100)\n", - " parameter_widget = pn.widgets.FloatInput(name='', value=0, width = 100)\n", - " feedback_widget = pn.widgets.StaticText(value='')\n", - " \n", - " all_parameter_widgets.append(parameter_widget)\n", - " all_feedback_widgets.append(feedback_widget)\n", - " \n", - " new_row = pn.Row(symbol_widget, parameter_widget, feedback_widget)\n", - " info_widgets.append(new_row)\n", - "\n", - " debug_widget = pn.widgets.StaticText(value='')\n", - " #attempt = pn.widgets.FloatInput(value=0)\n", - " \n", - " get_values_button = pn.widgets.Button(name=\"Load values\")\n", - " check_values_button = pn.widgets.Button(name=\"Check loaded values\")\n", - "\n", - " GV = classify_variables(globals())\n", - " FV = add_local_variables_to(IV,locals())\n", - "\n", - " # The error margin is set at 0.01% if it is not defined\n", - " if 'f_margin' not in FV.__dict__:\n", - " FV.__setattr__('f_margin', 0.0001)\n", - " \n", - " get_values_button.on_click(get_coded_values(FV, GV))\n", - " check_values_button.on_click(check_coded_values(FV, GV))\n", - " \n", - " row_buttons = pn.Row(get_values_button, check_values_button, debug_widget)\n", - " Input_col = pn.Column(row_buttons, *info_widgets)\n", - " display(Input_col)" - ] - }, - { - "cell_type": "markdown", - "id": "7b2b4c56-ba07-48ac-bf4e-ae7e0f09b6b5", - "metadata": {}, - "source": [ - "Check coding function" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fd77d985-2dd9-481a-9e09-4352aacffb5f", - "metadata": {}, - "outputs": [], - "source": [ - "# general code, that has to be defined once\n", - "class class_variables:\n", - " def __setattr__(self, key, value):\n", - " object.__setattr__(self, key, value)\n", - "\n", - "def classify_variables(params, params2 = {}, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " FV = class_variables()\n", - " for key, value in {**params,**params2}.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def add_local_variables_to(FV,locals, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " for key, value in locals.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def check_code_values(IV):\n", - " # The function variable (FV) is now defined as Input Variable (IV)\n", - " # The input variable and the newly defined local variable will be merged to FV\n", - "\n", - " \n", - " def get_coded_values(FV,GV):\n", - " def button_callback(b):\n", - " try:\n", - " for i, param in enumerate(FV.check_parameters):\n", - " \n", - " # get the value that students gave\n", - " response = getattr(GV, param)\n", - " \n", - " #store them in the widget\n", - " FV.all_parameter_widgets[i].value = response\n", - " \n", - " FV.debug_widget.value = ''\n", - " except:\n", - " FV.debug_widget.value = ' Careful, not all parameters are defined! '\n", - " \n", - " return button_callback\n", - "\n", - " def check_coded_values(FV,GV):\n", - " def button_callback(b):\n", - " FV.attempt.value += 1\n", - " \n", - " for i, param in enumerate(FV.check_parameters):\n", - " #response = getattr(GV, param)\n", - " response = FV.all_parameter_widgets[i].value\n", - " answer = getattr(FV, param)\n", - " \n", - " if np.abs(response - answer) < FV.f_margin * answer:\n", - " FV.all_feedback_widgets[i].value = 'Nice, this is good!'\n", - " \n", - " if np.abs(response - answer) >= FV.f_margin * answer:\n", - " if FV.attempt.value < 3:\n", - " FV.all_feedback_widgets[i].value = 'This one is incorrect, try again!'\n", - " if FV.attempt.value >= 3:\n", - " FV.all_feedback_widgets[i].value = 'This one is incorrect, the answer should be ' + str(answer) + '.'\n", - " \n", - " #print('Debug: ', param, response, answer)\n", - " \n", - " return button_callback\n", - "\n", - " all_parameter_widgets = []\n", - " info_widgets = []\n", - " all_feedback_widgets = []\n", - " for name, param in zip(IV.name_parameters, IV.check_parameters):\n", - " symbol_widget = pn.widgets.StaticText(name='', value= name, width = 100)\n", - " parameter_widget = pn.widgets.FloatInput(name='', value=0, width = 100)\n", - " feedback_widget = pn.widgets.StaticText(value='')\n", - " \n", - " all_parameter_widgets.append(parameter_widget)\n", - " all_feedback_widgets.append(feedback_widget)\n", - " \n", - " new_row = pn.Row(symbol_widget, parameter_widget, feedback_widget)\n", - " info_widgets.append(new_row)\n", - "\n", - " debug_widget = pn.widgets.StaticText(value='')\n", - " \n", - " get_values_button = pn.widgets.Button(name=\"Load values\")\n", - " check_values_button = pn.widgets.Button(name=\"Check loaded values\")\n", - "\n", - " GV = classify_variables(globals())\n", - " FV = add_local_variables_to(IV,locals())\n", - "\n", - " # The error margin is set at 0.01% if it is not defined\n", - " if 'f_margin' not in FV.__dict__:\n", - " FV.__setattr__('f_margin', 0.0001)\n", - " \n", - " get_values_button.on_click(get_coded_values(FV, GV))\n", - " check_values_button.on_click(check_coded_values(FV, GV))\n", - " \n", - " row_buttons = pn.Row(get_values_button, check_values_button, debug_widget)\n", - " Input_col = pn.Column(row_buttons, *info_widgets)\n", - " display(Input_col)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3af584e0-1fc0-42e5-9c28-384d832cb086", - "metadata": {}, - "outputs": [], - "source": [ - "def check_code_function(fig, horizontal_axis, function_name, correct_function, par_x_axis, f_margin = 0.001):\n", - " # The error margin (f_margin) is set at 0.1% if it is not defined\n", - "\n", - " # make the graph and show the correct answer\n", - " ax = fig.subplots()\n", - " pane = pn.pane.Matplotlib(fig, dpi=100)\n", - " \n", - " # Plot the answer if the students function is found\n", - " try:\n", - " # Load the student-made function from globals\n", - " function = globals()[function_name]\n", - "\n", - " # Add the arguments to the name of the function, for eval()\n", - " # + Replace the argument on the x-axis so that it can be assessed with list comprehension.\n", - " # https://docs.python.org/3/library/inspect.html\n", - " # https://peps.python.org/pep-0362/ \n", - " sig = signature(function)\n", - " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", - " function = function_name + str(sig_function)\n", - " title = function_name + str(sig)\n", - " title = title.replace('_', ' ')\n", - "\n", - " sig = signature(correct_function) \n", - " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", - " correct_function2 = str(correct_function.__name__) + sig_function\n", - "\n", - " # calculate the student's answer through eval()\n", - " student_answer = [eval(function) for par_x_axis in horizontal_axis]\n", - "\n", - " #calculate the correct answer\n", - " correct_answer = []\n", - " for par_x_axis in horizontal_axis:\n", - " correct_answer.append(eval(correct_function2))\n", - " # This code should work, but it does not\n", - " #correct_answer = [eval(correct_function2) for par_x_axis in horizontal_axis]\n", - "\n", - " # check if the answer is correct and plot it before/below lines\n", - " changes = np.array(student_answer) - np.array(correct_answer)\n", - " inaccuracy = np.abs(1-np.array(correct_answer)/np.array(student_answer))\n", - " \n", - " if np.max(changes) == 0:\n", - " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", - " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Perfect!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", - "\n", - " if np.max(changes) != 0 and np.max(inaccuracy) < f_margin:\n", - " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", - " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Good!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", - " \n", - " # plot the answers\n", - " line = ax.plot(horizontal_axis, student_answer, label = 'Your answer')\n", - " line = ax.plot(horizontal_axis, correct_answer, label = 'Correct answer')\n", - " ax.legend()\n", - " ax.set_title(title)\n", - " \n", - " except:\n", - " text_failed = 'Almost there, \\n your function can not be plotted, \\n please try to fix the bug'\n", - " x_ticks = ax.get_xticks()\n", - " y_ticks = ax.get_yticks()\n", - " text = ax.text(np.average(x_ticks),np.average(y_ticks), text_failed, fontsize=16, color = 'r', ha='center', va='center')\n", - "\n", - " # update the graph\n", - " display(pane)" - ] - }, - { - "cell_type": "markdown", - "id": "ba3c3e72-03e2-4e9f-82ea-e2efbdb23440", - "metadata": {}, - "source": [ - "# Student calculators" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "15aead20-86f7-4352-8aa7-293f3c6de169", - "metadata": {}, - "outputs": [], - "source": [ - "def wavelength_calculator():\n", - " # define widgets (with initial value for L)\n", - " T = pn.widgets.FloatInput(value=5, step=0.01, width=100)\n", - " h = pn.widgets.FloatInput(value=3, step=0.01, width=100)\n", - " \n", - " L_init = wave_length(T.value, h.value)\n", - " L = pn.widgets.FloatInput(value=L_init, step=0.001, width= 100, disabled=True)\n", - "\n", - " # change the value of L when a change in T or h is observed\n", - " def update_L_widget(event):\n", - " L.value = wave_length(T.value, h.value)\n", - "\n", - " T.param.watch(update_L_widget, 'value')\n", - " h.param.watch(update_L_widget, 'value')\n", - "\n", - " # set the surrounding layout, like headings and descriptions\n", - " ## set headings and the column width\n", - " title_input = pn.widgets.StaticText(value='Input', width=200)\n", - " title_output = pn.widgets.StaticText(value='Output', width=200)\n", - " heading = pn.widgets.StaticText(value='Wavelength calculator')\n", - " heading = pn.widgets.StaticText(value='Wavelength calculator')\n", - "\n", - " ## Add descriptions and width for alignment\n", - " symbol_T = pn.widgets.StaticText(value='T', width=1)\n", - " unit_T = pn.widgets.StaticText(value='s', width=1)\n", - " T_widget = pn.Row(symbol_T, T, unit_T)\n", - "\n", - " symbol_h = pn.widgets.StaticText(value='h', width = 1)\n", - " unit_h = pn.widgets.StaticText(value='m', width = 1)\n", - " h_widget = pn.Row(symbol_h, h, unit_h)\n", - "\n", - " symbol_L = pn.widgets.StaticText(value='L', width = 1)\n", - " unit_L = pn.widgets.StaticText(value='m', width = 1)\n", - " L_widget = pn.Row(symbol_L, L, unit_L)\n", - "\n", - " # add latex formula\n", - " text_formula = pn.widgets.StaticText(value='Solves iteratively:', width = 100)\n", - " formula = pn.pane.LaTeX(r\"$L=\\frac{gT^2}{2 \\pi} tanh( \\frac{2 \\pi h}{L})$\")\n", - " row_formula = pn.Row(text_formula, formula)\n", - "\n", - " ## merge the layout and display the result\n", - " input_widget = pn.Column(title_input, T_widget, h_widget)\n", - " output_widget = pn.Column(title_output, L_widget)\n", - " horizontal_allignment = pn.Row(input_widget, output_widget)\n", - " include_heading = pn.Column(heading,row_formula, horizontal_allignment)\n", - "\n", - " return include_heading\n", - "\n", - "#wavelength_calculator()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3056547d-0240-4de5-8618-b8c5bf1ba664", - "metadata": {}, - "outputs": [], - "source": [ - "def angular_frequency_calculator():\n", - " # define widgets (with initial value for L)\n", - " L = pn.widgets.FloatInput(value=5, step=0.01, width=100)\n", - " h = pn.widgets.FloatInput(value=3, step=0.01, width=100)\n", - "\n", - " w = pn.widgets.FloatInput(value=dispersion(2*np.pi/L.value, h.value), step=0.001, width= 100, disabled=True)\n", - "\n", - " # change the value of L when a change in T or h is observed\n", - " def update_L_widget(event):\n", - " w.value = dispersion(2*np.pi/L.value, h.value)\n", - "\n", - " L.param.watch(update_L_widget, 'value')\n", - " h.param.watch(update_L_widget, 'value')\n", - "\n", - " # set the surrounding layout, like headings and descriptions\n", - " ## set headings and the column width\n", - " title_input = pn.widgets.StaticText(value='Input', width=200)\n", - " title_output = pn.widgets.StaticText(value='Output', width=200)\n", - " heading = pn.widgets.StaticText(value='Angular frequency calculator (w)')\n", - "\n", - " ## Add descriptions and width for alignment\n", - " symbol_L = pn.widgets.StaticText(value='L', width=1)\n", - " unit_L = pn.widgets.StaticText(value='m', width=1)\n", - " L_widget = pn.Row(symbol_L, L, unit_L)\n", - "\n", - " symbol_h = pn.widgets.StaticText(value='h', width = 1)\n", - " unit_h = pn.widgets.StaticText(value='m', width = 1)\n", - " h_widget = pn.Row(symbol_h, h, unit_h)\n", - "\n", - " symbol_w = pn.widgets.StaticText(value='w', width = 1)\n", - " unit_w= pn.widgets.StaticText(value='m', width = 1)\n", - " w_widget = pn.Row(symbol_w, w, unit_w)\n", - "\n", - " # add latex formula\n", - " text_formula = pn.widgets.StaticText(value='Solves:', width = 100)\n", - " formula = pn.pane.LaTeX(r\"$\\omega = \\sqrt{ g k tanh(kh)}$\")\n", - " row_formula = pn.Row(text_formula, formula)\n", - " \n", - " ## merge the layout and display the result\n", - " input_widget = pn.Column(title_input, L_widget, h_widget)\n", - " output_widget = pn.Column(title_output, w_widget)\n", - " horizontal_allignment = pn.Row(input_widget, output_widget)\n", - " include_heading = pn.Column(heading, row_formula, horizontal_allignment)\n", - "\n", - " return include_heading\n", - "\n", - "#angular_frequency_calculator()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "40dc107c-26ef-479e-a4d4-3e1901a1469f", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_calculators():\n", - " allignment = pn.Row(wavelength_calculator(), angular_frequency_calculator())\n", - " return allignment\n", - "\n", - "#W2_calculators()" - ] - }, - { - "cell_type": "markdown", - "id": "965c5774-23de-4d5b-9e4a-14fab2357349", - "metadata": {}, - "source": [ - "# Questions" - ] - }, - { - "cell_type": "markdown", - "id": "5b99fe91-ecde-4ee4-904b-e66d0577d352", - "metadata": {}, - "source": [ - "### Q1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c16d12c8-2233-4b86-bf92-57bb8b8334cc", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q1():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " text_general = \"Can you asses the wavelength through an iterative approach when the wave period (T) is \" + str(T1) + \" seconds and the water depth (h) is \" + str(h1) + \" meter? Feel free to use the cell below to make the computations.\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - " text_widget = pn.widgets.StaticText(value=text_general)\n", - " \n", - " Q1_text = \"a) What is the deep water wavelength (L0)?\"\n", - " Q1_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(L, 2)\n", - " Q1_FB_G = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - " Q1_FB_W = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - " Q2_text = \"b) If you use the dispersion relation expressed in the wavelength, what is the wavelength in the first iteration?\"\n", - " Q2_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi) * np.tanh(2 * np.pi * h1 / L)\n", - " Q2_answer = round(L, 2)\n", - " Q2_FB_G = ''\n", - " Q2_FB_W = 'Try to fill in the deep water wavelength for the first iteration.'\n", - "\n", - " Q3_text = \"c) To what wavelength does this iteration converge?\"\n", - " Q3_unit = \" m\"\n", - " Q3_answer = round(wave_length(T1, h1), 2)\n", - " Q3_FB_G = ''\n", - " Q3_FB_W = 'There is a mistake, '\n", - "\n", - " questions = [Q1_text, Q2_text,Q3_text]\n", - " units = [Q1_unit, Q2_unit, Q3_unit]\n", - " answers =[Q1_answer, Q2_answer, Q3_answer]\n", - " FB_good = [Q1_FB_G, Q2_FB_G, Q3_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q2_FB_W, Q3_FB_W]\n", - "\n", - " \n", - " all_widgets = nummeric_question_body(questions, units, answers, FB_good, FB_wrong)\n", - " \n", - " display(pn.Column(text_widget,*all_widgets))\n", - " \n", - "#W2_Q1()" - ] - }, - { - "cell_type": "markdown", - "id": "8b4b7ef2-0f35-4bca-9bf0-7db34b2b95e5", - "metadata": {}, - "source": [ - "### Q2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e059e4b7-2efb-4c10-a729-296813951551", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q2():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " #T1 = 6.7\n", - " #h1 = 4.5\n", - " \n", - " text_general = \"Can you asses the wavelength through table B-3 of the book, when the wave period (T) is \" + str(T1) + \" seconds and the water depth (h) is \" + str(h1) + \" meter? Feel free to use the cell below to make the computations.\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - " text_widget = pn.widgets.StaticText(value=text_general)\n", - " \n", - " Q1_text = \"a) What is the ratio of h/L0? (Use 3 decimal numbers in this question)\"\n", - " Q1_unit = \"\"\n", - " L0 = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(h1 / L0, 3)\n", - " Q1_FB_G = ''\n", - " Q1_FB_W = ''\n", - "\n", - " Q2_text = \"b) What is the ratio of h/L, you can linear interpolate. (Use 3 decimal numbers in this question)\"\n", - " Q2_unit = \"\"\n", - "\n", - " # data from table B-3, page 536, of the book CD1\n", - " h_L0_serie = np.array(\n", - " [\n", - " 0,\n", - " 0.002,\n", - " 0.004,\n", - " 0.006,\n", - " 0.008,\n", - " 0.01,\n", - " 0.015,\n", - " 0.02,\n", - " 0.025,\n", - " 0.03,\n", - " 0.035,\n", - " 0.04,\n", - " 0.045,\n", - " 0.05,\n", - " 0.055,\n", - " 0.06,\n", - " 0.065,\n", - " 0.07,\n", - " 0.075,\n", - " 0.08,\n", - " 0.085,\n", - " 0.09,\n", - " 0.095,\n", - " 0.1,\n", - " 0.11,\n", - " 0.12,\n", - " 0.13,\n", - " 0.14,\n", - " 0.15,\n", - " 0.16,\n", - " 0.17,\n", - " 0.18,\n", - " 0.19,\n", - " 0.2,\n", - " 0.21,\n", - " 0.22,\n", - " 0.23,\n", - " 0.24,\n", - " 0.25,\n", - " 0.26,\n", - " 0.27,\n", - " 0.28,\n", - " 0.29,\n", - " 0.30,\n", - " 0.31,\n", - " 0.32,\n", - " 0.33,\n", - " 0.34,\n", - " 0.35,\n", - " 0.36,\n", - " 0.37,\n", - " 0.38,\n", - " 0.39,\n", - " 0.4,\n", - " 0.41,\n", - " 0.42,\n", - " 0.43,\n", - " 0.44,\n", - " 0.45,\n", - " 0.46,\n", - " 0.47,\n", - " 0.48,\n", - " 0.49,\n", - " 0.5,\n", - " 1,\n", - " ]\n", - " )\n", - " h_L_serie = np.array(\n", - " [\n", - " 0,\n", - " 0.0179,\n", - " 0.0253,\n", - " 0.0311,\n", - " 0.0360,\n", - " 0.0403,\n", - " 0.0496,\n", - " 0.0576,\n", - " 0.0648,\n", - " 0.0713,\n", - " 0.0775,\n", - " 0.0833,\n", - " 0.0888,\n", - " 0.0942,\n", - " 0.0993,\n", - " 0.104,\n", - " 0.109,\n", - " 0.114,\n", - " 0.119,\n", - " 0.123,\n", - " 0.128,\n", - " 0.132,\n", - " 0.137,\n", - " 0.141,\n", - " 0.150,\n", - " 0.158,\n", - " 0.167,\n", - " 0.175,\n", - " 0.183,\n", - " 0.192,\n", - " 0.2,\n", - " 0.208,\n", - " 0.217,\n", - " 0.225,\n", - " 0.234,\n", - " 0.242,\n", - " 0.251,\n", - " 0.259,\n", - " 0.268,\n", - " 0.277,\n", - " 0.285,\n", - " 0.294,\n", - " 0.303,\n", - " 0.312,\n", - " 0.321,\n", - " 0.330,\n", - " 0.339,\n", - " 0.349,\n", - " 0.358,\n", - " 0.367,\n", - " 0.377,\n", - " 0.386,\n", - " 0.395,\n", - " 0.405,\n", - " 0.415,\n", - " 0.424,\n", - " 0.434,\n", - " 0.433,\n", - " 0.453,\n", - " 0.463,\n", - " 0.472,\n", - " 0.482,\n", - " 0.492,\n", - " 0.502,\n", - " 1,\n", - " ]\n", - " )\n", - "\n", - " # read the table\n", - " id_lower = np.where(h_L0_serie <= h1 / L0)[0][\n", - " -1\n", - " ] # get the (last) id where the value is below h/L0\n", - " h_L_lower = h_L_serie[\n", - " id_lower\n", - " ] # use this id to get the new ratio H/L, the lower boundary\n", - " h_L_upper = h_L_serie[id_lower + 1] # the upper boundary\n", - "\n", - " # linear interpolate\n", - " slope = (h_L_upper - h_L_lower) / (h_L0_serie[id_lower + 1] - h_L0_serie[id_lower])\n", - " h_L = h_L_lower + slope * (h1 / L0 - h_L0_serie[id_lower])\n", - "\n", - " # print(h1/L0, id_lower, h_L_lower, h_L_upper, h_L)\n", - " Q2_answer = round(h_L, 3)\n", - " Q2_FB_G = ''\n", - " Q2_FB_W = ''\n", - "\n", - " Q3_text = (\n", - " \"c) What is the wavelength?\"\n", - " )\n", - " Q3_unit = \" m\"\n", - " Q3_answer = round(h1 / h_L, 2)\n", - " Q3_FB_G = ''\n", - " Q3_FB_W = ''\n", - "\n", - " Q4_text = \"d) What is the value tanh(kh), you can linearly interpolate. (Use 3 decimal numbers in this question)\"\n", - " Q4_unit = \"\"\n", - "\n", - " # data from table B-3\n", - " tanh_kh_serie = np.array(\n", - " [\n", - " 0,\n", - " 0.112,\n", - " 0.158,\n", - " 0.193,\n", - " 0.222,\n", - " 0.248,\n", - " 0.302,\n", - " 0.347,\n", - " 0.386,\n", - " 0.420,\n", - " 0.452,\n", - " 0.48,\n", - " 0.507,\n", - " 0.531,\n", - " 0.554,\n", - " 0.575,\n", - " 0.595,\n", - " 0.614,\n", - " 0.632,\n", - " 0.649,\n", - " 0.665,\n", - " 0.681,\n", - " 0.695,\n", - " 0.681,\n", - " 0.695,\n", - " 0.709,\n", - " 0.735,\n", - " 0.759,\n", - " 0.780,\n", - " 0.8,\n", - " 0.818,\n", - " 0.835,\n", - " 0.85,\n", - " 0.864,\n", - " 0.877,\n", - " 0.888,\n", - " 0.899,\n", - " 0.909,\n", - " 0.918,\n", - " 0.926,\n", - " 0.933,\n", - " 0.940,\n", - " 0.946,\n", - " 0.952,\n", - " 0.957,\n", - " 0.961,\n", - " 0.965,\n", - " 0.969,\n", - " 0.972,\n", - " 0.975,\n", - " 0.978,\n", - " 0.980,\n", - " 0.983,\n", - " 0.984,\n", - " 0.986,\n", - " 0.988,\n", - " 0.989,\n", - " 0.990,\n", - " 0.991,\n", - " 0.992,\n", - " 0.993,\n", - " 0.994,\n", - " 0.995,\n", - " 0.995,\n", - " 0.996,\n", - " 0.996,\n", - " 1,\n", - " ]\n", - " )\n", - "\n", - " # read the table\n", - " id_lower = np.where(h_L0_serie <= h1 / L0)[0][\n", - " -1\n", - " ] # get the (last) id where the value is below h/L0\n", - " tanh_kh_lower = tanh_kh_serie[id_lower]\n", - " tanh_kh_upper = tanh_kh_serie[id_lower + 1]\n", - "\n", - " # linear interpolate\n", - " slope = (tanh_kh_upper - tanh_kh_lower) / (\n", - " h_L0_serie[id_lower + 1] - h_L0_serie[id_lower]\n", - " )\n", - " tanh_kh = tanh_kh_lower + slope * (h1 / L0 - h_L0_serie[id_lower])\n", - "\n", - " Q4_answer = round(tanh_kh, 3)\n", - " Q4_FB_G = ''\n", - " Q4_FB_W = ''\n", - "\n", - " questions = [Q1_text, Q2_text,Q3_text, Q4_text]\n", - " units = [Q1_unit, Q2_unit, Q3_unit, Q4_unit]\n", - " answers =[Q1_answer, Q2_answer, Q3_answer, Q4_answer]\n", - " FB_good = [Q1_FB_G, Q2_FB_G, Q3_FB_G, Q4_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q2_FB_W, Q3_FB_W, Q4_FB_W]\n", - " \n", - " all_widgets = nummeric_question_body(questions, units, answers, FB_good, FB_wrong)\n", - " \n", - " display(pn.Column(text_widget,*all_widgets))\n", - "\n", - " #print(h1 / L0)\n", - " #print(h_L)\n", - " #print(h_L_lower)\n", - " #print(h_L_upper)\n", - " #print(tanh_kh_lower)\n", - " #print(tanh_kh_upper)\n", - " \n", - "#W2_Q2()" - ] - }, - { - "cell_type": "markdown", - "id": "2bfce4c6-b709-46f6-ad6e-73c08190c531", - "metadata": {}, - "source": [ - "### Q3" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "868626e5-6c5e-4564-8a24-e86d760b1388", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q3(): \n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " T1 = 6.7\n", - " h1 = 4.5\n", - "\n", - " text_general = \"A third method to calculate the wave length is through the implicit formula of Fenton, when the wave period (T) is \" + str(T1) + \" seconds and the water depth (h) is \" + str(h1) + \" meter? Feel free to use the cell below to make the computations.\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - " text_widget = pn.widgets.StaticText(value=text_general)\n", - "\n", - " def waveNumber_Fenton(T, d): # made by J.A.ArriagaGarcia (Jaime)\n", - " g = 9.81\n", - " omega = 2 * np.pi / T\n", - " k0 = omega * omega / g\n", - " alpha = k0 * d\n", - " beta = alpha * (np.tanh(alpha)) ** -0.5\n", - " k = (\n", - " (alpha + beta**2 * np.cosh(beta) ** -2)\n", - " / (np.tanh(beta) + beta * np.cosh(beta) ** -2)\n", - " / d\n", - " )\n", - " return k\n", - " \n", - " Q1_text = (\n", - " \"a) What is the wave number (k), following the explicit formula of Fentom?\"\n", - " )\n", - " Q1_unit = \"\"\n", - "\n", - " k1 = waveNumber_Fenton(T=T1, d=h1)\n", - " Q1_answer = round(k1, 2)\n", - " Q1_FB_G = ''\n", - " Q1_FB_W = ''\n", - "\n", - " Q2_text = \"b) What is the wave length, following the explicit formula of Fentom?\"\n", - " Q2_unit = \" m\"\n", - " Q2_answer = round(2 * np.pi / k1, 2)\n", - " Q2_FB_G = ''\n", - " Q2_FB_W = ''\n", - "\n", - " questions = [Q1_text, Q2_text]\n", - " units = [Q1_unit, Q2_unit]\n", - " answers =[Q1_answer, Q2_answer]\n", - " FB_good = [Q1_FB_G, Q2_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q2_FB_W]\n", - " \n", - " all_widgets = nummeric_question_body(questions, units, answers, FB_good, FB_wrong)\n", - " \n", - " display(pn.Column(text_widget,*all_widgets))\n", - "\n", - "#W2_Q3()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "19a25656-c42d-4795-9d5c-d55de55773ab", - "metadata": {}, - "outputs": [], - "source": [ - "def nummeric_question_body(questions, units, answers, FB_good, FB_wrong, random_order = False):\n", - " all_widgets = []\n", - " attempts = []\n", - "\n", - " order = np.arange(0, len(questions), 1)\n", - " if random_order == True:\n", - " shuffle(order)\n", - "\n", - " for i in np.array(order):\n", - " question, unit, answer, Q_FB_G, Q_FB_W = questions[i], units[i], answers[i], FB_good[i], FB_wrong[i]\n", - " id = i+1 \n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - " unit_widget = pn.widgets.StaticText(value=unit, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " #feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=500)\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " \n", - " Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget) \n", - " quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - " all_widgets.append(quiz_widget)\n", - "\n", - " # the values for the submit button are determined at the moment these are created.\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - " attempts.append(attempt)\n", - " submit_button.on_click(check_nummeric_answers(id, answer, unit, Q_FB_G, Q_FB_W, num_widget, feedback_widget, attempt))\n", - " \n", - " return all_widgets\n" - ] - }, - { - "cell_type": "markdown", - "id": "5642cd37-8603-43f9-92ce-febcf195bb4e", - "metadata": {}, - "source": [ - "### Q4" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4081d8c3-c14f-4580-9ca1-09b2ed959095", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q4():\n", - "\n", - " h = round(uniform(2, 4), 1) # m , random value from 2 to 4 (excluding 4)\n", - " L = round(uniform(25, 30), 1) # m\n", - " k = 2 * np.pi / L\n", - " omega = dispersion(k, h)\n", - "\n", - " Q1_text = (\n", - " \"What is the wave period (T) for a wave with a length (L) of \"\n", - " + str(L)\n", - " + \" meter when the depth (h) is \"\n", - " + str(h)\n", - " + \" meters?\"\n", - " )\n", - " Q1_unit = \" s\"\n", - " Q1_answer = T = round(2 * np.pi / omega, 2)\n", - " Q1_FB_G = ''\n", - " Q1_FB_W = ''\n", - "\n", - " \n", - " h = round(uniform(2, 4), 1) # m\n", - " L = round(uniform(20, 30), 1) # m\n", - " k = 2 * np.pi / L\n", - " omega = dispersion(k, h)\n", - " T = 2 * np.pi / omega\n", - "\n", - " Q2_text = (\n", - " \"What is the wave celerity (c) for waves with a length (L) of \"\n", - " + str(L)\n", - " + \" meter when the depth (h) is \"\n", - " + str(h)\n", - " + \" meters?\"\n", - " )\n", - " Q2_unit = \" m/s\"\n", - " Q2_answer = c = round(L / T, 2)\n", - " Q2_FB_G = ''\n", - " Q2_FB_W = ''\n", - "\n", - " \n", - " h = round(uniform(1, 2), 1)\n", - " L = round(uniform(20, 30), 1) # m\n", - " omega = dispersion(k, h)\n", - " T = 2 * np.pi / omega\n", - "\n", - " Q3_text = (\n", - " \"What is the wave celerity (c) for waves with a length (L) of \"\n", - " + str(L)\n", - " + \" meter when the depth (h) is \"\n", - " + str(h)\n", - " + \" meters?\"\n", - " )\n", - " Q3_unit = \" m/s\"\n", - " Q3_answer = c = round(L / T, 2)\n", - " Q3_FB_G = ''\n", - " Q3_FB_W = ''\n", - "\n", - "\n", - " h = round(uniform(1, 2), 1)\n", - " T = round(uniform(5, 7), 1)\n", - " L = wave_length(T, h)\n", - " Q4_text = (\n", - " \"What is the wave length (L) for a wave with a period (T) of \"\n", - " + str(T)\n", - " + \" seconds when the depth (h) is \"\n", - " + str(h)\n", - " + \" meters?\"\n", - " )\n", - " Q4_unit = \" m\"\n", - " Q4_answer = round(L, 2)\n", - " Q4_FB_G = ''\n", - " Q4_FB_W = ''\n", - " \n", - "\n", - " h = round(uniform(5, 7), 1) # km\n", - " L = 500000 # m\n", - " k = 2 * np.pi / L\n", - " omega = dispersion(k, h * 1000) # m to km\n", - " T = 2 * np.pi / omega\n", - " Q5_text = (\n", - " \"What is the propagation speed (c) of a tsunami wave train with (L) of 500 km in the deep ocean with a depth (h) of \"\n", - " + str(h)\n", - " + \" km?\"\n", - " )\n", - " Q5_unit = \" km/h\"\n", - " Q5_answer = c = round(L / T / 1000 * 3600, 2)\n", - " Q5_FB_G = ''\n", - " Q5_FB_W = ''\n", - "\n", - " \n", - " h = round(uniform(5, 7), 1) # km\n", - " T = round(uniform(30, 38), 0) # min\n", - " L = wave_length(T * 60, h)\n", - " Q6_text = (\n", - " \"What is the propagation speed of a tsunami wave train with a period (T) of \"\n", - " + str(T)\n", - " + \" minutes in the deep ocean with a depth (h) of \"\n", - " + str(h)\n", - " + \" km?\"\n", - " )\n", - " Q6_unit = \" km/h\"\n", - " Q6_answer = c = round(L / (T) / 1000 * 60, 2)\n", - " Q6_FB_G = ''\n", - " Q6_FB_W = ''\n", - "\n", - " \n", - " h = round(uniform(0.6, 10), 1)\n", - " T = round(uniform(2.5, 12), 1)\n", - " L = round(wave_length(T, h), 1) # releastic value for question, based on T\n", - "\n", - " k = 2 * np.pi / L\n", - " n = 0.5 + k * h / np.sinh(2 * k * h)\n", - " Q7_text = (\n", - " \"What is the value of n when the water depth (h) is \"\n", - " + str(h)\n", - " + \" m, and the wave length (L) is \"\n", - " + str(L)\n", - " + \" m?\"\n", - " )\n", - " Q7_unit = \"\"\n", - " Q7_answer = round(n, 2)\n", - " Q7_FB_G = ''\n", - " Q7_FB_W = ''\n", - " \n", - "\n", - " h = round(uniform(0.8, 10), 1)\n", - " T = round(uniform(2.5, 12), 1)\n", - " L = wave_length(T, h)\n", - " k = 2 * np.pi / L\n", - " c = L / T\n", - " n = 0.5 + k * h / np.sinh(2 * k * h)\n", - " cg = n * c\n", - "\n", - " Q8_text = (\n", - " \"What is the group velocity (cg) when the water depth (h) is \"\n", - " + str(h)\n", - " + \" m, and the wave period (T) is \"\n", - " + str(T)\n", - " + \" s?\"\n", - " )\n", - " Q8_unit = \" m/s\"\n", - " Q8_answer = round(cg, 2)\n", - " Q8_FB_G = ''\n", - " Q8_FB_W = ''\n", - "\n", - " \n", - " # get realistic value\n", - " h = round(uniform(1, 10), 1)\n", - " T = round(uniform(4, 8), 1)\n", - " L = round(wave_length(T, h), 1)\n", - "\n", - " omega = dispersion(k, h)\n", - " T = 2 * np.pi / omega\n", - " k = 2 * np.pi / L\n", - " c = L / T\n", - " n = 0.5 + k * h / np.sinh(2 * k * h)\n", - " cg = n * c\n", - "\n", - " Q9_text = (\n", - " \"At which speed does the wave energy propagate when the water depth (h) is \"\n", - " + str(h)\n", - " + \" m, and the wave length (L) is \"\n", - " + str(L)\n", - " + \" m?\"\n", - " )\n", - " Q9_unit = \" m/s\"\n", - " Q9_answer = round(cg, 2)\n", - " Q9_FB_G = ''\n", - " Q9_FB_W = ''\n", - "\n", - " questions = [Q1_text, Q2_text,Q3_text, Q4_text, Q5_text, Q6_text, Q7_text, Q8_text, Q9_text]\n", - " units = [Q1_unit, Q2_unit, Q3_unit, Q4_unit, Q5_unit, Q6_unit, Q7_unit, Q8_unit, Q9_unit]\n", - " answers =[Q1_answer, Q2_answer, Q3_answer, Q4_answer, Q5_answer, Q6_answer, Q7_answer, Q8_answer, Q9_answer]\n", - " FB_good = [Q1_FB_G, Q2_FB_G, Q3_FB_G, Q4_FB_G, Q5_FB_G, Q6_FB_G, Q7_FB_G, Q8_FB_G, Q9_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q2_FB_W, Q3_FB_W, Q4_FB_W, Q5_FB_W, Q6_FB_W, Q7_FB_W, Q8_FB_W, Q9_FB_W]\n", - "\n", - " all_widgets = nummeric_question_body(questions, units, answers, FB_good, FB_wrong, random_order = True)\n", - " \n", - " display(pn.Column(*all_widgets))\n", - "\n", - "#W2_Q4()" - ] - }, - { - "cell_type": "markdown", - "id": "3e3bc0d0-6ccd-41db-9ae1-e80bccb0caec", - "metadata": {}, - "source": [ - "### Q5" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b04482d0-ae89-4c47-8900-b07e47294aef", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q5():\n", - " h1 = uniform(0.5, 2)\n", - " h2 = uniform(0.5, 3.5)\n", - " h3 = uniform(0.5, 5)\n", - " h4 = uniform(2, 5)\n", - " h5 = uniform(0.3, 0.91)\n", - "\n", - " L1 = h1 / uniform(0.025, 0.05) # shallow water\n", - " L2 = h2 / uniform(0.051, 0.49) # intermediate water\n", - " L3 = h3 / uniform(0.5, 0.75) # deep water\n", - " L4 = h4 / uniform(0.025, 0.075) # shallow or intermediate water\n", - " L5 = h5 / uniform(0.25, 0.75) # intermediate water or deep water\n", - "\n", - " L_serie = [L1, L2, L3, L4, L5]\n", - " h_serie = [h1, h2, h3, h4, h5]\n", - "\n", - " # The possible answers (may very per question)\n", - " answers = [\"Shallow\", \"Intermediate\", \"Deep\"]\n", - "\n", - " Questions = []\n", - " correct_answers_id = []\n", - "\n", - " for L, h in zip(L_serie, h_serie):\n", - " L = round(L, 2)\n", - " h = round(h, 2)\n", - " Questions.append(\"L = \" + str(L) + \" m, h = \" + str(h) + \" m\")\n", - "\n", - " ratio = h / L\n", - "\n", - " if ratio <= 0.05:\n", - " correct_answers_id.append(0)\n", - " if ratio > 0.05 and ratio < 0.5:\n", - " correct_answers_id.append(1)\n", - " if ratio >= 0.5:\n", - " correct_answers_id.append(2)\n", - "\n", - " # define the widgets for visualization, make for each a row with question and answer\n", - " # Store all the rows with statements in a list for visualization, and in toggle_widgets for checking the answer\n", - " Rows = []\n", - " toggle_widgets = []\n", - " for i in range(len(correct_answers_id)):\n", - " question_widget = pn.widgets.StaticText(value=Questions[i], width = 150)#statement\n", - " \n", - " radio_group_widget = pn.widgets.RadioButtonGroup(name='Radio Button Group', options=answers, button_type='default')\n", - " toggle_widgets.append(radio_group_widget)\n", - " \n", - " add_row = pn.Row(question_widget, radio_group_widget)\n", - " Rows.append(add_row)\n", - "\n", - " # randomize the order of statements\n", - " shuffle(Rows)\n", - "\n", - " # add a submit button with a feedback option next to it\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=500)\n", - " submit_row = pn.Row(submit_button, feedback_widget)\n", - " \n", - " # include a question\n", - " text_general = \"Select if the wave described on the left experiences shallow, intermediate, or deep water.\"\n", - " text_widget = pn.widgets.StaticText(value=text_general)\n", - "\n", - " # structure the widgets and display thems\n", - " display(text_widget, *Rows, submit_row)\n", - "\n", - " # check the answer and give feedback\n", - " def check_answers(button):\n", - " score = 0\n", - "\n", - " for i in range(len(correct_answers_id)):\n", - " if toggle_widgets[i].value == answers[correct_answers_id[i]]:\n", - " score += 1\n", - "\n", - " # print(toggle_widget.value)\n", - " feedback_widget.value = \"Your score is \" + str(score) + \"/\" + str(len(correct_answers_id))\n", - "\n", - " submit_button.on_click(check_answers)\n", - "\n", - "#W2_Q5()" - ] - }, - { - "cell_type": "markdown", - "id": "2214d98a-b679-4f17-9c62-f601177d37b5", - "metadata": {}, - "source": [ - "### Q6" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "96768b36-d029-4e6f-bae3-cf9b6978320a", - "metadata": {}, - "outputs": [], - "source": [ - "from matplotlib.figure import Figure" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e4b8e892-47ce-4d01-9d6d-29e06aaa07be", - "metadata": {}, - "outputs": [], - "source": [ - "def check_answers_W2_Q6(id, answers, unit, FB_G, FB_W, num_widgets, feedback_widgets, attempt, final_score_widget, plots, panes, figures, par):\n", - " FV = par\n", - " omega_serie, T_serie, L_serie, k_serie, c_serie, n_serie, cg_serie = FV['omega_serie'], FV['T_serie'], FV['L_serie'], FV['k_serie'], FV['c_serie'], FV['n_serie'], FV['cg_serie']\n", - " \n", - " def button_callback(b):\n", - " attempt.value += 1\n", - "\n", - " # store responses in a list for plotting\n", - " responses = []\n", - " score = 0\n", - " for i in range(len(answers)):\n", - " num_widget = num_widgets[i]\n", - " feedback_widget = feedback_widgets[i]\n", - " answer = answers[i]\n", - " response = num_widget.value\n", - " responses.append(response)\n", - "\n", - " # if answer is correct\n", - " if np.abs(answer - num_widget.value) < limit_answer(answer):\n", - " score += 1\n", - " if len(FB_G) != 0:\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = 'Well done, this is correct!'\n", - "\n", - " # the answer is NOT within boundaries, provide feedback based on the number of attempts\n", - " if np.abs(answer - num_widget.value) >= limit_answer(answer):\n", - " \n", - " if attempt.value < 3 and len(FB_W) > 0:\n", - " feedback_widget.value = FB_W\n", - " \n", - " if attempt.value < 3 and len(FB_W) == 0:\n", - " feedback_widget.value = 'Oops, there seems to be a mistake'\n", - " \n", - " if attempt.value >= 3:\n", - " feedback_widget.value = 'The correct answer is ' + str(answer) + str(unit) + '.'\n", - "\n", - " final_score_widget.value = str(score) + '/' + str(len(answers))\n", - "\n", - " # Update the graphs\n", - " if id == 1:\n", - " ax = plots[0]\n", - " ax.clear()\n", - " line = ax.plot(omega_serie, L_serie, label = 'L [m]')\n", - " #ax.scatter(par['omega_points'], np.array(answers) * 1000, c=\"green\", s=30, label = 'correct answer')\n", - " ax.scatter(par['omega_points'], np.array([responses]) * 1000, c=\"#006AB5\", s=25, label = 'answer')\n", - " ax.set_title(par['titles'][0])\n", - " ax.legend()\n", - " ax.set_xlabel(\"$\\omega $ [rad/s]\")\n", - " panes[0].object = figures[0]\n", - " \n", - " if id == 2 or id == 3:\n", - " # the number of times question 2 and 3 are answered\n", - " attempt2 = par['attempts'][1].value\n", - " attempt3 = par['attempts'][2].value\n", - "\n", - " # the given answers\n", - " responses2 = return_answers_widgets(par['all_answers'][1])\n", - " responses3 = return_answers_widgets(par['all_answers'][2])\n", - "\n", - " ax = plots[1]\n", - " ax.clear()\n", - " if attempt2 > 0:\n", - " line = ax.plot(omega_serie, c_serie, label = 'c [m/s]', color = '#006AB5')\n", - " #ax.scatter(par['omega_points'], answers, color =\"#006AB5\", s=30, label = 'correct answer')\n", - " ax.scatter(par['omega_points'], responses2, color=\"#006AB5\", s=25, label = 'answer c')\n", - " ax.set_title(par['titles'][1])\n", - " ax.legend()\n", - " ax.set_xlabel(\"$\\omega $ [rad/s]\")\n", - " panes[1].object = figures[1]\n", - "\n", - " if attempt3 > 0:\n", - " line = ax.plot(omega_serie, cg_serie, label = 'cg [m/s]', color = '#003f6c')\n", - " ax.scatter(par['omega_points'], responses3, c=\"#003f6c\", s=25, label = 'answer cg')\n", - " ax.set_title(par['titles'][1])\n", - " ax.legend()\n", - " ax.set_xlabel(\"$\\omega $ [rad/s]\")\n", - " panes[1].object = figures[1]\n", - "\n", - " if id == 3:\n", - " ax = plots[2]\n", - " ax.clear()\n", - " line = ax.plot(omega_serie, n_serie, label = 'n [-]')\n", - " ax.set_title(par['titles'][2])\n", - " ax.legend()\n", - " ax.set_xlabel(\"$\\omega $ [rad/s]\")\n", - " panes[2].object = figures[2] \n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cc3768a8-6d9a-45f4-8319-ad3cbbb49185", - "metadata": {}, - "outputs": [], - "source": [ - "def return_answers_widgets(widgets):\n", - " answers = []\n", - " for widget in widgets:\n", - " answers.append(widget.value)\n", - " return answers" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0d24e00e-62bc-44b0-8d32-d8d26b00f5cd", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q6():\n", - " h = np.arange(3000, 6000 + 500, 500)\n", - " h = np.random.choice(h)\n", - "\n", - " T1 = 5 * 60\n", - " T4 = 30 * 60\n", - "\n", - " text_general = \"We are going to analyze the characteristics of tsunami waves at a depth of \" + str(h) + \" m. It will be analyzed for waves with a period of \" + str(T1/60) + \" minutes (point 1), at the shallow water boundary (point 2), the deep water boundary (point 3), and for waves with a period of \" + str(T4/60) + \" minutes (point 4).\"\n", - "\n", - " Q1 = \"What is the wave length of the tsunami wave at those 4 points?\"\n", - " Q1_labels = [\"L1\", \"L2\", \"L3\", \"L4\"]\n", - " Q1_unit = \" km\"\n", - "\n", - " L1 = wave_length(T=T1, h=h)\n", - " L2 = h / 0.05\n", - " L3 = h / 0.5\n", - " L4 = wave_length(T=T4, h=h)\n", - " Q1_answers = [round(L1 / 1000, 2), L2 / 1000, L3 / 1000, round(L4 / 1000, 2)]\n", - "\n", - " # The 4 points of interest in this question\n", - " w1 = 2 * np.pi / T1\n", - " w2 = dispersion(k=2 * np.pi / (L2), h=h)\n", - " w3 = dispersion(k=2 * np.pi / (L3), h=h)\n", - " w4 = 2 * np.pi / T4\n", - " omega_points = np.array([w1, w2, w3, w4])\n", - "\n", - " Q2 = \"What is the wave celerity (c) of the tsunami wave at those 4 points?\"\n", - " Q2_labels = [\"c1\", \"c2\", \"c3\", \"c4\"]\n", - " Q2_unit = \" m/s\"\n", - " T2 = 2 * np.pi / w2\n", - " T3 = 2 * np.pi / w3\n", - " \n", - " def calc_c(T,h):\n", - " L = wave_length(T, h)\n", - " return 9.81*T/(2*np.pi)*np.tanh(2*np.pi*h/L)\n", - "\n", - " c1 = L1 / T1\n", - " c2 = L2 / T2\n", - " c3 = L3 / T3\n", - " c4 = L4 / T4\n", - "\n", - " c1 = calc_c(T1,h)\n", - " c2 = calc_c(T2,h)\n", - " c3 = calc_c(T3,h)\n", - " c4 = calc_c(T4,h)\n", - " \n", - " Q2_answers = [round(c1, 2), round(c2, 2), round(c3, 2), round(c4, 2)]\n", - "\n", - " Q3 = \"What is the wave group velocity (cg) of the tsunami at those 4 points?\"\n", - " Q3_labels = [\"c_{g1}$\", \"c_{g2}\", \"c_{g3}\", \"c_{g4}\"]\n", - " Q3_unit = \" m/s\"\n", - "\n", - " k1 = 2 * np.pi / L1\n", - " k2 = 2 * np.pi / L2\n", - " k3 = 2 * np.pi / L3\n", - " k4 = 2 * np.pi / L4\n", - "\n", - " n1 = 0.5 + k1 * h / np.sinh(2 * k1 * h)\n", - " n2 = 0.5 + k2 * h / np.sinh(2 * k2 * h)\n", - " n3 = 0.5 + k3 * h / np.sinh(2 * k3 * h)\n", - " n4 = 0.5 + k4 * h / np.sinh(2 * k4 * h)\n", - "\n", - " cg1 = c1 * n1\n", - " cg2 = c2 * n2\n", - " cg3 = c3 * n3\n", - " cg4 = c4 * n4\n", - "\n", - " Q3_answers = [round(cg1, 2), round(cg2, 2), round(cg3, 2), round(cg4, 2)]\n", - "\n", - " # store the questions in a list\n", - " Questions = [Q1, Q2, Q3]\n", - " Unit_question = [Q1_unit, Q2_unit, Q3_unit]\n", - " answer_question = [Q1_answers, Q2_answers, Q3_answers]\n", - " label_question = [Q1_labels, Q2_labels, Q3_labels]\n", - "\n", - " # no feedback provided\n", - " FB_G, FB_W = '', ''\n", - "\n", - " def calc_c(T,h):\n", - " L = wave_length(T, h)\n", - " return 9.81*T/(2*np.pi)*np.tanh(2*np.pi*h/L)\n", - " \n", - " # the answers for the graph\n", - " omega_serie = np.linspace(w4, w3*2, 100)\n", - " T_serie = [2 * np.pi/w for w in omega_serie]\n", - " L_serie = [wave_length(T, h) for T in T_serie]\n", - " k_serie = [2*np.pi/L for L in L_serie]\n", - " #c_serie = [L/T for L,T in zip(L_serie, T_serie)]\n", - " c_serie = [calc_c(T,h) for T in T_serie]\n", - " n_serie = [0.5 + k*h/np.sinh(2*k*h) for k in k_serie]\n", - " cg_serie = [c*n for c,n in zip(c_serie, n_serie)]\n", - "\n", - " #set plot settings and make plots \n", - " titles = ['Wave length', 'Celerity', 'n']\n", - " figures = []\n", - " plots = []\n", - " panes = []\n", - " for i, (title) in enumerate(titles):\n", - " fig = Figure((5,2.5))\n", - " ax = fig.subplots()\n", - " ax.set_axis_off()\n", - " pane = pn.pane.Matplotlib(fig, dpi=96)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(bottom=0.25) # Add some extra space for the axis at the bottom\n", - " fig.subplots_adjust(left=0.2) # Add some space for the labels\n", - " figures.append(fig)\n", - " plots.append(ax)\n", - " panes.append(pane)\n", - " \n", - " # fill an empty list with widgets\n", - " all_question_widgets = []\n", - " all_answers = []\n", - " attempts = [] # widgets only used for counting how many times the submit button is pressed\n", - " id = 0 \n", - " for i, (question, unit, answers, label) in enumerate(zip(Questions, Unit_question, answer_question, label_question)):\n", - " id += 1\n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - "\n", - " Rows_answer = []\n", - " num_widgets = []\n", - " feedback_widgets = []\n", - " for number ,answer in enumerate(answers):\n", - " number_widget = pn.widgets.StaticText(value=str(number+1) + str(')'), width = 10)\n", - " unit_widget = pn.widgets.StaticText(value=unit, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=250)\n", - "\n", - " num_widgets.append(num_widget)\n", - " feedback_widgets.append(feedback_widget)\n", - " \n", - " Hbox = pn.Row(number_widget, num_widget, unit_widget, feedback_widget)\n", - " Rows_answer.append(Hbox)\n", - " all_answers.append(num_widgets)\n", - "\n", - " # Add a submit button with widget that returns the final score\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " submit_text_widget = pn.widgets.StaticText(value='Final score:', width = 70)\n", - " final_score_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=130)\n", - " row_submit = pn.Row(submit_button,submit_text_widget,final_score_widget)\n", - "\n", - " # The values for the submit button are determined at the moment these are created.\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - " attempts.append(attempt)\n", - "\n", - " # store the local parameters to be used inside the function\n", - " FV = locals()\n", - " submit_button.on_click(check_answers_W2_Q6(id, answers, unit, FB_G, FB_W, num_widgets, feedback_widgets, attempt, final_score_widget, plots, panes, figures, FV))\n", - "\n", - " # Structure the widgets\n", - " Vbox_answer = pn.Column(*Rows_answer, row_submit)\n", - " Hbox_plot = pn.Row(Vbox_answer, panes[i])\n", - " question_widget = pn.Column(question_widget, Hbox_plot)\n", - " all_question_widgets.append(question_widget)\n", - "\n", - " text_widget = pn.widgets.StaticText(value=text_general)\n", - " quiz_widget = pn.Column(text_widget, *all_question_widgets)\n", - "\n", - " return quiz_widget\n", - " \n", - "#W2_Q6()" - ] - }, - { - "cell_type": "markdown", - "id": "93276043-5205-4cdf-a498-e7ce5c621f44", - "metadata": {}, - "source": [ - "### Q7" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0954b4ff-2c03-4e30-a1d9-418687b499fd", - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets import interact, HBox, VBox" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9f72e46f-d0da-4906-ab43-e2c114a5750b", - "metadata": {}, - "outputs": [], - "source": [ - "def dispersion(k, h):\n", - " return (9.81 * k * np.tanh(k * h)) ** 0.5\n", - "\n", - "def W2_Q7_graph(T1, T2, T3, slope_in, d0):\n", - " %matplotlib inline\n", - " # bed profile\n", - " \n", - " slope = 1.0 / slope_in # bed slope [-]\n", - " d0 # offshore water depth [m]\n", - " x_max = round((d0+2)/slope)\n", - " x = np.arange(0, x_max + 1, 1) # cross-shore coordinate [m]\n", - " zbed = -(d0 - slope * x) # bed elevation [m]\n", - " h = -zbed # still water depth [m]\n", - " h[h < 0] = 0 # no negative depths\n", - "\n", - " # w is zero when h is 0, causing a divide by zero.\n", - " # shorten the lists if a water depth of 0 is reached.\n", - " x0_id = np.argwhere(h == 0)[0][0] # first location where water depth = 0\n", - " h_water = h[0:x0_id]\n", - " x_water = x[0:x0_id] \n", - "\n", - " # wave length through profile\n", - " L1 = [wave_length(T1, h) for h in h_water]\n", - " L2 = [wave_length(T2, h) for h in h_water]\n", - " L3 = [wave_length(T3, h) for h in h_water]\n", - " \n", - " # velocity profile\n", - " def calc_c(T,h):\n", - " L = wave_length(T, h)\n", - " return 9.81*T/(2*np.pi)*np.tanh(2*np.pi*h/L)\n", - " \n", - " c1 = [calc_c(T1,h) for h in h_water]\n", - " c2 = [calc_c(T2,h) for h in h_water]\n", - " c3 = [calc_c(T3,h) for h in h_water]\n", - "\n", - " fig, axs = plt.subplots(nrows = 3, ncols = 1, figsize = (9,6), sharex=True, sharey = False)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.1)\n", - "\n", - " # bathymetry\n", - " axs[0].plot(x, zbed, label=\"Bed (1:\" + str(round(slope_in,2)) + ')', color=\"k\")\n", - " axs[0].plot([0, x[x0_id]], [0, 0], color=\"gray\", label=\"Still water surface\")\n", - " axs[0].set_ylabel(\"y [m]\")\n", - " axs[0].legend(loc=\"lower right\")\n", - "\n", - " # wave length\n", - " axs[1].plot(x_water, L1, label=\"wave 1\")\n", - " axs[1].plot(x_water, L2, label=\"wave 2\")\n", - " axs[1].plot(x_water, L3, label=\"wave 3\")\n", - " axs[1].set_ylim(0, np.max(([L1], [L2], [L3]))*1.1)\n", - " axs[1].set_ylabel(\"Wavelength (L) [m]\")\n", - " axs[1].legend(loc=\"upper right\")\n", - "\n", - " # wave celerity\n", - " axs[2].plot(x_water, c1, label=\"wave 1\")\n", - " axs[2].plot(x_water, c2, label=\"wave 2\")\n", - " axs[2].plot(x_water, c3, label=\"wave 3\")\n", - " axs[2].set_xlim(0, np.max(x))\n", - " axs[2].set_ylim(0, np.max(([c1], [c2], [c3]))*1.1)\n", - " axs[2].set_ylabel(\"Celerity (c) [m/s]\")\n", - " axs[2].legend(loc=\"upper right\")\n", - " axs[2].set_xlabel('cross-shore location (x) [m]')\n", - " #axs[2].set_xticks('cross-shore location (x) [m]')\n", - "\n", - " # remove the lines related to the x-ticks\n", - " axs[0].xaxis.set_visible(False)\n", - " axs[1].xaxis.set_visible(False)\n", - "\n", - " # set title\n", - " axs[0].set_title(\"Wave characteristics in cross-shore direction\")\n", - "\n", - " # get values of xticks, change them to get x=0 at water boundary and positive direction offshore\n", - " xticks = axs[2].get_xticks()\n", - " new_ticks = np.ones(len(xticks)) * x_water[-1] - xticks + 1\n", - " axs[2].set_xticks(xticks, new_ticks)\n", - "\n", - " # Plot dots at the transition with intermediate water depth\n", - " #for L,c in zip((L1,L2,L3), (c1,c2,c3)):\n", - " # h_L = h_water/L\n", - " # if h_L[0] > 0.5:\n", - " # id_deep = np.argwhere(h_L <= 0.5)[0][0] # first location where water depth = 0\n", - " # axs[1].plot(x_water[id_deep], L[id_deep], 'ro', label=\"wave 3\")\n", - " # \n", - " # if h_L[0] > 0.05:\n", - " # id_shl = np.argwhere(h_L < 0.05)[0][0] # first location where water depth = 0\n", - " # axs[1].plot(x_water[id_shl], L[id_shl], 'ro', label=\"wave 3\")\n", - "\n", - "def W2_Q7_set_graph(FV, FV2):\n", - "\n", - " def button_callback(b):\n", - " if FV2['id'] == 1:\n", - " FV['T1'].value = 10\n", - " FV['T2'].value = 15\n", - " FV['T3'].value = 20\n", - " FV['slope'].value = 30\n", - " FV['d0'].value = 250\n", - " \n", - " if FV2['id'] == 2:\n", - " FV['T1'].value = 4\n", - " FV['T2'].value = 6\n", - " FV['T3'].value = 8\n", - " FV['slope'].value = 30\n", - " FV['d0'].value = 50\n", - "\n", - " if FV2['id'] == 3:\n", - " FV['T1'].value = 4\n", - " FV['T2'].value = 6\n", - " FV['T3'].value = 8\n", - " FV['slope'].value = 30\n", - " FV['d0'].value = 20\n", - " \n", - " return button_callback \n", - "\n", - "def W2_Q7_check_answers(FV, FV2):\n", - "\n", - " def button_callback(b):\n", - " all_answers = FV2['all_answers']\n", - " response = FV2['checkbutton_group'].value\n", - " \n", - " if response == FV2['answer']:\n", - " FV2['feedback_widget'].value = FV2['FG']\n", - " else:\n", - " FV2['feedback_widget'].value = FV2['FW']\n", - " \n", - " return button_callback \n", - " \n", - "\n", - "def W2_Q7_questions(FV):\n", - " all_answers = ['wave 1', 'wave 2', 'wave 3']\n", - " \n", - " Q1 = 'Which waves are in deep water at x = 6000?'\n", - " Ans1 = [all_answers[0], all_answers[1]]\n", - " FG_1 = 'Indeed, these two waves are in deep water. Wave 3, with a period of 20 seconds, is influenced by bottom friction.'\n", - " FW_1= 'Try another reasoning, what are the spatial changes for waves in deep water?'\n", - "\n", - " Q2 = 'Which of the waves are in intermediate water at x = 500?'\n", - " Ans2 = [all_answers[1], all_answers[2]]\n", - " FG_2 = 'Good Job, this wave is the only wave that in intermediate water depth'\n", - " FW_2 = 'Almost, which waves are affected by the changing water depth?'\n", - "\n", - " Q3= 'Which of the waves are in shallow water at x = 200?'\n", - " Ans3 = []\n", - " FG_3 = 'Indeed, all of the waves are in intermediate water, the waves are in shallow water around x = 50 m, where the celerity is only related to the water depth.'\n", - " FW_3= 'Which parameters are relevant for waves in shallow water?'\n", - "\n", - " number_of_questions = 3\n", - "\n", - " all_question_widgets =[]\n", - " for id in np.arange(1, number_of_questions+1,1):\n", - " question = eval('Q'+ str(id))\n", - " answer = eval('Ans' + str(id))\n", - " question_widget = pn.widgets.StaticText(value=question)\n", - " set_button = pn.widgets.Button(name=\"Set graph\")\n", - " checkbutton_group = pn.widgets.CheckButtonGroup(name='Check Button Group', value=[], options=all_answers)\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\")\n", - " FG = eval('FG_'+ str(id))\n", - " FW = eval('FW_'+ str(id))\n", - " \n", - " FV2 = {key: value for key, value in locals().items()}\n", - " #FV2 = {key: value for key, value in {**globals(), **locals()}.items()}\n", - " set_button.on_click(W2_Q7_set_graph(FV, FV2))\n", - " submit_button.on_click(W2_Q7_check_answers(FV, FV2))\n", - "\n", - " question_row = pn.Row(set_button, checkbutton_group, submit_button)\n", - " question_widget = pn.Column(question_widget, question_row, feedback_widget)\n", - " all_question_widgets.append(question_widget)\n", - " \n", - " return all_question_widgets\n", - "\n", - "def W2_Q7():\n", - " # Create interactive widgets, which require IPY Widgets, widgets from panel do not work\n", - " #L1 = pn.widgets.FloatSlider(name='Float Slider', start=0, end=3.141, step=0.01, value=1.57)\n", - " T1 = ipw.FloatSlider(value=4, min=0.01, max=50, step=0.01, description=\"T1 [s]\")\n", - " T2 = ipw.FloatSlider(value=7, min=0.01, max=50, step=0.01, description=\"T2 [s]\")\n", - " T3 = ipw.FloatSlider(value=25, min=0.01, max=50, step=0.01, description=\"T3 [s]\")\n", - "\n", - " slope = ipw.FloatSlider(value=30, min=0.1, max=50, step=0.1, description=\"slope 1:...\")\n", - " d0 = ipw.FloatSlider(value=50, min=0.1, max=500, step=0.1, description=\"depth [m]\")\n", - "\n", - " # Setup widget layout (User Interface) for the graph input\n", - " vbox1 = ipw.VBox([ipw.Label('Wave components', layout=ipw.Layout(align_self='center')),T1, T2, T3])\n", - " vbox2 = ipw.VBox([ipw.Label('General', layout=ipw.Layout(align_self='center')),slope,d0])\n", - " UI = ipw.HBox([vbox1, vbox2])\n", - " \n", - " # Use the interactive function to update the plot\n", - " graph = ipw.interactive_output(W2_Q7_graph, {'T1': T1,'T2':T2, 'T3': T3, 'slope_in': slope, 'd0': d0})\n", - "\n", - " FV = {key: value for key, value in locals().items()}\n", - " questions = W2_Q7_questions(FV)\n", - "\n", - " intro = 'In the following question we are going to assess the depth the waves experience by analyzing the graph above. You can answer the questions by first setting the graph, through the button. When the graph is fully loaded, you can select the appropriate waves and check the answer. Good luck!'\n", - " intro_widget = pn.widgets.StaticText(value=intro)\n", - " \n", - " display(UI, graph,intro_widget, *questions)\n", - "\n", - "#W2_Q7()" - ] - }, - { - "cell_type": "markdown", - "id": "137e2dd8-7923-44e1-b98d-72f9ac6ad9b8", - "metadata": {}, - "source": [ - "### Q8" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f2b06198-2790-4f1a-8b0a-17cfb43e8bb3", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q8():\n", - "\n", - " # define widgets\n", - " a1 = ipw.FloatText(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - " a2 = ipw.FloatText(value=0.5, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - " T1 = ipw.FloatText(value=8, min=0.01, max=250000, step=0.01, description='T [s]')\n", - " T2 = ipw.FloatText(value=4, min=0.01, max=250000, step=0.01, description='T [s]')\n", - "\n", - " phi_1 = ipw.FloatText(value=0, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " phi_2 = ipw.FloatText(value=0.25, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " \n", - " \n", - " depth = ipw.FloatText(value=7.5, min=1, max=250, step=0.01, description='h [m]')\n", - " xp = ipw.FloatText(value=0, min=0, step=0.1, description='x [m]')\n", - " tp = ipw.FloatText(value=0, min=1, step=0.1, description='t [s]')\n", - "\n", - " # Setup widget layout (User Interface) and display\n", - " vbox1 = ipw.VBox([ipw.Label('Wave 1', layout=ipw.Layout(align_self='center')),a1, T1, phi_1])\n", - " vbox2 = ipw.VBox([ipw.Label('Wave 2', layout=ipw.Layout(align_self='center')),a2, T2, phi_2])\n", - " vbox4 = ipw.VBox([ipw.Label('General', layout=ipw.Layout(align_self='center')),depth, xp, tp])\n", - " \n", - " ui = ipw.HBox([vbox1, vbox2, vbox4])\n", - "\n", - " def calc_eta(a1,T1,phi_1, a2,T2, phi_2, xp, tp, depth):\n", - " n_waves = 3\n", - " L1 = wave_length(T1,depth)\n", - " L2 = wave_length(T2,depth)\n", - " \n", - " L_group, T_group, c_g = group_stats(\n", - " k1=2 * np.pi / L1,\n", - " k2=2 * np.pi / L2,\n", - " w1=2 * np.pi / T1,\n", - " w2=2 * np.pi / T2,\n", - " )\n", - " \n", - " T = np.min([T1,T2])\n", - " L = np.max([L1, L2])\n", - " t = np.arange(0,n_waves*T_group+T/30,T/30)\n", - " x = np.arange(0,n_waves*L_group+L/30,L/30)\n", - " \n", - " fig, axs = plt.subplots(nrows = 3,ncols = 2,figsize = (9,5), sharex=False, sharey = False)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.06)\n", - " \n", - " # time based\n", - " ax1 = axs[0,0]\n", - " ax2 = axs[1,0]\n", - " ax3 = axs[2,0]\n", - " #space based\n", - " ax4 = axs[0,1]\n", - " ax5 = axs[1,1]\n", - " ax6 = axs[2,1]\n", - "\n", - " # calculate surface, including phase change\n", - " eta1_T = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp-phi_1*(2*np.pi))\n", - " eta2_T = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp-phi_2*(2*np.pi))\n", - " eta_T = eta1_T + eta2_T\n", - "\n", - " eta1_x= a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x-phi_1*(2*np.pi))\n", - " eta2_x= a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x-phi_2*(2*np.pi))\n", - " eta_x = eta1_x + eta2_x\n", - "\n", - " # calculate surface, without phase change\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - "\n", - " eta1_x_basic = a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x)\n", - " eta2_x_basic = a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x)\n", - " eta_x_basic = eta1_x_basic + eta2_x_basic\n", - "\n", - " # plot surface excluding phase change\n", - " ax1.plot(t,eta1_T_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax2.plot(t,eta2_T_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax3.plot(t, eta_T_basic, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " ax4.plot(x, eta1_x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax5.plot(x, eta2_x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax6.plot(x, eta_x_basic, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " \n", - " # plot surface including phase change\n", - " ax1.plot(t,eta1_T, label = '$\\eta_1$')\n", - " ax2.plot(t,eta2_T, label = '$\\eta_2$' )\n", - " ax3.plot(t,eta_T, label = '$\\eta_{1+2}$')\n", - " \n", - " ax4.plot(x,eta1_x, label = '$\\eta_1$')\n", - " ax5.plot(x,eta2_x, label = '$\\eta_2$')\n", - " ax6.plot(x,eta_x, label = '$\\eta_{1+2}$')\n", - "\n", - " # set vertical axis the same\n", - " amp = (a1+a2)*1.1\n", - " ax1.set_ylim(-amp,amp)\n", - " ax2.set_ylim(-amp,amp)\n", - " ax3.set_ylim(-amp,amp)\n", - " ax4.set_ylim(-amp,amp)\n", - " ax5.set_ylim(-amp,amp)\n", - " ax6.set_ylim(-amp,amp)\n", - "\n", - " # set horizontal axis\n", - " ax1.set_xlim(0,n_waves*T_group)\n", - " ax2.set_xlim(0,n_waves*T_group)\n", - " ax3.set_xlim(0,n_waves*T_group)\n", - " ax4.set_xlim(0,n_waves*L_group)\n", - " ax5.set_xlim(0,n_waves*L_group)\n", - " ax6.set_xlim(0,n_waves*L_group)\n", - "\n", - " # set labels\n", - " ax1.set_ylabel('$\\eta_1$ [m]')\n", - " ax2.set_ylabel('$\\eta_2$ [m]')\n", - " ax3.set_ylabel('$\\eta_{1+2}$ [m]')\n", - " \n", - " ax3.set_xlabel('t/T_{group}')\n", - " ax6.set_xlabel('x/L_{group}')\n", - "\n", - " # remove the lines related to the x-ticks and y-ticks\n", - " ax1.xaxis.set_visible(False)\n", - " ax2.xaxis.set_visible(False)\n", - " ax4.yaxis.set_visible(False)\n", - " ax5.yaxis.set_visible(False)\n", - " ax6.yaxis.set_visible(False)\n", - " \n", - " # set scaled ticks\n", - " if n_waves >= 1:\n", - " ax3.set_xticks(np.arange(0,n_waves//1 +1, 1)*T_group)\n", - " ax3.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - " ax6.set_xticks(np.arange(0,n_waves//1 +1, 1)*L_group)\n", - " ax6.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - "\n", - " else: # 3 times when the scale is smaller than 1\n", - " ax3.set_xticks([0,0.5*n_waves*T_group, n_waves*T_group])\n", - " ax3.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " ax6.set_xticks([0,0.5*n_waves*L_group, n_waves*L_group])\n", - " ax6.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " \n", - " # remove x and y ticks\n", - " ax4.set_xticklabels([], fontsize=0)\n", - " ax5.set_xticklabels([], fontsize=0)\n", - " \n", - " ax4.set_yticklabels([], fontsize=0)\n", - " ax5.set_yticklabels([], fontsize=0)\n", - " ax6.set_yticklabels([], fontsize=0)\n", - " \n", - " # set title\n", - " ax1.set_title('Time-based (x =' + str(xp) + ' m)')\n", - " ax4.set_title('Space-based (t =' + str(tp) + ' s)')\n", - "\n", - " # plot legends \n", - " legend1 = ax4.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend2 = ax5.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend3 = ax6.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - "\n", - " #ui = ipw.HBox([vbox1, vbox2, vbox3, vbox4])\n", - "\n", - " #update graph\n", - " out = ipw.interactive_output(calc_eta, {'a1': a1,'T1':T1, 'phi_1': phi_1, 'a2': a2,'T2':T2, 'phi_2': phi_2, 'xp':xp, 'tp': tp, 'depth':depth})\n", - "\n", - " display(ui,out)\n", - "\n", - " \n", - "#W2_Q8()" - ] - }, - { - "cell_type": "markdown", - "id": "853dfdbe-539d-4f70-a548-e6d93edabbbe", - "metadata": {}, - "source": [ - "## Wave groups" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1f854d2f-4c12-4ff4-ae37-ac2882bd65aa", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_wave_groups():\n", - " from scipy.signal import hilbert\n", - " # define widgets\n", - " a1 = ipw.FloatText(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - " a2 = ipw.FloatText(value=1.5, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - " T1 = ipw.FloatText(value=7, min=0.01, max=250000, step=0.01, description='T [s]')\n", - " T2 = ipw.FloatText(value=6.2, min=0.01, max=250000, step=0.01, description='T [s]')\n", - "\n", - " phi_1 = ipw.FloatText(value=0, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " phi_2 = ipw.FloatText(value=0, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " \n", - " n_waves = ipw.FloatText(value=3, min=0.1, max=10, step=0.1, description='n_{waves}')\n", - " \n", - " depth = ipw.FloatText(value=20, min=1, max=250, step=0.01, description='h [m]')\n", - " xp = ipw.FloatText(value=0, min=0, step=0.1, description='x [m]')\n", - " tp = ipw.FloatText(value=0, min=1, step=0.1, description='t [s]')\n", - " \n", - " L1 = ipw.FloatText(value=wave_length(T1.value,depth.value), description='L [m]', disabled=True)\n", - " L2 = ipw.FloatText(value=wave_length(T2.value,depth.value), description='L [m]', disabled=True)\n", - " Lgroup = ipw.FloatText(value=group_stats(k1=2 * np.pi / L1.value, k2=2 * np.pi / L2.value, w1=2 * np.pi / T1.value, w2=2 * np.pi / T2.value)[0], description='L_{group} [m]', disabled=True)\n", - "\n", - " c1 = ipw.FloatText(value=L1.value/T1.value, description='c [m/s]', disabled=True)\n", - " c2 = ipw.FloatText(value=L2.value/T2.value, description='c [m/s]', disabled=True)\n", - "\n", - " h_L1 = ipw.FloatText(value=depth.value/L1.value, description='h/L [m]', disabled=True)\n", - " h_L2 = ipw.FloatText(value=depth.value/L2.value, description='h/L [m]', disabled=True)\n", - " \n", - " # Setup widget layout (User Interface)\n", - " vbox1 = ipw.VBox([ipw.Label('Wave 1', layout=ipw.Layout(align_self='center')),a1, T1, c1, h_L1])\n", - " vbox2 = ipw.VBox([ipw.Label('Wave 2', layout=ipw.Layout(align_self='center')),a2, T2, c2, h_L2])\n", - " vbox3 = ipw.VBox([ipw.Label('Wave group', layout=ipw.Layout(align_self='center')), n_waves, Lgroup])\n", - " vbox4 = ipw.VBox([ipw.Label('General', layout=ipw.Layout(align_self='center')),depth, xp, tp])\n", - " \n", - " ui = ipw.HBox([vbox1, vbox2, vbox4])\n", - "\n", - " def calc_eta(a1,T1,phi_1, a2,T2, phi_2, L1,L2, n_waves,xp, tp, depth):\n", - " L1 = wave_length(T1,depth)\n", - " L2 = wave_length(T2,depth)\n", - " \n", - " L_group, T_group, c_g = group_stats(\n", - " k1=2 * np.pi / L1,\n", - " k2=2 * np.pi / L2,\n", - " w1=2 * np.pi / T1,\n", - " w2=2 * np.pi / T2,\n", - " )\n", - " \n", - " T = np.min([T1,T2])\n", - " L = np.max([L1, L2])\n", - " #requires additional x and t values to get a correct Hilbert transformation at the graph boundaries\n", - " #t = np.arange(0,n_waves*T_group+T/30,T/30)\n", - " #x = np.arange(0,n_waves*L_group+L/30,L/30)\n", - " t = np.arange(-0.5*n_waves*T_group,(n_waves+0.5)*T_group,T/30)\n", - " x = np.arange(-0.5*n_waves*L_group,(n_waves+0.5)*L_group,L/30)\n", - " \n", - " fig, axs = plt.subplots(nrows=3,ncols=2,figsize=(9,5), sharex=False, sharey = False)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.06)\n", - " \n", - " # time based\n", - " ax1 = axs[0,0]\n", - " ax2 = axs[1,0]\n", - " ax3 = axs[2,0]\n", - " #space based\n", - " ax4 = axs[0,1]\n", - " ax5 = axs[1,1]\n", - " ax6 = axs[2,1]\n", - "\n", - " # calculate surface, including phase change\n", - " eta1_T = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp-phi_1*(2*np.pi))\n", - " eta2_T = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp-phi_2*(2*np.pi))\n", - " eta_T = eta1_T + eta2_T\n", - "\n", - " eta1_x= a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x-phi_1*(2*np.pi))\n", - " eta2_x= a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x-phi_2*(2*np.pi))\n", - " eta_x = eta1_x + eta2_x\n", - "\n", - " # calculate surface, without phase change\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - "\n", - " eta1_x_basic = a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x)\n", - " eta2_x_basic = a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x)\n", - " eta_x_basic = eta1_x_basic + eta2_x_basic\n", - "\n", - " # calculate hilbert\n", - " eta_T_envelope = np.abs(hilbert(eta_T_basic))\n", - " eta_x_envelope = np.abs(hilbert(eta_x_basic))\n", - "\n", - " # carier wave\n", - " k_bar = (2*np.pi/L1 + 2*np.pi/L2)\n", - " w_bar = (2*np.pi/T1 + 2*np.pi/T2)/2\n", - " car_wave_t = (a1 + a2)*np.sin(w_bar*t - k_bar*xp)\n", - " car_wave_x = (a1 + a2)*np.sin(w_bar*tp - k_bar*x)\n", - "\n", - " # variable amplitude\n", - " Delta_k = 2*np.pi/L1 - 2*np.pi/L2# Delta k\n", - " Delta_w = 2*np.pi/T1 - 2*np.pi/T2\n", - " var_amp_t = (a1 + a2)*np.cos(Delta_w/2*t-Delta_k/2*xp)\n", - " var_amp_x = (a1 + a2)*np.cos(Delta_w/2*tp-Delta_k/2*x)\n", - "\n", - " # plot surface excluding phase change\n", - " ax3.plot(t, eta_T_basic, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " \n", - " # plot surface including phase change\n", - " ax1.plot(t,eta1_T, label = '$\\eta_1$')\n", - " ax2.plot(t,eta2_T, label = '$\\eta_2$' )\n", - " ax3.plot(t,eta_T, label = '$\\eta_{1+2}$')\n", - " ax3.plot(t,eta_T_envelope, label = 'Envelope')\n", - " #ax3.plot(t,np.abs(var_amp_t), label = 'abs: car wave')\n", - " \n", - " ax4.plot(x,eta1_x, label = '$\\eta_1$')\n", - " ax5.plot(x,eta2_x, label = '$\\eta_2$')\n", - " ax6.plot(x,eta_x, label = '$\\eta_{1+2}$')\n", - " ax6.plot(x,eta_x_envelope, label = 'Envelope')\n", - " #ax6.plot(x,np.abs(var_amp_x), label = 'abs: car wave')\n", - " \n", - "\n", - " # set vertical axis the same\n", - " amp = (a1+a2)*1.1\n", - " ax1.set_ylim(-amp,amp)\n", - " ax2.set_ylim(-amp,amp)\n", - " ax3.set_ylim(-amp,amp)\n", - " ax4.set_ylim(-amp,amp)\n", - " ax5.set_ylim(-amp,amp)\n", - " ax6.set_ylim(-amp,amp)\n", - "\n", - " # set horizontal axis\n", - " ax1.set_xlim(0,n_waves*T_group)\n", - " ax2.set_xlim(0,n_waves*T_group)\n", - " ax3.set_xlim(0,n_waves*T_group)\n", - " ax4.set_xlim(0,n_waves*L_group)\n", - " ax5.set_xlim(0,n_waves*L_group)\n", - " ax6.set_xlim(0,n_waves*L_group)\n", - "\n", - " # set labels\n", - " ax1.set_ylabel('$\\eta_1$ [m]')\n", - " ax2.set_ylabel('$\\eta_2$ [m]')\n", - " ax3.set_ylabel('$\\eta_{1+2}$ [m]')\n", - " \n", - " ax3.set_xlabel('t/T_{group}')\n", - " ax6.set_xlabel('x/L_{group}')\n", - "\n", - " # remove the lines related to the x-ticks and y-ticks\n", - " ax1.xaxis.set_visible(False)\n", - " ax2.xaxis.set_visible(False)\n", - " ax4.yaxis.set_visible(False)\n", - " ax5.yaxis.set_visible(False)\n", - " ax6.yaxis.set_visible(False)\n", - " \n", - " # set scaled ticks\n", - " if n_waves >= 1:\n", - " ax3.set_xticks(np.arange(0,n_waves//1 +1, 1)*T_group)\n", - " ax3.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - " ax6.set_xticks(np.arange(0,n_waves//1 +1, 1)*L_group)\n", - " ax6.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - "\n", - " else: # 3 times when the scale is smaller than 1\n", - " ax3.set_xticks([0,0.5*n_waves*T_group, n_waves*T_group])\n", - " ax3.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " ax6.set_xticks([0,0.5*n_waves*L_group, n_waves*L_group])\n", - " ax6.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " \n", - " # remove x and y ticks\n", - " ax4.set_xticklabels([], fontsize=0)\n", - " ax5.set_xticklabels([], fontsize=0)\n", - " \n", - " ax4.set_yticklabels([], fontsize=0)\n", - " ax5.set_yticklabels([], fontsize=0)\n", - " ax6.set_yticklabels([], fontsize=0)\n", - " \n", - " # set title\n", - " ax1.set_title('Time-based (x =' + str(xp) + ' m)')\n", - " ax4.set_title('Space-based (t =' + str(tp) + ' s)')\n", - "\n", - " # plot legends \n", - " legend1 = ax4.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend2 = ax5.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend3 = ax6.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - "\n", - " #update graph\n", - " out = ipw.interactive_output(calc_eta, {'a1': a1,'T1': T1, 'phi_1': phi_1, 'a2': a2,'T2':T2, 'phi_2': phi_2, 'L1' : L1, 'L2': L2, 'n_waves': n_waves, 'xp':xp, 'tp': tp, 'depth':depth})\n", - "\n", - " display(ui,out)\n", - "\n", - "#W2_wave_groups()" - ] - }, - { - "cell_type": "markdown", - "id": "8119418c-930a-4270-a110-1c6c04322e75", - "metadata": {}, - "source": [ - "## Q9 Coding" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "45a39f55-2955-42d9-961f-b9222e9d4683", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q9():\n", - " # the question-related parameters\n", - " T1 = round(uniform(5,7), 1)\n", - " T2 = round(uniform(7,10), 1)\n", - " h = 20\n", - "\n", - " # The question that is asked\n", - " question = 'Can you complete the code below when the periods of wave 1 and 2 are ' + str(T1) + ' and ' + str(T2) + ' seconds and the water depth is ' + str(h) + ' m? You can check your computation after running the code and loading the values. Please use the iterative approach to calculate the wave length.'\n", - "\n", - " # The calculation\n", - " L1 = wave_length(T1, h)\n", - " L2 = wave_length(T2, h)\n", - " \n", - " k1 = 2*np.pi/L1\n", - " k2 = 2*np.pi/L2\n", - " w1 = 2*np.pi/T1\n", - " w2 = 2*np.pi/T2\n", - "\n", - " Delta_k = np.abs(k2-k1)\n", - " Delta_w = np.abs(w2-w1)\n", - " k_average = np.average([k1,k2])\n", - " w_average = np.average([w1,w2])\n", - "\n", - " c1 = L1/T1\n", - " c2 = L2/T2\n", - "\n", - " c_average = np.average([c1,c2])\n", - " L_group, T_group, c_group = group_stats(k1, k2, w1, w2) \n", - " \n", - " # Required widgets for functionality, does not have to be changed\n", - " attempt_A = pn.widgets.FloatInput(value=0)\n", - " attempt_B = pn.widgets.FloatInput(value=0)\n", - " \n", - " question_widget = pn.widgets.StaticText(name='', value= question)\n", - " display(question_widget)\n", - "\n", - " # define a new global variable, so return (and related print) is prevented,\n", - " # + a required parameter to count the attempt, dont change the name.\n", - " global W2_Q9_param \n", - " # store the question-related parameters and \n", - " W2_Q9_param = T1, T2, h, Delta_k, Delta_w, k_average, w_average, c_average, L_group, T_group, c_group, attempt_A, attempt_B\n", - "\n", - "def Check_W2_Q9A():\n", - " T1, T2, h, Delta_k, Delta_w, k_average, w_average, c_average, L_group, T_group, c_group, attempt_A, attempt_B = W2_Q9_param\n", - " \n", - " # define the parameter names that have to be checked\n", - " check_parameters = ['Delta_k', 'Delta_w', 'k_average', 'w_average']\n", - "\n", - " # define the names of the parameters as they are displayed\n", - " name_parameters = ['Delta k', 'Delta w', 'k average', 'w average']\n", - " \n", - " # build the question\n", - " FV = classify_variables(locals())\n", - " check_code_values(FV)\n", - "\n", - "def Check_W2_Q9B():\n", - " T1, T2, h, Delta_k, Delta_w, k_average, w_average, c_average, L_group, T_group, c_group, attempt_A, attempt_B = W2_Q9_param\n", - " \n", - " # define the parameter names that have to be checked\n", - " check_parameters = ['c_average', 'c_group', 'L_group', 'T_group']\n", - "\n", - " # define the names of the parameters as they are displayed\n", - " name_parameters = ['cg', 'c group', 'L group', 'T group']\n", - " \n", - " # build the question\n", - " FV = classify_variables(locals())\n", - " check_code_values(FV)" - ] - }, - { - "cell_type": "markdown", - "id": "a809a317-7662-4599-b131-9c2db167433e", - "metadata": {}, - "source": [ - "## Q10" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c6fc094f-fbbb-4ffd-8596-80056087a711", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q10():\n", - "\n", - " question_start = 'Can you use formula 2.4.3 to complete the functions below to plot the water elevation? '\n", - " \n", - " try:\n", - " T1, T2, h, Delta_k, Delta_w, k_average, w_average, c_average, L_group, T_group, c_group, attempt_A, attempt_B = W2_Q9_param\n", - " L1 = wave_length(T1, h)\n", - " L2 = wave_length(T2, h)\n", - " question_data = 'You can use the conditions from above, with wave periods T1 and T2 of ' + str(T1) + ' and ' + str(T2) + ' seconds, and a water depth of ' + str(h) + ' meter. '\n", - "\n", - " except:\n", - " # the question-related parameters\n", - " T1 = round(uniform(5,7), 1)\n", - " T2 = round(uniform(7,10), 1) \n", - " h = 20\n", - "\n", - " L1 = wave_length(T1, h)\n", - " L2 = wave_length(T2, h)\n", - " L_group, T_group, c_group = group_stats(k1=2*np.pi/L1, k2=2*np.pi/L2, w1 = 2*np.pi/L1, w2 = 2*np.pi/L2)\n", - "\n", - " question_data = 'The wave periods T1 and T2 are ' + str(T1) + ' and ' + str(T2) + ' seconds, and the water depth is ' + str(h) + ' meter. '\n", - "\n", - " # the amplitudes, where the amplitude of wave 2 is a factor 15% to 100% larger\n", - " a1 = round(uniform(0.5,1.5), 1)\n", - " f_a2 = uniform(1.15,2)\n", - " a2 = round(a1 * f_a2, 1)\n", - " question_data2 = 'The amplitudes of the waves are ' + str(a1) + ' and ' + str(a2) + ' meter, respectively. ' \n", - "\n", - " # the maximum coordinate, 3 times the wave group length, rounded to 25 meters\n", - " x_max = 3*L_group//25*25+25\n", - " \n", - " # point of interest, near the end of the left wave group, rounded down with a stepsize of 25.\n", - " xp = L_group//25*25\n", - "\n", - " # maximum time, 3 times the wave group period\n", - " t_max = 3*T_group//10*10+10\n", - "\n", - " # time point of interest\n", - " tp = T_group//10*10\n", - "\n", - " question_end = ' The answer you coded will be plotted when you run the cell and the function is valid.'\n", - " \n", - " question_boundaries = 'One function is used to plot time series at x = ' + str(xp) + ' m from t = 0 to t = ' + str(t_max) + ' seconds, and one to plot the wave conditions from x=0 to x=' + str(x_max) +' m at t=' + str(tp) + ' seconds.'\n", - " question = question_start + question_data + question_data2+ question_boundaries + question_end\n", - "\n", - " # The question that is asked\n", - " \n", - " \n", - " # make the attempt counter, one for each subquestion\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - "\n", - " # Required widgets for functionality, should not be changed\n", - " question_widget = pn.widgets.StaticText(name='', value= question)\n", - " display(question_widget)\n", - "\n", - " # define a new global variable, with a unique name (This is related to week 2, question 9)\n", - " global W2_Q10_param \n", - " # store the question-related parameters and the widget 'attempt' \n", - " W2_Q10_param = a1, a2, T1, T2, L1, L2, x_max, xp, t_max, tp, L_group, T_group\n", - "\n", - "# uncomment to use information provided by question 9.\n", - "#print('question 9')\n", - "#W2_Q9()\n", - "\n", - "#print('question 10')\n", - "#W2_Q10()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ff6eb95b-fde7-47f9-914e-9f27c929944b", - "metadata": {}, - "outputs": [], - "source": [ - "def Show_Q10A():\n", - " # define the name of the function that the students will make\n", - " function_name = \"plot_eta_t\"\n", - "\n", - " # define the name of the parameter plotted on the horizontal axis\n", - " parameter_x_axis = 't'\n", - "\n", - " # set the horizontal axis of the graph\n", - " a1, a2, T1, T2, L1, L2, x_max, xp, t_max, tp, L_group, T_group = W2_Q10_param\n", - " horizontal_axis = np.arange(0,t_max + T_group/30,T_group/100)\n", - "\n", - " # define the correct function and its values along the x-axis.\n", - " def correct_function(a1, a2, T1, T2, L1, L2, t, xp):\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - " return eta_T_basic\n", - "\n", - " # set the acceptable computational error (ratio)\n", - " f_margin = 0.001 # 0.001 = 0.01%\n", - "\n", - " fig = Figure((5,2.5))\n", - " check_code_function(fig, horizontal_axis, function_name, correct_function, parameter_x_axis, f_margin)" - ] - }, - { - "cell_type": "raw", - "id": "9e297716-eac0-4539-9bf8-525ff76b4c01", - "metadata": {}, - "source": [ - "print('for testing student answer')\n", - "a1, a2, T1, T2, L1, L2, x_max, xp, t_max, tp, L_group, T_group = W2_Q10_param\n", - "def plot_eta_t(a1,a2,T1,T2,L1,L2,t,xp):\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - " return eta_T_basic\n", - "\n", - "Show_Q10A()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7446e994-f319-4b79-8895-fc29e5a400b4", - "metadata": {}, - "outputs": [], - "source": [ - "def Show_Q10B():\n", - " # define the name of the function that the students will make\n", - " function_name = \"plot_eta_x\"\n", - "\n", - " # define the name of the parameter plotted on the horizontal axis\n", - " parameter_x_axis = 'x'\n", - "\n", - " # set the horizontal axis of the graph\n", - " a1, a2, T1, T2, L1, L2, x_max, xp, t_max, tp, L_group, T_group = W2_Q10_param\n", - " horizontal_axis = np.arange(0,x_max + L_group/30,L_group/60)\n", - "\n", - " # define the correct function and its values along the x-axis.\n", - " def correct_function(a1, a2, T1, T2, L1, L2, tp, x):\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - " return eta_T_basic\n", - " \n", - " # set the acceptable computational error (ratio)\n", - " f_margin = 0.001 # 0.001 = 0.01%\n", - "\n", - " fig = Figure((5,2.5))\n", - " check_code_function(fig, horizontal_axis, function_name, correct_function, parameter_x_axis, f_margin)" - ] - }, - { - "cell_type": "raw", - "id": "50045a30-ec57-40d8-aa11-88f8c58f2ba3", - "metadata": {}, - "source": [ - "print('for testing student answer')\n", - "a1, a2, T1, T2, L1, L2, x_max, xp, t_max, tp, L_group, T_group = W2_Q10_param\n", - "def plot_eta_x(a1,a2,T1,T2,L1,L2,tp,x):\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - " return eta_T_basic - 1\n", - "\n", - "Show_Q10B()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d56d1b87-82ca-4e0c-986b-86a5d5a44d09", - "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.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/Chris/Initialize/Week_5_Initialize.ipynb b/notebooks/Chris/Initialize/Week_5_Initialize.ipynb deleted file mode 100644 index 24bd2e3..0000000 --- a/notebooks/Chris/Initialize/Week_5_Initialize.ipynb +++ /dev/null @@ -1,803 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "3a1b3da5-c7f8-42af-9e9d-9c88aa67f433", - "metadata": {}, - "source": [ - "# Start" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "63ec775a-e0b0-4858-8db0-14ecec8714bd", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import ipywidgets as widgets\n", - "#from IPython.display import display, HTML\n", - "from ipywidgets import interact\n", - "import matplotlib.animation as animation\n", - "\n", - "from matplotlib.ticker import MultipleLocator\n", - "from matplotlib.animation import FuncAnimation\n", - "\n", - "from random import shuffle\n", - "from random import uniform\n", - "\n", - "import IPython\n", - "\n", - "print('Packages succesfully loaded')" - ] - }, - { - "cell_type": "markdown", - "id": "7d8bd007-c6b9-4634-962d-4f2a38919983", - "metadata": {}, - "source": [ - "# General functions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a9215fcc-d74d-4b64-ab7e-c2433d3ad9fb", - "metadata": {}, - "outputs": [], - "source": [ - "def group_stats(k1,k2,w1,w2):\n", - " Delta_k = np.abs(k2-k1)\n", - " Delta_w = np.abs(w2-w1)\n", - " L = 2*np.pi/Delta_k\n", - " T = 2*np.pi/Delta_w\n", - " cg = Delta_w/Delta_k\n", - " return L,T, cg" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e65fd984-ccef-4dab-ae71-59d5b9d110c1", - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length(T,h):\n", - " L = 9.81*T**2/(2*np.pi)\n", - " L_all = [L]\n", - " \n", - " for i in range(1500):\n", - " L = 9.81*T**2/(2*np.pi)*np.tanh(2*np.pi*h/L)\n", - " L_all.append(L)\n", - " \n", - " if np.abs(L_all[-1] - L_all[-2]) < 0.0005:\n", - " break\n", - " \n", - " return round(L,13)" - ] - }, - { - "cell_type": "markdown", - "id": "f10bf329-1fe8-4c79-844f-dd99527496f9", - "metadata": {}, - "source": [ - "# Questions" - ] - }, - { - "cell_type": "markdown", - "id": "7b5483e0-c3b4-4a64-a9e3-f7c894cb0895", - "metadata": {}, - "source": [ - "## Q1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e01da787-e9ac-426b-8522-a433a9ed09cc", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_space_time_waves():\n", - "\n", - " # define widgets\n", - " a1 = widgets.FloatText(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - " a2 = widgets.FloatText(value=0.5, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - " T1 = widgets.FloatText(value=8, min=0.01, max=250000, step=0.01, description='T [s]')\n", - " T2 = widgets.FloatText(value=4, min=0.01, max=250000, step=0.01, description='T [s]')\n", - " \n", - " n_waves = widgets.FloatText(value=3, min=0.1, max=10, step=0.1, description='$n_{waves}$')\n", - " \n", - " depth = widgets.FloatText(value=7.5, min=1, max=250, step=0.01, description='h [m]')\n", - " xp = widgets.FloatText(value=0, min=0, step=0.1, description='x [m]')\n", - " tp = widgets.FloatText(value=0, min=1, step=0.1, description='t [s]')\n", - " \n", - " # Disabled/informative widgets\n", - " L1 = widgets.FloatText(value=wave_length(T1.value,depth.value), description='L [m]', disabled=True)\n", - " L2 = widgets.FloatText(value=wave_length(T2.value,depth.value), description='L [m]', disabled=True)\n", - " Lgroup = widgets.FloatText(value=group_stats(k1=2 * np.pi / L1.value, k2=2 * np.pi / L2.value, w1=2 * np.pi / T1.value, w2=2 * np.pi / T2.value)[0], description='L_{group} [m]', disabled=True)\n", - "\n", - " # Setup widget layout (User Interface) and display\n", - " vbox1 = widgets.VBox([widgets.Label('Wave 1', layout=widgets.Layout(align_self='center')),a1, T1, L1])\n", - " vbox2 = widgets.VBox([widgets.Label('Wave 2', layout=widgets.Layout(align_self='center')),a2, T2, L2])\n", - " vbox3 = widgets.VBox([widgets.Label('Wave group', layout=widgets.Layout(align_self='center')), n_waves, Lgroup])\n", - " vbox4 = widgets.VBox([widgets.Label('General', layout=widgets.Layout(align_self='center')),depth, xp, tp])\n", - " \n", - " ui = widgets.HBox([vbox1, vbox2, vbox3, vbox4])\n", - "\n", - " def calc_eta(a1,T1,a2,T2,L1,L2, n_waves,xp, tp, depth):\n", - " L1 = wave_length(T1,depth)\n", - " L2 = wave_length(T2,depth)\n", - " \n", - " L_group, T_group, c_g = group_stats(\n", - " k1=2 * np.pi / L1,\n", - " k2=2 * np.pi / L2,\n", - " w1=2 * np.pi / T1,\n", - " w2=2 * np.pi / T2,\n", - " )\n", - " \n", - " T = np.min([T1,T2])\n", - " L = np.max([L1, L2])\n", - " t = np.arange(0,n_waves*T_group+T/30,T/30)\n", - " x = np.arange(0,n_waves*L_group+L/30,L/30)\n", - " \n", - " fig, axs = plt.subplots(nrows = 3,ncols = 2,figsize = (9,5), sharex=False, sharey = False)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.06)\n", - " \n", - " # time based\n", - " ax1 = axs[0,0]\n", - " ax2 = axs[1,0]\n", - " ax3 = axs[2,0]\n", - " #space based\n", - " ax4 = axs[0,1]\n", - " ax5 = axs[1,1]\n", - " ax6 = axs[2,1]\n", - "\n", - " eta1_T = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp)\n", - " eta2_T = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp)\n", - " eta_T = eta1_T + eta2_T\n", - "\n", - " eta1_x= a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x)\n", - " eta2_x= a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x)\n", - " eta_x = eta1_x + eta2_x\n", - "\n", - " # plot surface\n", - " ax1.plot(t,eta1_T, label = '$\\eta_1$')\n", - " ax2.plot(t,eta2_T, label = '$\\eta_2$')\n", - " ax3.plot(t,eta_T, label = '$\\eta$')\n", - "\n", - " ax4.plot(x,eta1_x, label = '$\\eta_1$')\n", - " ax5.plot(x,eta2_x, label = '$\\eta_2$')\n", - " ax6.plot(x,eta_x, label = '$\\eta$')\n", - "\n", - " # set vertical axis the same\n", - " amp = (a1+a2)*1.1\n", - " ax1.set_ylim(-amp,amp)\n", - " ax2.set_ylim(-amp,amp)\n", - " ax3.set_ylim(-amp,amp)\n", - " ax4.set_ylim(-amp,amp)\n", - " ax5.set_ylim(-amp,amp)\n", - " ax6.set_ylim(-amp,amp)\n", - "\n", - " # set horizontal axis\n", - " ax1.set_xlim(0,n_waves*T_group)\n", - " ax2.set_xlim(0,n_waves*T_group)\n", - " ax3.set_xlim(0,n_waves*T_group)\n", - " ax4.set_xlim(0,n_waves*L_group)\n", - " ax5.set_xlim(0,n_waves*L_group)\n", - " ax6.set_xlim(0,n_waves*L_group)\n", - "\n", - " # set labels\n", - " ax1.set_ylabel('$\\eta_1$ [m]')\n", - " ax2.set_ylabel('$\\eta_2$ [m]')\n", - " ax3.set_ylabel('$\\eta_{1+2}$ [m]')\n", - " \n", - " ax3.set_xlabel('t/T_{group} [s]')\n", - " ax6.set_xlabel('x/L_{group} [m]')\n", - "\n", - " # remove the lines related to the x-ticks and y-ticks\n", - " ax1.xaxis.set_visible(False)\n", - " ax2.xaxis.set_visible(False)\n", - " ax4.yaxis.set_visible(False)\n", - " ax5.yaxis.set_visible(False)\n", - " ax6.yaxis.set_visible(False)\n", - " \n", - " # set scaled ticks\n", - " if n_waves >= 1:\n", - " ax3.set_xticks(np.arange(0,n_waves//1 +1, 1)*T_group)\n", - " ax3.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - " ax6.set_xticks(np.arange(0,n_waves//1 +1, 1)*L_group)\n", - " ax6.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - "\n", - " else: # 3 times when the scale is smaller than 1\n", - " ax3.set_xticks([0,0.5*n_waves*L_group, n_waves*L_group])\n", - " ax3.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " ax6.set_xticks([0,0.5*n_waves*L_group, n_waves*L_group])\n", - " ax6.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " \n", - " # remove x and y ticks\n", - " ax4.set_xticklabels([], fontsize=0)\n", - " ax5.set_xticklabels([], fontsize=0)\n", - " \n", - " ax4.set_yticklabels([], fontsize=0)\n", - " ax5.set_yticklabels([], fontsize=0)\n", - " ax6.set_yticklabels([], fontsize=0)\n", - " \n", - " # set title\n", - " ax1.set_title('Time based (x =' + str(xp) + ' m)')\n", - " ax4.set_title('Space based (t =' + str(tp) + ' s)')\n", - "\n", - " ui = widgets.HBox([vbox1, vbox2, vbox3, vbox4])\n", - "\n", - " #update graph\n", - " out = widgets.interactive_output(calc_eta, {'a1': a1,'T1':T1, 'a2': a2,'T2':T2, 'L1' : L1, 'L2': L2, 'n_waves': n_waves, 'xp':xp, 'tp': tp, 'depth':depth})\n", - "\n", - " display(ui,out)\n", - "\n", - " \n", - "\n", - "out = plot_space_time_waves()" - ] - }, - { - "cell_type": "markdown", - "id": "161a62fb-fce5-4570-9e78-7afdf31d7a58", - "metadata": {}, - "source": [ - "## " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3ee8b313-6a01-4716-846c-5ee110c78b46", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_phases():\n", - " \n", - " # define widgets\n", - " a1 = widgets.FloatSlider(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - " a2 = widgets.FloatSlider(value=0.5, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - " T1 = widgets.FloatSlider(value=8, min=0.01, max=250000, step=0.01, description='T [s]')\n", - " T2 = widgets.FloatSlider(value=4, min=0.01, max=250000, step=0.01, description='T [s]')\n", - "\n", - " phi_1 = widgets.FloatSlider(value=0, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " phi_2 = widgets.FloatSlider(value=0.25, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - "\n", - " # Setup widget layout (User Interface) and display\n", - " vbox1 = widgets.VBox([widgets.Label('Wave 1', layout=widgets.Layout(align_self='center')),a1, T1, phi_1])\n", - " vbox2 = widgets.VBox([widgets.Label('Wave 2', layout=widgets.Layout(align_self='center')),a2, T2, phi_2])\n", - "\n", - " ui = widgets.HBox([vbox1, vbox2])\n", - "\n", - " def calc_eta(a1,T1,phi_1,a2,T2,phi_2):\n", - " t = np.arange(0,3*T1,0.1)\n", - "\n", - " fig, axs = plt.subplots(nrows = 3, ncols = 2, figsize = (9,5), sharex=True, sharey = True)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.04)\n", - " \n", - " # time based\n", - " ax1 = axs[0,0]\n", - " ax2 = axs[1,0]\n", - " ax3 = axs[2,0]\n", - " #space based\n", - " ax4 = axs[0,1]\n", - " ax5 = axs[1,1]\n", - " ax6 = axs[2,1]\n", - "\n", - " # time based\n", - " ax1 = axs[0,0]\n", - " ax2 = axs[1,0]\n", - " ax3 = axs[2,0]\n", - " #space based\n", - " ax4 = axs[0,1]\n", - " ax5 = axs[1,1]\n", - " ax6 = axs[2,1]\n", - " \n", - " # calculate surface \n", - " eta1 = a1*np.cos(2*np.pi/T1*t-phi_1*(2*np.pi))\n", - " eta2 = a2*np.cos(2*np.pi/T2*t-phi_2*(2*np.pi))\n", - " eta = eta1+eta2\n", - " \n", - " eta_x1 = a1*np.cos(-2*np.pi/T1*t-phi_1*(2*np.pi))\n", - " eta_x2 = a2*np.cos(-2*np.pi/T2*t-phi_2*(2*np.pi))\n", - " eta_x = eta_x1 + eta_x2\n", - "\n", - " # calculate surface without phase change\n", - " eta_1_basic = a1*np.cos(2*np.pi/T1*t)\n", - " eta_2_basic = a2*np.cos(2*np.pi/T2*t)\n", - " eta_basic = eta1\n", - " \n", - " eta_1x_basic = a1*np.cos(-2*np.pi/T1*t)\n", - " eta_2x_basic = a2*np.cos(-2*np.pi/T2*t)\n", - " eta_x_basic = eta_1x_basic + eta_2x_basic\n", - " \n", - " # plot surface excluding phase change\n", - " ax1.plot(t,eta_1_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax2.plot(t,eta_2_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax3.plot(t, eta1, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " ax4.plot(t, eta_1x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax5.plot(t, eta_2x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax6.plot(t, eta_x1, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " \n", - " # plot surface including phase change\n", - " ax1.plot(t,eta1, label = '$\\eta_1$')\n", - " ax2.plot(t,eta2, label = '$\\eta_2$' )\n", - " ax3.plot(t,eta, label = '$\\eta_{1+2}$')\n", - " \n", - " ax4.plot(t,eta_x1, label = '$\\eta_1$')\n", - " ax5.plot(t,eta_x2, label = '$\\eta_2$')\n", - " ax6.plot(t,eta_x, label = '$\\eta_{1+2}$')\n", - " \n", - " # set vertical axis the same\n", - " amp = (a1+a2)*1.1\n", - " ax1.set_ylim(-amp,amp)\n", - " ax2.set_ylim(-amp,amp)\n", - " ax3.set_ylim(-amp,amp)\n", - " ax4.set_ylim(-amp,amp)\n", - " ax5.set_ylim(-amp,amp)\n", - " ax6.set_ylim(-amp,amp)\n", - " \n", - " # set horizontal axis\n", - " ax1.set_xlim(0,3*T1)\n", - " ax2.set_xlim(0,3*T1)\n", - " ax3.set_xlim(0,3*T1)\n", - " ax4.set_xlim(0,3*T1)\n", - " ax5.set_xlim(0,3*T1)\n", - " ax6.set_xlim(0,3*T1)\n", - " \n", - " # set labels\n", - " ax1.set_ylabel('$\\eta_1$ [m]')\n", - " ax2.set_ylabel('$\\eta_2$ [m]')\n", - " ax3.set_ylabel('$\\eta_{1+2}$ [m]')\n", - " \n", - " ax3.set_xlabel('t/$T_1$ [s]')\n", - " ax6.set_xlabel('x/$L_1$ [m]')\n", - " \n", - " # set scaled ticks\n", - " ax3.set_xticks([0, T1,T1*2, T1*3])\n", - " ax3.set_xticklabels([0,1,2,3])\n", - " \n", - " ax6.set_xticks([0,T1,T1*2, T1*3])\n", - " ax6.set_xticklabels([0,1,2,3])\n", - " \n", - " # plot legends \n", - " legend1 = ax4.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend2 = ax5.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend3 = ax6.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " \n", - " # set title\n", - " ax1.set_title('Time based')\n", - " ax4.set_title('Space based')\n", - " \n", - " # remove x and y ticks\n", - " ax4.set_xticklabels([], fontsize=0)\n", - " ax5.set_xticklabels([], fontsize=0)\n", - " \n", - " ax4.set_yticklabels([], fontsize=0)\n", - " ax5.set_yticklabels([], fontsize=0)\n", - " ax6.set_yticklabels([], fontsize=0)\n", - "\n", - " # initialize graph\n", - " #out = calc_eta(a1.value,T1.value,phi_1.value,a2.value,T2.value,phi_2.value)\n", - " \n", - " #update graph\n", - " out = widgets.interactive_output(calc_eta, {'a1': a1,'T1':T1, 'phi_1': phi_1, 'a2': a2,'T2':T2, 'phi_2': phi_2})\n", - " \n", - "\n", - " \n", - " # display outcomes\n", - " display(ui, out)\n", - "\n", - "plot_phases()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6505ece5-bb59-4292-b9b2-53f2b17d8005", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bfab2a6d-389a-4698-82fe-625cc4f20208", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_space_time_waves():\n", - "\n", - " # define widgets\n", - " a1 = widgets.FloatText(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - " a2 = widgets.FloatText(value=0.5, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - " T1 = widgets.FloatText(value=8, min=0.01, max=250000, step=0.01, description='T [s]')\n", - " T2 = widgets.FloatText(value=4, min=0.01, max=250000, step=0.01, description='T [s]')\n", - "\n", - " phi_1 = widgets.FloatText(value=0, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " phi_2 = widgets.FloatText(value=0.25, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " \n", - " n_waves = widgets.FloatText(value=3, min=0.1, max=10, step=0.1, description='n_{waves}')\n", - " \n", - " depth = widgets.FloatText(value=7.5, min=1, max=250, step=0.01, description='h [m]')\n", - " xp = widgets.FloatText(value=0, min=0, step=0.1, description='x [m]')\n", - " tp = widgets.FloatText(value=0, min=1, step=0.1, description='t [s]')\n", - " \n", - " # Disabled/informative widgets\n", - " L1 = widgets.FloatText(value=wave_length(T1.value,depth.value), description='L [m]', disabled=True)\n", - " L2 = widgets.FloatText(value=wave_length(T2.value,depth.value), description='L [m]', disabled=True)\n", - " Lgroup = widgets.FloatText(value=group_stats(k1=2 * np.pi / L1.value, k2=2 * np.pi / L2.value, w1=2 * np.pi / T1.value, w2=2 * np.pi / T2.value)[0], description='L_{group} [m]', disabled=True)\n", - "\n", - " # Setup widget layout (User Interface) and display\n", - " vbox1 = widgets.VBox([widgets.Label('Wave 1', layout=widgets.Layout(align_self='center')),a1, T1, phi_1, L1])\n", - " vbox2 = widgets.VBox([widgets.Label('Wave 2', layout=widgets.Layout(align_self='center')),a2, T2, phi_2, L2])\n", - " vbox3 = widgets.VBox([widgets.Label('Wave group', layout=widgets.Layout(align_self='center')), n_waves, Lgroup])\n", - " vbox4 = widgets.VBox([widgets.Label('General', layout=widgets.Layout(align_self='center')),depth, xp, tp])\n", - " \n", - " ui = widgets.HBox([vbox1, vbox2, vbox3, vbox4])\n", - "\n", - " def calc_eta(a1,T1,phi_1, a2,T2, phi_2, L1,L2, n_waves,xp, tp, depth):\n", - " L1 = wave_length(T1,depth)\n", - " L2 = wave_length(T2,depth)\n", - " \n", - " L_group, T_group, c_g = group_stats(\n", - " k1=2 * np.pi / L1,\n", - " k2=2 * np.pi / L2,\n", - " w1=2 * np.pi / T1,\n", - " w2=2 * np.pi / T2,\n", - " )\n", - " \n", - " T = np.min([T1,T2])\n", - " L = np.max([L1, L2])\n", - " t = np.arange(0,n_waves*T_group+T/30,T/30)\n", - " x = np.arange(0,n_waves*L_group+L/30,L/30)\n", - " \n", - " fig, axs = plt.subplots(nrows = 3,ncols = 2,figsize = (9,5), sharex=False, sharey = False)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.06)\n", - " \n", - " # time based\n", - " ax1 = axs[0,0]\n", - " ax2 = axs[1,0]\n", - " ax3 = axs[2,0]\n", - " #space based\n", - " ax4 = axs[0,1]\n", - " ax5 = axs[1,1]\n", - " ax6 = axs[2,1]\n", - "\n", - " # calculate surface, including phase change\n", - " eta1_T = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp-phi_1*(2*np.pi))\n", - " eta2_T = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp-phi_2*(2*np.pi))\n", - " eta_T = eta1_T + eta2_T\n", - "\n", - " eta1_x= a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x-phi_1*(2*np.pi))\n", - " eta2_x= a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x-phi_2*(2*np.pi))\n", - " eta_x = eta1_x + eta2_x\n", - "\n", - " # calculate surface, without phase change\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - "\n", - " eta1_x_basic = a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x)\n", - " eta2_x_basic = a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x)\n", - " eta_x_basic = eta1_x_basic + eta2_x_basic\n", - "\n", - " # plot surface excluding phase change\n", - " ax1.plot(t,eta1_T_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax2.plot(t,eta2_T_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax3.plot(t, eta_T_basic, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " ax4.plot(x, eta1_x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax5.plot(x, eta2_x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax6.plot(x, eta_x_basic, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " \n", - " # plot surface including phase change\n", - " ax1.plot(t,eta1_T, label = '$\\eta_1$')\n", - " ax2.plot(t,eta2_T, label = '$\\eta_2$' )\n", - " ax3.plot(t,eta_T, label = '$\\eta_{1+2}$')\n", - " \n", - " ax4.plot(x,eta1_x, label = '$\\eta_1$')\n", - " ax5.plot(x,eta2_x, label = '$\\eta_2$')\n", - " ax6.plot(x,eta_x, label = '$\\eta_{1+2}$')\n", - "\n", - " # set vertical axis the same\n", - " amp = (a1+a2)*1.1\n", - " ax1.set_ylim(-amp,amp)\n", - " ax2.set_ylim(-amp,amp)\n", - " ax3.set_ylim(-amp,amp)\n", - " ax4.set_ylim(-amp,amp)\n", - " ax5.set_ylim(-amp,amp)\n", - " ax6.set_ylim(-amp,amp)\n", - "\n", - " # set horizontal axis\n", - " ax1.set_xlim(0,n_waves*T_group)\n", - " ax2.set_xlim(0,n_waves*T_group)\n", - " ax3.set_xlim(0,n_waves*T_group)\n", - " ax4.set_xlim(0,n_waves*L_group)\n", - " ax5.set_xlim(0,n_waves*L_group)\n", - " ax6.set_xlim(0,n_waves*L_group)\n", - "\n", - " # set labels\n", - " ax1.set_ylabel('$\\eta_1$ [m]')\n", - " ax2.set_ylabel('$\\eta_2$ [m]')\n", - " ax3.set_ylabel('$\\eta_{1+2}$ [m]')\n", - " \n", - " ax3.set_xlabel('t/T_{group}')\n", - " ax6.set_xlabel('x/L_{group}')\n", - "\n", - " # remove the lines related to the x-ticks and y-ticks\n", - " ax1.xaxis.set_visible(False)\n", - " ax2.xaxis.set_visible(False)\n", - " ax4.yaxis.set_visible(False)\n", - " ax5.yaxis.set_visible(False)\n", - " ax6.yaxis.set_visible(False)\n", - " \n", - " # set scaled ticks\n", - " if n_waves >= 1:\n", - " ax3.set_xticks(np.arange(0,n_waves//1 +1, 1)*T_group)\n", - " ax3.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - " ax6.set_xticks(np.arange(0,n_waves//1 +1, 1)*L_group)\n", - " ax6.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - "\n", - " else: # 3 times when the scale is smaller than 1\n", - " ax3.set_xticks([0,0.5*n_waves*T_group, n_waves*T_group])\n", - " ax3.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " ax6.set_xticks([0,0.5*n_waves*L_group, n_waves*L_group])\n", - " ax6.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " \n", - " # remove x and y ticks\n", - " ax4.set_xticklabels([], fontsize=0)\n", - " ax5.set_xticklabels([], fontsize=0)\n", - " \n", - " ax4.set_yticklabels([], fontsize=0)\n", - " ax5.set_yticklabels([], fontsize=0)\n", - " ax6.set_yticklabels([], fontsize=0)\n", - " \n", - " # set title\n", - " ax1.set_title('Time-based (x =' + str(xp) + ' m)')\n", - " ax4.set_title('Space-based (t =' + str(tp) + ' s)')\n", - "\n", - " # plot legends \n", - " legend1 = ax4.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend2 = ax5.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend3 = ax6.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - "\n", - " ui = widgets.HBox([vbox1, vbox2, vbox3, vbox4])\n", - "\n", - " #update graph\n", - " out = widgets.interactive_output(calc_eta, {'a1': a1,'T1':T1, 'phi_1': phi_1, 'a2': a2,'T2':T2, 'phi_2': phi_2, 'L1' : L1, 'L2': L2, 'n_waves': n_waves, 'xp':xp, 'tp': tp, 'depth':depth})\n", - "\n", - " display(ui,out)\n", - "\n", - " \n", - "\n", - "out = plot_space_time_waves()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "eae82fb3-f190-41c8-806b-3fcc0bb9f1c7", - "metadata": {}, - "outputs": [], - "source": [ - "import ipywidgets as ipw" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8897fb4a-1259-42a7-8cc9-874893609165", - "metadata": {}, - "outputs": [], - "source": [ - "def W2_Q8():\n", - "\n", - " # define widgets\n", - " a1 = ipw.FloatText(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - " a2 = ipw.FloatText(value=0.5, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - " T1 = ipw.FloatText(value=8, min=0.01, max=250000, step=0.01, description='T [s]')\n", - " T2 = ipw.FloatText(value=4, min=0.01, max=250000, step=0.01, description='T [s]')\n", - "\n", - " phi_1 = ipw.FloatText(value=0, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " phi_2 = ipw.FloatText(value=0.25, min=-1, max=1, step=0.01, description='phi [2 pi rad]')#'$\\phi$ [2 $\\pi$ rad]')\n", - " \n", - " n_waves = ipw.FloatText(value=3, min=0.1, max=10, step=0.1, description='n_{waves}')\n", - " \n", - " depth = ipw.FloatText(value=7.5, min=1, max=250, step=0.01, description='h [m]')\n", - " xp = ipw.FloatText(value=0, min=0, step=0.1, description='x [m]')\n", - " tp = ipw.FloatText(value=0, min=1, step=0.1, description='t [s]')\n", - " \n", - " # Disabled/informative widgets\n", - " L1 = ipw.FloatText(value=wave_length(T1.value,depth.value), description='L [m]', disabled=True)\n", - " L2 = ipw.FloatText(value=wave_length(T2.value,depth.value), description='L [m]', disabled=True)\n", - " Lgroup = ipw.FloatText(value=group_stats(k1=2 * np.pi / L1.value, k2=2 * np.pi / L2.value, w1=2 * np.pi / T1.value, w2=2 * np.pi / T2.value)[0], description='L_{group} [m]', disabled=True)\n", - "\n", - " # Setup widget layout (User Interface) and display\n", - " vbox1 = ipw.VBox([ipw.Label('Wave 1', layout=ipw.Layout(align_self='center')),a1, T1, phi_1, L1])\n", - " vbox2 = ipw.VBox([ipw.Label('Wave 2', layout=ipw.Layout(align_self='center')),a2, T2, phi_2, L2])\n", - " vbox3 = ipw.VBox([ipw.Label('Wave group', layout=ipw.Layout(align_self='center')), n_waves, Lgroup])\n", - " vbox4 = ipw.VBox([ipw.Label('General', layout=ipw.Layout(align_self='center')),depth, xp, tp])\n", - " \n", - " ui = ipw.HBox([vbox1, vbox2, vbox3, vbox4])\n", - "\n", - " def calc_eta(a1,T1,phi_1, a2,T2, phi_2, L1,L2, n_waves,xp, tp, depth):\n", - " L1 = wave_length(T1,depth)\n", - " L2 = wave_length(T2,depth)\n", - " \n", - " L_group, T_group, c_g = group_stats(\n", - " k1=2 * np.pi / L1,\n", - " k2=2 * np.pi / L2,\n", - " w1=2 * np.pi / T1,\n", - " w2=2 * np.pi / T2,\n", - " )\n", - " \n", - " T = np.min([T1,T2])\n", - " L = np.max([L1, L2])\n", - " t = np.arange(0,n_waves*T_group+T/30,T/30)\n", - " x = np.arange(0,n_waves*L_group+L/30,L/30)\n", - " \n", - " fig, axs = plt.subplots(nrows = 3,ncols = 2,figsize = (9,5), sharex=False, sharey = False)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.06)\n", - " \n", - " # time based\n", - " ax1 = axs[0,0]\n", - " ax2 = axs[1,0]\n", - " ax3 = axs[2,0]\n", - " #space based\n", - " ax4 = axs[0,1]\n", - " ax5 = axs[1,1]\n", - " ax6 = axs[2,1]\n", - "\n", - " # calculate surface, including phase change\n", - " eta1_T = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp-phi_1*(2*np.pi))\n", - " eta2_T = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp-phi_2*(2*np.pi))\n", - " eta_T = eta1_T + eta2_T\n", - "\n", - " eta1_x= a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x-phi_1*(2*np.pi))\n", - " eta2_x= a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x-phi_2*(2*np.pi))\n", - " eta_x = eta1_x + eta2_x\n", - "\n", - " # calculate surface, without phase change\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - "\n", - " eta1_x_basic = a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x)\n", - " eta2_x_basic = a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x)\n", - " eta_x_basic = eta1_x_basic + eta2_x_basic\n", - "\n", - " # plot surface excluding phase change\n", - " ax1.plot(t,eta1_T_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax2.plot(t,eta2_T_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax3.plot(t, eta_T_basic, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " ax4.plot(x, eta1_x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax5.plot(x, eta2_x_basic, color = 'grey', linestyle = '--', label = 'reference')\n", - " ax6.plot(x, eta_x_basic, color = 'grey', linestyle = '--', label = '$\\eta_1$')\n", - " \n", - " # plot surface including phase change\n", - " ax1.plot(t,eta1_T, label = '$\\eta_1$')\n", - " ax2.plot(t,eta2_T, label = '$\\eta_2$' )\n", - " ax3.plot(t,eta_T, label = '$\\eta_{1+2}$')\n", - " \n", - " ax4.plot(x,eta1_x, label = '$\\eta_1$')\n", - " ax5.plot(x,eta2_x, label = '$\\eta_2$')\n", - " ax6.plot(x,eta_x, label = '$\\eta_{1+2}$')\n", - "\n", - " # set vertical axis the same\n", - " amp = (a1+a2)*1.1\n", - " ax1.set_ylim(-amp,amp)\n", - " ax2.set_ylim(-amp,amp)\n", - " ax3.set_ylim(-amp,amp)\n", - " ax4.set_ylim(-amp,amp)\n", - " ax5.set_ylim(-amp,amp)\n", - " ax6.set_ylim(-amp,amp)\n", - "\n", - " # set horizontal axis\n", - " ax1.set_xlim(0,n_waves*T_group)\n", - " ax2.set_xlim(0,n_waves*T_group)\n", - " ax3.set_xlim(0,n_waves*T_group)\n", - " ax4.set_xlim(0,n_waves*L_group)\n", - " ax5.set_xlim(0,n_waves*L_group)\n", - " ax6.set_xlim(0,n_waves*L_group)\n", - "\n", - " # set labels\n", - " ax1.set_ylabel('$\\eta_1$ [m]')\n", - " ax2.set_ylabel('$\\eta_2$ [m]')\n", - " ax3.set_ylabel('$\\eta_{1+2}$ [m]')\n", - " \n", - " ax3.set_xlabel('t/T_{group}')\n", - " ax6.set_xlabel('x/L_{group}')\n", - "\n", - " # remove the lines related to the x-ticks and y-ticks\n", - " ax1.xaxis.set_visible(False)\n", - " ax2.xaxis.set_visible(False)\n", - " ax4.yaxis.set_visible(False)\n", - " ax5.yaxis.set_visible(False)\n", - " ax6.yaxis.set_visible(False)\n", - " \n", - " # set scaled ticks\n", - " if n_waves >= 1:\n", - " ax3.set_xticks(np.arange(0,n_waves//1 +1, 1)*T_group)\n", - " ax3.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - " ax6.set_xticks(np.arange(0,n_waves//1 +1, 1)*L_group)\n", - " ax6.set_xticklabels(np.arange(0,n_waves//1 +1, 1))\n", - "\n", - " else: # 3 times when the scale is smaller than 1\n", - " ax3.set_xticks([0,0.5*n_waves*T_group, n_waves*T_group])\n", - " ax3.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " ax6.set_xticks([0,0.5*n_waves*L_group, n_waves*L_group])\n", - " ax6.set_xticklabels([0, 0.5*n_waves, n_waves])\n", - " \n", - " # remove x and y ticks\n", - " ax4.set_xticklabels([], fontsize=0)\n", - " ax5.set_xticklabels([], fontsize=0)\n", - " \n", - " ax4.set_yticklabels([], fontsize=0)\n", - " ax5.set_yticklabels([], fontsize=0)\n", - " ax6.set_yticklabels([], fontsize=0)\n", - " \n", - " # set title\n", - " ax1.set_title('Time-based (x =' + str(xp) + ' m)')\n", - " ax4.set_title('Space-based (t =' + str(tp) + ' s)')\n", - "\n", - " # plot legends \n", - " legend1 = ax4.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend2 = ax5.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - " legend3 = ax6.legend(loc='center left', bbox_to_anchor=(1.001, 0.5))\n", - "\n", - " ui = ipw.HBox([vbox1, vbox2, vbox3, vbox4])\n", - "\n", - " #update graph\n", - " out = ipw.interactive_output(calc_eta, {'a1': a1,'T1':T1, 'phi_1': phi_1, 'a2': a2,'T2':T2, 'phi_2': phi_2, 'L1' : L1, 'L2': L2, 'n_waves': n_waves, 'xp':xp, 'tp': tp, 'depth':depth})\n", - "\n", - " display(ui,out)\n", - "\n", - " \n", - "\n", - "W2_Q8()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9e60d917-6b5a-4e96-a092-04dcfa8d8991", - "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.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/Chris/cookbook.ipynb b/notebooks/Chris/cookbook.ipynb deleted file mode 100644 index 50aa0ba..0000000 --- a/notebooks/Chris/cookbook.ipynb +++ /dev/null @@ -1,6683 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "a884f34f-7321-4e5c-b47f-2548cdc7abd5", - "metadata": {}, - "source": [ - "# Cookbook" - ] - }, - { - "cell_type": "markdown", - "id": "3eb32805-96b1-41a3-a0b3-aa5a6b66e76e", - "metadata": {}, - "source": [ - "In this notebook are the basics explained for making questions with widgets. The first part gives an overview of the questions. The second part elaborates on how these questions are built and gives insight into the encountered problems, the choices that are made, and what alternatives are available. The third section shows results from the previous studies, done with IPY widgets and also uses dictionaries to set up questions. If you have any questions, feel free to contact C.D.Hoogervorst@tudelft.nl." - ] - }, - { - "cell_type": "markdown", - "id": "9b03be8a-676f-4c6e-846e-072fc9962dca", - "metadata": {}, - "source": [ - "## Cookbook essence" - ] - }, - { - "cell_type": "markdown", - "id": "b6ac1efe-abb7-4397-ad13-9eab84575d03", - "metadata": {}, - "source": [ - "This section gives the short and sweet codes that can be used for making questions that use widgets. Similar questions can be made by changing the content of the question (the question, choices, answer, feedback, etc.) and storing them in the list. Understanding the structure and the characteristics of components is key for making situational layouts. the second part of this cookbook gives here more detailed insight.
\n", - "\n", - "The examples start simple, to the end are some additional options like animations and graphs that draws line and dots with mouse input." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35423b9e-ecb1-4b50-b1f7-06a4b5c53735", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import panel as pn\n", - "pn.extension(\"ipywidgets\", 'katex')\n", - "import ipywidgets as ipw\n", - "from matplotlib.animation import FuncAnimation\n", - "from matplotlib.ticker import MultipleLocator\n", - "from matplotlib.figure import Figure\n", - "\n", - "from random import shuffle, uniform\n", - "\n", - "from scipy.interpolate import CubicSpline\n", - "\n", - "import sys\n", - "from inspect import signature" - ] - }, - { - "cell_type": "markdown", - "id": "84b47195-e324-4f62-82ca-3c3f1c120ae3", - "metadata": {}, - "source": [ - "### Short introduction" - ] - }, - { - "cell_type": "markdown", - "id": "1a889b60-85a7-47be-8b26-896e02625c5c", - "metadata": {}, - "source": [ - "Here is a short explanation before diving into the code." - ] - }, - { - "cell_type": "markdown", - "id": "37468c9d-0da8-4156-8771-464e0540ff71", - "metadata": {}, - "source": [ - "#### Widgets" - ] - }, - { - "cell_type": "markdown", - "id": "3154530b-b611-45c7-92e7-0a2c31f2904a", - "metadata": {}, - "source": [ - "Widgets are used to give input to a code. Values etc are now provided by changing the value of a slider, checking a box, or pressing on a graph rather than changing the values in the code and then run it. The benefit is that the impacts of changing parameters can be displayed immediately and that inputs can be checked as in a question. Here a (submit) button comes in, to start checking the input with the exact answer.\n", - "\n", - "\"import panel as pn\" imports the widgets that are used, Over here you can find the [component gallery](https://panel.holoviz.org/reference/index.html) that shows and explaines the various widgets that are used.
\n", - "- pn.widgets.StaticText (etc.): is used for having text, to ask questions, give feedback, etc.
\n", - "- pn.widgets.Select, pn.widgets.Checkbox, pn.widgets.FloatInput, etc. are used as input for students to answer questions
\n", - "- pn.widgets.Button: is used for a button the go through the given answers
\n", - "- feedback_widget = pn.widgets.TextInput is designed as a text input but is now used as a way to give feedback to students. The feedback is provided through feedback_widget.value = ...
" - ] - }, - { - "cell_type": "markdown", - "id": "ac1c7ed4-a2c2-42cc-a2fa-0b9ca6ce6500", - "metadata": {}, - "source": [ - "#### The structure of the questions" - ] - }, - { - "cell_type": "markdown", - "id": "78ad657e-354c-42b7-84d4-3b7fa0a596e3", - "metadata": {}, - "source": [ - "Functions are made that can be called to show the function. These functions can be put into a package that can be installed with pip and then imported. Jupiter Book also gives the opportunity to show/hide specific cells.
\n", - "\n", - "Each function/question has the following structure, where only the first 2 dots are question-dependent:\n", - "- A part that builds the questions, and then asks the question to students.\n", - " - Random numbers can be generated and then included in the text.\n", - "- A part that defines the answers, and calculates them in numerical questions.\n", - " - This is done for each of the (sub) questions\n", - " - Then all the information is stored in various lists, one for questions, one for inputs of students, one for answers, one for feedback, etc.\n", - "- Then the widgets are built by looping over the lists. \n", - "- The widgets are structured and displayed (The code can also end with this)\n", - "- The functionality submit button (or other reactive parts) is defined.\n", - " - the submit_button.on_click(function) intiates a function\n", - " - the .on_click might run once when running the code the first time, therefore a nested function is made def button_callback(b), where b stands for the press-button-event.\n", - " - The function that is reached (between brackets) should be above the submit_button.on_click." - ] - }, - { - "cell_type": "markdown", - "id": "313b231a-d5d9-4406-ae05-2a82d0eaf2f7", - "metadata": {}, - "source": [ - "#### Randomize questions" - ] - }, - { - "cell_type": "markdown", - "id": "c9a60088-91ee-459b-ab81-7cf43928439c", - "metadata": {}, - "source": [ - "An important aspect of the questions is to generate random numbers or randomize the order of the questions/options.\n", - "These aspects are considered in the code below, but to highlight them:\n", - "\n", - "- shuffle() is an option to randomize the order of what is insight it.
\n", - "- uniform(a, b), provides a random number from a to b, excluding the value of b. It has many decimal numbers, so it is usually in the round(number, decimals) function, that defines the number of decimals. This results into x = round(uniform(a,b), decimals))
\n", - "- np.random.choice() picks a random value from a list. If you would like a random value from 10 to and including 20 with steps of 5, this can be done by:
x = np.arange(10, 20 + 5, 5) and then
x = np.random.choice(x).
" - ] - }, - { - "cell_type": "markdown", - "id": "6c9bd073-ae65-45ad-acdb-98e07d537b9c", - "metadata": {}, - "source": [ - "#### Working with lists" - ] - }, - { - "cell_type": "markdown", - "id": "485d8365-da07-4017-9e38-56877cfcc2d5", - "metadata": {}, - "source": [ - "Lists are used many times, not only to store widgets but also to calculate a range of values, for example, the change along a cross-section. List comprehension is a powerful and concise way to make these calculations, in contrast to \"for loops\" that are commonly used. Many interesting applications can be found [here](https://www.w3schools.com/python/python_lists_comprehension.asp). One example is shown below." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dec45b8a-d567-4aee-ab76-9d1ee6864d49", - "metadata": {}, - "outputs": [], - "source": [ - "def func(T):\n", - " return T**2\n", - "\n", - "# the values of T that are considered\n", - "T_range = np.arange(0,5+0.25,0.25)\n", - "\n", - "# the y-values for each T\n", - "y = [func(T) for T in T_range]\n", - "\n", - "# and plot in a small figure\n", - "plt.figure(figsize = (2,1))\n", - "plt.plot(T_range,y);" - ] - }, - { - "cell_type": "markdown", - "id": "b031e1a5-61e1-4d04-9f5e-6bf04315b8a4", - "metadata": {}, - "source": [ - "#### Working with many variables" - ] - }, - { - "cell_type": "markdown", - "id": "16b0cf2f-f073-4ecf-be7d-65eef1846d54", - "metadata": {}, - "source": [ - "Here is a short introduction to working with many variables in functions, the second part of this cookbook elaborates on this. One benefit is that not all the parameters have to be written as argument when the function is defined and when this function is called." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1252280e-3468-4f52-9a3b-6b92cead3e45", - "metadata": {}, - "outputs": [], - "source": [ - "# general code, that has to be defined once\n", - "class class_variables:\n", - " def __setattr__(self, key, value):\n", - " object.__setattr__(self, key, value)\n", - "\n", - "def classify_variables(params, params2 = {}, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " FV = class_variables()\n", - " for key, value in {**params,**params2}.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "# The implementation of storing the variables as classes inside functions\n", - "\n", - "def nested_function(FV,GV, AV):\n", - " print('Value of local parameter:', FV.local_parameter)\n", - " print('Value of global parameter', GV.global_parameter)\n", - " print('Value of global parameter', AV.global_parameter)\n", - "\n", - "def function():\n", - " local_parameter = 2\n", - "\n", - " # Load and store all the global variables, globals() (can also be inside the nested function)\n", - " GV = classify_variables(globals())\n", - "\n", - " # Load and store all the function variables, the locals()\n", - " FV = classify_variables(locals())\n", - "\n", - " # Load and store all variables, the locals and globals.\n", - " # In the other code is this called FV.\n", - " AV = classify_variables(locals(), globals())\n", - " \n", - " nested_function(FV, GV, AV)\n", - "\n", - "global_parameter = 5\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "b1a9d294-050c-4fcf-9cb5-534ed89386c2", - "metadata": {}, - "source": [ - "### Single multiple choice question" - ] - }, - { - "cell_type": "markdown", - "id": "6e13f2ed-9053-4fbf-b498-14528c0343df", - "metadata": {}, - "source": [ - "The code below gives one single multiple-choice question. It is the most basic question in providing the question information, making the widgets, displaying them in a structured way, and setting up a submit button.
\n", - "\n", - "The code is divided into a function with the general code (single_multiple_choice) and a function with the question (ask_single_multiple_choice). The benefit is that single_multiple_choice() only has to be coded/imported once, preventing repetitive code. The ask_single_multiple_choice() can be named such that it becomes unique, for example, by referring to a chapter or week the question is in with an id/number. Having the questions stored inside a function prevents all the parameters from being stored in the memory, and duplicate names (so overwriting) are prevented." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c1754d3f-8fc7-4f97-81ee-b94409b20b86", - "metadata": {}, - "outputs": [], - "source": [ - "def single_multiple_choice(question, choices, answer, hint, comment):\n", - " # Make the required widgets\n", - " question_widget = pn.widgets.StaticText(value=question)\n", - " choices_widget = pn.widgets.Select(options=choices, name=\"\")\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - "\n", - " # Build the structure by aligning the widgets\n", - " submit_row = pn.Row(submit_button, feedback_widget)\n", - " quiz_widget = pn.Column(question_widget, choices_widget, submit_row)\n", - " display(quiz_widget)\n", - "\n", - " def check_answers(event):\n", - " chosen_answer = choices_widget.value\n", - " \n", - " if chosen_answer == answer:\n", - " feedback_widget.value = comment\n", - " else:\n", - " feedback_widget.value = hint\n", - "\n", - " # Run the function check_answers when the submit button is pressed\n", - " submit_button.on_click(check_answers)\n", - "\n", - "def ask_single_multiple_choice():\n", - " # The information of the question\n", - " question_1 = \"A large continental shelf width is at a:\"\n", - " choices_1 = [\"Leading edge\", \"Trailing edge\", \"Marginal sea\"]\n", - " answer_1 = choices_1[1] # 0-based index\n", - " hint_1 = \"Unfortunately not, here is a hint ...\"\n", - " comment_1 = \"Indeed, some additional information...\"\n", - "\n", - " single_multiple_choice(question_1, choices_1, answer_1, hint_1, comment_1)\n", - "\n", - "ask_single_multiple_choice()" - ] - }, - { - "cell_type": "markdown", - "id": "2dcf5019-0e1e-4bca-907e-922235ad8923", - "metadata": {}, - "source": [ - "### Several combined multiple-choice questions" - ] - }, - { - "cell_type": "markdown", - "id": "43376cb3-fd8a-47a7-a468-cb32d4fc1ac3", - "metadata": {}, - "source": [ - "Several multiple-choice questions can be asked, and then a total score can be provided to the students. Each of the multiple-choice options can be provided with feedback. Below are two functions, the top one only provides the final score, while the second one provides feedback for each subquestion." - ] - }, - { - "cell_type": "markdown", - "id": "fef8cc45-6b18-4278-b31a-4e5f3e84bd21", - "metadata": {}, - "source": [ - "#### Providing final score, without feedback" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e12d4a5b-a22c-4f13-b193-d858a8d6b81e", - "metadata": {}, - "outputs": [], - "source": [ - "# the general function that has to be coded/imported once\n", - "def several_multiple_choice(questions, choices, answers):\n", - " # An empty list to store the widgets\n", - " all_widgets = ([]) # for visualization, store all the widgets in the order they are going to be displayed\n", - " question_widgets = [] # store all the question widgets in a list\n", - " choices_widgets = [] # store all the choices widgets in a list\n", - "\n", - " # make the widgets in a loop, one widget states the question and one shows the options that can be selected.\n", - " for question, choice, answer in zip(questions, choices, answers):\n", - " question_widget = pn.widgets.StaticText(value=question)\n", - " choices_widget = pn.widgets.Select(options=choice, name=\"\")\n", - "\n", - " # store the widgets on type, make looping through them possible, the order is unaffected by randomizing\n", - " question_widgets.append(question_widget)\n", - " choices_widgets.append(choices_widget)\n", - "\n", - " # store the widgets (again) for display purposes, they will be ordered vertically in pn.Column\n", - " all_widgets.append(question_widget)\n", - " all_widgets.append(choices_widget)\n", - "\n", - " # make a submit button and a feedback button\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\")\n", - "\n", - " # allign the submit button and the feedback widget horizontally\n", - " HBox_check = pn.Row(submit_button, feedback_widget)\n", - " all_widgets.append(HBox_check)\n", - "\n", - " # unpacks the list by an asterix (*)\n", - " quiz_widget = pn.Column(*all_widgets)\n", - "\n", - " # make a function to calculate the score and to give feedback\n", - " def check_answers(event):\n", - " score = 0\n", - "\n", - " for i in range(len(questions)):\n", - " answer = choices_widgets[i].value\n", - " correct_answer = answers[i]\n", - "\n", - " if answer == correct_answer:\n", - " score += 1\n", - "\n", - " feedback_widget.value = (\"Your score is \" + str(score) + \"/\" + str(len(questions)))\n", - "\n", - " submit_button.on_click(check_answers)\n", - " display(quiz_widget)\n", - "\n", - "\n", - "# a unique question, which should have a unique name.\n", - "def ask_several_multiple_choice():\n", - " # The information of the questions\n", - " question_1 = \"A large continental shelf width is at a:\"\n", - " choices_1 = [\"Leading edge\", \"Trailing edge\", \"marginal sea\"]\n", - " answer_1 = choices_1[1]\n", - "\n", - " question_2 = \"The oceanic geoid is: \"\n", - " choices_2 = [\n", - " \"An oval shape\",\n", - " \"The shape of the ocean surface under only gravity forces\",\n", - " \"A geo triangle with a different shape\",\n", - " \"The actual ocean surface\",\n", - " ]\n", - " answer_2 = choices_2[1]\n", - "\n", - " # store the questions in a list (the eval() function can be used to prevent making long list, see second section)\n", - " questions = [question_1, question_2]\n", - " choices = [choices_1, choices_2]\n", - " answers = [answer_1, answer_2]\n", - "\n", - " several_multiple_choice(questions, choices, answers)\n", - "\n", - "\n", - "ask_several_multiple_choice()" - ] - }, - { - "cell_type": "markdown", - "id": "bef6e0c5-a78c-4689-bbb3-a1356aa3d85f", - "metadata": {}, - "source": [ - "#### Final score including feedback for subquestions." - ] - }, - { - "cell_type": "markdown", - "id": "4c8550f3-e761-4f0f-b266-79e66709bcab", - "metadata": {}, - "source": [ - "Each subquestion can be provided with different feedback, which is not included in the function above.\n", - "\n", - "In the code below is the feedback_widget renamed to final_feedback_widget. And is a feedback_widget made in the loop. The check_answers function is extended by providing the correct text (value) when the submit_button is pressed.\n", - "\n", - "feedback_widget = pn.widgets.StaticText() might be a easy alternative for having longer feedback. See more in the second part under the heading \"Layout feedback widget\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ca58f0df-0112-40b1-87fd-9f375ce30ce9", - "metadata": {}, - "outputs": [], - "source": [ - "def several_multiple_choice_feedback(questions, choices, answers, hints, comments): \n", - " # An empty list to store the widgets\n", - " all_widgets = [] # For visualization, store all the widgets in the order they are going to be displayed\n", - " question_widgets = [] # Store all the question widgets in a list\n", - " choices_widgets = [] # store all the choices widgets in a list\n", - " feedback_widgets = [] # store all the feedback widgets for subquestions\n", - " \n", - " # make the widgets in a loop, one widget states the question and one shows the options that can be selected.\n", - " for question, choice, answer in zip(questions, choices, answers):\n", - " question_widget = pn.widgets.StaticText(value=question)\n", - " choices_widget = pn.widgets.Select(options=choice, name=\"\")\n", - " feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\")\n", - "\n", - " # store the widgets on type, make looping through them possible, the order is unaffected by randomizing\n", - " question_widgets.append(question_widget)\n", - " choices_widgets.append(choices_widget)\n", - " feedback_widgets.append(feedback_widget)\n", - "\n", - " # store the widgets (again) for display purposes. It remain the same objects as in the previous list. \n", - " allign_question_feedback = pn.Row(choices_widget, feedback_widget)\n", - " \n", - " all_widgets.append(question_widget)\n", - " all_widgets.append(allign_question_feedback)\n", - " \n", - " # make a submit button and a feedback button\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " final_feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\")\n", - " \n", - " # Allign the submit button and the feedback widget horizontally\n", - " HBox_check = pn.Row(submit_button, final_feedback_widget)\n", - " all_widgets.append(HBox_check)\n", - " \n", - " # unpacks the list by an asterisk (*)\n", - " quiz_widget = pn.Column(*all_widgets)\n", - " \n", - " # make a function to calculate the score and to give feedback\n", - " def check_answers(event):\n", - " score = 0\n", - " \n", - " for i in range(len(questions)):\n", - " answer = choices_widgets[i].value\n", - " correct_answer = answers[i]\n", - " \n", - " if answer == correct_answer:\n", - " score += 1\n", - " feedback_widgets[i].value = comments[i]\n", - "\n", - " if answer != correct_answer:\n", - " feedback_widgets[i].value = hints[i]\n", - " \n", - " final_feedback_widget.value = \"Your score is \" + str(score) + \"/\" + str(len(questions))\n", - " \n", - " submit_button.on_click(check_answers)\n", - " display(quiz_widget)\n", - "\n", - "\n", - "def ask_several_multiple_choice_feedback():\n", - " # The information of the questions\n", - " question_1 = \"A large continental shelf width is at a:\"\n", - " choices_1 = [\"Leading edge\", \"Trailing edge\", \"marginal sea\"]\n", - " answer_1 = choices_1[1]\n", - " hint_1 = \"Unfortunately not, here is a hint ...\"\n", - " comment_1 = \"Indeed, .. some additional information ... \"\n", - " \n", - " question_2 = \"The oceanic geoid is: \"\n", - " choices_2 = [\n", - " \"An oval shape\",\n", - " \"The shape of the ocean surface under only gravity forces\",\n", - " \"A geo triangle with a different shape\",\n", - " \"The actual ocean surface\",\n", - " ]\n", - " answer_2 = choices_2[1]\n", - " hint_2 = \"Unfortunately not, here is a hint ...\"\n", - " comment_2 = \"Indeed, .. some additional information ... \"\n", - " \n", - " # store the questions in a list\n", - " questions = [question_1, question_2]\n", - " choices = [choices_1, choices_2]\n", - " answers = [answer_1, answer_2]\n", - " hints = [hint_1, hint_2]\n", - " comments = [comment_1, comment_2]\n", - "\n", - " several_multiple_choice_feedback(questions, choices, answers, hints, comments)\n", - "\n", - "ask_several_multiple_choice_feedback()" - ] - }, - { - "cell_type": "markdown", - "id": "e1191fad-5166-400c-8632-11b8fc02f85e", - "metadata": {}, - "source": [ - "### Multiple selection" - ] - }, - { - "cell_type": "markdown", - "id": "00604036-4e36-4c8a-8f69-c68dcf87f095", - "metadata": {}, - "source": [ - "In the code below are correct and false statements presented. Students have to select the check boxes related to correct answers. Students gain points when they choose the correct answers but lose points if they check incorrect answers. The lowest score is 0 points." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d83de5f6-dfba-49c6-be30-868342be6312", - "metadata": {}, - "outputs": [], - "source": [ - "def multiple_selection(correct_statements, false_statements):\n", - " # Make an empty list to store the widgets (references), checkboxes, and true/false statements sorted.\n", - " check_boxes = [] # all the boxes to click\n", - " all_statements = [] # all the statements\n", - " \n", - " # An empty list for visualization to store the HBoxes that contain the widgets, one statement and the corresponding checkbox\n", - " all_widgets = []\n", - " \n", - " for statement in correct_statements + false_statements:\n", - " add_statement = pn.widgets.StaticText(value=statement, width=150)\n", - " check_box_widget = pn.widgets.Checkbox(value=False, width=120)\n", - " HBox1 = pn.Row(add_statement, check_box_widget)\n", - " \n", - " all_statements.append(add_statement)\n", - " check_boxes.append(check_box_widget)\n", - " all_widgets.append(HBox1)\n", - " \n", - " # randomize the order of statements\n", - " shuffle(all_widgets)\n", - " \n", - " # add submit button and output, which come on the bottom\n", - " submit_button = pn.widgets.Button(name='Check')\n", - " output_widget = pn.widgets.TextInput(value='', placeholder='', disabled=False)\n", - " \n", - " # make an additional HBox for aligning the submit button and the output widget\n", - " HBox2 = pn.Row(submit_button, output_widget)\n", - " all_widgets.append(HBox2)\n", - " \n", - " # align all the HBoxes beneath each other (oldest below if not randomized) and display them.\n", - " quiz_widget = pn.Column(*all_widgets)\n", - " \n", - " # Check the checkbox for each statement and calculate the score.\n", - " def check_answers(event):\n", - " score = 0\n", - " \n", - " for i in range(len(check_boxes)):\n", - " check_box = check_boxes[i]\n", - " statement = all_statements[i].value\n", - " \n", - " if statement in correct_statements:\n", - " if check_box.value == True:\n", - " score += 1\n", - " else:\n", - " score -= 0\n", - " \n", - " if statement not in correct_statements:\n", - " if check_box.value == True:\n", - " score -= 1\n", - " else:\n", - " score -= 0\n", - " \n", - " score = np.max([score, 0])\n", - " output_widget.value = 'Your final score is: ' + str(score)\n", - " \n", - " submit_button.on_click(check_answers)\n", - " display(quiz_widget)\n", - "\n", - "\n", - "def ask_multiple_selection():\n", - " correct_statements = [\"Correct\", \"The earth is round\", \"Good\"]\n", - " false_statements = [\"False\", \"The earth is a cube\"]\n", - "\n", - " multiple_selection(correct_statements, false_statements)\n", - "\n", - "\n", - "ask_multiple_selection()" - ] - }, - { - "cell_type": "markdown", - "id": "f218103f-4a1a-4eb7-bc49-fc51a073ca0d", - "metadata": {}, - "source": [ - "### Select correct statements" - ] - }, - { - "cell_type": "markdown", - "id": "64eb57ee-516a-45bd-a862-8024e1f8cd83", - "metadata": {}, - "source": [ - "For this question do students have to select the correct statement out of multiple options. They gain a final score that indicates the number of correct answers. (Losing points with a wrong answer can be added as well). The order of the questions is randomized" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5fd9d248-ef9c-4476-b7e3-96421a46d200", - "metadata": {}, - "outputs": [], - "source": [ - "def select_statement(questions, answers, correct_answers_id):\n", - " # define the widgets for visualization, make for each a row with a question and answer\n", - " # Store all the rows with statements in a list for visualization, and in toggle_widgets for checking the answer\n", - " Rows = []\n", - " toggle_widgets = []\n", - " for i in range(len(correct_answers_id)):\n", - " question_widget = pn.widgets.StaticText(value=questions[i], width = 150) #statement\n", - " \n", - " radio_group_widget = pn.widgets.RadioButtonGroup(name='Radio Button Group', options=answers, button_type='default')\n", - " toggle_widgets.append(radio_group_widget)\n", - " \n", - " add_row = pn.Row(question_widget, radio_group_widget)\n", - " Rows.append(add_row)\n", - "\n", - " # randomize the order of statements\n", - " shuffle(Rows)\n", - "\n", - " # Add a submit button with a feedback option next to it\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=500)\n", - " submit_row = pn.Row(submit_button, feedback_widget)\n", - " \n", - " # include a question\n", - " text_general = \"Select if the wave described on the right experiences shallow, intermediate, or deep water.\"\n", - " text_widget = pn.widgets.StaticText(value=text_general)\n", - "\n", - " # Structure the widgets and display them\n", - " display(text_widget, *Rows, submit_row)\n", - "\n", - " # check the answer and give feedback\n", - " def check_answers(button):\n", - " score = 0\n", - "\n", - " for i in range(len(correct_answers_id)):\n", - " if toggle_widgets[i].value == answers[correct_answers_id[i]]:\n", - " score += 1\n", - "\n", - " # print(toggle_widget.value)\n", - " feedback_widget.value = \"Your score is \" + str(score) + \"/\" + str(len(correct_answers_id))\n", - "\n", - " submit_button.on_click(check_answers)\n", - "\n", - "\n", - "def ask_select_statement():\n", - "\n", - " # define the questions and store them in a list\n", - " Q1 = 'Question 1'\n", - " Q2 = 'Question 2'\n", - " Q3 = 'Question 3'\n", - " questions = [Q1, Q2, Q3]\n", - "\n", - " # The possible answers (may vary per question)\n", - " answers = [\"Shallow\", \"Intermediate\", \"Deep\"]\n", - "\n", - " # The correct answers, 0-based id, can be calculated with functions when working with randomized numbers\n", - " correct_answers_id = [0,1,2]\n", - "\n", - " select_statement(questions, answers, correct_answers_id)\n", - "\n", - "\n", - "ask_select_statement()" - ] - }, - { - "cell_type": "markdown", - "id": "2b68f457-0f60-4c6c-8670-c16472196a7b", - "metadata": {}, - "source": [ - "### Select/slide figures" - ] - }, - { - "cell_type": "markdown", - "id": "cbf90ac8-ea5a-41ec-a807-2e8e40a03ed4", - "metadata": {}, - "source": [ - "make a slider in which students can use a slider to select a figure. How path_figures can be defined using Git Hub have to be coordinated. The code is now as raw text to prevent it from running and giving an error notification that the figures are not found. An improved version will be made when these options have been streamlined.\n", - "\n", - "The figures and code that displays them through widgets was made by Mario van den Berg, for the test notebook about the escofier curves." - ] - }, - { - "cell_type": "raw", - "id": "b0b3dfbf-5291-4d8a-a64a-080670f033de", - "metadata": {}, - "source": [ - "# Load the images\n", - "images = [\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_1.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_2.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_3.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_4.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_5.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_6.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_7.png\"))\n", - "]\n", - "\n", - "# Create the slider widget\n", - "slider = ipw.IntSlider(min=0, max=len(images)-1, step=1, value=0)\n", - "\n", - "# Display the current image\n", - "image_widget = ipw.Image(value=images[slider.value]._repr_png_(), format='png', width='95%')\n", - "\n", - "# Define the update function\n", - "def update_image(change):\n", - " image_widget.value = images[change.new]._repr_png_()\n", - "\n", - "# Attach the update function to the slider\n", - "slider.observe(update_image, names='value')\n", - "\n", - "# Display the widgets\n", - "display(slider, image_widget)" - ] - }, - { - "cell_type": "markdown", - "id": "b31ea2b2-9094-4f4b-bfe6-26b83ef9b902", - "metadata": {}, - "source": [ - "### Numerical question" - ] - }, - { - "cell_type": "markdown", - "id": "37792f41-469e-440e-8828-d3400243045e", - "metadata": {}, - "source": [ - "The numerical question should consider various aspects.
\n", - "- It should check the answer, where students can give more decimal numbers than requested. The question should give feedback, optionally when the answer is wrong and/or correct.\n", - "When 3 attempts are made the correct answer is given.\n", - "- The function Q1() defines the questions, feedback, etc. It now shows 3 times the same question, which can be changed by changing the lists questions, units, answers, FB_good, and FB_wrong.
\n", - "- The function nummeric_question_body() is the core of the layout. This can be changed if a different structure of widgets is preferred.\n", - "\n", - "Note that functions can have a lot of input parameters (So can the check_nummeric_answers can quickly require many more parameters). It is possible to prevent defining all the widgets in all the functions by storing all the local parameters in one parameter that is passed on to the function. The value of the parameters can then be extracted from this parameter. The second part of this cookbook (Full recipe) elaborates on this." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "37113f2e-eb1d-4bae-a601-cc14474d82b9", - "metadata": {}, - "outputs": [], - "source": [ - "def limit_answer(x):\n", - " s = str(x)\n", - "\n", - " # inspired by: https://stackoverflow.com/questions/35585950/find-the-number-of-digits-after-the-decimal-point\n", - " if not '.' in s:\n", - " n_decimal = 0\n", - " else:\n", - " n_decimal = len(s) - s.index('.') - 1\n", - "\n", - " range = 5*10**-(n_decimal+1)\n", - "\n", - " return range\n", - "\n", - "def check_nummeric_answers(id, answer, unit, FB_G, FB_W, num_widget, feedback_widget, attempt):\n", - " \n", - " def button_callback(b):\n", - " attempt.value += 1\n", - "\n", - " # the answer is within the boundaries, print positive feedback\n", - " if np.abs(answer - num_widget.value) < limit_answer(answer):\n", - " if len(FB_G) != 0:\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = 'Well done, this is correct!'\n", - "\n", - " # the answer is NOT within boundaries, provide feedback based on the number of attempts\n", - " if np.abs(answer - num_widget.value) >= limit_answer(answer):\n", - "\n", - " if attempt.value < 3 and len(FB_W) > 0:\n", - " feedback_widget.value = FB_W\n", - " \n", - " if attempt.value < 3 and len(FB_W) == 0:\n", - " feedback_widget.value = 'Oops, there seems to be a mistake'\n", - " \n", - " if attempt.value >= 3:\n", - " feedback_widget.value = 'The correct answer is ' + str(answer) + str(unit) + '.'\n", - "\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - "def nummeric_question_body(questions, units, answers, FB_good, FB_wrong, random_order = False):\n", - " all_widgets = []\n", - " attempts = []\n", - "\n", - " order = np.arange(0, len(questions), 1)\n", - " if random_order == True:\n", - " shuffle(order)\n", - "\n", - " for i in np.array(order):\n", - " question, unit, answer, Q_FB_G, Q_FB_W = questions[i], units[i], answers[i], FB_good[i], FB_wrong[i]\n", - " id = i+1 \n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - " unit_widget = pn.widgets.StaticText(value=unit, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " #feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=500)\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " \n", - " Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget) \n", - " quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - " all_widgets.append(quiz_widget)\n", - "\n", - " # the values for the submit button are determined at the moment these are created.\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - " attempts.append(attempt)\n", - " submit_button.on_click(check_nummeric_answers(id, answer, unit, Q_FB_G, Q_FB_W, num_widget, feedback_widget, attempt))\n", - " \n", - " return all_widgets\n", - "\n", - "\n", - "def Q1():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " text_general = \"Can you asses the wave length in three different ways? Firstly through an iterative approach. The wave period (T) is \" + str(T1) + \" seconds, and the water depth (h) is \" + str(h1) + \" m?\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - " \n", - " Q1_text = \"Q1a) What is the deep water wavelength?\"\n", - " Q1_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(L, 2)\n", - " Q1_FB_G = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - " Q1_FB_W = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - " questions = [Q1_text, Q1_text,Q1_text]\n", - " units = [Q1_unit, Q1_unit, Q1_unit]\n", - " answers =[Q1_answer, Q1_answer, Q1_answer]\n", - " FB_good = [Q1_FB_G, Q1_FB_G, Q1_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q1_FB_W, Q1_FB_W]\n", - "\n", - " \n", - " all_widgets = nummeric_question_body(questions, units, answers, FB_good, FB_wrong, random_order = True)\n", - " \n", - " display(pn.Column(text_widget,*all_widgets))\n", - " \n", - "Q1()" - ] - }, - { - "cell_type": "markdown", - "id": "ce4a499a-ddb4-4974-bf2f-e26030c0b86c", - "metadata": {}, - "source": [ - "### Update graphs with sliders" - ] - }, - { - "cell_type": "markdown", - "id": "35593f0b-4fb4-4b7e-a2ab-ac79be4ea377", - "metadata": {}, - "source": [ - "Plots can be redrawn when parameters are changed. This can be done with the [Param component](https://panel.holoviz.org/reference/panes/Param.html) of panel. The coding is quite difficult, while it is easy with the interact function of IPY widgets. The application of the interact function is shown below. In the future, this might be replaced with another tool. This transition with questions is probably relatively easy since the majority of the code is related to the content of the questions. One downside is that is difficult to hide the automatically generated output ()
\n", - "\n", - "The structure is relatively simple. The (slider) widgets are defined in a function, while the other parts (the calculation, plotting, and optionally questioning) are inside a function that is called with interact()." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0f1ad134-6ad3-45c5-bc30-5e05a40e714e", - "metadata": {}, - "outputs": [], - "source": [ - "from ipywidgets import interact\n", - "\n", - "def plot_slider_graph(a, L):\n", - " fig, axs = plt.subplots(nrows = 1, ncols = 1, figsize = (9,6), sharex=True, sharey = False)\n", - " fig.subplots_adjust(hspace=0.05)\n", - " fig.subplots_adjust(wspace=0.04)\n", - " \n", - " x = np.linspace(0,12,100)\n", - " \n", - " eta = a * np.sin(2 * np.pi / L * x)\n", - " axs.plot(x, eta)\n", - "\n", - "def slider_graph():\n", - " # Create interactive widgets, requires IPY Widgets, widgets from panel do not work\n", - " #L = pn.widgets.FloatSlider(name='Float Slider', start=0, end=3.141, step=0.01, value=1.57)\n", - " a = ipw.FloatSlider(value=4, min=0.01, max=50, step=0.01, description=\"a [s]\")\n", - " L = ipw.FloatSlider(value=6, min=0.01, max=50, step=0.01, description=\"L [s]\")\n", - "\n", - " # Use the interactive function to update the plot\n", - " Plot = interact(plot_slider_graph, a=a, L=L);\n", - " display(Plot)\n", - "\n", - "slider_graph()" - ] - }, - { - "cell_type": "markdown", - "id": "ad4cc517-b7d6-4c80-824b-7e9092a3ed6c", - "metadata": {}, - "source": [ - "### Updating graphs with (submit) buttons" - ] - }, - { - "cell_type": "markdown", - "id": "67e2c067-e9b3-40e1-8482-af4eff60892a", - "metadata": {}, - "source": [ - "Plots can be redrawn when parameters are changed. This can be done with the [Param component](https://panel.holoviz.org/reference/panes/Param.html) of panel. The code below shows the implementation of having a graph updated when a (submit) button is pressed. The graphs are added similarly to other widgets, now with additional attention to defining the axis of the graph (ax), in which the figure is displayed (fig), and the panes widget (pane). The code below shows them for making two graphs. \n", - "\n", - "It is not recommended to have calculations etc. It is hard to return values. The simplest way (or workaround) is by using widgets and changing the value. Below is the counter used as input to change the value of the graph each time the button is pressed. Further on in this notebook is a section related to working with parameters in functions." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5a79214a-9a89-4797-ac82-89f5cf1c2632", - "metadata": {}, - "outputs": [], - "source": [ - "#set plot settings and make plots \n", - "titles = ['plot1', 'graph2']\n", - "figures = []\n", - "plots = []\n", - "panes = []\n", - "for title in titles:\n", - " fig = Figure((5,2.5))\n", - " ax = fig.subplots()\n", - " pane = pn.pane.Matplotlib(fig, dpi=96)\n", - "\n", - " # Add some extra space for labels at the axis\n", - " fig.subplots_adjust(bottom=0.25) \n", - " fig.subplots_adjust(left=0.2)\n", - "\n", - " # store everything in a list\n", - " figures.append(fig)\n", - " plots.append(ax)\n", - " panes.append(pane)\n", - "\n", - "# set a function that defines what has to be done when the button is pressed\n", - "def plot_graph(figures, plots, panes, start_value_right, counter):\n", - "\n", - " def button_callback(b):\n", - " ax = plots[0]\n", - " ax.clear() # remove previous lines\n", - " ax.plot(np.linspace(0,10,10), np.linspace(0,start_value_right + counter.value ,10), label = 'L [m]')\n", - " ax.set_xlabel(\"x-label\")\n", - " panes[0].object = figures[0]\n", - "\n", - " ax = plots[1]\n", - " ax.clear() # remove previous lines\n", - " ax.plot(np.linspace(0,10,10), np.linspace(0,start_value_right - counter.value ,10), label = 'L [m]')\n", - " ax.set_xlabel(\"x-label\")\n", - " panes[1].object = figures[1]\n", - "\n", - " counter.value += 1\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - "start_value_right = 2\n", - "\n", - "counter = pn.widgets.FloatInput(value=0)\n", - "\n", - "submit_button = pn.widgets.Button(name=\"Update graph\")\n", - "submit_button.on_click(plot_graph(figures, plots, panes, start_value_right , counter))\n", - "\n", - "pn.Column(*panes, submit_button)" - ] - }, - { - "cell_type": "markdown", - "id": "114b2fc1-949b-4c3d-883a-f8011697a872", - "metadata": {}, - "source": [ - "### Coding question - Check value of parameter(s)" - ] - }, - { - "cell_type": "markdown", - "id": "fcaa1475-ae30-4c20-beb4-8998775ad59e", - "metadata": {}, - "source": [ - "Students can be asked to compute values of specific parameters in a code field. The students can then load their result into a widget, which has the option to check their results with the answers and gives feedback. It gives a notification if one of the parameters are not defined when loading. It also gives the correct answer after 3 attempts, When the widget does not have to be runned to update the values, which is the case in notebooks.\n", - "\n", - "Below is the code that has to be defined/imported only once. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f9ff99e2-bda7-4ffa-b7e3-23b8c34db278", - "metadata": {}, - "outputs": [], - "source": [ - "# general code, that has to be defined once\n", - "class class_variables:\n", - " def __setattr__(self, key, value):\n", - " object.__setattr__(self, key, value)\n", - "\n", - "def classify_variables(locals, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " FV = class_variables()\n", - " for key, value in {**globals(), **locals}.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def add_local_variables_to(FV,locals, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " for key, value in locals.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def check_code_values(IV):\n", - " # The function variable (FV) is now defined as Input Variable (IV)\n", - " # The input variable and the newly defined local variable will be merged to FV\n", - "\n", - " \n", - " def get_coded_values(FV,GV):\n", - " def button_callback(b):\n", - " try:\n", - " for i, param in enumerate(FV.check_parameters):\n", - " \n", - " # get the value that students gave\n", - " response = getattr(GV, param)\n", - " \n", - " #store them in the widget\n", - " FV.all_parameter_widgets[i].value = response\n", - " \n", - " FV.debug_widget.value = ''\n", - " except:\n", - " FV.debug_widget.value = ' Careful, not all parameters are defined! '\n", - " \n", - " return button_callback\n", - "\n", - " def check_coded_values(FV,GV):\n", - " def button_callback(b):\n", - " FV.attempt.value += 1\n", - " \n", - " for i, param in enumerate(FV.check_parameters):\n", - " #response = getattr(GV, param)\n", - " response = FV.all_parameter_widgets[i].value\n", - " answer = getattr(FV, param)\n", - " \n", - " if np.abs(response - answer) < FV.f_margin * answer:\n", - " FV.all_feedback_widgets[i].value = 'Nice, this is good!'\n", - " \n", - " if np.abs(response - answer) >= FV.f_margin * answer:\n", - " if FV.attempt.value < 3:\n", - " FV.all_feedback_widgets[i].value = 'This one is incorrect, try again!'\n", - " if FV.attempt.value >= 3:\n", - " FV.all_feedback_widgets[i].value = 'This one is incorrect, the answer should be ' + str(answer) + '.'\n", - " \n", - " #print('Debug: ', param, response, answer)\n", - " \n", - " return button_callback\n", - "\n", - " all_parameter_widgets = []\n", - " info_widgets = []\n", - " all_feedback_widgets = []\n", - " for name, param in zip(IV.name_parameters, IV.check_parameters):\n", - " symbol_widget = pn.widgets.StaticText(name='', value= name, width = 100)\n", - " parameter_widget = pn.widgets.FloatInput(name='', value=0, width = 100)\n", - " feedback_widget = pn.widgets.StaticText(value='')\n", - " \n", - " all_parameter_widgets.append(parameter_widget)\n", - " all_feedback_widgets.append(feedback_widget)\n", - " \n", - " new_row = pn.Row(symbol_widget, parameter_widget, feedback_widget)\n", - " info_widgets.append(new_row)\n", - "\n", - " debug_widget = pn.widgets.StaticText(value='')\n", - " \n", - " get_values_button = pn.widgets.Button(name=\"Load values\")\n", - " check_values_button = pn.widgets.Button(name=\"Check loaded values\")\n", - "\n", - " GV = classify_variables(globals())\n", - " FV = add_local_variables_to(IV,locals())\n", - "\n", - " # The error margin is set at 0.01% if it is not defined\n", - " if 'f_margin' not in FV.__dict__:\n", - " FV.__setattr__('f_margin', 0.0001)\n", - " \n", - " get_values_button.on_click(get_coded_values(FV, GV))\n", - " check_values_button.on_click(check_coded_values(FV, GV))\n", - " \n", - " row_buttons = pn.Row(get_values_button, check_values_button, debug_widget)\n", - " Input_col = pn.Column(row_buttons, *info_widgets)\n", - " display(Input_col)" - ] - }, - { - "cell_type": "markdown", - "id": "176bdc12-876a-4c4c-a575-03b855aa0fe3", - "metadata": {}, - "source": [ - "The first function is used to ask the question to students and define the values of the parameters. And the second is used to check the answer. Note that the names of the functions should be unique." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "17c3e622-4052-497e-8a4d-3ca59cc20be9", - "metadata": {}, - "outputs": [], - "source": [ - "def Question():\n", - " # the question-related parameters\n", - " L1 = 6\n", - " L2 = round(uniform(7,10), 1)\n", - "\n", - " # The question that is asked\n", - " question = 'What is k1 and k2 when the lengts of wave 1 and 2 are ' + str(L1) + ' and ' + str(L2) + ' meter? Complete and run the code below and you can check your results.'\n", - " \n", - " # make the attempt counter, one for each subquestion\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - "\n", - " # Required widgets for functionality, should not be changed\n", - " question_widget = pn.widgets.StaticText(name='', value= question)\n", - " display(question_widget)\n", - "\n", - " # define a new global variable, with a unique name (This is related to week 2, question 9)\n", - " global W2_Q9_param \n", - " # store the question-related parameters and the widget 'attempt' \n", - " # These parameters can also be stored as classes\n", - " W2_Q9_param = L1, L2, attempt\n", - "\n", - "def Check_Question():\n", - " L1, L2, attempt = W2_Q9_param\n", - " \n", - " # define the parameter names that have to be checked\n", - " check_parameters = ['k1', 'k2']\n", - "\n", - " # define the names of the parameters as they are displayed\n", - " name_parameters = ['k 1', 'k 2']\n", - "\n", - " # define the answers for the questions, which are described in check_parameters\n", - " k1 = 2*np.pi/L1\n", - " k2 = 2*np.pi/L2\n", - "\n", - " # a standard part that stores the parameters and displays the UI\n", - " # f_margin = 0, used to define the maximum error, which a a factor to the answer, default 0.0001 = 0.01%\n", - " FV = classify_variables(locals())\n", - " check_code_values(FV)" - ] - }, - { - "cell_type": "markdown", - "id": "0181213d-ad8c-4d73-9af6-892b90b90c6e", - "metadata": {}, - "source": [ - "The question in the Jupiter (note)book can be loaded through:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "81fcc9e1-6a5d-44be-a42c-b757553fb84d", - "metadata": {}, - "outputs": [], - "source": [ - "Question()" - ] - }, - { - "cell_type": "markdown", - "id": "359511eb-b9f6-4921-b4c9-1e885ee819fd", - "metadata": {}, - "source": [ - "A structure of the code has to be given, at least the names of the parameters. The parts that students have to fill in can be indicated by 3 dots or red text, as in:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c6050889-1c33-4a93-8c23-30eb1030543a", - "metadata": {}, - "outputs": [], - "source": [ - "k1 = ...\n", - "k2 = 'fill this in'\n", - "\n", - "Check_Question()" - ] - }, - { - "cell_type": "markdown", - "id": "15fef2a8-8d09-4c66-9711-35aa5c35f91a", - "metadata": {}, - "source": [ - "One possible answer of students can be:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6eb1d893-d8ec-48d1-b19a-51d95a86bbe6", - "metadata": {}, - "outputs": [], - "source": [ - "L1 = 6\n", - "k1 = 2*np.pi/L1*0.9999\n", - "k2 = 2\n", - "\n", - "Check_Question()" - ] - }, - { - "cell_type": "markdown", - "id": "301ef29b-3f19-40ad-b45d-3316891f676e", - "metadata": {}, - "source": [ - "### Coding question - Check a function and make a graph" - ] - }, - { - "cell_type": "markdown", - "id": "e0923f1d-b0a4-492b-bc63-68261030d811", - "metadata": {}, - "source": [ - "The function below loads the function the students make. It will plot this function if this is possible otherwise, it will give a warning. \n", - "The check_code_function() should be loaded/imported once, no changes have to be made." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4fe88e5e-014c-41e9-97d1-d42e6dd287d6", - "metadata": {}, - "outputs": [], - "source": [ - "def check_code_function(fig, horizontal_axis, function_name, correct_function, par_x_axis, f_margin = 0.001):\n", - " # The error margin (f_margin) is set at 0.1% if it is not defined\n", - "\n", - " # make the graph and show the correct answer\n", - " ax = fig.subplots()\n", - " pane = pn.pane.Matplotlib(fig, dpi=100)\n", - " \n", - " # Plot the answer if the students function is found\n", - " try:\n", - " # Load the student-made function from globals\n", - " function = globals()[function_name]\n", - "\n", - " # Add the arguments to the name of the function, for eval()\n", - " # + Replace the argument on the x-axis so that it can be assessed with list comprehension.\n", - " # https://docs.python.org/3/library/inspect.html\n", - " # https://peps.python.org/pep-0362/ \n", - " sig = signature(function)\n", - " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", - " function = function_name + str(sig_function)\n", - " title = function_name + str(sig)\n", - " title = title.replace('_', ' ')\n", - "\n", - " sig = signature(correct_function) \n", - " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", - " correct_function2 = str(correct_function.__name__) + sig_function\n", - "\n", - " # calculate the student's answer through eval()\n", - " student_answer = [eval(function) for par_x_axis in horizontal_axis]\n", - "\n", - " #calculate the correct answer\n", - " correct_answer = []\n", - " for par_x_axis in horizontal_axis:\n", - " correct_answer.append(eval(correct_function2))\n", - " # This code should work, but it does not\n", - " #correct_answer = [eval(correct_function2) for par_x_axis in horizontal_axis]\n", - "\n", - " # check if the answer is correct and plot it before/below lines\n", - " changes = np.array(student_answer) - np.array(correct_answer)\n", - " inaccuracy = np.abs(1-np.array(correct_answer)/np.array(student_answer))\n", - " \n", - " if np.max(changes) == 0:\n", - " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", - " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Perfect!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", - "\n", - " if np.max(changes) != 0 and np.max(inaccuracy) < f_margin:\n", - " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", - " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Good!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", - " \n", - " # plot the answers\n", - " line = ax.plot(horizontal_axis, student_answer, label = 'Your answer')\n", - " line = ax.plot(horizontal_axis, correct_answer, label = 'Correct answer')\n", - " ax.legend()\n", - " ax.set_title(title)\n", - " \n", - " except:\n", - " text_failed = 'Almost there, \\n your function can not be plotted, \\n please try to fix the bug'\n", - " x_ticks = ax.get_xticks()\n", - " y_ticks = ax.get_yticks()\n", - " text = ax.text(np.average(x_ticks),np.average(y_ticks), text_failed, fontsize=16, color = 'r', ha='center', va='center')\n", - "\n", - " # update the graph\n", - " display(pane)" - ] - }, - { - "cell_type": "markdown", - "id": "a91c6671-026a-43ff-adad-73447d914e33", - "metadata": {}, - "source": [ - "This is the code related to building one question, and will be loaded/imported into the notebook
\n", - "This question/function defines:\n", - "- the name of the function is that students make\n", - "- the name of the variable that is used on the x-axis\n", - "- the correct function\n", - "- the maximum allowable error margin (is a factor)\n", - "- the size of the figure" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e2666f03-f2cb-4dfb-a875-059335321eec", - "metadata": {}, - "outputs": [], - "source": [ - "def Show_question_1():\n", - " # define the name of the function that the students will make\n", - " function_name = \"student_function\"\n", - "\n", - " # define the name of the parameter plotted on the horizontal axis\n", - " parameter_x_axis = 'x'\n", - "\n", - " # set the horizontal axis of the graph\n", - " horizontal_axis = np.arange(0,10+1,1)\n", - "\n", - " # define the correct function\n", - " def correct_function(a,b,x):\n", - " y = a*x + b\n", - " return y\n", - "\n", - " # set the acceptable computational error (ratio)\n", - " f_margin = 0.001 # 0.001 = 0.01%\n", - "\n", - " # set the size of the figure\n", - " fig = Figure((5,2.5))\n", - "\n", - " # call the function that builds the backend.\n", - " check_code_function(fig, horizontal_axis, function_name, correct_function, parameter_x_axis, f_margin)" - ] - }, - { - "cell_type": "markdown", - "id": "1853ec75-2a94-4d8e-8b9f-df301d460781", - "metadata": {}, - "source": [ - "The cell below shows an example of how the question can be asked in the notebook that is provided to students. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1a963d87-a461-471e-9c15-7ef5ac54fd8d", - "metadata": {}, - "outputs": [], - "source": [ - "def student_function(x,...):\n", - " y = 'fill this secion in'\n", - " return y\n", - " \n", - "Show_question_1()" - ] - }, - { - "cell_type": "markdown", - "id": "413c97c6-b80c-4a33-975f-8fbaf75d291b", - "metadata": {}, - "source": [ - "Students should define the values of the parameters en the function, which can include new parameters/arguments. The correct funcion should be y=ax+b." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ba788de5-5c7f-4626-8fcc-6fea84a0afd6", - "metadata": {}, - "outputs": [], - "source": [ - "a,b,c = 1,2,0.5,\n", - "x = 3\n", - "def student_function(a,b,x,c):\n", - " y = a*x + b*2 - c*x\n", - " return y\n", - "\n", - "Show_question_1()" - ] - }, - { - "cell_type": "markdown", - "id": "bc12a11d-787f-42a1-927f-6d6b7de8fd96", - "metadata": {}, - "source": [ - "It will give positive response when the function is correct" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2c72bc15-a15c-44c1-8ed6-eba1522f6388", - "metadata": {}, - "outputs": [], - "source": [ - "def student_function(a,b,x):\n", - " y = a*x + b\n", - " return y\n", - "\n", - "Show_question_1()" - ] - }, - { - "cell_type": "markdown", - "id": "1d693b75-28d0-42fa-aa39-03a1768a37f5", - "metadata": {}, - "source": [ - "### Animation graph" - ] - }, - { - "cell_type": "markdown", - "id": "e9624e5d-767e-43c5-8479-2a05513f5e9b", - "metadata": {}, - "source": [ - "The code below gives the option to run an animation that shows harmonic wave components propagating over time." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d2e19499-3f09-4cfd-b099-1b62b0d4d69b", - "metadata": {}, - "outputs": [], - "source": [ - "# set input parameters (can be made with widgets)\n", - "%matplotlib widget\n", - "a = 1\n", - "w = 0.35\n", - "k = 0.25\n", - "delta_t = 30 # ms\n", - "\n", - "# Set initial conditions\n", - "t = 0\n", - "x = np.linspace(0, 100, 1000) # m\n", - "eta = a * np.sin(w * t - k * x)\n", - "\n", - "# Create figure, axes, and initial values\n", - "fig, ax = plt.subplots()\n", - "line, = ax.plot(x, eta)\n", - "\n", - "def update_line(change):\n", - " t = change.new\n", - " eta = a * np.sin(w * t - k * x)\n", - " line.set_ydata(eta)\n", - " fig.canvas.draw()\n", - " #print(t)\n", - "\n", - "discrete_player = pn.widgets.DiscretePlayer(name='Discrete Player', options=np.arange(0,500,delta_t/1000).tolist(), value=0, loop_policy='loop', interval = delta_t)\n", - "discrete_player.param.watch(update_line, 'value')\n", - "\n", - "pn.panel(discrete_player)" - ] - }, - { - "cell_type": "markdown", - "id": "005fee16-a822-44be-b2f6-b132fe571635", - "metadata": {}, - "source": [ - "### Animation graph with widgets" - ] - }, - { - "cell_type": "markdown", - "id": "54f5d24a-afa1-4a88-bf5a-b6745138322c", - "metadata": {}, - "source": [ - "Animated graphs with widgets can use widget input. The widget.observe(function) is required, that will call a function when the value of the widget changes. An application is added as soon as it is realized." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f2355b5e-8196-4ed0-8ad5-22c98d5c94d2", - "metadata": {}, - "outputs": [], - "source": [ - "# Coming soon, it has been functional with IPY widgets before the change to Git Lab has been made." - ] - }, - { - "cell_type": "markdown", - "id": "c4ac4ffe-e6ed-4268-ad99-6b955f40a514", - "metadata": {}, - "source": [ - "### Draw on a graph" - ] - }, - { - "cell_type": "markdown", - "id": "5dc50835-9244-413e-ae81-b0a320cac7eb", - "metadata": {}, - "source": [ - "The code below gives the option to make a line that passes through the drawn points, using cubic splines. In this way students can be asked to draw a graph. The relative values, gradients, or areas underneath the line can be assessed to give a score to the student." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5455e1b5-84a5-4740-b010-35c80682022b", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "\n", - "# list to store x and y coordinates and the lines\n", - "points_x = [0,10]\n", - "points_y = [1,1]\n", - "\n", - "# list of drawn objects\n", - "lines = []\n", - "\n", - "def mouse_click(event):\n", - "\n", - " \n", - " if event.button == 1: # 1 Left mouse button, 3 right mouse button\n", - " points_x.append(event.xdata)\n", - " points_y.append(event.ydata)\n", - " \n", - " scatter1 = ax.scatter(points_x, points_y, c='k', s=40)\n", - " scatter2 = ax.scatter(points_x, points_y, c='w', s=20)\n", - " \n", - " if len(points_x) > 1:\n", - " \n", - " # group the points\n", - " coords = zip(points_x, points_y)\n", - "\n", - " # sort the points on x-coordinate\n", - " sorted_pairs = sorted(coords, key=lambda parameter: parameter[0])\n", - " sorted_x, sorted_y = zip(*sorted_pairs)\n", - " \n", - " # define cubic spline interpolation\n", - " f = CubicSpline(sorted_x, sorted_y, bc_type='natural')\n", - " x_new = np.linspace(0, 10, 1000)\n", - " y_new = f(x_new)\n", - " \n", - " # remove previous lines\n", - " for line in lines:\n", - " line.remove()\n", - " lines.clear()\n", - " \n", - " # plot the new line\n", - " line, = ax.plot(x_new,y_new, linestyle='-', color = 'k')\n", - " lines.append(line)\n", - " \n", - " \n", - " if event.button == 3: # 1 Left mouse button, 3 right mouse button\n", - " \n", - " # get mouse location\n", - " mouse_x = event.xdata\n", - " mouse_y = event.ydata\n", - "\n", - " # get the distances from all the points to the mouse in a list\n", - " distance = []\n", - " for x,y in zip(points_x, points_y):\n", - " distance.append( ((x-mouse_x)**2 + (y-mouse_y)**2)**0.5)\n", - "\n", - " # get the id of the point nearest to the mouse\n", - " id_low = distance.index(min(distance))\n", - " \n", - " if id_low != 0:# and id_low != (len(points_x)):\n", - "\n", - " # delete the closest point\n", - " del(points_x[id_low])\n", - " del(points_y[id_low])\n", - " \n", - " # regroup the points\n", - " coords = zip(points_x, points_y)\n", - "\n", - " # sort the points on x-coordinate\n", - " sorted_pairs = sorted(coords, key=lambda parameter: parameter[0])\n", - " sorted_x, sorted_y = zip(*sorted_pairs)\n", - "\n", - " # define new cubic spline interpolation\n", - " f = CubicSpline(sorted_x, sorted_y, bc_type='natural')\n", - " x_new = np.linspace(0, 10, 100)\n", - " y_new = f(x_new)\n", - "\n", - " # Clear the plot and reset axis settings\n", - " ax.clear() \n", - " ax.set_xlim(0, 10)\n", - " ax.set_ylim(0, 3)\n", - " ax.set_xlabel('Alongshore location (x) [m]')\n", - " ax.set_ylabel('sediment transport rate magnitude')\n", - "\n", - " ## remove previous lines\n", - " for line in lines:\n", - " line.remove()\n", - " lines.clear()\n", - "\n", - " # plot the remaining dots and new spline\n", - " scatter1 = ax.scatter(points_x, points_y, c='k', s=40)\n", - " scatter2 = ax.scatter(points_x, points_y, c='w', s=20)\n", - "\n", - " line, = ax.plot(x_new,y_new, linestyle='-', color = 'k')\n", - " lines.append(line)\n", - "\n", - " fig.canvas.draw()\n", - "\n", - "# Create figure\n", - "fig, ax = plt.subplots()\n", - "fig.canvas.toolbar_visible = False # dont show toolbar\n", - "ax.set_xlim(0, 10)\n", - "#ax.set_ylim(0, 3)\n", - "\n", - "# make initial plot\n", - "scatter1 = ax.scatter(points_x, points_y, c='k', s=40)\n", - "scatter2 = ax.scatter(points_x, points_y, c='w', s=20)\n", - "\n", - "line, = ax.plot(points_x,points_y, linestyle='-', color = 'k')\n", - "lines.append(line)\n", - "\n", - "# activate the function mouse_click when a button is pressed\n", - "fig.canvas.mpl_connect('button_press_event', mouse_click);\n", - "\n", - "plt.xlabel('Alongshore location (x) [m]')\n", - "plt.ylabel('sediment transport rate magnitude')\n", - "\n", - "plt.show();" - ] - }, - { - "cell_type": "markdown", - "id": "7bf40c0a-d649-486f-a208-03167a6b5d68", - "metadata": {}, - "source": [ - "## Full recipe" - ] - }, - { - "cell_type": "markdown", - "id": "97e40bde-a643-4046-97e9-ab3f85001e11", - "metadata": {}, - "source": [ - "In this section of the notebook are the basics for various question types shown. The guide starts with the implementation of making widgets in setting up questions. After that are functions used, to gain additional functionalities and clearer code. The end result is added in the first part of this cookbook. " - ] - }, - { - "cell_type": "markdown", - "id": "bc37e6cf-6d09-4e48-81ce-6a6eaecfdf15", - "metadata": {}, - "source": [ - "More information on the implementation of IPY Widgets in panel can be found at:
\n", - "https://panel.holoviz.org/reference/panes/IPyWidget.html\n", - "\n", - "All the possible widgets are explained at:
\n", - "https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20List.html
\n", - "https://panel.holoviz.org/reference/index.html#widgets
\n", - "Combining these with other utilities in panel gives the possibility to ask a large variety of questions to students." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dc783d4f-02db-42ac-bd85-c294b4f00153", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import panel as pn\n", - "pn.extension(\"ipywidgets\", 'katex')\n", - "import ipywidgets as ipw\n", - "from matplotlib.animation import FuncAnimation\n", - "from matplotlib.ticker import MultipleLocator\n", - "\n", - "from random import shuffle, uniform" - ] - }, - { - "cell_type": "markdown", - "id": "095ced43-3bb6-4a96-b0ba-1e6d87c57d66", - "metadata": {}, - "source": [ - "### Multiple choice" - ] - }, - { - "cell_type": "markdown", - "id": "65f97d58-82cf-468d-b37c-5e45a9abb3ad", - "metadata": {}, - "source": [ - "Here is an example of a multiple-choice selection question. It does not focus on the layout of the widgets. The layout can further be improved by aligning the question_widget and choices_widget (and submit button and feedback widget) through one HBox and by setting the width and optionally colors etc of the widgets." - ] - }, - { - "cell_type": "markdown", - "id": "9bfdf346-bfd3-4758-96ae-f5b65fda3122", - "metadata": {}, - "source": [ - "#### Single question" - ] - }, - { - "cell_type": "markdown", - "id": "d1d5d45a-e6a5-4daa-a84e-c9e51814a15a", - "metadata": {}, - "source": [ - "Give a single question where students can select one answer from a pull-down list." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aec2e474-b0f0-42b8-b0f8-3520bb6d509a", - "metadata": {}, - "outputs": [], - "source": [ - "# The information of the question\n", - "question_1 = \"A large continental shelf width is at a:\"\n", - "choices_1 = [\"Leading edge\", \"Trailing edge\", \"Marginal sea\"]\n", - "answer_1 = choices_1[1]\n", - "hint_1 = \"Unfortunately not, here is a hint ...\"\n", - "comment_1 = \"Indeed, some additional information...\"\n", - "\n", - "# Make the required widgets\n", - "question_widget = pn.widgets.StaticText(value=question_1)\n", - "choices_widget = pn.widgets.Select(options=choices_1, name=\"\")\n", - "submit_button = pn.widgets.Button(name=\"Submit\")\n", - "feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - "\n", - "# Align the widgets vertically\n", - "quiz_widget = pn.Column(question_widget, choices_widget, submit_button, feedback_widget)\n", - "\n", - "def check_answers(event):\n", - " chosen_answer = choices_widget.value\n", - " correct_answer = answer_1\n", - "\n", - " if chosen_answer == correct_answer:\n", - " feedback_widget.value = comment_1\n", - " else:\n", - " feedback_widget.value = hint_1\n", - "\n", - "# Run the function check_answers when the submit button is pressed\n", - "submit_button.on_click(check_answers)\n", - "\n", - "# display the panel\n", - "pn.panel(quiz_widget).servable()" - ] - }, - { - "cell_type": "markdown", - "id": "0e58b824-c2eb-4f69-bcca-fab978d96437", - "metadata": {}, - "source": [ - "This question can be improved by alligning the submit button and feedback widget next to each other, which can be done by pn.Row()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3647a2db-89d9-4eb0-830b-a4b0c2a4becd", - "metadata": {}, - "outputs": [], - "source": [ - "def single_multiple_choice():\n", - " # The information of the question\n", - " question_1 = \"A large continental shelf width is at a:\"\n", - " choices_1 = [\"Leading edge\", \"Trailing edge\", \"Marginal sea\"]\n", - " answer_1 = choices_1[1]\n", - " hint_1 = \"Unfortunately not, here is a hint ...\"\n", - " comment_1 = \"Indeed, some additional information...\"\n", - "\n", - " # Make the required widgets\n", - " question_widget = pn.widgets.StaticText(value=question_1)\n", - " choices_widget = pn.widgets.Select(options=choices_1, name=\"\")\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - "\n", - " def check_answers(event):\n", - " chosen_answer = choices_widget.value\n", - " correct_answer = answer_1\n", - " \n", - " if chosen_answer == correct_answer:\n", - " feedback_widget.value = comment_1\n", - " else:\n", - " feedback_widget.value = hint_1\n", - "\n", - " # Run the function check_answers when the submit button is pressed\n", - " submit_button.on_click(check_answers)\n", - "\n", - " # Align the widgets\n", - " submit_row = pn.Row(submit_button, feedback_widget)\n", - " quiz_widget = pn.Column(question_widget, choices_widget, submit_row)\n", - " display(quiz_widget)\n", - "\n", - "single_multiple_choice()" - ] - }, - { - "cell_type": "markdown", - "id": "ba862327-c012-41c1-aa98-959261de608f", - "metadata": {}, - "source": [ - "#### Multiple questions" - ] - }, - { - "cell_type": "markdown", - "id": "b9409067-354c-44f5-be2b-14d5d81414ce", - "metadata": {}, - "source": [ - "Ask multiple questions and give the total number of correct answers in the score." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "014aaffd-6391-413e-b862-690bf03a7ae4", - "metadata": {}, - "outputs": [], - "source": [ - "def Multiple_questions():\n", - " # The information of the questions\n", - " question_1 = \"A large continental shelf width is at a:\"\n", - " choices_1 = [\"Leading edge\", \"Trailing edge\", \"marginal sea\"]\n", - " answer_1 = choices_1[1]\n", - " hint_1 = \"Unfortunately not, here is a hint ...\"\n", - " comment_1 = \"Indeed, .. some additional information ... \"\n", - " \n", - " question_2 = \"The oceanic geoid is: \"\n", - " choices_2 = [\n", - " \"An oval shape\",\n", - " \"The shape of the ocean surface under only gravity forces\",\n", - " \"A geo triangle with a different shape\",\n", - " \"The actual ocean surface\",\n", - " ]\n", - " answer_2 = choices_2[1]\n", - " hint_2 = \"Unfortunately not, here is a hint ...\"\n", - " comment_2 = \"Indeed, .. some additional information ... \"\n", - " \n", - " # store the questions in a list\n", - " questions = [question_1, question_2]\n", - " choices = [choices_1, choices_2]\n", - " answers = [answer_1, answer_2]\n", - " hints = [hint_1, hint_2]\n", - " comments = [comment_1, comment_2]\n", - " \n", - " # An empty list to store the widgets\n", - " all_widgets = [] # for visualization, store all the widgets in the order they are going to be displayed\n", - " question_widgets = [] # store all the question widgets in a list\n", - " choices_widgets = [] # store all the choices widgets in a list\n", - " \n", - " # make the widgets in a loop, one widget states the question and one shows the options that can be selected.\n", - " for question, choice, answer, hint, comment in zip(questions, choices, answers, hints, comments):\n", - " question_widget = pn.widgets.StaticText(value=question)\n", - " choices_widget = pn.widgets.Select(options=choice, name=\"\")\n", - " \n", - " question_widgets.append(question_widget)\n", - " choices_widgets.append(choices_widget)\n", - " all_widgets.append(question_widget)\n", - " all_widgets.append(choices_widget)\n", - " \n", - " # make a submit button and a feedback button\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\")\n", - " \n", - " # allign the submit button and the feedback widget horizontally\n", - " HBox_check = pn.Row(submit_button, feedback_widget)\n", - " all_widgets.append(HBox_check)\n", - " \n", - " # unpacks the list by an asterix (*)\n", - " quiz_widget = pn.Column(*all_widgets)\n", - " \n", - " # make a function to calculate the score and to give feedback\n", - " def check_answers(event):\n", - " score = 0\n", - " \n", - " for i in range(len(questions)):\n", - " answer = choices_widgets[i].value\n", - " correct_answer = answers[i]\n", - " \n", - " if answer == correct_answer:\n", - " score += 1\n", - " \n", - " feedback_widget.value = \"Your score is \" + str(score) + \"/\" + str(len(questions))\n", - " \n", - " submit_button.on_click(check_answers)\n", - " \n", - " # display the panel\n", - " display(quiz_widget)\n", - "\n", - "Multiple_questions()" - ] - }, - { - "cell_type": "markdown", - "id": "c461594f-75a7-4faa-9355-344058ff424a", - "metadata": {}, - "source": [ - "The code below includes bars for feedback. The width of the widgets is also defined.
\n", - "feedback_widget = pn.widgets.StaticText() might be a easy alternative for having longer feedback. See more in the second part under the heading \"Layout feedback widget\"
\n", - "In the code below is the feedback_widget renamed to final_feedback_widget. And is a feedback_widget made in the loop. The check_answers function is extended by providing the correct text (value) when the submit_button is pressed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0aaf9f38-274c-47ec-a04f-3343fb1cf119", - "metadata": {}, - "outputs": [], - "source": [ - "def several_multiple_choice():\n", - " # The information of the questions\n", - " question_1 = \"A large continental shelf width is at a:\"\n", - " choices_1 = [\"Leading edge\", \"Trailing edge\", \"marginal sea\"]\n", - " answer_1 = choices_1[1]\n", - " hint_1 = \"Unfortunately not, here is a hint ...\"\n", - " comment_1 = \"Indeed, .. some additional information ... \"\n", - " \n", - " question_2 = \"The oceanic geoid is: \"\n", - " choices_2 = [\n", - " \"An oval shape\",\n", - " \"The shape of the ocean surface under only gravity forces\",\n", - " \"A geo triangle with a different shape\",\n", - " \"The actual ocean surface\",\n", - " ]\n", - " answer_2 = choices_2[1]\n", - " hint_2 = \"Unfortunately not, here is a hint ...\"\n", - " comment_2 = \"Indeed, .. some additional information ... \"\n", - " \n", - " # store the questions in a list\n", - " questions = [question_1, question_2]\n", - " choices = [choices_1, choices_2]\n", - " answers = [answer_1, answer_2]\n", - " hints = [hint_1, hint_2]\n", - " comments = [comment_1, comment_2]\n", - " \n", - " # An empty list to store the widgets\n", - " all_widgets = [] # For visualization, store all the widgets in the order they are going to be displayed\n", - " question_widgets = [] # Store all the question widgets in a list\n", - " choices_widgets = [] # store all the choices widgets in a list\n", - " feedback_widgets = [] # store all the feedback widgets for subquestions\n", - " \n", - " # make the widgets in a loop, one widget states the question and one shows the options that can be selected.\n", - " for question, choice, answer in zip(questions, choices, answers):\n", - " question_widget = pn.widgets.StaticText(value=question, width = 1000)\n", - " choices_widget = pn.widgets.Select(options=choice, name=\"\", width = 400)\n", - " feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width = 600)\n", - "\n", - " # store the widgets on type, make looping through them possible, the order is unaffected by randomizing\n", - " question_widgets.append(question_widget)\n", - " choices_widgets.append(choices_widget)\n", - " feedback_widgets.append(feedback_widget)\n", - "\n", - " # store the widgets (again) for display purposes. It remain the same objects as in the previous list. \n", - " allign_question_feedback = pn.Row(choices_widget, feedback_widget)\n", - " \n", - " all_widgets.append(question_widget)\n", - " all_widgets.append(allign_question_feedback)\n", - " \n", - " # make a submit button and a feedback button\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " final_feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\")\n", - " \n", - " # Allign the submit button and the feedback widget horizontally\n", - " HBox_check = pn.Row(submit_button, final_feedback_widget)\n", - " all_widgets.append(HBox_check)\n", - " \n", - " # unpacks the list by an asterisk (*)\n", - " quiz_widget = pn.Column(*all_widgets)\n", - " \n", - " # make a function to calculate the score and to give feedback\n", - " def check_answers(event):\n", - " score = 0\n", - " \n", - " for i in range(len(questions)):\n", - " answer = choices_widgets[i].value\n", - " correct_answer = answers[i]\n", - " \n", - " if answer == correct_answer:\n", - " score += 1\n", - " feedback_widgets[i].value = comments[i]\n", - "\n", - " if answer != correct_answer:\n", - " feedback_widgets[i].value = hints[i]\n", - " \n", - " final_feedback_widget.value = \"Your score is \" + str(score) + \"/\" + str(len(questions))\n", - " \n", - " submit_button.on_click(check_answers)\n", - " display(quiz_widget)\n", - "\n", - "several_multiple_choice()" - ] - }, - { - "cell_type": "markdown", - "id": "3dac822e-63dd-4d82-a843-8fe26e67e035", - "metadata": {}, - "source": [ - "### Multiple selection" - ] - }, - { - "cell_type": "markdown", - "id": "5a4940c9-597b-460b-8e9e-0fd74d272c58", - "metadata": {}, - "source": [ - "In the code below are correct and false statements presented. Students have to select the check boxes related to correct answers. Students gain points when they choose the correct answers but lose points if they check incorrect answers. The lowest score is 0 points." - ] - }, - { - "cell_type": "markdown", - "id": "4997d987-5d71-40de-8a0a-53a34c3626bf", - "metadata": {}, - "source": [ - "An alternative check box that has a different layout.
\n", - "toggle = pn.widgets.Toggle(name='Toggle', button_type='success')
\n", - "https://panel.holoviz.org/reference/widgets/Toggle.html" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55622e3d-784c-4890-8226-83b0275976a8", - "metadata": {}, - "outputs": [], - "source": [ - "def multiple_selection():\n", - " correct_statements = [\"Correct\", \"The earth is round\", \"Good\"]\n", - " false_statements = [\"False\", \"The earth is a cube\"]\n", - " \n", - " # Make empty list to store the widgets (references), checkbox, and true/false statements sorted.\n", - " check_boxes = [] # all the boxes to click\n", - " all_statements = [] # all the statements\n", - " \n", - " # An empty list for visualization to store the HBoxes that contain the widgets, one statement and the corresponding checkbox\n", - " all_widgets = []\n", - " \n", - " # Create Panel widgets\n", - " pn.extension()\n", - " \n", - " for statement in correct_statements + false_statements:\n", - " add_statement = pn.widgets.StaticText(value=statement, width=150)\n", - " check_box_widget = pn.widgets.Checkbox(value=False, width=120)\n", - " HBox1 = pn.Row(add_statement, check_box_widget)\n", - " \n", - " all_statements.append(add_statement)\n", - " check_boxes.append(check_box_widget)\n", - " all_widgets.append(HBox1)\n", - " \n", - " # randomize the order of statements\n", - " shuffle(all_widgets)\n", - " \n", - " # add submit button and output, which come on the bottom\n", - " submit_button = pn.widgets.Button(name='Submit')\n", - " output_widget = pn.widgets.TextInput(value='', placeholder='', disabled=False)\n", - " \n", - " # make an additional HBox for aligning the submit button and the output widget\n", - " HBox2 = pn.Row(submit_button, output_widget)\n", - " all_widgets.append(HBox2)\n", - " \n", - " # align all the HBoxes beneath each other (oldest below if not randomized) and display them.\n", - " quiz_widget = pn.Column(*all_widgets)\n", - " \n", - " # Check the checkbox for each statement and calculate the score.\n", - " def check_answers(event):\n", - " score = 0\n", - " \n", - " for i in range(len(check_boxes)):\n", - " check_box = check_boxes[i]\n", - " statement = all_statements[i].value\n", - " \n", - " if statement in correct_statements:\n", - " if check_box.value == True:\n", - " score += 1\n", - " else:\n", - " score -= 0\n", - " \n", - " if statement not in correct_statements:\n", - " if check_box.value == True:\n", - " score -= 1\n", - " else:\n", - " score -= 0\n", - " \n", - " score = np.max([score, 0])\n", - " output_widget.value = 'Your final score is: ' + str(score)\n", - " \n", - " submit_button.on_click(check_answers)\n", - " \n", - " display(quiz_widget)\n", - "\n", - "multiple_selection()" - ] - }, - { - "cell_type": "markdown", - "id": "f0b538e6-6713-458d-99e0-f6f3768b0195", - "metadata": {}, - "source": [ - "### Select correct statement" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "86487ad1-8a1a-4375-8f9d-4ed034571112", - "metadata": {}, - "outputs": [], - "source": [ - "def select_statement():\n", - "\n", - " # define the questions and store them in a list\n", - " Q1 = 'Question 1'\n", - " Q2 = 'Question 2'\n", - " questions = [Q1, Q2]\n", - "\n", - " # the possible answers (may very per question)\n", - " answers = [\"Shallow\", \"Intermediate\", \"Deep\"]\n", - "\n", - " # the correct answers, 0-based id\n", - " correct_answers_id = [0,1]\n", - "\n", - " # define the widgets for visualization, make for each a row with question and answer\n", - " # store all the rows with statements in a list for visualization, and in toggle_widgets for checking the answer\n", - " Rows = []\n", - " toggle_widgets = []\n", - " for i in range(len(correct_answers_id)):\n", - " question_widget = pn.widgets.StaticText(value=questions[i], width = 150)#statement\n", - " \n", - " radio_group_widget = pn.widgets.RadioButtonGroup(name='Radio Button Group', options=answers, button_type='default')\n", - " toggle_widgets.append(radio_group_widget)\n", - " \n", - " add_row = pn.Row(question_widget, radio_group_widget)\n", - " Rows.append(add_row)\n", - "\n", - " # randomize the order of statements\n", - " shuffle(Rows)\n", - "\n", - " # add a submit button with a feedback option next to it\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=500)\n", - " submit_row = pn.Row(submit_button, feedback_widget)\n", - " \n", - " # include a question\n", - " text_general = \"Select if the wave described on the right experiences shallow, intermediate, or deep water.\"\n", - " text_widget = pn.widgets.StaticText(value=text_general)\n", - "\n", - " # structure the widgets and display thems\n", - " display(text_widget, *Rows, submit_row)\n", - "\n", - " # check the answer and give feedback\n", - " def check_answers(button):\n", - " score = 0\n", - "\n", - " for i in range(len(correct_answers_id)):\n", - " if toggle_widgets[i].value == answers[correct_answers_id[i]]:\n", - " score += 1\n", - "\n", - " # print(toggle_widget.value)\n", - " feedback_widget.value = \"Your score is \" + str(score) + \"/\" + str(len(correct_answers_id))\n", - "\n", - " submit_button.on_click(check_answers)\n", - "\n", - "select_statement()" - ] - }, - { - "cell_type": "markdown", - "id": "00cfbb95-4fa6-445c-8940-80aa70a3db1d", - "metadata": {}, - "source": [ - "### Select figures" - ] - }, - { - "cell_type": "markdown", - "id": "da18023f-e902-4008-8b37-f8e66bd99346", - "metadata": {}, - "source": [ - "make a slider in which students can use a slider to select a figure. How path_figures can be defined using Git Hub have to be coordinated. The code is now as raw text to prevent it from running and giving an error notification that the figures are not found.\n", - "\n", - "The figures and code that displays them through widgets was made by Mario van den Berg, for the test notebook about the escofier curves." - ] - }, - { - "cell_type": "raw", - "id": "a4663ef6-38c7-4d71-98e3-f08b8721bc32", - "metadata": {}, - "source": [ - "# Load the images\n", - "images = [\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_1.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_2.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_3.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_4.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_5.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_6.png\")),\n", - " Image.open(Path.joinpath(path_figures, \"04_Escoffier_interactive_7.png\"))\n", - "]\n", - "\n", - "# Create the slider widget\n", - "slider = ipw.IntSlider(min=0, max=len(images)-1, step=1, value=0)\n", - "\n", - "# Display the current image\n", - "image_widget = ipw.Image(value=images[slider.value]._repr_png_(), format='png', width='95%')\n", - "\n", - "# Define the update function\n", - "def update_image(change):\n", - " image_widget.value = images[change.new]._repr_png_()\n", - "\n", - "# Attach the update function to the slider\n", - "slider.observe(update_image, names='value')\n", - "\n", - "# Display the widgets\n", - "display(slider, image_widget)" - ] - }, - { - "cell_type": "markdown", - "id": "2b6bca4c-5c46-43e7-890b-4947c0fcefb9", - "metadata": {}, - "source": [ - "### Numerical questions" - ] - }, - { - "cell_type": "markdown", - "id": "23712226-88ec-4ac8-92e5-b742f3c9d5f6", - "metadata": {}, - "source": [ - "Two variants are made, one gives directly the correct answer andd one has the capability of giving a textual feedback." - ] - }, - { - "cell_type": "markdown", - "id": "1ea3b1f6-3874-4008-86b7-b6fc0d180703", - "metadata": {}, - "source": [ - "#### Direct answer in feedback" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7f51a82b-3a0a-4913-b970-d94141ebb995", - "metadata": {}, - "outputs": [], - "source": [ - "T1 = 6#s\n", - "\n", - "Question = \"Q1) What is the deep water wavelength?\"\n", - "units = \" m\"\n", - "L = 9.81 * T1**2 / (2 * np.pi)\n", - "answer = round(L, 2)\n", - "\n", - "question_widget = pn.widgets.StaticText(value=Question, width = 750)\n", - "unit_widget = pn.widgets.StaticText(value=units, width = 10)\n", - "num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - "feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - "submit_button = pn.widgets.Button(name=\"Submit\")\n", - "\n", - "Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget)\n", - "quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - "\n", - "def check_answers(button, answer = answer):\n", - " # get value from widget and check if this corresponds with the answer\n", - " response = num_widget.value\n", - "\n", - " if response == answer: # if the answer is correct\n", - " feedback_widget.value = str(\"Good job, this is correct\")\n", - " else: # the answer is wrong\n", - " feedback_widget.value = str(\"Incorrect, the answer should be \" + str(answer) + str(units) + \", please try again.\")\n", - "\n", - "submit_button.on_click(check_answers)\n", - "\n", - "display(quiz_widget)\n", - "\n", - "#quiz_widget.servable() # display is recommended when it is put in a function." - ] - }, - { - "cell_type": "markdown", - "id": "92525126-a836-4915-aea9-c4139b468a6b", - "metadata": {}, - "source": [ - "#### Textual feedback" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0402dd9d-16a8-42cf-b08c-51d0da9147e5", - "metadata": {}, - "outputs": [], - "source": [ - "T1 = 6#s\n", - "\n", - "question = \"Q1) What is the deep water wavelength?\"\n", - "units = \" m\"\n", - "L = 9.81 * T1**2 / (2 * np.pi)\n", - "answer = round(L, 2)\n", - "Feedback_correct = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - "Feedback_wrong = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - "\n", - "question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - "unit_widget = pn.widgets.StaticText(value=units, width = 10)\n", - "num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - "feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - "submit_button = pn.widgets.Button(name=\"Submit\")\n", - "\n", - "Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget)\n", - "quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - "\n", - "def check_answers(button, answer = answer):\n", - " # get value from widget and check if this corresponds with the answer\n", - " response = num_widget.value\n", - "\n", - " if response == answer: # if the answer is correct\n", - " feedback_widget.value = Feedback_correct\n", - " else: # the answer is wrong\n", - " feedback_widget.value = Feedback_wrong\n", - "\n", - "submit_button.on_click(check_answers)\n", - "\n", - "display(quiz_widget)\n" - ] - }, - { - "cell_type": "markdown", - "id": "cdc7e820-9911-422c-a9f3-900f4a2eeeb5", - "metadata": {}, - "source": [ - "### Coding question: Check values of parameter" - ] - }, - { - "cell_type": "markdown", - "id": "648c719b-2522-4960-aa15-77272fc3aeff", - "metadata": {}, - "source": [ - "Students can be asked to compute values of specific parameters in a code field. The students can then load their result into a widget, which has the option to check their results with the answers and gives feedback. It gives a notification if one of the parameters is not defined when loading. It also gives the correct answer after 3 attempts." - ] - }, - { - "cell_type": "markdown", - "id": "4bd0a3a6-45e7-4276-bfa0-ccb23faa1baf", - "metadata": {}, - "source": [ - "Below is the code that builds a User Interface (UI), assesses their input, and gives a response. It has to be defined/imported only once. It uses a class to store parameter values, as described in the section 'Working with widgets/parameters/variables in functions'." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8ef9e308-8326-4dab-900b-62c9fd1556bf", - "metadata": {}, - "outputs": [], - "source": [ - "# general code, that has to be defined once\n", - "class class_variables:\n", - " def __setattr__(self, key, value):\n", - " object.__setattr__(self, key, value)\n", - "\n", - "def classify_variables(params, params2 = {}, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " FV = class_variables()\n", - " for key, value in {**params,**params2}.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def add_local_variables_to(FV,locals, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " for key, value in locals.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def check_code_values(IV):\n", - " # The function variable (FV) is now defined as Input Variable (IV)\n", - " # The input variable and the newly defined local variable will be merged to FV\n", - "\n", - " \n", - " def get_coded_values(FV,GV):\n", - " def button_callback(b):\n", - " try:\n", - " for i, param in enumerate(FV.check_parameters):\n", - " \n", - " # get the value that students gave\n", - " response = getattr(GV, param)\n", - " \n", - " #store them in the widget\n", - " FV.all_parameter_widgets[i].value = response\n", - " \n", - " FV.debug_widget.value = ''\n", - " except:\n", - " FV.debug_widget.value = ' Careful, not all parameters are defined! '\n", - " \n", - " return button_callback\n", - "\n", - " def check_coded_values(FV,GV):\n", - " def button_callback(b):\n", - " FV.attempt.value += 1\n", - " \n", - " for i, param in enumerate(FV.check_parameters):\n", - " #response = getattr(GV, param)\n", - " response = FV.all_parameter_widgets[i].value\n", - " answer = getattr(FV, param)\n", - " \n", - " if np.abs(response - answer) < FV.f_margin * answer:\n", - " FV.all_feedback_widgets[i].value = 'Nice, this is good!'\n", - " \n", - " if np.abs(response - answer) >= FV.f_margin * answer:\n", - " if FV.attempt.value < 3:\n", - " FV.all_feedback_widgets[i].value = 'This one is incorrect, try again!'\n", - " if FV.attempt.value >= 3:\n", - " FV.all_feedback_widgets[i].value = 'This one is incorrect, the answer should be ' + str(answer) + '.'\n", - " \n", - " #print('Debug: ', param, response, answer)\n", - " \n", - " return button_callback\n", - "\n", - " all_parameter_widgets = []\n", - " info_widgets = []\n", - " all_feedback_widgets = []\n", - " for name, param in zip(IV.name_parameters, IV.check_parameters):\n", - " symbol_widget = pn.widgets.StaticText(name='', value= name, width = 100)\n", - " parameter_widget = pn.widgets.FloatInput(name='', value=0, width = 100)\n", - " feedback_widget = pn.widgets.StaticText(value='')\n", - " \n", - " all_parameter_widgets.append(parameter_widget)\n", - " all_feedback_widgets.append(feedback_widget)\n", - " \n", - " new_row = pn.Row(symbol_widget, parameter_widget, feedback_widget)\n", - " info_widgets.append(new_row)\n", - "\n", - " debug_widget = pn.widgets.StaticText(value='')\n", - " #attempt = pn.widgets.FloatInput(value=0)\n", - " \n", - " get_values_button = pn.widgets.Button(name=\"Load values\")\n", - " check_values_button = pn.widgets.Button(name=\"Check loaded values\")\n", - "\n", - " GV = classify_variables(globals())\n", - " FV = add_local_variables_to(IV,locals())\n", - "\n", - " # The error margin is set at 0.01% if it is not defined\n", - " if 'f_margin' not in FV.__dict__:\n", - " FV.__setattr__('f_margin', 0.0001)\n", - " \n", - " get_values_button.on_click(get_coded_values(FV, GV))\n", - " check_values_button.on_click(check_coded_values(FV, GV))\n", - " \n", - " row_buttons = pn.Row(get_values_button, check_values_button, debug_widget)\n", - " Input_col = pn.Column(row_buttons, *info_widgets)\n", - " display(Input_col)" - ] - }, - { - "cell_type": "markdown", - "id": "fdc9d298-467c-4744-8e43-e2ebd2067bb1", - "metadata": {}, - "source": [ - "The function below is used to ask the question to the students, it defines the given question and parameters, displays the question on the screen, and has a widget to store the number of attempts that students have made to check their answers.\n", - "\n", - "The structure is:\n", - "- Give the question-rated parameters\n", - "- Define the question\n", - "- Make additional required widgets (don't change them) \n", - "- Make a globally defined parameter, you should give it a unique name.\n", - "- Contribute the question-related parameter and the attempt (widget) to the global variable.\n", - "\n", - "The question-related parameters and the attempt counter are stored in a global variable, so the return and related print are prevented. The attempt (counter) is updated, which is simplified by making it a widget/object rather than a parameter." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9bb20a72-134f-41f3-94b7-6a9a461bb713", - "metadata": {}, - "outputs": [], - "source": [ - "def Question():\n", - " # the question-related parameters\n", - " L1 = 6\n", - " L2 = round(uniform(7,10), 1)\n", - "\n", - " # The question that is asked\n", - " question = 'What is k1 and k2 when the lengts of wave 1 and 2 are ' + str(L1) + ' and ' + str(L2) + ' meter?'\n", - "\n", - "\n", - " # make the attempt counter, one for each subquestion\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - " # In case of multiple sub-question, this can be like.\n", - " #attempt_A = pn.widgets.FloatInput(value=0)\n", - " #attempt_B = pn.widgets.FloatInput(value=0)\n", - "\n", - " \n", - " # Required widgets for functionality, should not be changed\n", - " question_widget = pn.widgets.StaticText(name='', value= question)\n", - " display(question_widget)\n", - "\n", - " # define a new global variable, with a unique name\n", - " global W2_Q9_param \n", - " # Store the question-related parameters and the attempt widgets.\n", - " # This can be done in a 'list; (as below) or a class, with classify_variables()\n", - " # The benefit of this way is that the 'list' of parameters can be copied-paste\n", - " # The benefit of classes is that it is less prone to errors when other functions/questions use them.\n", - " W2_Q9_param = L1, L2, attempt # or with sub-questions: W2_Q9_param = L1, L2, attempt_A, attempt_B" - ] - }, - { - "cell_type": "markdown", - "id": "e3a0f7e4-6850-43e2-81ce-8b1cbb7f1561", - "metadata": {}, - "source": [ - "The following function is used to check the answers the students give.
\n", - "It has the following structure:
\n", - "- Load the parameters stored in the global variable.\n", - " - Make sure that the attempt is named correctly, in case multiple similar widgets are defined.\n", - "- Define the parameter names that have to be checked\n", - "- Define how these parameters are shown on the screen, think about spaces and underscores.\n", - "- Compute the answers\n", - "- A standard part that calls the basic function, which should not be changed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "45b83b00-6c8f-454d-8271-d2f4258df0ad", - "metadata": {}, - "outputs": [], - "source": [ - "def Check_Question():\n", - " # Load the variables, and make sure the attempt is related to the correct counter when there are sub-questions, like attempt = attempt_B\n", - " L1, L2, attempt = W2_Q9_param\n", - " \n", - " # define the parameter names that have to be checked\n", - " check_parameters = ['k1', 'k2']\n", - "\n", - " # define the names of the parameters as they are displayed\n", - " name_parameters = ['k 1', 'k 2']\n", - "\n", - " # define the answers for the questions, which are described in check_parameters\n", - " k1 = 2*np.pi/L1\n", - " k2 = 2*np.pi/L2\n", - "\n", - " # a standard part that stores the parameters and displays the UI\n", - " # f_margin = 0, used to define the maximum error, which a a factor to the answer, default 0.0001 = 0.01%\n", - " FV = classify_variables(locals())\n", - " check_code_values(FV)" - ] - }, - { - "cell_type": "markdown", - "id": "3a254574-1417-4c2c-8cad-9201066c915d", - "metadata": {}, - "source": [ - "The part below comes in the notebooks/Jupyter Books:
\n", - "- It starts with displaying the question, through Question()\n", - "- A cell for the coding questions, it consist of the structure of the code and explanation. The parts students have to code are indicated by 3 dots or red text." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5a4b3cf5-9b4f-4a04-a9ed-d39908624eaa", - "metadata": {}, - "outputs": [], - "source": [ - "Question()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c26c31f3-7bf2-470d-a6bc-9447d125442f", - "metadata": {}, - "outputs": [], - "source": [ - "k1 = ...\n", - "k2 = 'fill this in'\n", - "\n", - "Check_Question()" - ] - }, - { - "cell_type": "markdown", - "id": "5adfe1bd-0eb5-4d41-8305-8da9fb2ec61e", - "metadata": {}, - "source": [ - "Below is the code when a student has filled in valid numbers, from which k1 is correct." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e78925b5-4193-4d1c-be35-b31c154b754e", - "metadata": {}, - "outputs": [], - "source": [ - "L1 = 6\n", - "k1 = 2*np.pi/L1\n", - "k2 = 2\n", - "\n", - "Check_Question()" - ] - }, - { - "cell_type": "markdown", - "id": "6eb70acd-67fb-4bb0-aa2c-62e9a32721f4", - "metadata": {}, - "source": [ - "### Coding question: Check and plot function" - ] - }, - { - "cell_type": "markdown", - "id": "97111320-94b1-4f52-b9ab-423b8900605a", - "metadata": {}, - "source": [ - "The function below loads the function the students make. It will plot this function if this is possible, otherwise it will give a warning. Furtheron is a generic function that can be used to make questions." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "faaf8f2e-5803-4246-962f-4f4a8af26a3b", - "metadata": {}, - "outputs": [], - "source": [ - "def Question():\n", - "\n", - " # define the parameters, which can be loaded from globals()\n", - " a, b = 1,2\n", - "\n", - " # set the horizontal axis of the graph\n", - " horizontal_axis = np.arange(0,10+1,1)\n", - "\n", - " # define the correct function and its values along the x-axis.\n", - " def correct_function(a,b,x):\n", - " y = a*x + b\n", - " return y\n", - " \n", - " correct_answer = [correct_function(a,b,x) for x in horizontal_axis]\n", - "\n", - " # make the graph and show the correct answer\n", - " fig = Figure((5,2.5))\n", - " ax = fig.subplots()\n", - " pane = pn.pane.Matplotlib(fig, dpi=100)\n", - " \n", - " # Plot the answer if the students function is found\n", - " try:\n", - " GV = classify_variables(globals())\n", - " student_function = GV.student_function\n", - " student_answer = [student_function(a,b,x) for x in horizontal_axis]# the arguments should be defined\n", - " line = ax.plot(horizontal_axis, student_answer, label = 'Your answer')\n", - " line = ax.plot(horizontal_axis, correct_answer, label = 'Correct answer')\n", - " ax.legend()\n", - " except:\n", - " text_failed = 'Almost there, \\n your function can not be plotted, \\n please try to fix the bug'\n", - " x_ticks = ax.get_xticks()\n", - " y_ticks = ax.get_yticks()\n", - " text = ax.text(np.average(x_ticks),np.average(y_ticks), text_failed, fontsize=16, color = 'r', ha='center', va='center')\n", - "\n", - " # update the graph\n", - " display(pane)" - ] - }, - { - "cell_type": "markdown", - "id": "c4b1aad9-2d2f-4e0b-af6b-ab8ae0d9e2d8", - "metadata": {}, - "source": [ - "This is a very basic way of setting up the question. And will not give a valid graph." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8c3510f4-f0ca-42c7-b776-9d1f84c039cf", - "metadata": {}, - "outputs": [], - "source": [ - "def student_function(a,b,x):\n", - " y = ...\n", - " return y\n", - "\n", - "a,b,x, = 1,2,3\n", - "\n", - "Question()" - ] - }, - { - "cell_type": "markdown", - "id": "b423d437-7354-47cd-8e5e-8e799afd4ca3", - "metadata": {}, - "source": [ - "Here is a valid function, with a small error." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "19816cfc-bd28-4f38-a50f-d69b3f8a9fc4", - "metadata": {}, - "outputs": [], - "source": [ - "def student_function(a,b,x):\n", - " y = a*x + b*2\n", - " return y\n", - "\n", - "Question()" - ] - }, - { - "cell_type": "markdown", - "id": "f0aee30e-15aa-461d-aba2-de45b3d44761", - "metadata": {}, - "source": [ - "In the cell below is the code more generalized. The list comprehension requires that the arguments are defined. This is solved by building a string of this function with arguments and then evaluating this. In this way can also additional parameters be introduced when the student is completely wrong." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "81914de0-7639-4174-a434-7c707678695a", - "metadata": {}, - "outputs": [], - "source": [ - "def check_code_function(fig, horizontal_axis, function_name, correct_function, par_x_axis, f_margin = 0.001):\n", - " # The error margin (f_margin) is set at 0.1% if it is not defined\n", - "\n", - " # make the graph and show the correct answer\n", - " ax = fig.subplots()\n", - " pane = pn.pane.Matplotlib(fig, dpi=100)\n", - " \n", - " # Plot the answer if the students function is found\n", - " try:\n", - " # Load the student-made function from globals\n", - " function = globals()[function_name]\n", - "\n", - " # Add the arguments to the name of the function, for eval()\n", - " # + Replace the argument on the x-axis so that it can be assessed with list comprehension.\n", - " # https://docs.python.org/3/library/inspect.html\n", - " # https://peps.python.org/pep-0362/ \n", - " sig = signature(function)\n", - " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", - " function = function_name + str(sig_function)\n", - " title = function_name + str(sig)\n", - " title = title.replace('_', ' ')\n", - "\n", - " sig = signature(correct_function) \n", - " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", - " correct_function2 = str(correct_function.__name__) + sig_function\n", - "\n", - " # calculate the student's answer through eval()\n", - " student_answer = [eval(function) for par_x_axis in horizontal_axis]\n", - "\n", - " #calculate the correct answer\n", - " correct_answer = []\n", - " for par_x_axis in horizontal_axis:\n", - " correct_answer.append(eval(correct_function2))\n", - " # This code should work, but it does not\n", - " #correct_answer = [eval(correct_function2) for par_x_axis in horizontal_axis]\n", - "\n", - " # check if the answer is correct and plot it before/below lines\n", - " changes = np.array(student_answer) - np.array(correct_answer)\n", - " inaccuracy = np.abs(1-np.array(correct_answer)/np.array(student_answer))\n", - " \n", - " if np.max(changes) == 0:\n", - " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", - " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Perfect!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", - "\n", - " if np.max(changes) != 0 and np.max(inaccuracy) < f_margin:\n", - " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", - " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Good!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", - " \n", - " # plot the answers\n", - " line = ax.plot(horizontal_axis, student_answer, label = 'Your answer')\n", - " line = ax.plot(horizontal_axis, correct_answer, label = 'Correct answer')\n", - " ax.legend()\n", - " ax.set_title(title)\n", - " \n", - " except:\n", - " text_failed = 'Almost there, \\n your function can not be plotted, \\n please try to fix the bug'\n", - " x_ticks = ax.get_xticks()\n", - " y_ticks = ax.get_yticks()\n", - " text = ax.text(np.average(x_ticks),np.average(y_ticks), text_failed, fontsize=16, color = 'r', ha='center', va='center')\n", - "\n", - " # update the graph\n", - " display(pane)" - ] - }, - { - "cell_type": "markdown", - "id": "9b0a2a2c-dc32-468d-9275-ea9f4a2d21aa", - "metadata": {}, - "source": [ - "The question related to one function, now with less repetive code compared to the first case." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64f2a647-5f16-41c5-87fa-bfd7e14a1093", - "metadata": {}, - "outputs": [], - "source": [ - "def Question():\n", - " # define the name of the function that the students will make\n", - " function_name = \"student_function\"\n", - "\n", - " # define the name of the parameter plotted on the horizontal axis\n", - " parameter_x_axis = 'x'\n", - "\n", - " # set the horizontal axis of the graph\n", - " horizontal_axis = np.arange(0,10+1,1)\n", - "\n", - " # define the correct function\n", - " def correct_function(a,b,x):\n", - " y = a*x + b\n", - " return y\n", - "\n", - " # set the acceptable computational error (ratio)\n", - " f_margin = 0.001 # 0.001 = 0.01%\n", - "\n", - " # set the size of the figure\n", - " fig = Figure((5,2.5))\n", - "\n", - " # call the function that builds the backend.\n", - " check_code_function(fig, horizontal_axis, function_name, correct_function, parameter_x_axis, f_margin)" - ] - }, - { - "cell_type": "markdown", - "id": "ef0ce1ec-812c-4c9a-a8fb-019c456de403", - "metadata": {}, - "source": [ - "An arbitrary wrong answer, the student introduced a new parameter. Note that the x-axis starts at 0, which gives the divide by zero warning with c, which is irrelevant." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ead612c5-a6f9-4bb0-9ee4-35a99985d188", - "metadata": {}, - "outputs": [], - "source": [ - "a,b,x, = 1,2,3\n", - "c = 4\n", - "def student_function(a,b,x,c):\n", - " y = a*x + b*2 - c/x\n", - " return y\n", - " \n", - "Question()" - ] - }, - { - "cell_type": "markdown", - "id": "2ca1a41b-9d4e-4290-9ef4-a4a40fa955c6", - "metadata": {}, - "source": [ - "In the current function, the parameter of the x-axis should be correctly named. It can not plot the graph if the name of the horizontal axis variable is different. So it is recommended to give this parameter, together with the name of the function, to the students. It can be useful to mention that the other relevant parameters should be added in the arguments, it is not possible to have ... or a text in here. The basic layout, without additional explanation is shown below. Replacing more_arguments with dots (...) or a comment will cause an error, which is not necessarily a problem." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f673f6f8-f223-49ba-ba33-7e97b27ad77c", - "metadata": {}, - "outputs": [], - "source": [ - "def name_function(x, more_arguments):\n", - " y = ...\n", - " return y" - ] - }, - { - "cell_type": "markdown", - "id": "5547238e-d5b9-43ab-a9e7-73a0bacb0677", - "metadata": {}, - "source": [ - "### Moving graph" - ] - }, - { - "cell_type": "markdown", - "id": "fa0ac595-096f-48a4-a2ab-271de816d470", - "metadata": {}, - "source": [ - "Three different options for making moving graphs are depicted below. The recommended way is using Discrete Player.\n", - "\n", - "The FuncAnimation works to make graphs, the challenge is to stop the graph (for which automatically a button came in %notebook). So for this is a self-made button required.\n", - "ipw.Play() may be a good alternative. However, the challenge is here if you want to have steps smaller than 1." - ] - }, - { - "cell_type": "markdown", - "id": "0686b157-0155-405b-b9c1-818bedc1ea01", - "metadata": {}, - "source": [ - "#### FuncAnimation" - ] - }, - { - "cell_type": "markdown", - "id": "8c71d90f-d49a-4bea-9d5d-626c6d887364", - "metadata": {}, - "source": [ - "The funcanimation is an interesting tool for displaying graphs that move over time. One downside is that a button has to be coded that can be used to stop the graph. Otherwise it will be running the entire time while it is quite computationally demanding." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9e0e3437-63e5-41b2-acce-888574c944be", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "# manual set interval between frames in seconds\n", - "delta_t = 0.1\n", - "\n", - "# setup linear mesh (x) and duration before time (t) is reset\n", - "x = np.linspace(0, 100, 1000)# m\n", - "t = np.arange(0,1000,delta_t) #\n", - "\n", - "a = 1\n", - "w = 0.35\n", - "k = 0.25\n", - "\n", - "# Create figure and axes\n", - "fig, ax = plt.subplots()\n", - "\n", - "# Compute initial displacement\n", - "eta = a*np.sin(k*x)\n", - "\n", - "# Plot initial wave\n", - "line, = ax.plot(x, eta)\n", - "\n", - "# update the line for each frame\n", - "def update(frame):\n", - " t = delta_t * frame\n", - " eta = a*np.sin(w*t-k*x)\n", - " line.set_ydata(eta)\n", - "\n", - " # usefull in testing, stop the graph at frame 100 without the need for a button/widget\n", - " #if frame > 50:\n", - " # animation.event_source.stop()\n", - "\n", - "# Create animation\n", - "animation = FuncAnimation(fig, update, frames=len(t), interval=delta_t*1000)\n", - "\n", - "# create stop button that stops the graph\n", - "stop_button = ipw.Button(description=\"stop\")\n", - "display(stop_button)\n", - "\n", - "def stop_graph(button):\n", - " animation.event_source.stop()\n", - " plt.close() # deletes the graph that has been stopped, to prevent making multiple graphs that fill the memory)\n", - " \n", - "stop_button.on_click(stop_graph)" - ] - }, - { - "cell_type": "markdown", - "id": "a6bf3aa7-ed7b-43fb-b4a8-1b1d93d07fb0", - "metadata": {}, - "source": [ - "#### play widget" - ] - }, - { - "cell_type": "markdown", - "id": "cbd2da9e-60dd-44d0-8841-ca54070126fb", - "metadata": {}, - "source": [ - "The play widget tool has inbuild options to play and stop the widget. However the stepsize is 1, even when the related widget has a finer resolution. So this tool is not suitable when it should give a smooth graph. The slider below is used for time, which means that the finest temporal resolution is one second." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "11d7a95a-2543-4db2-a262-f1935a9ba945", - "metadata": {}, - "outputs": [], - "source": [ - "#%matplotlib widget\n", - "# set input parameters (can be made with widgets)\n", - "a = 1\n", - "w = 0.35\n", - "k = 0.25\n", - "\n", - "# Set inital conditions\n", - "t = 0\n", - "x = np.linspace(0, 100, 1000) # m\n", - "eta = a * np.sin(w*t - k*x)\n", - "\n", - "# Create figure, axes, and initial values\n", - "fig, ax = plt.subplots()\n", - "line, = ax.plot(x, eta)\n", - "\n", - "play = ipw.Play(\n", - " value=0,\n", - " min=0,\n", - " max=100,\n", - " step=1,\n", - " interval=500, # miliseconds\n", - " description=\"Press play\",\n", - " disabled=False,\n", - ")\n", - "\n", - "slider = ipw.FloatSlider(min=0, max=100, step=0.1)\n", - "ipw.jslink((play, 'value'), (slider, 'value'))\n", - "\n", - "\n", - "def update_line(change):\n", - " t = change.new\n", - " eta = a * np.sin(w * t - k * x)\n", - " line.set_ydata(eta)\n", - " fig.canvas.draw()\n", - "\n", - "\n", - "slider.observe(update_line, names='value')\n", - "\n", - "ipw.HBox([play, slider])" - ] - }, - { - "cell_type": "markdown", - "id": "9fef885d-9522-4852-860f-68f0c4bfa4c1", - "metadata": {}, - "source": [ - "#### Discrete player" - ] - }, - { - "cell_type": "markdown", - "id": "f2f8da14-62fc-414b-999e-6d4c607d9394", - "metadata": {}, - "source": [ - "The discrete player is the third option that has been studied. It gives the option to have a fine temporal resolution and an inbuild tool to stop the graph. Not all the options have been studied." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9436857e-214c-4e1b-b64c-31a051d80118", - "metadata": {}, - "outputs": [], - "source": [ - "# set input parameters (can be made with widgets)\n", - "a = 1\n", - "w = 0.35\n", - "k = 0.25\n", - "delta_t = 30 # ms\n", - "\n", - "# Set initial conditions\n", - "t = 0\n", - "x = np.linspace(0, 100, 1000) # m\n", - "eta = a * np.sin(w * t - k * x)\n", - "\n", - "# Create figure, axes, and initial values\n", - "fig, ax = plt.subplots()\n", - "line, = ax.plot(x, eta)\n", - "\n", - "def update_line(change):\n", - " t = change.new\n", - " eta = a * np.sin(w * t - k * x)\n", - " line.set_ydata(eta)\n", - " fig.canvas.draw()\n", - " #print(t)\n", - "\n", - "discrete_player = pn.widgets.DiscretePlayer(name='Discrete Player', options=np.arange(0,500,delta_t/1000).tolist(), value=0, loop_policy='loop', interval = delta_t)\n", - "discrete_player.param.watch(update_line, 'value')\n", - "\n", - "pn.panel(discrete_player)" - ] - }, - { - "cell_type": "markdown", - "id": "eaeaaba8-ad9a-4563-b3aa-af6495e4e0d7", - "metadata": {}, - "source": [ - "### Interactive graph and spline" - ] - }, - { - "cell_type": "markdown", - "id": "fc65a88f-49b6-4da4-85ee-1a90baecabea", - "metadata": {}, - "source": [ - "A widget that can be used to set points on a graph with a left mouse click. And remove the closest point to the cursur by a right mouse click. \n", - "\n", - "A Spline will be fit through the dots. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ea7a6d44-af85-4dbf-b787-76914bb54ba2", - "metadata": {}, - "outputs": [], - "source": [ - "#import numpy as np\n", - "#import matplotlib.pyplot as plt\n", - "from scipy.interpolate import CubicSpline" - ] - }, - { - "cell_type": "markdown", - "id": "3bb522d2-4a99-4a48-8edb-e18903c1a283", - "metadata": {}, - "source": [ - "https://pythonnumericalmethods.berkeley.edu/notebooks/chapter17.03-Cubic-Spline-Interpolation.html" - ] - }, - { - "cell_type": "markdown", - "id": "caca7897-518b-4f8e-998f-f3797d174cff", - "metadata": {}, - "source": [ - "#### Only dots with spline" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f8dfeb29-7c03-4583-9ce9-fbd0abe9460e", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "\n", - "# list to store x and y coordinates and the lines\n", - "points_x = [0,10]\n", - "points_y = [1,1]\n", - "\n", - "# list of drawn objects\n", - "lines = []\n", - "\n", - "def mouse_click(event):\n", - "\n", - " \n", - " if event.button == 1: # 1 Left mouse button, 3 right mouse button\n", - " points_x.append(event.xdata)\n", - " points_y.append(event.ydata)\n", - " \n", - " scatter1 = ax.scatter(points_x, points_y, c='k', s=40)\n", - " scatter2 = ax.scatter(points_x, points_y, c='w', s=20)\n", - " \n", - " if len(points_x) > 1:\n", - " \n", - " # group the points\n", - " coords = zip(points_x, points_y)\n", - "\n", - " # sort the points on x-coordinate\n", - " sorted_pairs = sorted(coords, key=lambda parameter: parameter[0])\n", - " sorted_x, sorted_y = zip(*sorted_pairs)\n", - " \n", - " # define cubic spline interpolation\n", - " f = CubicSpline(sorted_x, sorted_y, bc_type='natural')\n", - " x_new = np.linspace(0, 10, 1000)\n", - " y_new = f(x_new)\n", - " \n", - " # remove previous lines\n", - " for line in lines:\n", - " line.remove()\n", - " lines.clear()\n", - " \n", - " # plot the new line\n", - " line, = ax.plot(x_new,y_new, linestyle='-', color = 'k')\n", - " lines.append(line)\n", - " \n", - " \n", - " if event.button == 3: # 1 Left mouse button, 3 right mouse button\n", - " \n", - " # get mouse location\n", - " mouse_x = event.xdata\n", - " mouse_y = event.ydata\n", - "\n", - " # get the distances from all the points to the mouse in a list\n", - " distance = []\n", - " for x,y in zip(points_x, points_y):\n", - " distance.append( ((x-mouse_x)**2 + (y-mouse_y)**2)**0.5)\n", - "\n", - " # get the id of the point nearest to the mouse\n", - " id_low = distance.index(min(distance))\n", - " \n", - " if id_low != 0:# and id_low != (len(points_x)):\n", - "\n", - " # delete the closest point\n", - " del(points_x[id_low])\n", - " del(points_y[id_low])\n", - " \n", - " # regroup the points\n", - " coords = zip(points_x, points_y)\n", - "\n", - " # sort the points on x-coordinate\n", - " sorted_pairs = sorted(coords, key=lambda parameter: parameter[0])\n", - " sorted_x, sorted_y = zip(*sorted_pairs)\n", - "\n", - " # define new cubic spline interpolation\n", - " f = CubicSpline(sorted_x, sorted_y, bc_type='natural')\n", - " x_new = np.linspace(0, 10, 100)\n", - " y_new = f(x_new)\n", - "\n", - " # Clear the plot and reset axis settings\n", - " ax.clear() \n", - " ax.set_xlim(0, 10)\n", - " ax.set_ylim(0, 3)\n", - " ax.set_xlabel('Alongshore location (x) [m]')\n", - " ax.set_ylabel('sediment transport rate magnitude')\n", - "\n", - " ## remove previous lines\n", - " for line in lines:\n", - " line.remove()\n", - " lines.clear()\n", - "\n", - " # plot the remaining dots and new spline\n", - " scatter1 = ax.scatter(points_x, points_y, c='k', s=40)\n", - " scatter2 = ax.scatter(points_x, points_y, c='w', s=20)\n", - "\n", - " line, = ax.plot(x_new,y_new, linestyle='-', color = 'k')\n", - " lines.append(line)\n", - "\n", - " fig.canvas.draw()\n", - "\n", - " # debug\n", - " #ax.text(0.02, 0.95, y_new, transform=ax.transAxes, fontsize=5, bbox={'facecolor': 'white'})\n", - "\n", - "# Create figure\n", - "fig, ax = plt.subplots()\n", - "fig.canvas.toolbar_visible = False # dont show toolbar\n", - "ax.set_xlim(0, 10)\n", - "#ax.set_ylim(0, 3)\n", - "\n", - "# make initial plot\n", - "scatter1 = ax.scatter(points_x, points_y, c='k', s=40)\n", - "scatter2 = ax.scatter(points_x, points_y, c='w', s=20)\n", - "\n", - "line, = ax.plot(points_x,points_y, linestyle='-', color = 'k')\n", - "lines.append(line)\n", - "\n", - "# activate the function mouse_click when a button is pressed\n", - "fig.canvas.mpl_connect('button_press_event', mouse_click);\n", - "\n", - "plt.xlabel('Alongshore location (x) [m]')\n", - "plt.ylabel('sediment transport rate magnitude')\n", - "\n", - "plt.show();" - ] - }, - { - "cell_type": "markdown", - "id": "a052cc75-49a7-40b7-ae0e-d0099497e0a2", - "metadata": {}, - "source": [ - "#### Dots with spline and gradient plotted" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7dcf2b23-0a4e-4da7-a6a1-3faf64ae5e05", - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib widget\n", - "\n", - "# list to store x and y coordinates and the lines\n", - "points_x = [0,10]\n", - "points_y = [1,1]\n", - "\n", - "# list of drawn objects\n", - "lines = []\n", - "lines_gradient = []\n", - "\n", - "def calc_gradient(x,y):\n", - " dx = np.diff(x)\n", - " dy = np.diff(y)\n", - " print(len(x), len(dx))\n", - " return dy/dx\n", - "\n", - "\n", - "def mouse_click(event):\n", - " ax = axs[0]\n", - " \n", - " if event.button == 1 and ax.contains(event)[0]: # 1 Left mouse button, within figure boundary\n", - " points_x.append(event.xdata)\n", - " points_y.append(event.ydata)\n", - " \n", - " scatter1 = ax.scatter(points_x, points_y, c='k', s=40)\n", - " scatter2 = ax.scatter(points_x, points_y, c='w', s=20)\n", - " \n", - " if len(points_x) > 1:\n", - " \n", - " # group the points\n", - " coords = zip(points_x, points_y)\n", - "\n", - " # sort the points on x-coordinate\n", - " sorted_pairs = sorted(coords, key=lambda parameter: parameter[0])\n", - " sorted_x, sorted_y = zip(*sorted_pairs)\n", - " \n", - " # define cubic spline interpolation\n", - " f = CubicSpline(sorted_x, sorted_y, bc_type='natural')\n", - " x_new = np.linspace(0, 10, 1000)\n", - " y_new = f(x_new)\n", - " \n", - " # remove previous lines\n", - " for line in lines:\n", - " line.remove()\n", - " lines.clear()\n", - " \n", - " # plot the new line\n", - " line, = ax.plot(x_new,y_new, linestyle='-', color = 'k')\n", - " lines.append(line)\n", - "\n", - " # plot gradient \n", - "\n", - " for line in lines_gradient:\n", - " line.remove()\n", - " lines_gradient.clear()\n", - "\n", - " y_gradient = np.gradient(y_new,x_new)\n", - " gradient, = axs[1].plot(x_new,y_gradient, linestyle='-', color = 'k')\n", - " lines_gradient.append(gradient)\n", - " \n", - " \n", - " if event.button == 3 and ax.contains(event)[0] and len(points_x) >= 3: #3 right mouse button, within figure boundary, and at least 3 points\n", - " \n", - " # get mouse location\n", - " mouse_x = event.xdata\n", - " mouse_y = event.ydata\n", - "\n", - " # get the distances from all the points to the mouse in a list\n", - " distance = []\n", - " for x,y in zip(points_x, points_y):\n", - " distance.append( ((x-mouse_x)**2 + (y-mouse_y)**2)**0.5)\n", - "\n", - " # get the id of the point nearest to the mouse\n", - " id_low = distance.index(min(distance))\n", - " \n", - " if id_low != 0:\n", - "\n", - " # delete the closest point\n", - " del(points_x[id_low])\n", - " del(points_y[id_low])\n", - " \n", - " # regroup the points\n", - " coords = zip(points_x, points_y)\n", - "\n", - " # sort the points on x-coordinate\n", - " sorted_pairs = sorted(coords, key=lambda parameter: parameter[0])\n", - " sorted_x, sorted_y = zip(*sorted_pairs)\n", - "\n", - " # define new cubic spline interpolation\n", - " f = CubicSpline(sorted_x, sorted_y, bc_type='natural')\n", - " x_new = np.linspace(0, 10, 100)\n", - " y_new = f(x_new)\n", - "\n", - " # Clear the plot and reset axis settings\n", - " ax.clear() \n", - " ax.set_xlim(0, 10)\n", - " ax.set_ylim(0, 3)\n", - " ax.set_xlabel('Alongshore location (x) [m]')\n", - " ax.set_ylabel('sediment transport rate magnitude')\n", - "\n", - " ## remove previous lines\n", - " for line in lines:\n", - " line.remove()\n", - " lines.clear()\n", - "\n", - " # plot the remaining dots and new spline\n", - " scatter1 = ax.scatter(points_x, points_y, c='k', s=40)\n", - " scatter2 = ax.scatter(points_x, points_y, c='w', s=20)\n", - "\n", - " line, = ax.plot(x_new,y_new, linestyle='-', color = 'k')\n", - " lines.append(line)\n", - "\n", - " fig.canvas.draw()\n", - "\n", - " # debug\n", - " #axs[0].text(0.02, 0.95, y_new, transform=ax.transAxes, fontsize=5, bbox={'facecolor': 'white'})\n", - "\n", - "# Create figure\n", - "fig, axs = plt.subplots(2,1, sharex = True)\n", - "fig.canvas.toolbar_visible = False\n", - "axs[0].set_xlim(0, 10)\n", - "\n", - "\n", - "# make initial plot\n", - "scatter1 = axs[0].scatter(points_x, points_y, c='k', s=40)\n", - "scatter2 = axs[0].scatter(points_x, points_y, c='w', s=20)\n", - "\n", - "axs[1].axhline(0, color='silver', linestyle='--')\n", - "y_gradient = np.gradient(points_y,points_x)\n", - "gradient, = axs[1].plot(points_x,y_gradient, linestyle='-', color = 'k')\n", - "lines_gradient.append(gradient)\n", - "\n", - "line, = axs[0].plot(points_x,points_y, linestyle='-', color = 'k')\n", - "lines.append(line)\n", - "\n", - "# activate the function mouse_click when a button is pressed\n", - "fig.canvas.mpl_connect('button_press_event', mouse_click);\n", - "\n", - "axs[1].set_xlabel('Alongshore location (x) [m]')\n", - "axs[0].set_ylabel('sediment transport \\n rate magnitude')\n", - "axs[1].set_ylabel('sediment transport \\n rate gradient')\n", - "\n", - "plt.show();" - ] - }, - { - "cell_type": "markdown", - "id": "2862cc07-809f-4f93-9d1a-3a019e659fc9", - "metadata": {}, - "source": [ - "### Adding changing plots as widgets" - ] - }, - { - "cell_type": "markdown", - "id": "0431d68a-2738-45be-95fd-6b5f11f2201f", - "metadata": {}, - "source": [ - "Plots can be redrawn when parameters are changed. This can be done with the [Param component](https://panel.holoviz.org/reference/panes/Param.html) of panel. The code below shows the implementation of having a graph updated when a (submit) button is pressed. The graphs are added similarly to other widgets, now with additional attention to defining the axis of the graph (ax), in which the figure is displayed (fig), and the panes widget (pane). The code below shows them for making two graphs. \n", - "\n", - "It is not recommended to have calculations etc. It is hard to return values. The simplest way (or workaround) is by using widgets and changing the value. Below is the counter used as input to change the value of the graph each time the button is pressed. Further on in this notebook is a section related to working with parameters in functions." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6cc4d1ac-6733-4949-8411-23a5e3c0f7be", - "metadata": {}, - "outputs": [], - "source": [ - "from matplotlib.figure import Figure" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "11cd8a84-3a71-4730-818e-f903db8f8460", - "metadata": {}, - "outputs": [], - "source": [ - "#set plot settings and make plots \n", - "titles = ['plot1', 'graph2']\n", - "figures = []\n", - "plots = []\n", - "panes = []\n", - "for title in titles:\n", - " fig = Figure((5,2.5))\n", - " ax = fig.subplots()\n", - " pane = pn.pane.Matplotlib(fig, dpi=96)\n", - "\n", - " # Add some extra space for labels at the axis\n", - " fig.subplots_adjust(bottom=0.25) \n", - " fig.subplots_adjust(left=0.2)\n", - "\n", - " # store everything in a list\n", - " figures.append(fig)\n", - " plots.append(ax)\n", - " panes.append(pane)\n", - "\n", - "# set a function that defines what has to be done when the button is pressed\n", - "def plot_graph(figures, plots, panes, start_value_right, counter):\n", - "\n", - " def button_callback(b):\n", - " ax = plots[0]\n", - " ax.clear() # remove previous lines\n", - " ax.plot(np.linspace(0,10,10), np.linspace(0,start_value_right + counter.value ,10), label = 'L [m]')\n", - " ax.set_xlabel(\"x-label\")\n", - " panes[0].object = figures[0]\n", - "\n", - " ax = plots[1]\n", - " ax.clear() # remove previous lines\n", - " ax.plot(np.linspace(0,10,10), np.linspace(0,start_value_right - counter.value ,10), label = 'L [m]')\n", - " ax.set_xlabel(\"x-label\")\n", - " panes[1].object = figures[1]\n", - "\n", - " counter.value += 1\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - "start_value_right = 2\n", - "\n", - "counter = pn.widgets.FloatInput(value=0)\n", - "\n", - "submit_button = pn.widgets.Button(name=\"Update graph\")\n", - "submit_button.on_click(plot_graph(figures, plots, panes, start_value_right , counter))\n", - "\n", - "pn.Column(*panes, submit_button)" - ] - }, - { - "cell_type": "markdown", - "id": "25fc19d0-9dfc-43f5-9dbf-e685d3d9ed70", - "metadata": {}, - "source": [ - "### Improving numerical questions" - ] - }, - { - "cell_type": "markdown", - "id": "88f02eaa-1b41-4cb2-bf8f-cf8ceb430319", - "metadata": {}, - "source": [ - "The numerical questions are improved through various features/capabilities. The sections below improve the code stepwise, the final result will be used in the final cookbook." - ] - }, - { - "cell_type": "markdown", - "id": "1dba651c-724d-48ac-8d84-00d691039b4c", - "metadata": {}, - "source": [ - "#### Structure of widgets within code" - ] - }, - { - "cell_type": "markdown", - "id": "7321fd1c-aa9a-47a3-870f-6edaf7db8c6b", - "metadata": {}, - "source": [ - "In the code below are the widgets build within the function that also defines the questions. The same question is shown multiple times to visualize the impact of asking various questions." - ] - }, - { - "cell_type": "markdown", - "id": "5c87241e-11b4-48be-9385-b28a59745a54", - "metadata": {}, - "source": [ - "##### Display widgets seperatly " - ] - }, - { - "cell_type": "markdown", - "id": "27eb2d58-e2e8-4ced-a9aa-d79df952aea5", - "metadata": {}, - "source": [ - "The function check_nummeric_answers can be left outside the function. The benefit of keeping it inside is that this function can be changed without that it has impact on other questions that call a function that is named similarly." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "73fcebda-079f-4615-8776-1f31d2c7af9c", - "metadata": {}, - "outputs": [], - "source": [ - "def Q1():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " text_general = \"Can you asses the wavelength in three different ways? Firstly through an iterative approach, then through tables (Appendix B, table B-3 of the book), and lastly via the formula of Fentom. The wave period ($T$) is \" + str(T1) + \" seconds, and the water depth ($h$) is \" + str(h1) + \" m?\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - " display(text_widget)\n", - "\n", - " Q1_text = \"Q1a) What is the deep water wavelength?\"\n", - " Q1_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(L, 2)\n", - " Q1_FB_G = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - " Q1_FB_W = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - " questions = [Q1_text, Q1_text,Q1_text]\n", - " units = [Q1_unit, Q1_unit, Q1_unit]\n", - " answers = [Q1_answer, Q1_answer, Q1_answer]\n", - " FB_good = [Q1_FB_G, Q1_FB_G, Q1_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q1_FB_W, Q1_FB_W]\n", - " \n", - " # The code below does not have to be changed, only if the layout has to be changed.\n", - "\n", - " # A function that stores the values of the parameters when the submit button is made.\n", - " def check_nummeric_answers(id, num_widget, answer, feedback_widget, FB_G, FB_W):\n", - "\n", - " def button_callback(b):\n", - " #print(\"debug question:\", id, ', response:' ,num_widget.value, ', answer:',answer)\n", - " \n", - " if answer == num_widget.value:\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = FB_W\n", - " print(num_widget.value)\n", - " \n", - " return button_callback # required, otherwise, it gives TypeError: 'NoneType' object is not callable\n", - "\n", - " \n", - " id = 0\n", - " for question, units, answer, Q_FB_G, Q_FB_W in zip(questions, units, answers, FB_good, FB_wrong):\n", - " id += 1\n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - " unit_widget = pn.widgets.StaticText(value=units, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " \n", - " Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget) \n", - " quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - " display(quiz_widget)\n", - "\n", - " # the varies \n", - " submit_button.on_click(check_nummeric_answers(id, num_widget, answer, feedback_widget, Q_FB_G, Q_FB_W))\n", - "Q1()" - ] - }, - { - "cell_type": "markdown", - "id": "c60663a3-36ad-4755-bfd7-d5ea80e6e902", - "metadata": {}, - "source": [ - "##### Display widgets in one column " - ] - }, - { - "cell_type": "markdown", - "id": "57cfc5aa-700f-4f75-9d56-9ac4fb6ce4f9", - "metadata": {}, - "source": [ - "The widgets are added into one column before displaying, which prevents that widgets can move relatively to one another." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "40e2ea75-a14c-4e5d-8383-24a8c77646f9", - "metadata": {}, - "outputs": [], - "source": [ - "def check_nummeric_answers(id, num_widget, answer, feedback_widget, FB_G, FB_W):\n", - "\n", - " def button_callback(b):\n", - " #print(\"debug question:\", id, ', response:' ,num_widget.value, ', answer:',answer)\n", - "\n", - " if answer == num_widget.value:\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = FB_W\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - "def Q1():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " text_general = \"Can you asses the wave length in three different ways? Firstly through an iterative approach, then through tables (Appendix B, table B-3 of the book), and lastly via the formula of Fentom. The wave period ($T$) is \" + str(T1) + \" seconds, and the water depth ($h$) is \" + str(h1) + \" m?\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - "\n", - " Q1_text = \"Q1a) What is the deep water wavelength?\"\n", - " Q1_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(L, 2)\n", - " Q1_FB_G = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - " Q1_FB_W = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - " questions = [Q1_text, Q1_text,Q1_text]\n", - " units = [Q1_unit, Q1_unit, Q1_unit]\n", - " answers =[Q1_answer, Q1_answer, Q1_answer]\n", - " FB_good = [Q1_FB_G, Q1_FB_G, Q1_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q1_FB_W, Q1_FB_W]\n", - " \n", - " all_widgets = []\n", - " id = 0\n", - " for question, units, answer, Q_FB_G, Q_FB_W in zip(questions, units, answers, FB_good, FB_wrong):\n", - " id += 1\n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - " unit_widget = pn.widgets.StaticText(value=units, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " \n", - " Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget) \n", - " quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - " all_widgets.append(quiz_widget)\n", - " \n", - "\n", - " # the values for the submit button are determined at the moment these are created.\n", - " submit_button.on_click(check_nummeric_answers(id, num_widget, answer, feedback_widget, Q_FB_G, Q_FB_W))\n", - "\n", - " display(pn.Column(text_widget, *all_widgets))\n", - "Q1()" - ] - }, - { - "cell_type": "markdown", - "id": "6bfb42b0-632e-4311-a12c-372b5f951c8e", - "metadata": {}, - "source": [ - "#### Call functions when building the question" - ] - }, - { - "cell_type": "markdown", - "id": "18cc6bba-8a41-4a6c-b072-d4caf202d399", - "metadata": {}, - "source": [ - "Implementing functions can reduce repetition and thus the size of the code. The downside is that the code can become more complex, since the parameters have to be defined in the function and when calling it. Two different approaches are used below. First a function that contains the whole structure of setting up the questions and building the user interface. The second part consists of multiple functions that refer to each other. The benefit is that nesting is less required, which improves the structure and is therefore easier to read. However, it makes it harder to change since multiple questions can refer to the same function that you would like to change." - ] - }, - { - "cell_type": "markdown", - "id": "ad443572-8768-4af9-9626-a31a612be1a7", - "metadata": {}, - "source": [ - "The widgets can be shown in various ways: \n", - "- Display widgets individually in a loop, through display(...). It is easier to implement but could give the possibility that the widgets are not on a fixed position, relative to each other. \n", - "- Make a list of widgets (in a loop) and display it in one command:
\n", - " - through .servable(), it is more complex but allows previewing with Panel.\n", - " - through display(), now with a small adjustment where an empty list is filled with the widgets in a loop, then combined in one column and displayed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4dc31b33-0ac5-4c9d-8c21-a13bf73e19ed", - "metadata": {}, - "outputs": [], - "source": [ - "def check_nummeric_answers(id, num_widget, answer, feedback_widget, FB_G, FB_W):\n", - "\n", - " def button_callback(b):\n", - " #print(\"debug question:\", id, ', response:' ,num_widget.value, ', answer:',answer)\n", - "\n", - " if answer == num_widget.value:\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = FB_W\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - "\n", - "def nummeric_question_body(questions, units, answers, FB_good, FB_wrong):\n", - " all_widgets = []\n", - " id = 0\n", - " for question, units, answer, Q_FB_G, Q_FB_W in zip(questions, units, answers, FB_good, FB_wrong):\n", - " id += 1\n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - " unit_widget = pn.widgets.StaticText(value=units, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " \n", - " Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget) \n", - " quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - " all_widgets.append(quiz_widget)\n", - "\n", - " # the values for the submit button are determined at the moment these are created.\n", - " submit_button.on_click(check_nummeric_answers(id, num_widget, answer, feedback_widget, Q_FB_G, Q_FB_W))\n", - " \n", - " return all_widgets\n", - "\n", - "\n", - "def Q1():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " text_general = \"Can you asses the wave length in three different ways? Firstly through an iterative approach, then through tables (Appendix B, table B-3 of the book), and lastly via the formula of Fentom. The wave period ($T$) is \" + str(T1) + \" seconds, and the water depth ($h$) is \" + str(h1) + \" m?\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - " \n", - " Q1_text = \"Q1a) What is the deep water wavelength?\"\n", - " Q1_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(L, 2)\n", - " Q1_FB_G = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - " Q1_FB_W = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - " questions = [Q1_text, Q1_text,Q1_text]\n", - " units = [Q1_unit, Q1_unit, Q1_unit]\n", - " answers =[Q1_answer, Q1_answer, Q1_answer]\n", - " FB_good = [Q1_FB_G, Q1_FB_G, Q1_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q1_FB_W, Q1_FB_W]\n", - " \n", - " all_widgets = nummeric_question_body(questions, units, answers, FB_good, FB_wrong)\n", - " \n", - " display(pn.Column(text_widget,*all_widgets))\n", - " \n", - "Q1()" - ] - }, - { - "cell_type": "markdown", - "id": "e8c93442-e172-4c8b-97ab-c56985dcfc71", - "metadata": {}, - "source": [ - "#### Build a range for the answer" - ] - }, - { - "cell_type": "markdown", - "id": "16449fe9-a199-4549-b3ca-b75345a36f89", - "metadata": {}, - "source": [ - "When students fillin more decimal numbers than asked lead to a response of an incorrect answer. Below is a function used to check the number of decimals in the given answer and uses this to check if the answer of students is within the boundaries." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "563079d3-cba6-4ff4-8cf4-4f6176774703", - "metadata": {}, - "outputs": [], - "source": [ - "def limit_answer(x):\n", - " s = str(x)\n", - "\n", - " # inspired by: https://stackoverflow.com/questions/35585950/find-the-number-of-digits-after-the-decimal-point\n", - " if not '.' in s:\n", - " n_decimal = 0\n", - " else:\n", - " n_decimal = len(s) - s.index('.') - 1\n", - "\n", - " range = 5*10**-(n_decimal+1)\n", - "\n", - " return range" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a61c4668-c14e-4b6d-bc94-c2d5aa7d4fea", - "metadata": {}, - "outputs": [], - "source": [ - "def check_nummeric_answers(id, num_widget, answer, feedback_widget, FB_G, FB_W):\n", - "\n", - " def button_callback(b):\n", - " print(\"debug question:\", id, ', response:' ,num_widget.value, ', answer:',answer)\n", - "\n", - " if np.abs(answer - num_widget.value) < limit_answer(answer):\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = FB_W\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - "def Q1():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " text_general = \"Can you asses the wave length in three different ways? Firstly through an iterative approach, then through tables (Appendix B, table B-3 of the book), and lastly via the formula of Fentom. The wave period ($T$) is \" + str(T1) + \" seconds, and the water depth ($h$) is \" + str(h1) + \" m?\"\n", - " text_widget = pn.widgets.StaticText(value=text_general)#, width = 750)\n", - " \n", - " Q1_text = \"Q1a) What is the deep water wavelength?\"\n", - " Q1_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(L, 2)\n", - " Q1_FB_G = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - " Q1_FB_W = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - " questions = [Q1_text, Q1_text,Q1_text]\n", - " units = [Q1_unit, Q1_unit, Q1_unit]\n", - " answers =[Q1_answer, Q1_answer, Q1_answer]\n", - " FB_good = [Q1_FB_G, Q1_FB_G, Q1_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q1_FB_W, Q1_FB_W]\n", - " \n", - " all_widgets = nummeric_question_body(questions, units, answers, FB_good, FB_wrong)\n", - " \n", - " display(pn.Column(text_widget,*all_widgets))\n", - " \n", - "Q1()" - ] - }, - { - "cell_type": "markdown", - "id": "8903b41d-9cee-4d57-923b-f81a6b06f681", - "metadata": {}, - "source": [ - "#### Give correct answer after x attempts" - ] - }, - { - "cell_type": "markdown", - "id": "b3f6626f-ccb4-4d7a-87e6-f6d7e74eaef0", - "metadata": {}, - "source": [ - "This section adds the possibility that after x attempts the correct answer is given. This question builds on the previous question where the answer should be inside a range (due to rounding)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "08e1b690-ed6d-47ad-89e4-c36ed171b114", - "metadata": {}, - "outputs": [], - "source": [ - "def check_nummeric_answers(id, answer, unit, FB_G, FB_W, num_widget, feedback_widget, attempt):\n", - " \n", - " def button_callback(b):\n", - " attempt.value += 1\n", - " print(\"debug question:\", id, 'attempt', attempt.value ,', response:' ,num_widget.value, ', answer:',answer)\n", - "\n", - " # the answer is within the boundaries, print positive feedback\n", - " if np.abs(answer - num_widget.value) < limit_answer(answer):\n", - " if len(FB_G) != 0:\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = 'Well done, this is correct!'\n", - "\n", - " # the answer is NOT within boundaries, provide feedback based on the number of attempts\n", - " if np.abs(answer - num_widget.value) >= limit_answer(answer):\n", - "\n", - " if attempt.value < 3 and len(FB_W) > 0:\n", - " feedback_widget.value = FB_W\n", - " \n", - " if attempt.value < 3 and len(FB_W) == 0:\n", - " feedback_widget.value = 'Oops, there seems to be a mistake'\n", - " \n", - " if attempt.value >= 3:\n", - " feedback_widget.value = 'The correct answer is ' + str(answer) + str(unit) + '.'\n", - "\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - "def nummeric_question_body(questions, units, answers, FB_good, FB_wrong):\n", - " all_widgets = []\n", - " attempts = []\n", - " id = 0\n", - " for question, units, answer, Q_FB_G, Q_FB_W in zip(questions, units, answers, FB_good, FB_wrong):\n", - " id += 1\n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - " unit_widget = pn.widgets.StaticText(value=units, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " \n", - " Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget) \n", - " quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - " all_widgets.append(quiz_widget)\n", - "\n", - " # the values for the submit button are determined at the moment these are created.\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - " attempts.append(attempt)\n", - " submit_button.on_click(check_nummeric_answers(id, answer, units, Q_FB_G, Q_FB_W, num_widget, feedback_widget, attempt))\n", - " \n", - " return all_widgets\n", - "\n", - "def Q1():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " text_general = \"Can you asses the wave length in three different ways? Firstly through an iterative approach, then through tables (Appendix B, table B-3 of the book), and lastly via the formula of Fentom. The wave period ($T$) is \" + str(T1) + \" seconds, and the water depth ($h$) is \" + str(h1) + \" m?\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - " \n", - " Q1_text = \"Q1a) What is the deep water wavelength?\"\n", - " Q1_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(L, 2)\n", - " Q1_FB_G = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - " Q1_FB_W = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - " questions = [Q1_text, Q1_text,Q1_text]\n", - " units = [Q1_unit, Q1_unit, Q1_unit]\n", - " answers =[Q1_answer, Q1_answer, Q1_answer]\n", - " FB_good = [Q1_FB_G, Q1_FB_G, Q1_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q1_FB_W, Q1_FB_W]\n", - "\n", - " \n", - " all_widgets = nummeric_question_body(questions, units, answers, FB_good, FB_wrong)\n", - " \n", - " display(pn.Column(text_widget,*all_widgets))\n", - " \n", - "Q1()" - ] - }, - { - "cell_type": "markdown", - "id": "ed5cff51-db8b-4558-875b-885fa2a1170f", - "metadata": {}, - "source": [ - "#### Randomize order of questions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6d18baa7-3477-4683-a3aa-576d290d37b4", - "metadata": {}, - "outputs": [], - "source": [ - "def nummeric_question_body(questions, units, answers, FB_good, FB_wrong, random_order = False):\n", - " all_widgets = []\n", - " attempts = []\n", - "\n", - " order = np.arange(0, len(questions), 1)\n", - " if random_order == True:\n", - " shuffle(order)\n", - "\n", - " for i in np.array(order):\n", - " question, unit, answer, Q_FB_G, Q_FB_W = questions[i], units[i], answers[i], FB_good[i], FB_wrong[i]\n", - " id = i+1 \n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - " unit_widget = pn.widgets.StaticText(value=unit, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " #feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=500)\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " \n", - " Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget) \n", - " quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - " all_widgets.append(quiz_widget)\n", - "\n", - " # the values for the submit button are determined at the moment these are created.\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - " attempts.append(attempt)\n", - " submit_button.on_click(check_nummeric_answers(id, answer, unit, Q_FB_G, Q_FB_W, num_widget, feedback_widget, attempt))\n", - " \n", - " return all_widgets\n", - "\n", - "\n", - "def Q1():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " text_general = \"Can you asses the wave length in three different ways? Firstly through an iterative approach. The wave period ($T$) is \" + str(T1) + \" seconds, and the water depth ($h$) is \" + str(h1) + \" m?\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - " \n", - " Q1_text = \"Q1a) What is the deep water wavelength?\"\n", - " Q1_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(L, 2)\n", - " Q1_FB_G = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - " Q1_FB_W = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - " questions = [Q1_text, Q1_text,Q1_text]\n", - " units = [Q1_unit, Q1_unit, Q1_unit]\n", - " answers =[Q1_answer, Q1_answer, Q1_answer]\n", - " FB_good = [Q1_FB_G, Q1_FB_G, Q1_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q1_FB_W, Q1_FB_W]\n", - "\n", - " \n", - " all_widgets = nummeric_question_body(questions, units, answers, FB_good, FB_wrong, random_order = True)\n", - " \n", - " display(pn.Column(text_widget,*all_widgets))\n", - " \n", - "Q1()" - ] - }, - { - "cell_type": "markdown", - "id": "6b652736-5af5-43e9-b821-6aaa363e8823", - "metadata": {}, - "source": [ - "#### Getting values of multiple nummerical widgets" - ] - }, - { - "cell_type": "markdown", - "id": "2b2bea8c-5aa7-414b-9d0c-40db3fb998ce", - "metadata": {}, - "source": [ - "The code below can be usefull and used to get the values of all the widgets that are stored in a list.\n", - "One case is the answers/responses provided by students in num_widgets, for example through:" - ] - }, - { - "cell_type": "raw", - "id": "aadefba5-c4be-46f0-8bcc-cf1ca21c911d", - "metadata": {}, - "source": [ - "#store the given answers from all the questions\n", - "all_answers = []\n", - "\n", - "for question ... in questions:\n", - " # store the numerical widgets related to one question\n", - " num_widgets = [] \n", - "\n", - " for answer in answers:\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " num_widgets.append(num_widget)\n", - "\n", - " all_answers.append(num_widgets)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c76719e7-a33f-42f9-a506-a83ec532516b", - "metadata": {}, - "outputs": [], - "source": [ - "def return_answers_widgets(widgets):\n", - " answers = []\n", - " for widget in widgets:\n", - " answers.append(widget.value)\n", - " return answers\n", - "\n", - "# And get the given answers to question 1.\n", - "#answers_question2 = return_answers_widgets(all_answers[1])" - ] - }, - { - "cell_type": "markdown", - "id": "844a5ca5-8c9c-4b67-bf21-f1659670327b", - "metadata": {}, - "source": [ - "### Layout feedback widget" - ] - }, - { - "cell_type": "markdown", - "id": "55622221-f2b4-48b3-a641-58775910e2b9", - "metadata": {}, - "source": [ - "Various text widgets from panel can be used to give feedback, which have various benefits and downsizes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "16546c89-056e-48b9-9bed-394dc553f7ce", - "metadata": {}, - "outputs": [], - "source": [ - "feedback_widget1 = pn.widgets.TextInput(value=\"widget 1: same layout, height not flexible\", name=\"\", width=500)\n", - "\n", - "feedback_widget2 = pn.widgets.TextAreaInput(value=\"widget 2, widget heigth flexibale, tekst different layout than at other widgets\", name=\"\", width=500, height = 60)\n", - "feedback_widget2.style = {'font-size': '4px'}# this does not work\n", - "\n", - "feedback_widget3 = pn.widgets.StaticText(value='widget 3, height will change automatically to the content. The layout of the tekst is the same. No boundaries of the widget are shown.', width = 500)\n", - "\n", - "display(pn.Column(feedback_widget1,feedback_widget2, feedback_widget3))" - ] - }, - { - "cell_type": "markdown", - "id": "77c0c121-74c2-4355-9fc2-26ed208c5ddc", - "metadata": {}, - "source": [ - "The height of the widget can be changed by adding a few lines at the bottom in the function check_nummeric_answers." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "71029c3b-7323-4377-b230-c5575bd027c5", - "metadata": { - "jp-MarkdownHeadingCollapsed": true - }, - "outputs": [], - "source": [ - "def round_up(x, base=5):\n", - " nr = base * (round(x//base)+1)\n", - " nr = np.round(nr, 14)# to prevent values like .000000000000001\n", - " return nr\n", - "\n", - "def check_nummeric_answers(id, answer, unit, FB_G, FB_W, num_widget, feedback_widget, attempt):\n", - " \n", - " def button_callback(b):\n", - " attempt.value += 1\n", - " print(\"debug question:\", id, 'attempt', attempt.value ,', response:' ,num_widget.value, ', answer:',answer)\n", - "\n", - " # the answer is within the boundaries, print positive feedback\n", - " if np.abs(answer - num_widget.value) < limit_answer(answer):\n", - " if len(FB_G) != 0:\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = 'Well done, this is correct!'\n", - "\n", - " # the answer is NOT within boundaries, provide feedback based on the number of attempts\n", - " if np.abs(answer - num_widget.value) >= limit_answer(answer):\n", - "\n", - " if attempt.value < 3:\n", - " feedback_widget.value = FB_W\n", - " else:\n", - " feedback_widget.value = 'The correct answer is ' + str(answer) + str(unit) + '.'\n", - "\n", - " new_height = int(round_up(len(feedback_widget.value)/2, 40))\n", - " feedback_widget.height = new_height\n", - " print('debug: feedback widget height:', new_height)\n", - " \n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable" - ] - }, - { - "cell_type": "markdown", - "id": "a5c6dc95-5798-44ab-b2d5-d1aefc5887f3", - "metadata": {}, - "source": [ - "### Working with widgets/parameters/variables in functions" - ] - }, - { - "cell_type": "markdown", - "id": "bbd23e9a-08b3-4c8b-b283-364fb76cea8c", - "metadata": {}, - "source": [ - "It can become a challenge to manage all the parameters/widgets when functions are used. All the parameters (or variables in Python terminology) have to be defined in the function and described when the function is called. Especially when a list of widgets is used for various numerical questions that have various answers and graphs are plotted with the recent values. A workaround is to store the values of the local - or global-defined parameters into a single parameter that is given to a function. This parameter should then be unpacked or looked up in the function to make the calculation possible. Globals() are the parameters that are previous defined outside functions. Parameters that are defined inside functions are only local valid/stored, and are described with locals()." - ] - }, - { - "cell_type": "markdown", - "id": "8befcc1b-1772-4b5c-a41a-a9a849e62437", - "metadata": {}, - "source": [ - "#### Parameters should be defined to change them" - ] - }, - { - "cell_type": "markdown", - "id": "96861124-8f78-41e5-8d7e-dd8d69186e26", - "metadata": {}, - "source": [ - "The code below shows that parameters should be defined between the brackets in order to work with them." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "021fe120-7328-4935-9a99-587c21bc45a3", - "metadata": {}, - "outputs": [], - "source": [ - "global_variable = 1\n", - "\n", - "def function():\n", - " print(global_variable)\n", - "\n", - " # Executing the code below gives: UnboundLocalError: cannot access local variable 'global_variable' where it is not associated with a value\n", - " # global_variable += 1\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "af2cdc93-7299-47be-91c3-7787279c677d", - "metadata": {}, - "source": [ - "The code below shows that it is possible to manage a few parameters by defining them as input parameter and defining them when calling the function. The below prints the input value when the function is called." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dbc5b14f-9f89-4858-b210-29da11476888", - "metadata": {}, - "outputs": [], - "source": [ - "global_variable = 1\n", - "\n", - "def function(global_variable):\n", - " print(global_variable)\n", - "\n", - " global_variable += 1\n", - " return global_variable\n", - "\n", - "global_variable = function(global_variable)\n", - "global_variable = function(global_variable)\n", - "global_variable = function(global_variable)" - ] - }, - { - "cell_type": "markdown", - "id": "cabe32ea-0a1e-4d97-9226-09245c3bdda5", - "metadata": {}, - "source": [ - "#### Storing all global and local parameters in one parameter" - ] - }, - { - "cell_type": "markdown", - "id": "7ddd64a2-1dc5-4a16-ac51-c9a742c34dd9", - "metadata": {}, - "source": [ - "The function below stores all the global parameters in a single parameter, which is then given to the function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "75989247-fb20-44b9-a29c-6c8f128d7b71", - "metadata": {}, - "outputs": [], - "source": [ - "def function(all_parameters):\n", - " global_variable = all_parameters['global_variable']\n", - " print(global_variable)\n", - "\n", - " global_variable += 1\n", - " return global_variable\n", - "\n", - "all_parameters = globals()\n", - "global_variable = function(all_parameters)" - ] - }, - { - "cell_type": "markdown", - "id": "96559828-9456-4f0c-8ec3-756b7aed04e3", - "metadata": {}, - "source": [ - "Local variables are parameters are all the parameters that are valid within the (environment) of the function. The locals() consider only parameters that are defined in the function itself or are given to it (which are the values between the brackets)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fe8cae9f-7254-4409-9539-27b01d65e296", - "metadata": { - "jp-MarkdownHeadingCollapsed": true - }, - "outputs": [], - "source": [ - "def function(all_parameters):\n", - " global_variable = all_parameters['global_variable']\n", - " global_variable += 1\n", - "\n", - " print(global_variable)\n", - "\n", - " new_local = 'local parameter'\n", - " all_local_parameters = (locals())\n", - " # the code below prints all the local parameters, with new_local on the bottom.\n", - " #print(all_local_parameters)\n", - " \n", - " return global_variable\n", - "\n", - "all_parameters = globals()\n", - "global_variable = function(all_parameters)" - ] - }, - { - "cell_type": "markdown", - "id": "14fd2bf6-e632-4985-9ee7-4187f13f3ac5", - "metadata": {}, - "source": [ - "The code below stores all the local variables." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb2dbba2-c868-49cc-ba0c-dd8b7c09c3a4", - "metadata": {}, - "outputs": [], - "source": [ - "def function():\n", - " new_local = 'local parameter in the function'\n", - " par1 = 5\n", - " par2 = 4\n", - " \n", - " function_parameters = {key: value for key, value in locals().items()}\n", - " # \n", - "\n", - " print('The function_parameters')\n", - " print(function_parameters)\n", - "\n", - " print('The value of par1:', function_parameters['par1'])\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "7f6150f7-fab7-4343-88b9-4f243738b3f9", - "metadata": {}, - "source": [ - "Doing both the global and local parameters can be computationally demanding when it is inside a function in the following way (combining dictonaries):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0993fdc-f88b-44c7-b722-e02a6f20f0f2", - "metadata": {}, - "outputs": [], - "source": [ - "function_parameters = {key: value for key, value in {**globals(), **locals()}.items()}" - ] - }, - { - "cell_type": "markdown", - "id": "7eaadb66-54de-4015-8575-a3d9ec97188d", - "metadata": {}, - "source": [ - "In the following way can newly defined parameters be included." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "46421aee-c52b-46c9-b7a3-19835f44e601", - "metadata": {}, - "outputs": [], - "source": [ - "function_parameters = {key: value for key, value in {**globals(), **locals()}.items()}\n", - "\n", - "def function():\n", - " new_par = 4\n", - " new_par2 = 5\n", - "\n", - " new_pars = {key: value for key, value in locals().items()}\n", - " function_parameters.update(new_pars)\n", - "\n", - " print(function_parameters['new_par'])\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "c705f9a1-efbd-4ad9-8ba1-ff360dd8fa71", - "metadata": {}, - "source": [ - " Implementation in question structrure " - ] - }, - { - "cell_type": "markdown", - "id": "79f17399-ed12-47bf-b7ca-ae773428b235", - "metadata": {}, - "source": [ - "The code below shows that information can be passed on to nested functions, although parameters change. It also shows that it is not required to store all widgets in lists to have it functional." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c7f25cf6-4a46-49b3-8ce8-732fcdfeb0cf", - "metadata": {}, - "outputs": [], - "source": [ - "import panel as pn\n", - "\n", - "def print_function(FV):\n", - " def button_callback(event):\n", - " print('button: ', FV['value'], 'value of widget', FV['num_widget'].value)\n", - "\n", - " return button_callback\n", - "\n", - "def function():\n", - " values = [1, 2, 3]\n", - "\n", - " def nested_function(values):\n", - " rows = []\n", - " for value in values:\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " rows.append(pn.Row(submit_button, num_widget))\n", - "\n", - " function_parameters = {key: value for key, value in {**globals(), **locals()}.items()}\n", - " submit_button.on_click(print_function(function_parameters))\n", - " \n", - " return rows\n", - "\n", - " widgets = nested_function(values)\n", - " display(*widgets)\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "e9272e70-e663-48f7-bc81-d3380e9f1607", - "metadata": {}, - "source": [ - "This principle is applied to a numerical question in the code below. A debug print() function is added." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "54395e82-dddd-4de4-ae4a-b51b701ac545", - "metadata": {}, - "outputs": [], - "source": [ - "def limit_answer(x):\n", - " s = str(x)\n", - "\n", - " # inspired by: https://stackoverflow.com/questions/35585950/find-the-number-of-digits-after-the-decimal-point\n", - " if not '.' in s:\n", - " n_decimal = 0\n", - " else:\n", - " n_decimal = len(s) - s.index('.') - 1\n", - "\n", - " range = 5*10**-(n_decimal+1)\n", - "\n", - " return range\n", - "\n", - "def check_nummeric_answers(FV, attempt):\n", - " \n", - " def button_callback(b):\n", - " answer = FV['answer']\n", - " unit = FV['unit']\n", - " num_widget = FV['num_widget']\n", - " feedback_widget = FV['feedback_widget']\n", - " FB_W = FV['Q_FB_W']\n", - " FB_G = FV['Q_FB_G']\n", - " \n", - " attempt.value += 1\n", - "\n", - " print('Debug: attempt:', attempt.value, ', Value of the widget:', num_widget.value)\n", - "\n", - " # the answer is within the boundaries, print positive feedback\n", - " if np.abs(answer - num_widget.value) < limit_answer(answer):\n", - " if len(FB_G) != 0:\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = 'Well done, this is correct!'\n", - "\n", - " # the answer is NOT within boundaries, provide feedback based on the number of attempts\n", - " if np.abs(answer - num_widget.value) >= limit_answer(answer):\n", - "\n", - " if attempt.value < 3 and len(FB_W) > 0:\n", - " feedback_widget.value = FB_W\n", - " \n", - " if attempt.value < 3 and len(FB_W) == 0:\n", - " feedback_widget.value = 'Oops, there seems to be a mistake'\n", - " \n", - " if attempt.value >= 3:\n", - " feedback_widget.value = 'The correct answer is ' + str(answer) + str(unit) + '.'\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - "def nummeric_question_body(FV, random_order = False):\n", - " #FV = FV.copy()\n", - " \n", - " all_widgets = []\n", - " attempts = []\n", - "\n", - " order = np.arange(0, len(FV['questions']), 1)\n", - " if random_order == True:\n", - " shuffle(order)\n", - "\n", - " for i in np.array(order):\n", - " question, unit, answer, Q_FB_G, Q_FB_W = FV['questions'][i], FV['units'][i], FV['answers'][i], FV['FB_good'][i], FV['FB_wrong'][i]\n", - " id = i+1 \n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - " unit_widget = pn.widgets.StaticText(value=unit, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " #feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=500)\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " \n", - " Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget) \n", - " quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - " all_widgets.append(quiz_widget)\n", - "\n", - " # the values for the submit button are determined at the moment these are created.\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - " attempts.append(attempt)\n", - "\n", - " FV2 = {key: value for key, value in {**globals(), **locals()}.items()}\n", - " submit_button.on_click(check_nummeric_answers(FV2, attempt))\n", - " \n", - " return all_widgets\n", - "\n", - "\n", - "def Q1():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " text_general = \"Can you asses the wave length in three different ways? Firstly through an iterative approach. The wave period ($T$) is \" + str(T1) + \" seconds, and the water depth ($h$) is \" + str(h1) + \" m?\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - " \n", - " Q1_text = \"Q1a) What is the deep water wavelength?\"\n", - " Q1_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(L, 2)\n", - " Q1_FB_G = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - " Q1_FB_W = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - " questions = [Q1_text, Q1_text,Q1_text]\n", - " units = [Q1_unit, Q1_unit, Q1_unit]\n", - " answers =[Q1_answer, Q1_answer, Q1_answer]\n", - " FB_good = [Q1_FB_G, Q1_FB_G, Q1_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q1_FB_W, Q1_FB_W]\n", - "\n", - " FV = {key: value for key, value in {**globals(), **locals()}.items()}\n", - " all_widgets = nummeric_question_body(FV, random_order = True)\n", - " \n", - " display(pn.Column(text_widget,*all_widgets))\n", - " \n", - "Q1()" - ] - }, - { - "cell_type": "markdown", - "id": "d6358e94-fb50-4e12-876c-6cd554ef419a", - "metadata": {}, - "source": [ - "#### Storing data in classes, for dot notation." - ] - }, - { - "cell_type": "markdown", - "id": "84a0e750-1d06-4ebd-ad26-14d929f26849", - "metadata": {}, - "source": [ - "Shorter notation can help in having an overview of the code. The dot notification can also be useful, it will reduce the number of brackets etc. This can be achieved by working with classes. The code below gives an example.
\n", - "\n", - "A shorter parameter name can also increase the readability, as long as the parameters are self-explanatory. A very short name can be used if it is constantly used for one specific goal, like storing all the local variables in it. Another way of naming parameters is by following naming conventions, such as the CamelCase. Here every new word in a parameter starts with a capital so spaces/underscores can be left out. In this project are some opinions that do not want capitals in naming, so that convention will not be used in this project." - ] - }, - { - "cell_type": "markdown", - "id": "63904156-b6b5-4a49-9b97-3f5aca5e67b9", - "metadata": {}, - "source": [ - "The code below stores the parameter in globals() as a class. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "22d339f9-cc48-4ea5-8f15-299a73556622", - "metadata": {}, - "outputs": [], - "source": [ - "class class_variables:\n", - " def __setattr__(self, key, value):\n", - " object.__setattr__(self, key, value)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "234936f3-c76e-43d5-b2de-0a7cb08ea0d4", - "metadata": {}, - "outputs": [], - "source": [ - "global_1 = 5\n", - "global_2 = 2\n", - "\n", - "FV = class_variables()\n", - "for key, value in globals().items():\n", - " FV.__setattr__(key, value)\n", - "\n", - "print(FV.global_1, FV.global_2)" - ] - }, - { - "cell_type": "markdown", - "id": "69a76edf-1e9f-49de-8f5b-80abe9b55d1a", - "metadata": {}, - "source": [ - "Now in a function, so it can be called." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "13ec6ee3-83d6-4b48-9423-6dc220e6b7a5", - "metadata": {}, - "outputs": [], - "source": [ - "def classify_variables(params): \n", - " FV = class_variables()\n", - " for key, value in params.items():\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "FV = classify_variables(globals())\n", - "print(FV.global_1, FV.global_2)" - ] - }, - { - "cell_type": "markdown", - "id": "b2ae78cd-7577-4b93-b737-ee861da65632", - "metadata": {}, - "source": [ - "A similar thing can be done with locals(). The code below shows that both the local variables are not stored in globals() and vice versa. The name FV is used for Function_Variables, related to the default name in Python of locals within a function, which is the final goal of this section." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f6b44016-b4ab-420b-bcc3-f199f4716bfe", - "metadata": {}, - "outputs": [], - "source": [ - "def function():\n", - " par1 = 5\n", - " par2 = 4\n", - "\n", - " FV = classify_variables(globals())\n", - " print(FV.global_1, FV.global_2)\n", - "\n", - " # gives no valid result\n", - " #print(FV.par1)\n", - " \n", - " FV = classify_variables(locals())\n", - " print(FV.par1)\n", - "\n", - " # gives no valid result\n", - " #print(FV.global_1, FV.global_2)\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "30e577dc-8518-47a4-86bc-b0fa35a9b899", - "metadata": {}, - "source": [ - "The function below is improved, so it can store locals and globals together." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "289c6fb8-d87d-4e90-905f-72eb50073183", - "metadata": {}, - "outputs": [], - "source": [ - "def classify_variables(params, params2 = None): \n", - " FV = class_variables()\n", - " for key, value in params.items():\n", - " FV.__setattr__(key, value)\n", - "\n", - " if len(params2) > 0:\n", - " for key, value in params2.items():\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def function():\n", - " par1 = 5\n", - " par2 = 4\n", - "\n", - " FV = classify_variables(globals(), locals())\n", - " print(FV.global_1, FV.global_2)\n", - " print(FV.par1)\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "a8fbd224-eca2-4d93-b1f7-0252a96c2ffe", - "metadata": {}, - "source": [ - "The global variables are always accessible, not necessarily editable. So this can be used to simplify the function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f3ea7c43-271e-4aa5-a726-52e612b07ad9", - "metadata": {}, - "outputs": [], - "source": [ - "def classify_variables(locals): \n", - " FV = class_variables()\n", - " for key, value in locals.items():\n", - " FV.__setattr__(key, value)\n", - "\n", - " for key, value in globals().items():\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def function():\n", - " par1 = 5\n", - " par2 = 4\n", - "\n", - " FV = classify_variables(locals())\n", - " print(FV.global_1, FV.global_2)\n", - " print(FV.par1)\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "26de40ac-8a5f-4bca-b3c7-fe894bdf662e", - "metadata": {}, - "source": [ - "The code can be shortened by merging the dictonaries of globals() and locals()." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "418cb68e-2b5b-41f5-8745-41b67fff759f", - "metadata": {}, - "outputs": [], - "source": [ - "def classify_variables(locals): \n", - " FV = class_variables()\n", - " for key, value in {**globals(), **locals}.items():\n", - " FV.__setattr__(key, value)\n", - "\n", - " return FV\n", - "\n", - "def function():\n", - " par1 = 5\n", - " par2 = 4\n", - "\n", - " FV = classify_variables(locals())\n", - " print(FV.global_1, FV.global_2)\n", - " print(FV.par1)\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "e6b1ad4b-ec60-4aa6-b77e-6ec9cac1ee14", - "metadata": {}, - "source": [ - "Storing the globals everytime might not be prefered, so that will be left out. It will be stored as parameters that will be unpacked." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d92d1c4b-de3c-4796-a979-39bbb3391122", - "metadata": {}, - "outputs": [], - "source": [ - "global_1 = 5\n", - "global_2 = 2\n", - "\n", - "def classify_variables(params, params2 = {}): \n", - " FV = class_variables()\n", - " for key, value in {**params,**params2}.items():\n", - " FV.__setattr__(key, value)\n", - "\n", - " return FV\n", - "\n", - "def function():\n", - " par1_new = 5\n", - " par2_new = 4\n", - "\n", - " FV = classify_variables(globals(), locals())\n", - " print(FV.global_1, FV.global_2)\n", - " print(FV.par1_new, FV.par2_new)\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "6604968a-bb8b-4b3b-9948-a947ef18d088", - "metadata": {}, - "source": [ - "Parameters can become memory demanding when data sets are stored in it, for example. It can be beneficial to only store files up to a specific size. This is applied in the code below. Here a maximum size is only considered when it is defined when calling the function. A max_size_MB smaller then or equal to 0 does not filter parameters based on its size." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1b63a181-9810-4742-bd68-a91bd1a868a0", - "metadata": {}, - "outputs": [], - "source": [ - "import sys" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d1c2dc9f-7e7f-4c7f-bcba-bed73e7ac680", - "metadata": {}, - "outputs": [], - "source": [ - "def classify_variables(params, params2 = {}, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " FV = class_variables()\n", - " for key, value in {**params,**params2}.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "global_1 = 5\n", - "global_2 = 2\n", - "\n", - "def function():\n", - " par1_new = 5\n", - " par2_new = 4\n", - "\n", - " FV = classify_variables(locals(), globals())\n", - " print(FV.global_1, FV.global_2)\n", - " print(FV.par1_new, FV.par2_new)\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "64ec8cca-5b53-4280-8117-c6e4de791143", - "metadata": {}, - "source": [ - "Another aspect is that new parameters can be defined in nested functions. These have to be included to FV to pass on the information, unless the traditional way of passing parameters is used. This is probably the easiest way, since not many parameters will be defined in nested functions." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2e456e1f-0cdd-4ae8-879f-4df3b2b27748", - "metadata": {}, - "outputs": [], - "source": [ - "def add_local_variables_to(FV,locals, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " for key, value in locals.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def nested_function(FV):\n", - " new_par1 = 9\n", - " FV = add_local_variables_to(FV,locals())\n", - "\n", - " print(FV.new_par1)\n", - "\n", - "def function():\n", - " par1 = 5\n", - " par2 = 4\n", - "\n", - " FV = classify_variables(locals())\n", - " nested_function(FV)\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "25eae2ca-e5b7-48a8-990e-aaf34d383afd", - "metadata": {}, - "source": [ - "##### implementing in numerical questions " - ] - }, - { - "cell_type": "markdown", - "id": "c181c428-966b-4db0-8ec7-1ed4624659ed", - "metadata": {}, - "source": [ - "The code below shows the implementation in numerical questions. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "110bc8f2-d345-4129-9c78-52f2d865e4ba", - "metadata": {}, - "outputs": [], - "source": [ - "import sys" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "911389d4-5085-4e3d-b6ea-949049d36461", - "metadata": {}, - "outputs": [], - "source": [ - "class class_variables:\n", - " def __setattr__(self, key, value):\n", - " object.__setattr__(self, key, value)\n", - "\n", - "def classify_variables(params, params2 = {}, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " FV = class_variables()\n", - " for key, value in {**params,**params2}.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "# not required in this example\n", - "def add_local_variables_to(FV,params, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " for key, value in params.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def limit_answer(x):\n", - " s = str(x)\n", - "\n", - " # inspired by: https://stackoverflow.com/questions/35585950/find-the-number-of-digits-after-the-decimal-point\n", - " if not '.' in s:\n", - " n_decimal = 0\n", - " else:\n", - " n_decimal = len(s) - s.index('.') - 1\n", - "\n", - " range = 5*10**-(n_decimal+1)\n", - "\n", - " return range\n", - "\n", - "def check_nummeric_answers(FV, attempt):\n", - " \n", - " def button_callback(b):\n", - " answer = FV.answer\n", - " unit = FV.unit\n", - " num_widget = FV.num_widget\n", - " feedback_widget = FV.feedback_widget\n", - " FB_W = FV.Q_FB_W\n", - " FB_G = FV.Q_FB_G\n", - " \n", - " attempt.value += 1\n", - " print('Debug: attempt:', attempt.value, ', Value of the widget:', num_widget.value)\n", - " \n", - " # the answer is within the boundaries, print positive feedback\n", - " if np.abs(answer - num_widget.value) < limit_answer(answer):\n", - " if len(FB_G) != 0:\n", - " feedback_widget.value = FB_G\n", - " else:\n", - " feedback_widget.value = 'Well done, this is correct!'\n", - "\n", - " # the answer is NOT within boundaries, provide feedback based on the number of attempts\n", - " if np.abs(answer - num_widget.value) >= limit_answer(answer):\n", - "\n", - " if attempt.value < 3 and len(FB_W) > 0:\n", - " feedback_widget.value = FB_W\n", - " \n", - " if attempt.value < 3 and len(FB_W) == 0:\n", - " feedback_widget.value = 'Oops, there seems to be a mistake'\n", - " \n", - " if attempt.value >= 3:\n", - " feedback_widget.value = 'The correct answer is ' + str(answer) + str(unit) + '.'\n", - "\n", - "\n", - " return button_callback # otherwise gives TypeError: 'NoneType' object is not callable\n", - "\n", - "def nummeric_question_body(FV, random_order = False):\n", - " #FV = FV.copy()\n", - " \n", - " all_widgets = []\n", - " attempts = []\n", - "\n", - " order = np.arange(0, len(FV.questions), 1)\n", - " if random_order == True:\n", - " shuffle(order)\n", - "\n", - " for i in np.array(order):\n", - " question, unit, answer, Q_FB_G, Q_FB_W = FV.questions[i], FV.units[i], FV.answers[i], FV.FB_good[i], FV.FB_wrong[i]\n", - " id = i+1 \n", - " question_widget = pn.widgets.StaticText(value=question, width = 750)\n", - " unit_widget = pn.widgets.StaticText(value=unit, width = 10)\n", - " num_widget = pn.widgets.FloatInput(value=0, step=0.01, width = 100)\n", - " #feedback_widget = pn.widgets.TextInput(value=\"\", name=\"\", width=500)\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=500)\n", - " submit_button = pn.widgets.Button(name=\"Submit\")\n", - " \n", - " Hbox = pn.Row(num_widget, unit_widget, submit_button, feedback_widget) \n", - " quiz_widget = pn.Column(question_widget, Hbox)\n", - "\n", - " all_widgets.append(quiz_widget)\n", - "\n", - " # the values for the submit button are determined at the moment these are created.\n", - " attempt = pn.widgets.FloatInput(value=0)\n", - " attempts.append(attempt)\n", - "\n", - " FV2 = classify_variables(locals())\n", - " submit_button.on_click(check_nummeric_answers(FV2, attempt))\n", - " \n", - " return all_widgets\n", - "\n", - "\n", - "def Q1():\n", - " T1 = round(uniform(5, 8), 1)\n", - " h1 = round(uniform(0.5, 5), 1)\n", - "\n", - " text_general = \"Can you asses the wave length in three different ways? Firstly through an iterative approach. The wave period ($T$) is \" + str(T1) + \" seconds, and the water depth ($h$) is \" + str(h1) + \" m?\"\n", - " text_widget = pn.widgets.StaticText(value=text_general, width = 750)\n", - " \n", - " Q1_text = \"Q1a) What is the deep water wavelength?\"\n", - " Q1_unit = \" m\"\n", - " L = 9.81 * T1**2 / (2 * np.pi)\n", - " Q1_answer = round(L, 2)\n", - " Q1_FB_G = 'Indeed, the deep water wavelength is in this way related to the wave period.'\n", - " Q1_FB_W = 'There is a mistake, the only variable is the wave period.'\n", - "\n", - " questions = [Q1_text, Q1_text,Q1_text]\n", - " units = [Q1_unit, Q1_unit, Q1_unit]\n", - " answers =[Q1_answer, Q1_answer, Q1_answer]\n", - " FB_good = [Q1_FB_G, Q1_FB_G, Q1_FB_G]\n", - " FB_wrong = [Q1_FB_W, Q1_FB_W, Q1_FB_W]\n", - "\n", - " FV = classify_variables(locals())\n", - " all_widgets = nummeric_question_body(FV, random_order = True)\n", - " \n", - " display(pn.Column(text_widget,*all_widgets))\n", - " \n", - "Q1()" - ] - }, - { - "cell_type": "markdown", - "id": "22f45f15-57f7-48f8-8ed8-ce2022d3b40a", - "metadata": {}, - "source": [ - "##### Store parameters manually" - ] - }, - { - "cell_type": "markdown", - "id": "374ebe8a-6803-4c4a-a62a-83a11f126c98", - "metadata": {}, - "source": [ - "#### Storing specific parameters (keys), rather than doing it through locals() or globals()" - ] - }, - { - "cell_type": "markdown", - "id": "844a8087-77ab-4753-a7cf-0158f18b3aa1", - "metadata": {}, - "source": [ - "It might be useful to store or add only specific parameters, for this a dictionary has to be made that includes all the predefined parameter names (keys). These keys can then be added to another class with the function add_local_variables_to()." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4a7ee576-10e4-4603-b257-7b7b2916cf3f", - "metadata": {}, - "outputs": [], - "source": [ - "par1 = 5.2\n", - "par2 = 4.2\n", - "\n", - "class class_variables:\n", - " def __setattr__(self, key, value):\n", - " object.__setattr__(self, key, value)\n", - "\n", - "def classify_variables(params, params2 = {}, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " FV = class_variables()\n", - " for key, value in {**params,**params2}.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def add_local_variables_to(FV,params, max_size_MB = 0):\n", - " max_size = max_size_MB * 1024*1024\n", - " for key, value in params.items():\n", - " if sys.getsizeof(value) < max_size or max_size <= 0:\n", - " FV.__setattr__(key, value)\n", - " return FV\n", - "\n", - "def build_global_dictionary(keys):\n", - " return {key: globals()[key] for key in keys}\n", - "\n", - "keys = ['par1', 'par2']\n", - "params_dict = build_global_dictionary(keys)\n", - "GV = classify_variables(params_dict)\n", - "\n", - "print(GV.par1, GV.par2)" - ] - }, - { - "cell_type": "markdown", - "id": "c5399a0b-fe32-4b21-b2e5-d3aafa43b934", - "metadata": {}, - "source": [ - "The same can be done for locals(). These have to be given to the function to work properly." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0d6e9cf6-7269-4bed-949f-19a3544c8dc0", - "metadata": {}, - "outputs": [], - "source": [ - "def build_local_dictionary(keys, locals):\n", - " return {key: locals[key] for key in keys}\n", - "\n", - "def function():\n", - " par1_local = 6\n", - " par2_local = 7\n", - "\n", - " keys = ['par1_local']\n", - " params_dict = build_local_dictionary(keys, locals())\n", - " FV = classify_variables(params_dict)\n", - "\n", - " print(FV.par1_local)\n", - "\n", - "function()" - ] - }, - { - "cell_type": "markdown", - "id": "40718629-036c-42d2-b8c6-fc8813b6a6c5", - "metadata": {}, - "source": [ - "### Widget calculator for students" - ] - }, - { - "cell_type": "markdown", - "id": "811d989b-7083-43d6-9fe5-1fc4354c59fe", - "metadata": {}, - "source": [ - "Students have to do calculations. On the one hand, we want to offer a way that coding (skill) is not required, on the other hand, it brings very useful insights. Some of the calculations are also repetitive, and doing the calculation multiple times is for the students not useful (especially if they do it by hand or an iterative calculation in Excel, for example). One solution can be to give students a tool that does the calculation for them. So students have to give the input parameters and they immediately get the answer. The request for students is to do the calculation themself once. They can compare their answer with the tool or an answer to a specific question.
\n", - "\n", - "One tool that can be used is the param component (https://panel.holoviz.org/reference/panes/Param.html)\n", - "It is for now made with the same widgets as it is done before.\n", - "\n", - "The functions that are used to calculate the answers can be called in this tool. The function of calculating the wavelength will be used in the example below." - ] - }, - { - "cell_type": "markdown", - "id": "5bbef6f6-ec7f-48da-8964-0cda682909fd", - "metadata": {}, - "source": [ - "#### Display wave length calculator" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5502d65b-a659-48a2-bcb9-6d95a69f7628", - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length(T, h):\n", - " L = 9.81 * T**2 / (2 * np.pi)\n", - " L_all = [L]\n", - "\n", - " for i in range(1500):\n", - " L = 9.81 * T**2 / (2 * np.pi) * np.tanh(2 * np.pi * h / L)\n", - " L_all.append(L)\n", - "\n", - " if np.abs(L_all[-1] - L_all[-2]) < 0.0005:\n", - " break\n", - "\n", - " return round(L, 13)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "38a8f7be-1e95-45e6-841b-3f7ba00a1b68", - "metadata": {}, - "outputs": [], - "source": [ - "def wavelength_calculator():\n", - " # define widgets (with initial value for L)\n", - " T = pn.widgets.FloatInput(value=5, step=0.01, width=100)\n", - " h = pn.widgets.FloatInput(value=3, step=0.01, width=100)\n", - " \n", - " L_init = wave_length(T.value, h.value)\n", - " L = pn.widgets.FloatInput(value=L_init, step=0.001, width= 100, disabled=True)\n", - "\n", - " # change the value of L when a change in T or h is observed\n", - " def update_L_widget(event):\n", - " L.value = wave_length(T.value, h.value)\n", - "\n", - " T.param.watch(update_L_widget, 'value')\n", - " h.param.watch(update_L_widget, 'value')\n", - "\n", - " # set the surrounding layout, like headings and descriptions\n", - " ## set headings and the column width\n", - " title_input = pn.widgets.StaticText(value='Input', width=200)\n", - " title_output = pn.widgets.StaticText(value='Output', width=200)\n", - " heading = pn.widgets.StaticText(value='Wavelength calculator')\n", - "\n", - " ## Add descriptions and width for alignment\n", - " symbol_T = pn.widgets.StaticText(value='T', width=1)\n", - " unit_T = pn.widgets.StaticText(value='s', width=1)\n", - " T_widget = pn.Row(symbol_T, T, unit_T)\n", - "\n", - " symbol_h = pn.widgets.StaticText(value='h', width = 1)\n", - " unit_h = pn.widgets.StaticText(value='m', width = 1)\n", - " h_widget = pn.Row(symbol_h, h, unit_h)\n", - "\n", - " symbol_L = pn.widgets.StaticText(value='L', width = 1)\n", - " unit_L = pn.widgets.StaticText(value='m', width = 1)\n", - " L_widget = pn.Row(symbol_L, L, unit_L)\n", - "\n", - " # add latex formula\n", - " text_formula = pn.widgets.StaticText(value='Solves iteratively:', width = 100)\n", - " formula = pn.pane.LaTeX(r\"$L=\\frac{gT^2}{2 \\pi} tanh( \\frac{2 \\pi h}{L})$\")\n", - " row_formula = pn.Row(text_formula, formula)\n", - "\n", - " ## merge the layout and display the result\n", - " input_widget = pn.Column(title_input, T_widget, h_widget)\n", - " output_widget = pn.Column(title_output, L_widget)\n", - " horizontal_allignment = pn.Row(input_widget, output_widget)\n", - " include_heading = pn.Column(heading,row_formula, horizontal_allignment)\n", - "\n", - " display(include_heading)\n", - "\n", - "wavelength_calculator()" - ] - }, - { - "cell_type": "markdown", - "id": "af5df706-33dc-43d0-87d1-a36fc5a9c6c5", - "metadata": {}, - "source": [ - "### Allign multiple calculators (or widgets)" - ] - }, - { - "cell_type": "markdown", - "id": "baec36ca-96ea-4b20-bc9e-2baee6b8d094", - "metadata": {}, - "source": [ - "It is possible to structure various widgets next to each other when the functions return the widgets rather than only displaying. A function directly displays a widget if it is called, as is demonstrated in the code below. The widgets can be structured through pn.Row , pn.Column, and pn.Tabs (and more)." - ] - }, - { - "cell_type": "markdown", - "id": "66d65eb9-31e1-4d98-98e8-4a38483bab67", - "metadata": {}, - "source": [ - "#### return rather than dislay" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f2dd950a-83b8-40b4-893b-872149961335", - "metadata": {}, - "outputs": [], - "source": [ - "def wavelength_calculator():\n", - " # define widgets (with initial value for L)\n", - " T = pn.widgets.FloatInput(value=5, step=0.01, width=100)\n", - " h = pn.widgets.FloatInput(value=3, step=0.01, width=100)\n", - " \n", - " L_init = wave_length(T.value, h.value)\n", - " L = pn.widgets.FloatInput(value=L_init, step=0.001, width= 100, disabled=True)\n", - "\n", - " # change the value of L when a change in T or h is observed\n", - " def update_L_widget(event):\n", - " L.value = wave_length(T.value, h.value)\n", - "\n", - " T.param.watch(update_L_widget, 'value')\n", - " h.param.watch(update_L_widget, 'value')\n", - "\n", - " # set the surrounding layout, like headings and descriptions\n", - " ## set headings and the column width\n", - " title_input = pn.widgets.StaticText(value='Input', width=200)\n", - " title_output = pn.widgets.StaticText(value='Output', width=200)\n", - " heading = pn.widgets.StaticText(value='Wavelength calculator')\n", - "\n", - " ## Add descriptions and width for alignment\n", - " symbol_T = pn.widgets.StaticText(value='T', width=1)\n", - " unit_T = pn.widgets.StaticText(value='s', width=1)\n", - " T_widget = pn.Row(symbol_T, T, unit_T)\n", - "\n", - " symbol_h = pn.widgets.StaticText(value='h', width = 1)\n", - " unit_h = pn.widgets.StaticText(value='m', width = 1)\n", - " h_widget = pn.Row(symbol_h, h, unit_h)\n", - "\n", - " symbol_L = pn.widgets.StaticText(value='L', width = 1)\n", - " unit_L = pn.widgets.StaticText(value='m', width = 1)\n", - " L_widget = pn.Row(symbol_L, L, unit_L)\n", - "\n", - " # add latex formula\n", - " text_formula = pn.widgets.StaticText(value='Solves iteratively:', width = 100)\n", - " formula = pn.pane.LaTeX(r\"$L=\\frac{gT^2}{2 \\pi} tanh( \\frac{2 \\pi h}{L})$\")\n", - " row_formula = pn.Row(text_formula, formula)\n", - "\n", - " ## merge the layout and display the result\n", - " input_widget = pn.Column(title_input, T_widget, h_widget)\n", - " output_widget = pn.Column(title_output, L_widget)\n", - " horizontal_allignment = pn.Row(input_widget, output_widget)\n", - " include_heading = pn.Column(heading,row_formula, horizontal_allignment)\n", - "\n", - " return include_heading\n", - "\n", - "wavelength_calculator()" - ] - }, - { - "cell_type": "markdown", - "id": "b082732e-c113-4258-a1bd-20e08df985c9", - "metadata": {}, - "source": [ - "#### Allign multiple calculator tools" - ] - }, - { - "cell_type": "markdown", - "id": "04a141d3-77cf-43a9-ab9a-eb85940d039f", - "metadata": {}, - "source": [ - "The wavelength_calculator is placed in the same row and then displayed, so the widgets are horizontally alligined." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ad98c0d2-fede-491f-9744-aca4ad456b87", - "metadata": {}, - "outputs": [], - "source": [ - "def calculators():\n", - " allignment = pn.Row(wavelength_calculator(), wavelength_calculator())\n", - " display(allignment)\n", - "\n", - "calculators()" - ] - }, - { - "cell_type": "markdown", - "id": "123eaab6-47b0-4403-bcc1-c9c4c562d3c4", - "metadata": {}, - "source": [ - "#### place widget in tabs" - ] - }, - { - "cell_type": "markdown", - "id": "c949b23b-4468-4550-8bb9-4a20a5691263", - "metadata": {}, - "source": [ - "The widgets can be structured in various ways.
\n", - "https://panel.holoviz.org/reference/index.html#layouts\n", - "\n", - "One possible way is through Tabs.
\n", - "https://panel.holoviz.org/reference/layouts/Tabs.html\n", - "\n", - "Here is the same widget displayed in 2 different tabs." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ca14823f-3ec7-4c09-9b48-81d06164831b", - "metadata": {}, - "outputs": [], - "source": [ - "tabs = pn.Tabs(('wave length calulator', wavelength_calculator()), ('other calculator', wavelength_calculator()))\n", - "display(tabs)" - ] - }, - { - "cell_type": "markdown", - "id": "b16bd783-7c17-4d0c-818b-20b4374b21db", - "metadata": {}, - "source": [ - "## Prestudie results" - ] - }, - { - "cell_type": "markdown", - "id": "eab65ea2-2aa0-4e1c-b750-e6c43cff0028", - "metadata": {}, - "source": [ - "A study of the implementation of IPY widgets has been done, and after that, a switch to the widgets of panel has been made. The structure is very similar, although some different coding challenges are occurring. The results are shown below. Some uninstalled (or expired versions) packages might be required, therefore is the code placed as raw text." - ] - }, - { - "cell_type": "markdown", - "id": "faeeb54f-b673-4a58-b762-2e83cd5f4f72", - "metadata": {}, - "source": [ - "### A single question" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1182239a-7468-405c-bdba-386d116dd4ec", - "metadata": {}, - "outputs": [], - "source": [ - "# the information of the question\n", - "question_1 = \"A large continental shelf width is at a:\"\n", - "choices_1 = [\"Leading edge\", \"Trailing edge\", \"marginal sea\"]\n", - "answer_1 = choices_1[1]\n", - "hint_1 = \"Unfortunately not, here is a hint ...\"\n", - "comment_1 = \"Indeed, .. some additional information ... \"\n", - "\n", - "# make the required widgets\n", - "question_widget = ipw.Label(value=question_1)\n", - "choices_widget = ipw.Dropdown(options=choices_1, description=\"\", disabled=False)\n", - "submit_button = ipw.Button(description=\"Submit\")\n", - "feedback_widget = ipw.Text(value=\"\", placeholder=\"\", description=\"\", disabled=False,layout=ipw.Layout(width=\"500px\"))\n", - "\n", - "# align the widgets vertically\n", - "quiz_widget = ipw.VBox([question_widget] + [choices_widget] + [submit_button] + [feedback_widget])\n", - "\n", - "# display the widgets\n", - "display(quiz_widget)\n", - "\n", - "def check_answers(button):\n", - " chosen_answer = choices_widget.value\n", - " correct_answer = answer_1\n", - "\n", - " if chosen_answer == correct_answer:\n", - " feedback_widget.value = comment_1\n", - " else:\n", - " feedback_widget.value = hint_1\n", - "\n", - "# Run the function check_answers when the submit button is pressed\n", - "submit_button.on_click(check_answers)" - ] - }, - { - "cell_type": "markdown", - "id": "345c2bc6-59aa-4827-bffd-f66baeedd0c0", - "metadata": {}, - "source": [ - "### Multiple questions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "79657e7f-de99-40f9-8484-6037cc978cea", - "metadata": {}, - "outputs": [], - "source": [ - "# the information of the questions\n", - "question_1 = \"A large continental shelf width is at a:\"\n", - "choices_1 = [\"Leading edge\", \"Trailing edge\", \"marginal sea\"]\n", - "answer_1 = choices_1[1]\n", - "hint_1 = \"Unfortunately not, here is a hint ...\"\n", - "comment_1 = \"Indeed, .. some additional information ... \"\n", - "\n", - "question_2 = \"The oceanic geoid is: \"\n", - "choices_2 = [\"An oval shape\", \"The shape of the ocean surface under only gravity forces\", \"A geo triangle with a different shape\", \"The actual ocean surface\"]\n", - "answer_2 = choices_2[1]\n", - "hint_2 = \"Unfortunately not, here is a hint ...\"\n", - "comment_2 = \"Indeed, .. some additional information ... \"\n", - "\n", - "# store the questions in a list\n", - "questions = [question_1, question_2]\n", - "choices = [choices_1, choices_2]\n", - "answers = [answer_1, answer_2]\n", - "hints = [hint_1, hint_2]\n", - "comments = [comment_1, comment_2]\n", - "\n", - "# an empty list to store the widgets\n", - "all_widgets = [] # for visualization, store all the widgets in the order they are going to be displayed\n", - "question_widgets = [] # store all the question widgets in a list\n", - "choices_widgets = [] # store all the choices widgets in a list\n", - "\n", - "# make the widgets in a loop, one widget states the question and one shows the options that can be selected.\n", - "for question, choice, answer, hint, comment in zip(questions, choices, answers, hints, comments):\n", - " question_widget = ipw.Label(value=question,layout=ipw.Layout(width=\"300px\"))\n", - " choices_widget = ipw.Dropdown(\n", - " options=choice, description=\"Choices:\", disabled=False, layout=ipw.Layout(width=\"300px\"))\n", - "\n", - " all_widgets.append(question_widget)\n", - " all_widgets.append(choices_widget)\n", - " question_widgets.append(question_widget)\n", - " choices_widgets.append(choices_widget)\n", - "\n", - "# make a submit button and a feedback button\n", - "submit_button = ipw.Button(description=\"Submit\")\n", - "feedback_widget = ipw.Text(value=\"\", placeholder=\"\",description=\"Feedback:\",disabled=False, layout=ipw.Layout(width=\"500px\"))\n", - "\n", - "# allign the submit button and the feedback widget horizontally\n", - "HBox_check = ipw.HBox([submit_button, feedback_widget])\n", - "\n", - "# allign the widgets vertically and display them.\n", - "Vbox = ipw.VBox(all_widgets + [HBox_check])\n", - "display(Vbox)\n", - "\n", - "\n", - "# make a function to calculate the score and to give feedback\n", - "def check_answers(button):\n", - " score = 0\n", - "\n", - " for i in range(len(questions)):\n", - " answer = choices_widgets[i].value\n", - " correct_answer = answers[i]\n", - "\n", - " if answer == correct_answer:\n", - " score += 1\n", - "\n", - " feedback_widget.value = \"Your score is \" + str(score) + \"/\" + str(len(questions))\n", - "\n", - "\n", - "submit_button.on_click(check_answers)" - ] - }, - { - "cell_type": "markdown", - "id": "aa0f55c0-e87a-417f-9634-dffafa9cc972", - "metadata": {}, - "source": [ - "### Multiple selection" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "34645f22-d57d-40bc-ab42-53417d0999a0", - "metadata": {}, - "outputs": [], - "source": [ - "correct_statements = [\"Correct\", \"The earth is round\", \"Good\"]\n", - "false_statements = [\"False\", \"The earth is a cube\"]\n", - "\n", - "# Make empty list to store the widgets (refences), checkbox and true/false statements sorted.\n", - "check_boxes = [] # all the boxes to click\n", - "all_statements = [] # all the statements\n", - "\n", - "# An empty list for visualization to store the Hboxes that contains the widgets, one statement and the corresponding checkbox\n", - "all_widgets = []\n", - "\n", - "for statement in correct_statements + false_statements:\n", - " add_statement = ipw.Label(value=statement, layout=ipw.Layout(width=\"150px\"))\n", - " check_box_widget = ipw.Checkbox(value=False, description=\"\", layout=ipw.Layout(width=\"120px\"))\n", - " HBox1 = ipw.HBox([add_statement] + [check_box_widget])\n", - "\n", - " all_statements.append(add_statement)\n", - " check_boxes.append(check_box_widget)\n", - " all_widgets.append(HBox1)\n", - "\n", - "# randomize the order of statements\n", - "shuffle(all_widgets)\n", - "\n", - "# add submit button and output with, which come on the bottom \n", - "submit_button = ipw.Button(description='Submit')\n", - "output_widget = ipw.Text(value= '', placeholder='', description='', disabled=False)\n", - "\n", - "# make an additional Hbox for alligning the submit button and the output widget\n", - "HBox2 = ipw.HBox([submit_button] + [output_widget])\n", - "all_widgets.append(HBox2)\n", - "\n", - "# allign all the Hboxes beneath each other (oldest below if not randomized) and display them.\n", - "quiz_widget = ipw.VBox(all_widgets)\n", - "display(quiz_widget)\n", - "\n", - "# Check the checkbox for each statement and calculate the score.\n", - "def check_answers(button):\n", - " score = 0\n", - "\n", - " for i in range(len(check_boxes)): \n", - " check_box = check_boxes[i]\n", - " statement = all_statements[i].value\n", - "\n", - " if statement in correct_statements:\n", - " \n", - " if check_box.value == True:\n", - " score += 1 \n", - " #print(\"Checkbox is checked for: \", statement, '+1=', score)\n", - " else:\n", - " score -= 0\n", - " #print(\"Checkbox is checked for: \", statement, '-0=', score)\n", - " \n", - " if statement not in correct_statements:\n", - " if check_box.value == True:\n", - " score -= 1\n", - " #print(\"Checkbox is unchecked for: \", statement, '-1=', score)\n", - " \n", - " else:\n", - " score -= 0\n", - " #print(\"Checkbox is unchecked for: \", statement, '+0=', score)\n", - " \n", - " score = np.max([score, 0])\n", - " output_widget.value = str('Your final score is:' + str(score))\n", - " #print('Your final score is:', score)\n", - "\n", - "submit_button.on_click(check_answers)" - ] - }, - { - "cell_type": "markdown", - "id": "aa6ff084-8afd-4872-a72e-12ebc1866ab0", - "metadata": {}, - "source": [ - "### Multiple Choice questions using dictonaries." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3756ac3c-8db2-43fa-9677-c085e972ea9d", - "metadata": {}, - "outputs": [], - "source": [ - "import ipywidgets as widgets # the same as ipw" - ] - }, - { - "cell_type": "markdown", - "id": "b63bab4e-c817-4451-8f58-6bfe69db4117", - "metadata": {}, - "source": [ - "Inspired by:
\n", - "https://ipywidgets.readthedocs.io/en/7.6.2/examples/Widget%20List.html
\n", - "https://www.makeuseof.com/python-make-interactive-quiz-game/" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e1147dd6-8b58-4cd7-a799-8d8b06a530a9", - "metadata": {}, - "outputs": [], - "source": [ - "# with widget a rule usually has to end with a ',' to prevent errors\n", - "\n", - "# Multiple-choice questions with answers, hints, and commentary. In the function each part between {} is assesed independently in a loop.\n", - "single_question = [\n", - " {\n", - " 'question': 'How many answers are availabe in this question?',\n", - " 'choices': ['One', 'Two', 'Three', 'Four'],\n", - " 'answer': 'Four',\n", - " 'hint' : 'This is unfortunate, please count the answers again.',\n", - " 'comment': 'You passed the first question.'\n", - " }\n", - "]\n", - "\n", - "First_questions = [\n", - " {\n", - " 'question': 'A large continental shelf width is at a',\n", - " 'choices': ['Leading edge', 'Trailing edge', 'marginal sea'],\n", - " 'answer': 'Trailing edge',\n", - " 'hint': 'The continental shelf must not sink',\n", - " 'comment': 'Indeed, a large continental shelf width does not occur at a leading edge'\n", - " },\n", - " {\n", - " 'question': 'The oceanic geoid is',\n", - " 'choices': ['An oval shape', 'The shape of the ocean surface under only gravity forces', 'A geotriangle with a different shape', 'The actual ocean surface'],\n", - " 'answer': 'The shape of the ocean surface under only gravity forces',\n", - " 'hint': 'This is not the definition of a geoid',\n", - " 'comment': 'Indeed, gravitation by landmasses and ice influence the shape of the ocean surface'\n", - " }\n", - "]# these questions are asked/tested a few cells down\n", - "\n", - "# Create a quiz, with widget for the answers (choices), a submit button which checks the given answer (widget input value) and gives responses\n", - "def multiple_choice_quiz(questions):\n", - " # Make a widget for each question\n", - " question_widgets = [] # a list of all the questions, alternating question (label) with corresponding choices (Dropdown). (If two different list are made then it will first display all the questions and then all the answers)\n", - " for question in questions:\n", - " question_widget = widgets.Label(value=question['question'],)\n", - " choices_widget = widgets.Dropdown(options=question['choices'], description='Choices:', disabled=False,)\n", - " \n", - " question_widgets.append(question_widget)\n", - " question_widgets.append(choices_widget)\n", - "\n", - " # Create a submit button widget\n", - " submit_button = widgets.Button(description='Submit',)\n", - "\n", - " # Combine the question widgets and the submit button into a vertical box\n", - " quiz_widget = widgets.VBox(question_widgets + [submit_button])\n", - " display(quiz_widget) \n", - " \n", - " # Check the answers when the submit button is clicked, which is called below. \n", - " def check_answers(button):\n", - " correct_answers = 0\n", - " print('')\n", - " \n", - " # check if the answers are answered correctly or wrong and then gives a response\n", - " for i in range(len(questions)):\n", - " if question_widgets[2*i+1].value == questions[i]['answer']:# answer is good\n", - " correct_answers += 1\n", - " \n", - " if len(questions[i]['comment']) > 0: # commentary is typed\n", - " print(questions[i]['comment'])\n", - " \n", - " if question_widgets[2*i+1].value != questions[i]['answer'] and len(questions[i]['hint']) > 0: #answer is wrong and a hint can be given\n", - " print(questions[i]['hint'])\n", - " \n", - " print('You got', correct_answers, 'out of', len(questions), 'questions correct.')\n", - "\n", - " # Run function check_answers when the submit button is pressed\n", - " submit_button.on_click(check_answers)\n", - "\n", - " return quiz_widget" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "69d713b4-28d4-45c6-82df-b7d754e1ea1b", - "metadata": {}, - "outputs": [], - "source": [ - "quiz_widget = multiple_choice_quiz(single_question)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "47b74144-bba9-4e11-b3b7-6659edd6d05d", - "metadata": {}, - "outputs": [], - "source": [ - "quiz_widget = multiple_choice_quiz(First_questions)" - ] - }, - { - "cell_type": "markdown", - "id": "247f45ff-c7dd-4d37-95a0-33f00828c984", - "metadata": {}, - "source": [ - "### Multiple choice questions using dictoniaries and xarray" - ] - }, - { - "cell_type": "markdown", - "id": "5b2458a0-b690-4037-ace5-4756062b9db4", - "metadata": {}, - "source": [ - "A nice option would be to select a specific question. So all the questions can be described in one dictionary, and then specific ones are asked. This gives the opportunity to have a large question pool from which a few are presented, randomizing the knowledge questions.
\n", - "\n", - "The function has worked in the past (june 2023), but it now gives an error, it goes wrong in building the xarray (something has changed, and could not be fixed fast). The code is now as raw text." - ] - }, - { - "cell_type": "raw", - "id": "29000e1d-6d31-475e-b7f2-8c0ad1384387", - "metadata": {}, - "source": [ - "import xarray as xr" - ] - }, - { - "cell_type": "raw", - "id": "9259825c-df70-4602-9e97-f6778cbad6cb", - "metadata": {}, - "source": [ - "# gets the local path, the main notebook is one level above in the folder structure.\n", - "path_parent = Path.cwd()\n", - "\n", - "# It will gives the parent location when runned by the %run command in the run_test_questions notebook, which is the location of this file.\n", - "if path_parent.name == 'Questions':\n", - " path_parent = Path.cwd().parent\n", - " print('This code is runned locally, not through %run, go to parent location')\n", - "\n", - "# The figures are in a subfolder\n", - "path_figures = Path.joinpath(path_parent, \"Figures\")\n", - "path_figures" - ] - }, - { - "cell_type": "raw", - "id": "3bb88edb-fb76-4f86-89f6-aa0039969ef8", - "metadata": {}, - "source": [ - "path_figures = 'not specified'\n", - "\n", - "all_questions = [\n", - " {\n", - " 'id': '1A',\n", - " 'question': 'A large continental shelf width is at a',\n", - " 'choices': np.array(['Leading edge', 'Trailing edge', 'marginal sea']),\n", - " 'answer': 'Trailing edge',\n", - " 'hint': 'The continental shelf must not sink',\n", - " 'comment': 'Indeed, a large continental shelf width does not occur at a leading edge',\n", - " 'figure' : ''\n", - " },\n", - " {\n", - " 'id': '10',\n", - " 'question': 'The oceanic geoid is',\n", - " 'choices': ['An oval shape', 'The shape of the ocean surface under only gravity forces', 'A geotriangle with a different shape', 'The actual ocean surface'],\n", - " 'answer': 'The shape of the ocean surface under only gravity forces',\n", - " 'hint': 'This is not the definition of a geoid',\n", - " 'comment': 'Indeed, gravitation by landmasses and ice influence the shape of the ocean surface',\n", - " 'figure' : '' #[Path.joinpath(path_figures, \"Test_figure.png\")]\n", - " }\n", - "]" - ] - }, - { - "cell_type": "raw", - "id": "0f96d154-7a15-4d2f-8b85-10bfa207a01a", - "metadata": {}, - "source": [ - "%%capture output \n", - "# prevents giving a notification that it can be done differently, no code or comments can be placed above this Line magic function\n", - "\n", - "# reorganize the data and put it in an xarray\n", - "ids = []\n", - "questions = []\n", - "choices = []\n", - "answers = []\n", - "hints = []\n", - "comments = []\n", - "figures = []\n", - "\n", - "for i in range(len(all_questions)):\n", - " ids.append(all_questions[i]['id'])\n", - " questions.append(all_questions[i]['question'])\n", - " choices.append(all_questions[i]['choices'])\n", - " answers.append(all_questions[i]['answer'])\n", - " hints.append(all_questions[i]['hint'])\n", - " comments.append(all_questions[i]['comment'])\n", - " figures.append(all_questions[i]['figure'])\n", - " \n", - "XR_questions = xr.Dataset(data_vars={\"id\": ids})\n", - "XR_questions['question'] = (('id'), questions)\n", - "XR_questions['choices'] = (('id'), choices)\n", - "XR_questions['answer'] = (('id'), answers)\n", - "XR_questions['hint'] = (('id'), hints)\n", - "XR_questions['comment'] = (('id'), comments);\n", - "XR_questions['figures'] = (('id'), figures);" - ] - }, - { - "cell_type": "raw", - "id": "656d03c1-1a78-417e-8df8-cda581956b8b", - "metadata": {}, - "source": [ - "def Q2B(id):\n", - " # select question out of data\n", - " question_temp = XR_questions.sel(id = id)\n", - " \n", - " # makes the widgets\n", - " question_widget = widgets.Label(value=question_temp['question'].values.tolist(),)\n", - " choices_widget = widgets.Dropdown(options=question_temp['choices'].values.tolist(), description='Choices:', disabled=False,)\n", - " submit_button = widgets.Button(description='Submit',)\n", - " \n", - " # load answer, hint, and commentary for responses\n", - " answer = question_temp['answer'].values.tolist()\n", - " hint = question_temp['hint'].values.tolist()\n", - " comment = question_temp['comment'].values.tolist()\n", - " \n", - " # load and plot figures if they are defined for the question\n", - " figures = question_temp['figures'].values.tolist()\n", - " \n", - " if len(figures)>0:\n", - " for i in range(len(figures)):\n", - " img = np.asarray(Image.open(figures[i]))\n", - " plt.imshow(img);\n", - " plt.axis('off'); \n", - " \n", - " quiz_widget = widgets.VBox([question_widget] + [choices_widget] + [submit_button])\n", - " display(quiz_widget) \n", - " \n", - " def check_answers(button):\n", - " #print('correct answer: ', answer) # print for debug\n", - " #print('given answer: ', choices_widget.value) # print for debug\n", - " if choices_widget.value == answer: # if the answer is correct\n", - " print('You gave the correct answer')\n", - " print(comment)\n", - " \n", - " else:\n", - " print(hint)\n", - " \n", - " submit_button.on_click(check_answers)" - ] - }, - { - "cell_type": "raw", - "id": "02fda55b-5509-4f4a-a5a7-6f9af23ffeab", - "metadata": {}, - "source": [ - "Q2B('1A')" - ] - }, - { - "cell_type": "raw", - "id": "9b3b6a3d-eb77-4d87-b1d1-728dc76aab27", - "metadata": {}, - "source": [ - "Q2B('10')" - ] - }, - { - "cell_type": "markdown", - "id": "54f1aeba-181d-4578-84b8-072f92f492c4", - "metadata": {}, - "source": [ - "### Select true using dictonaries and xarray" - ] - }, - { - "cell_type": "markdown", - "id": "f7a5f4d4-0809-4462-bfae-f380a982da20", - "metadata": {}, - "source": [ - "With a similar structure as before, now having good and wrong statements. Also here goes something wrong in building the xarray, the function has worked in june 2023." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "217130f2-d799-41e2-8280-e7aa8a9d1b8b", - "metadata": {}, - "outputs": [], - "source": [ - "all_questions = [\n", - " {\n", - " 'id': '1A',\n", - " 'question': 'Which statements are True?',\n", - " 'correct': ['This is correct', 'Good'],\n", - " 'false': ['This is false', 'Wrong', 'Not good'],\n", - " 'hint': '' ,\n", - " 'comment': '',\n", - " 'figure' : ''\n", - " },\n", - " {\n", - " 'id': '1B',\n", - " 'question': 'Which statements are True?',\n", - " 'correct': ['The earth is round'],\n", - " 'false': ['The earth is flat', 'The earth is a square'],\n", - " 'hint': '-',\n", - " 'comment': '-',\n", - " 'figure' : ''# [Path.joinpath(path_figures, \"Test_figure.png\")]\n", - " }\n", - "]" - ] - }, - { - "cell_type": "raw", - "id": "b3c2ce05-5840-4014-8435-c5307b3c205b", - "metadata": {}, - "source": [ - "%%capture output \n", - "# prevents giving a notification that it can be done differently, no code or comments can be placed above this Line magic function\n", - "\n", - "# reorganize the data and put it in an xarray\n", - "ids = []\n", - "questions = []\n", - "correct_all = []\n", - "false_all = []\n", - "hints = []\n", - "comments = []\n", - "figures = []\n", - "\n", - "for i in range(len(all_questions)):\n", - " ids.append(all_questions[i]['id'])\n", - " questions.append(all_questions[i]['question'])\n", - " correct_all.append(all_questions[i]['correct'])\n", - " false_all.append(all_questions[i]['false'])\n", - " hints.append(all_questions[i]['hint'])\n", - " comments.append(all_questions[i]['comment'])\n", - " figures.append(all_questions[i]['figure'])\n", - " \n", - "XR_questions = xr.Dataset(data_vars={\"id\": ids})\n", - "XR_questions['question'] = (('id'), questions)\n", - "XR_questions['correct'] = (('id'), correct_all)\n", - "XR_questions['false'] = (('id'), false_all)\n", - "XR_questions['hint'] = (('id'), hints)\n", - "XR_questions['comment'] = (('id'), comments);\n", - "XR_questions['figures'] = (('id'), figures);" - ] - }, - { - "cell_type": "raw", - "id": "f8efcb7d-250b-41f5-b91d-be87d48e8c59", - "metadata": {}, - "source": [ - "def Q2C(id):\n", - " # select question out of data\n", - " question_temp = XR_questions.sel(id = id)\n", - " question_temp\n", - "\n", - " # Make empty list to store the widgets (refences), checkbox and true/false statements sorted. \n", - " check_boxes = [] # all the boxes to click\n", - " all_statements = []# all the statments\n", - "\n", - " # An empty list for visualization to store the Hboxes that contains the widgets, one statement and the corresponding checkbox\n", - " statement_widgets = []\n", - "\n", - " # loop true all the correct and false statement\n", - " for statement in (question_temp['correct'].values.tolist() + question_temp['false'].values.tolist()): \n", - " \n", - " # for each true/false statement make a tekst widget and checkbox\n", - " add_statement = widgets.Label(value=statement,)\n", - " check_box_widget = widgets.Checkbox(value=False, description = '')\n", - "\n", - " # store all the widgets on type in a list\n", - " all_statements.append(add_statement)\n", - " check_boxes.append(check_box_widget)\n", - "\n", - " # visualizationof the widgets\n", - " add_statement.layout.width = '300px'\n", - " add_statement.layout.flex = 'none' # dont adjust when hbox size changes\n", - " check_box_widget.layout.min_width\n", - " check_box_widget.layout.flex = 'none'\n", - " \n", - " # make an horizontal box, placing the checkbox next to the true/false statement.\n", - " HBox = widgets.HBox([add_statement] + [check_box_widget])\n", - " HBox.layout.justify_content = 'flex-start' # dont adjust the layout on the window \n", - " statement_widgets.append(HBox)\n", - "\n", - " shuffle(statement_widgets)# randomize the order of statements\n", - "\n", - " # add submit button on the bottom and display the widgets\n", - " submit_button = widgets.Button(description='Submit',)\n", - " \n", - " # add feedback button, that returns the final score after submitting the answer\n", - " output_widget = widgets.Text(value= '', placeholder='', description='', disabled=False)\n", - " \n", - " # make an additional Hbox for alligning the submit button and the output widget\n", - " HBox2 = widgets.HBox([submit_button] + [output_widget])\n", - " HBox2.layout.justify_content = 'flex-start'\n", - "\n", - " statement_widgets.append(HBox2)# and add them to the list of HBox, which will be depicted on the bottom in the VBox.\n", - " \n", - " # allign all the Hboxes beneath each other (oldest below) and display them.\n", - " quiz_widget = widgets.VBox(statement_widgets)\n", - " display(quiz_widget)\n", - "\n", - " # load and plot figures if they are defined for the question\n", - " figures = question_temp['figures'].values.tolist()\n", - " if len(figures)>0:\n", - " for i in range(len(figures)):\n", - " img = np.asarray(Image.open(figures[i]))\n", - " plt.imshow(img);\n", - " plt.axis('off'); \n", - " \n", - " # Check if the checkbox for each statement and calculate the score.\n", - " def check_answers(button):\n", - " score = 0\n", - "\n", - " for i in range(len(check_boxes)): \n", - " check_box = check_boxes[i]\n", - " statement = all_statements[i].value\n", - "\n", - " if statement in question_temp['correct'].values.tolist():\n", - " points = 1\n", - " else:\n", - " points = -1 \n", - "\n", - " if check_box.value == True:\n", - " score += points\n", - " #print(\"Checkbox is checked for: \", statement, '+', points, '=', score)\n", - " else:\n", - " score -= points\n", - " #print(\"Checkbox is unchecked for: \", statement,'+', points, '=', score)\n", - "\n", - " score = np.max([score, 0])\n", - " output_widget.value = str('Your final score is:' + str(score))\n", - " #print('Your final score is:', score)\n", - "\n", - " submit_button.on_click(check_answers)\n" - ] - }, - { - "cell_type": "raw", - "id": "a56a63ba-8662-4b26-9632-8941a4dbe56c", - "metadata": {}, - "source": [ - "Q2C('1A')" - ] - }, - { - "cell_type": "raw", - "id": "aee60203-b32b-4ecb-834a-07ed7d0f2478", - "metadata": {}, - "source": [ - "Q2C('1B')" - ] - }, - { - "cell_type": "markdown", - "id": "62c804ac-9878-4f80-b977-7be37e0507cb", - "metadata": {}, - "source": [ - "And one improved version, also not working due to the xarray." - ] - }, - { - "cell_type": "raw", - "id": "ecd1ed2d-067c-44a6-8b43-0ffcea32db03", - "metadata": {}, - "source": [ - "def Q2C_V2(id):\n", - " # select question out of data\n", - " question_temp = XR_questions.sel(id = id)\n", - " question_temp\n", - "\n", - "\n", - " check_boxes = []\n", - " all_statements = []\n", - "\n", - " statement_widgets = []\n", - "\n", - "\n", - " for statement in (question_temp['correct'].values.tolist() + question_temp['false'].values.tolist()): \n", - " add_statement = widgets.Label(value=statement,)\n", - " check_box_widget = widgets.Checkbox(value=False, description = '')\n", - "\n", - " all_statements.append(add_statement)\n", - " check_boxes.append(check_box_widget)\n", - "\n", - " # visualizationof the widgets\n", - " add_statement.layout.width = '300px'\n", - " add_statement.layout.flex = 'none' # dont adjust when hbox size changes\n", - " check_box_widget.layout.min_width\n", - " check_box_widget.layout.flex = 'none'\n", - "\n", - " HBox = widgets.HBox([add_statement] + [check_box_widget])\n", - " HBox.layout.justify_content = 'flex-start'\n", - " statement_widgets.append(HBox)\n", - "\n", - " shuffle(statement_widgets)# randomize the order of statements\n", - "\n", - " # add submit button on the bottom and display the widgets\n", - " submit_button = widgets.Button(description='Submit',)\n", - " \n", - " # add feedback button\n", - " output_widget = widgets.Text(value= '', placeholder='', description='', disabled=False)\n", - " \n", - " HBox2 = widgets.HBox([submit_button] + [output_widget])\n", - " HBox2.layout.justify_content = 'flex-start'\n", - " \n", - " statement_widgets.append(HBox2)\n", - " \n", - " quiz_widget = widgets.VBox(statement_widgets)\n", - " display(quiz_widget)\n", - "\n", - " # load and plot figures if they are defined for the question\n", - " figures = question_temp['figures'].values.tolist()\n", - " if len(figures)>0:\n", - " for i in range(len(figures)):\n", - " img = np.asarray(Image.open(figures[i]))\n", - " plt.imshow(img);\n", - " plt.axis('off'); \n", - " \n", - " # Check if the checkbox for each statement and calculate the score.\n", - " def check_answers(button):\n", - " score = 0\n", - "\n", - " for i in range(len(check_boxes)): \n", - " check_box = check_boxes[i]\n", - " statement = all_statements[i].value\n", - "\n", - " if statement in question_temp['correct'].values.tolist():\n", - " \n", - " if check_box.value == True:\n", - " score += 1\n", - " #print(\"Checkbox is checked for: \", statement, '+1=', score)\n", - " else:\n", - " score -= 0\n", - " #print(\"Checkbox is checked for: \", statement, '-0=', score)\n", - " \n", - " else:\n", - " if check_box.value == True:\n", - " score -= 1\n", - " #print(\"Checkbox is unchecked for: \", statement, '-1=', score)\n", - " \n", - " else:\n", - " score -= 0\n", - " #print(\"Checkbox is unchecked for: \", statement, '+0=', score)\n", - " \n", - " score = np.max([score, 0])\n", - " output_widget.value = str('Your final score is:' + str(score))\n", - " #print('Your final score is:', score)\n", - "\n", - " submit_button.on_click(check_answers)\n" - ] - }, - { - "cell_type": "raw", - "id": "ef50368d-cf8c-4f21-8b2f-4e1d1bb19fef", - "metadata": {}, - "source": [ - "Q2C_V2('1A')" - ] - }, - { - "cell_type": "markdown", - "id": "13edcfc3-a4fe-4ea0-af8e-e9ce7829c33f", - "metadata": {}, - "source": [ - "### Wave components progating over time" - ] - }, - { - "cell_type": "markdown", - "id": "7317af38-d191-40fa-8f46-5333eb2d5d6f", - "metadata": {}, - "source": [ - "#### Plot one wave component" - ] - }, - { - "cell_type": "markdown", - "id": "96b4ee6e-ea8a-4775-89b6-1f4c80938f50", - "metadata": {}, - "source": [ - "Usefull links to make vboxes:
\n", - "https://ipywidgets.readthedocs.io/en/7.6.2/examples/Widget%20List.html
\n", - "https://kapernikov.com/ipywidgets-with-matplotlib/
\n", - "https://stackoverflow.com/questions/58416763/change-the-alignment-of-the-content-of-a-label-in-ipywidget
" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aeca0ad1-c7fe-4011-ab49-f41defc18e54", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from matplotlib.animation import FuncAnimation\n", - "import ipywidgets as widgets" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "346f91a7-3911-402c-b38b-35b7b29bffe1", - "metadata": {}, - "outputs": [], - "source": [ - "# required to have animation displayed\n", - "%matplotlib widget" - ] - }, - { - "cell_type": "raw", - "id": "4b398faf-6b84-4ae9-a68e-d1be5e3f9eb5", - "metadata": {}, - "source": [ - "# manual set interval between frames in secondes\n", - "delta_t = 0.1\n", - "\n", - "# setup linear mesh (x) and duration before time (t) is reset\n", - "x = np.linspace(0, 100, 1000)\n", - "t = np.arange(0,1000,delta_t)\n", - "\n", - "# Define parameters and widgets and display User Interface (UI)\n", - "a = widgets.FloatSlider(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - "w = widgets.FloatSlider(value=0.35, min=0, max=5, step=0.01, description='\\u03C9 [rad/s]')\n", - "k = widgets.FloatSlider(value=0.25, min=0, max=5, step=0.01, description='k [rad/m]')\n", - "UI = widgets.HBox([a, w, k])\n", - "display(UI)\n", - "\n", - "# Create figure and axes\n", - "fig, ax = plt.subplots()\n", - "\n", - "# Compute initial displacement\n", - "eta = a.value*np.sin(k.value*x)\n", - "\n", - "# Plot initial wave\n", - "line, = ax.plot(x, eta)\n", - "\n", - "# update the line for each frame\n", - "def update(frame):\n", - " t = delta_t * frame\n", - " eta = a.value*np.sin(w.value*t-k.value*x)\n", - " line.set_ydata(eta)\n", - " \n", - " plt.ylim(- a.value * 1.1, a.value*1.1)# change limits of plots based on wave amplitude, in function so it responds to the widget\n", - "\n", - "# Create animation\n", - "animation = FuncAnimation(fig, update, frames=len(t), interval=delta_t*1000)\n", - "\n", - "# Show animation\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "b168d56d-1f7c-4281-afa4-0ca8d5db9608", - "metadata": {}, - "source": [ - "#### Plot 3 wave components with amplitude, wave number and radial frequency as input parameters" - ] - }, - { - "cell_type": "raw", - "id": "e8a6fcd0-c520-40d3-88fc-2046c1204da5", - "metadata": {}, - "source": [ - "# manual set interval between frames in secondes\n", - "delta_t = 0.1\n", - "\n", - "# setup linear mesh (x) and duration before time (t) is reset\n", - "x = np.linspace(0, 100, 1000)\n", - "t = np.arange(0,1000,delta_t)\n", - "\n", - "# Define parameters and widgets\n", - "a1 = widgets.FloatSlider(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - "a2 = widgets.FloatSlider(value=0, min=0, max=20, step=0.01, description='a [m]')\n", - "a3 = widgets.FloatSlider(value=0, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - "w1 = widgets.FloatSlider(value=0.35, min=0, max=5, step=0.01, description='\\u03C9 [rad/s]')\n", - "w2 = widgets.FloatSlider(value=0.35, min=0, max=5, step=0.01, description='\\u03C9 [rad/s]')\n", - "w3 = widgets.FloatSlider(value=0.35, min=0, max=5, step=0.01, description='\\u03C9 [rad/s]')\n", - "\n", - "k1 = widgets.FloatSlider(value=0.5, min=0, max=5, step=0.01, description='k [rad/m]')\n", - "k2 = widgets.FloatSlider(value=0.5, min=0, max=5, step=0.01, description='k [rad/m]')\n", - "k3 = widgets.FloatSlider(value=0.5, min=0, max=5, step=0.01, description='k [rad/m]')\n", - "\n", - "# Setup widget layout (User Interface) and display\n", - "vbox1 = widgets.VBox([widgets.Label('Wave 1', layout=widgets.Layout(align_self='center')),a1, w1, k1])\n", - "vbox2 = widgets.VBox([widgets.Label('Wave 2', layout=widgets.Layout(align_self='center')),a2, w2, k2])\n", - "vbox3 = widgets.VBox([widgets.Label('Wave 3', layout=widgets.Layout(align_self='center')),a3, w3, k3])\n", - "\n", - "ui = widgets.HBox([vbox1, vbox2, vbox3])\n", - "display(ui)\n", - "\n", - "# Create figure and axes\n", - "fig, ax = plt.subplots()\n", - "\n", - "# Compute initial displacement\n", - "eta = a1.value*np.sin(k1.value*x) + a2.value*np.sin(k2.value*x) + a3.value*np.sin(k3.value*x)\n", - "eta1 = a1.value*np.sin(k1.value*x)\n", - "eta2 = a2.value*np.sin(k2.value*x)\n", - "eta3 = a3.value*np.sin(k3.value*x)\n", - "\n", - "# Plot initial wave\n", - "line, = ax.plot(x, eta, label = 'eta', color = 'k')\n", - "line1, = ax.plot(x, eta1, label = 'wave 1', linewidth= 0.5)\n", - "line2, = ax.plot(x, eta2, label = 'wave 2', linewidth= 0.5)\n", - "line3, = ax.plot(x, eta3, label = 'wave 3', linewidth= 0.5) \n", - "\n", - "def update(frame):\n", - " # get current time\n", - " t = delta_t * frame\n", - "\n", - " # calculate sea surface elevation\n", - " eta = a1.value*np.sin(w1.value*t-k1.value*x) + a2.value*np.sin(w2.value*t-k2.value*x) + a3.value*np.sin(w3.value*t-k3.value*x)\n", - " eta1 = a1.value*np.sin(w1.value*t-k1.value*x)\n", - " eta2 = a2.value*np.sin(w2.value*t-k2.value*x)\n", - " eta3 = a3.value*np.sin(w3.value*t-k3.value*x)\n", - "\n", - " # adjust sea surface elevation (line) in plot\n", - " line.set_ydata(eta)\n", - "\n", - " # adjust line of wave component or set thinkness to 0 (not visible) if amplitude is zero\n", - " if a1.value > 0:\n", - " line1.set_ydata(eta1)\n", - " line1.set_linewidth(0.5)\n", - " else:\n", - " line1.set_linewidth(0)\n", - "\n", - " if a2.value > 0:\n", - " line2.set_ydata(eta2)\n", - " line2.set_linewidth(0.5)\n", - " else:\n", - " line2.set_linewidth(0)\n", - "\n", - " if a3.value > 0:\n", - " line3.set_ydata(eta3)\n", - " line3.set_linewidth(0.5)\n", - " else:\n", - " line3.set_linewidth(0)\n", - "\n", - " # adjust the boundaries of the plot\n", - " amp = a1.value +a2.value + a3.value\n", - " plt.ylim(-amp*1.1, amp*1.1)# change limits of plots based on wave amplitude\n", - "\n", - "# Create animation\n", - "animation = FuncAnimation(fig, update, frames=len(t), interval=delta_t*1000)\n", - "\n", - "# Show animation\n", - "plt.legend(loc = 'upper right')\n", - "plt.show()" - ] - }, - { - "cell_type": "markdown", - "id": "943cb926-34bf-43e8-9c7b-c42e71214fc5", - "metadata": {}, - "source": [ - "#### Plot 3 wave components with amplitude, wave period, and length as input parameters" - ] - }, - { - "cell_type": "raw", - "id": "6a327ecd-7f03-46c9-aecb-ac9726ab07f4", - "metadata": {}, - "source": [ - "def Q5():# manual set interval between frames in secondes\n", - " # manual set interval between frames in secondes\n", - " delta_t = 0.1\n", - "\n", - " # setup linear mesh (x) and duration before time (t) is reset\n", - " x = np.linspace(0, 500, 5000)\n", - " t = np.arange(0,1000,delta_t)\n", - "\n", - " # Define parameters and widgets\n", - " a1 = widgets.FloatSlider(value=1, min=0, max=20, step=0.01, description='a [m]')\n", - " a2 = widgets.FloatSlider(value=1.3, min=0, max=20, step=0.01, description='a [m]')\n", - " a3 = widgets.FloatSlider(value=0, min=0, max=20, step=0.01, description='a [m]')\n", - "\n", - " T1 = widgets.FloatSlider(value=5, min=0.01, max=25, step=0.01, description='T [s]')\n", - " T2 = widgets.FloatSlider(value=7.5, min=0.01, max=25, step=0.01, description='T [s]')\n", - " T3 = widgets.FloatSlider(value=6, min=0.01, max=25, step=0.01, description='T [s]')\n", - "\n", - " L1 = widgets.FloatSlider(value=10, min=0.01, max=50, step=0.01, description='L [m]')\n", - " L2 = widgets.FloatSlider(value=11, min=0.01, max=50, step=0.01, description='L [m]')\n", - " L3 = widgets.FloatSlider(value=10.5, min=0.01, max=50, step=0.01, description='L [m]')\n", - "\n", - " # Setup widget layout (User Interface) and display\n", - " vbox1 = widgets.VBox([widgets.Label('Wave 1', layout=widgets.Layout(align_self='center')),a1, T1, L1])\n", - " vbox2 = widgets.VBox([widgets.Label('Wave 2', layout=widgets.Layout(align_self='center')),a2, T2, L2])\n", - " vbox3 = widgets.VBox([widgets.Label('Wave 3', layout=widgets.Layout(align_self='center')),a3, T3, L3])\n", - "\n", - " ui = widgets.HBox([vbox1, vbox2, vbox3])\n", - " display(ui)\n", - "\n", - " # Create figure and axes\n", - " fig, ax = plt.subplots(figsize = (9,5))\n", - "\n", - " # Compute initial displacement\n", - " eta = a1.value*np.sin(2*np.pi/L1.value*x) + a2.value*np.sin(2*np.pi/L2.value*x) + a3.value*np.sin(2*np.pi/L1.value*x)\n", - " eta1 = a1.value*np.sin(2*np.pi/L1.value*x)\n", - " eta2 = a2.value*np.sin(2*np.pi/L2.value*x)\n", - " eta3 = a3.value*np.sin(2*np.pi/L3.value*x)\n", - "\n", - " # Plot initial wave\n", - " line, = ax.plot(x, eta, label = '\\u03B7', color = 'k')\n", - " line1, = ax.plot(x, eta1, label = 'wave 1', linewidth= 0.5, color = '#011f4b')\n", - " line2, = ax.plot(x, eta2, label = 'wave 2', linewidth= 0.5, color = 'r')#03396c\n", - " line3, = ax.plot(x, eta3, label = 'wave 3', linewidth= 0.5, color = '#368713') \n", - "\n", - " def update2(frame):\n", - " # get current time\n", - " t = delta_t * frame\n", - "\n", - " # calculate sea surface elevation\n", - " eta = a1.value*np.sin(2*np.pi/T1.value*t-2*np.pi/L1.value*x) + a2.value*np.sin(2*np.pi/T2.value*t-2*np.pi/L2.value*x) + a3.value*np.sin(2*np.pi/T3.value*t-2*np.pi/L3.value*x)\n", - " eta1 = a1.value*np.sin(2*np.pi/T1.value*t-2*np.pi/L1.value*x)\n", - " eta2 = a2.value*np.sin(2*np.pi/T2.value*t-2*np.pi/L2.value*x)\n", - " eta3 = a3.value*np.sin(2*np.pi/T3.value*t-2*np.pi/L3.value*x)\n", - "\n", - " # adjust sea surface elevation (line) in plot\n", - " line.set_ydata(eta)\n", - "\n", - " # adjust line of wave component or set thinkness to 0 (not visible) if amplitude is zero\n", - " if a1.value > 0:\n", - " line1.set_ydata(eta1)\n", - " line1.set_linewidth(0.5)\n", - " else:\n", - " line1.set_linewidth(0)\n", - "\n", - " if a2.value > 0:\n", - " line2.set_ydata(eta2)\n", - " line2.set_linewidth(0.5)\n", - " else:\n", - " line2.set_linewidth(0)\n", - "\n", - " if a3.value > 0:\n", - " line3.set_ydata(eta3)\n", - " line3.set_linewidth(0.5)\n", - " else:\n", - " line3.set_linewidth(0)\n", - "\n", - " # adjust the boundaries of the plot\n", - " amp = a1.value +a2.value + a3.value\n", - " plt.ylim(-amp*1.1, amp*1.1)# change limits of plots based on wave amplitude\n", - "\n", - " # Create animation\n", - " animation = FuncAnimation(fig, update2, frames=len(t), interval=delta_t*1000)\n", - "\n", - " # Show animation\n", - " plt.legend(loc = 'upper right')\n", - " plt.xlim(0,500)\n", - " plt.xlabel('x [m]')\n", - " plt.xlabel('y [m]')\n", - " plt.show()\n", - " \n", - " return animation, a1,a2,a3,T1,T2,T3,L1,L2,L3 # animation is required to prevent freezing of the picture, a1 is the widget, exporting a1.value will result in not updating the widget value" - ] - }, - { - "cell_type": "markdown", - "id": "05e30ef4-ff77-467c-b820-a724a0c80011", - "metadata": {}, - "source": [ - "#### Ask question where the answer is based on the input on the graph" - ] - }, - { - "cell_type": "markdown", - "id": "e02082be-f3c8-47e1-9bb4-44d0337a60d1", - "metadata": {}, - "source": [ - "The function above can be used as input for questions. " - ] - }, - { - "cell_type": "raw", - "id": "850d2abe-730f-4093-b258-1c9e81c7515b", - "metadata": {}, - "source": [ - "anim5 = Q5()\n", - "\n", - "def Q6(id):\n", - " # select question out of data\n", - " question_temp = XR_questions.sel(id = id)\n", - " \n", - " # makes the widgets\n", - " question_widget = widgets.Label(value=question_temp['question'].values.tolist(),)\n", - " num_widget = widgets.FloatText(value=7.51, description='Answer:', disabled=False, step = 0.01,)\n", - " submit_button = widgets.Button(description='Submit',)\n", - " \n", - " # load answer, hint, and commentary for responses\n", - " answer = question_temp['answer'].values.tolist()\n", - " hint = question_temp['hint'].values.tolist()\n", - " comment = question_temp['comment'].values.tolist()\n", - " \n", - " # load and plot figures if they are defined for the question\n", - " figures = question_temp['figures'].values.tolist()\n", - " \n", - " if len(figures)>0:\n", - " for i in range(len(figures)):\n", - " img = np.asarray(Image.open(figures[i]))\n", - " plt.imshow(img);\n", - " plt.axis('off'); \n", - " \n", - " #place all the horizontal boxes in one vertical box, and display it.\n", - " quiz_widget = widgets.VBox([question_widget] + [num_widget] + [submit_button])\n", - " display(quiz_widget) \n", - "\n", - " def check_answers(button, answer = answer): # answer has to be called to prevent reference before assignment\n", - " \n", - " # get and update values for parameters\n", - " animation,a1,a2,a3,T1,T2,T3,L1,L2,L3 = anim5[0], anim5[1].value, anim5[2].value, anim5[3].value, anim5[4].value, anim5[5].value, anim5[6].value, anim5[7].value, anim5[8].value, anim5[9].value\n", - " \n", - " # evaluate the formula and round the answer to 2 decimals\n", - " answer = round(eval(answer),2)\n", - " \n", - " # get value from widget and check if this corresponds with the answer\n", - " response = num_widget.value\n", - " print('The answer you give is', response)\n", - "\n", - " if response == answer: # if the answer is correct\n", - " print(comment)\n", - " else: # the answer is wrong\n", - " print('incorrect', hint, '-->', answer)\n", - " \n", - " submit_button.on_click(check_answers)" - ] - } - ], - "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.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/Chris/week_2.ipynb b/notebooks/Chris/week_2.ipynb deleted file mode 100644 index 212d3e6..0000000 --- a/notebooks/Chris/week_2.ipynb +++ /dev/null @@ -1,675 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "08b7ff1c-1e3c-4917-bc39-d046ecfa68eb", - "metadata": {}, - "source": [ - "
\n", - " \n", - "
\n", - "\n", - "Before you is the Jupiter Notebook of week 2. This notebook will be incorporated in a Jupiter Book, which has the option to hide the code of the cells. " - ] - }, - { - "cell_type": "markdown", - "id": "562c17ce-311a-46be-acdf-e360ce7dd3ee", - "metadata": {}, - "source": [ - "# Week 2 Oceanic waves and tide" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "593e05c2-cc9c-4c0d-a9bb-bf0fee870c29", - "metadata": {}, - "outputs": [], - "source": [ - "%run Initialize/Week_2_Initialize.ipynb" - ] - }, - { - "cell_type": "markdown", - "id": "ce68662a-75b4-482d-bb7f-81a4f8b5e1e1", - "metadata": {}, - "source": [ - "Wind-generated and tidal waves can travel great distances until they finally reach the coast. In this section, we discuss the propagation of both individual waves and the behaviour of a group of waves together. It is recommended to read chapter ?3? of the book Coastal Dynamics when studying this codebook." - ] - }, - { - "cell_type": "markdown", - "id": "aff5fd63-f3ea-44f8-9f8e-7f5d3093b220", - "metadata": {}, - "source": [ - "## 2.1) pre-knowledge on waves" - ] - }, - { - "cell_type": "markdown", - "id": "adc11ad6-a933-4eb2-ae1e-c3ccc9282a81", - "metadata": {}, - "source": [ - "This optional section focuses on the material studied in the course waves (CIE....) that is related to the dispersion relationship. We will discuss how the wavelength can be calculated through an iterative approach, by tables, and by an explicit formula and then ask you to calculate various wave parameters." - ] - }, - { - "cell_type": "markdown", - "id": "31bed71d-9b3b-4fa5-af1b-c10b831b92b4", - "metadata": {}, - "source": [ - "### 2.1.1) Dispersion relation" - ] - }, - { - "cell_type": "markdown", - "id": "7f0bd34e-08c4-4982-896f-2048a7d66d13", - "metadata": {}, - "source": [ - "We will discuss how the wavelength can be calculated through an iterative approach, by tables, and by an explicit formula. You may reuse the code you made for the course Waves." - ] - }, - { - "cell_type": "markdown", - "id": "2a114d05-5f24-4e67-9d96-db68b161b910", - "metadata": {}, - "source": [ - "#### Iterative approach" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "97b3d1ef-453f-4d14-8d9b-04f946859ff0", - "metadata": {}, - "outputs": [], - "source": [ - "W2_Q1()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "949fa014-71b0-4bab-a725-f4a9928c7a43", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "571d9f63-5282-4b09-945b-b15a1b9e7f41", - "metadata": {}, - "source": [ - "#### Wave length through table" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9d501454-fe56-4519-a24f-c67031a4218c", - "metadata": {}, - "outputs": [], - "source": [ - "W2_Q2()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6a6ed4f5-73a2-4608-9a0b-78604e6bb825", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "0be84b2b-bce7-49f1-9886-9319d134191b", - "metadata": {}, - "source": [ - "#### Explicit formula" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ee3b2444-0ade-4387-ae7a-97d86d803ec9", - "metadata": {}, - "outputs": [], - "source": [ - "W2_Q3()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "451e2351-93f3-48ba-85a5-d4da1023b2ea", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "6ed154de-80a9-4b69-ab37-07aa09da42ee", - "metadata": {}, - "source": [ - "### 2.1.2) Wave characteristics" - ] - }, - { - "cell_type": "markdown", - "id": "1396d7b3-fe65-4105-a1cc-03db7ff11489", - "metadata": {}, - "source": [ - "The dispersion relation can be used to calculate and interchange between the wave length and wave period and thereby asses their related parameters. Can you calculate the asked wave characteristics in the questions below? " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "426fe0d5-f7a5-4c7d-a5e7-8755c23a56f1", - "metadata": {}, - "outputs": [], - "source": [ - "W2_Q4()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d15a07ab-14be-4228-85c4-36d4f5c850ed", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "7f22d79a-8d43-4896-b945-60308b37e733", - "metadata": {}, - "source": [ - "### 2.1.3) Shallow, intermediate, and deep water" - ] - }, - { - "cell_type": "markdown", - "id": "d087bc0d-2be5-4acb-9eb2-e2a3472a1053", - "metadata": {}, - "source": [ - "The wave experiences deep, intermediate, or shallow water conditions. Can you asses which conditions the wave experiences? You can use the cell below for your computations." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ce0a360e-4558-4ebc-8b1b-f161cdd66608", - "metadata": {}, - "outputs": [], - "source": [ - "W2_Q5()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fca378b7-7841-42a9-8f3f-83becd1a473c", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "9121657e-c30b-4690-a84c-cdc1719ebcb5", - "metadata": {}, - "source": [ - "## 2.2) Dispersion relation for tsunami waves" - ] - }, - { - "cell_type": "markdown", - "id": "ca701a8c-c317-479e-9286-70f894afe575", - "metadata": {}, - "source": [ - "The dispersion relation can be used for all kinds of waves, including tsunami waves. Can you asses the characteristic of tsunami waves at various water depth classifications? Previously created functions and the cell below can be used to answer the question." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cf276d18-2b08-445f-ae9a-60c76fc7e3f8", - "metadata": {}, - "outputs": [], - "source": [ - "W2_Q6()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e9e71193-992a-4d51-8246-d8b5159c0426", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "22671e3e-a2c0-4b12-883d-c405d476e939", - "metadata": {}, - "source": [ - "## 2.3) Dispersion relation in the cross-shore direction" - ] - }, - { - "cell_type": "markdown", - "id": "011ad064-a9f8-4da1-90e9-603a9e76a8f7", - "metadata": {}, - "source": [ - "The water depth can sometimes influence the wave propagation speed, as we have seen in the previous question. There are two types of exceptions, namely deep water and shallow water. In deep water is the depth so large that the orbital velocities of the wave do not reach the bottom and no interaction between the waves and the bed is possible. In this case, the wave celerity is only affected by the wave period. The other extreme is in shallow water, where only the water depth influences the wave celerity, and the wave period is irrelevant. This can lead to waves with the same celerity (non-dispersive waves) even though they have different characteristics. Both of these waves should meet the requirements for shallow water conditions. In other words, although both waves are in the same location, one can be in shallow water while the other is not, and so both waves have a different celerity. \n", - "\n", - "You check the wavelength and celerity for 3 wave components in the cross-shore direction in the interactive graph below. The wave celerity constant is at the deep water location, then the wave celerity decreases in the intermediate water depth. In one section of the graphs, the celerity for all 3 wave components is equal, which indicates that all the 3 wave components experience shallow water, where the wave period is irrelevant as only the water depth influences the propagation speed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0faea40d-0f0d-4d05-a35a-9dbd53ee8e31", - "metadata": {}, - "outputs": [], - "source": [ - "W2_Q7()" - ] - }, - { - "cell_type": "markdown", - "id": "54250805-5302-4c09-a3f9-03f7b164d2c2", - "metadata": {}, - "source": [ - "## 2.4) Wave groups" - ] - }, - { - "cell_type": "markdown", - "id": "bc29525f-15a1-4458-a95a-259a1a7317f9", - "metadata": {}, - "source": [ - "Multiple waves (components) can coexist, influencing the sea surface elevation, currents, etc. The sea surface elevation can be described by substituting the harmonic components, as expressed in formula 2.4.1.\n", - "\n", - "\\begin{equation}\n", - "\\tag{2.4.1}\n", - "\\eta = a_1 sin(w_1t-k_1x) + a_2 sin(w_2t-k_2x)\n", - "\\end{equation}\n", - "\n", - "This can be rewritten to formula 2.4.2, when $a_1 = a_2 = a$.\n", - "\n", - "\\begin{equation}\n", - "\\tag{2.4.2}\n", - "\\eta = 2a sin(\\frac{\\omega_2+\\omega_1}{2}t - \\frac{k_2+k_1}{2}x)cos(\\frac{\\omega_2-\\omega_1}{2}t-\\frac{k_2-k_1}{2}x)\n", - "\\end{equation}\n", - "\n", - "Which can be expressed in terms of differences ($\\Delta$) and averaged values (indicated with a bar) for the radial frequency and wave number, resulting in:\n", - "\n", - "\\begin{equation}\n", - "\\tag{2.4.3}\n", - "\\eta = 2a sin(\\bar\\omega t - \\bar k x)cos(\\frac{\\Delta\\omega}{2}t - \\frac{\\Delta k}{2}x)\n", - "\\end{equation}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9e9f4ddc-98a1-4896-b19d-34297103384d", - "metadata": {}, - "outputs": [], - "source": [ - "W2_wave_groups()" - ] - }, - { - "cell_type": "markdown", - "id": "bdfbf043-583b-4ad4-92c5-9664e1d92a13", - "metadata": {}, - "source": [ - "### 2.4.1) Spatial and temporal wave group patterns" - ] - }, - { - "cell_type": "markdown", - "id": "89614265-870a-4c11-a7ce-5fabfaecb109", - "metadata": {}, - "source": [ - "#### Calculate wave group characteristics" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1a575170-610d-4d4b-9471-5a686740c9c8", - "metadata": {}, - "outputs": [], - "source": [ - "W2_Q9()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "43be3c3f-7d88-42e2-be25-d0812fd79751", - "metadata": {}, - "outputs": [], - "source": [ - "h = ...\n", - "T1, T2 = ...,...\n", - "\n", - "Delta_k = ...\n", - "Delta_w = ...\n", - "k_average = ...\n", - "w_average = ...\n", - "\n", - "Check_W2_Q9A()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "44823983-1707-4ca2-91e5-bc53f3bf51c7", - "metadata": {}, - "outputs": [], - "source": [ - "c_average = ...\n", - "c_group = ...\n", - "L_group = ...\n", - "T_group = ...\n", - "\n", - "Check_W2_Q9B()" - ] - }, - { - "cell_type": "markdown", - "id": "4f145774-f905-4991-ab99-833c77178371", - "metadata": {}, - "source": [ - " Temporarily, for showing, a made computation, the first answer checks a computational margin of 0.01% " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8ce476e0-35a7-40bb-9d8f-e550f3f49460", - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length(T, h):\n", - " L = 9.81 * T**2 / (2 * np.pi)\n", - " L_all = [L]\n", - "\n", - " for i in range(1500):\n", - " L = 9.81 * T**2 / (2 * np.pi) * np.tanh(2 * np.pi * h / L)\n", - " L_all.append(L)\n", - "\n", - " if np.abs(L_all[-1] - L_all[-2]) < 0.0005:\n", - " #plt.plot(L_all)\n", - " break\n", - " \n", - " return round(L, 13)\n", - "\n", - "h = 20\n", - "T1, T2 = 5.5,9.3\n", - "\n", - "# The calculation, where the previously made wavelength formula can be used.\n", - "L1 = wave_length(T1, h)\n", - "L2 = wave_length(T2, h)\n", - "\n", - "k1 = 2*np.pi/L1\n", - "k2 = 2*np.pi/L2\n", - "w1 = 2*np.pi/T1\n", - "w2 = 2*np.pi/T2\n", - "\n", - "Delta_k = np.abs(k2-k1) * 0.9999\n", - "Delta_w = np.abs(w2-w1)\n", - "k_average = np.average([k1,k2])\n", - "w_average = np.average([w1,w2])\n", - "\n", - "Check_W2_Q9A()" - ] - }, - { - "cell_type": "markdown", - "id": "e8b49270-8780-4095-9d08-1a6fb5597cc8", - "metadata": {}, - "source": [ - "#### Visualize spatial and temporal patterns" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fd1dcecc-d1a4-4d72-a182-e9f5d8f5cc01", - "metadata": {}, - "outputs": [], - "source": [ - "W2_Q10()\n", - "# [temporarily comment] The question loads information from question 9 if it is executed, otherwise, it will generate new random values. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "998f8906-8a26-415e-8797-5649bafd4f0b", - "metadata": {}, - "outputs": [], - "source": [ - "# Use this code to make a plot to make a time series of the water elevation. \n", - "# Define the function such that it will calculate the water elevation at an arbitrary time (t)\n", - "\n", - "def plot_eta_t(t,...):\n", - " ...\n", - " eta = ...\n", - " return eta\n", - "\n", - "Show_Q10A()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "554ee6af-2a51-4e8b-a37d-0ef6cb6e9e2c", - "metadata": {}, - "outputs": [], - "source": [ - "a1, a2, T1, T2, L1, L2, x_max, xp, t_max, tp, L_group, T_group = W2_Q10_param\n", - "def plot_eta_t(a1,a2,T1,T2,L1,L2,t,xp):\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*t-2*np.pi/L1*xp)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*t-2*np.pi/L2*xp)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - " return eta_T_basic - 1\n", - "\n", - "Show_Q10A()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c78490db-45fa-412f-b368-b8af833fce52", - "metadata": {}, - "outputs": [], - "source": [ - "# Use this code to calculate the water elevation\n", - "# Define the function such that it will calculate the water elevation at an arbitrary location (x)\n", - "\n", - "def plot_eta_x(x...):\n", - " ...\n", - " eta =\n", - " return eta\n", - "\n", - "Show_Q10B())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "df21a37e-7657-41a2-8f8c-6b7dad839bb2", - "metadata": {}, - "outputs": [], - "source": [ - "a1, a2, T1, T2, L1, L2, x_max, xp, t_max, tp, L_group, T_group = W2_Q10_param\n", - "def plot_eta_x(a1,a2,T1,T2,L1,L2,tp,x):\n", - " eta1_T_basic = a1*np.sin(2*np.pi/T1*tp-2*np.pi/L1*x)\n", - " eta2_T_basic = a2*np.sin(2*np.pi/T2*tp-2*np.pi/L2*x)\n", - " eta_T_basic = eta1_T_basic + eta2_T_basic\n", - " return eta_T_basic - 1\n", - "\n", - "Show_Q10B()" - ] - }, - { - "cell_type": "markdown", - "id": "8ad1c1c2-0132-4915-8ed4-2a236320ed35", - "metadata": {}, - "source": [ - "### 2.4.2) Wavegroup composition" - ] - }, - { - "cell_type": "raw", - "id": "c47c7f62-9d2f-4edd-b453-0e46390adc9b", - "metadata": {}, - "source": [ - "Judith, can you think for questions and a way to build a narative?\n", - "Below are some questions I could come up with." - ] - }, - { - "cell_type": "markdown", - "id": "4c4272cf-4a7e-42f4-a76c-4f0188b5535e", - "metadata": {}, - "source": [ - "Questions\n", - "- If you would measure the water elevation in deep water when these two wave conditions occur, are the measured time series identical to values calculated in the previous question?\n", - " - Yes, substitution is possible with 2 two identical wave components that have similar wave heights\n", - " - no, time series are also affected by the wave phase (correct)\n", - " - no, the harmonic wave components can not always be substituted\n", - " - no, the wave group itself is influencing the time series \n", - "- The speed of the carrier wave is related to the speed of:\n", - " - wave component 1, wave component 2, c_average, c_group (correct)\n", - "- How can the speed of the carrier wave be expressed?\n", - " - w_average/k_average (correct)\n", - " - The group velocity: c_group (Delta_omega/ Delta_k)\n", - " - The average celerity of the wave components, c_average\n", - " - The radial frequency based on the combined period w = 2 pi / (T1 + T2)\n", - "- How can the energy propagation be expressed in shallow water:\n", - " - w_average/k_average \n", - " - The group velocity: c_group (Delta_omega/ Delta_k) (correct)\n", - " - The average celerity of the wave components, c_average\n", - " - The radial frequency based on the combined period w = 2 pi / (T1 + T2)\n", - "- a question related to c_average" - ] - }, - { - "cell_type": "markdown", - "id": "01214996-52f0-42f6-bffb-83846b883785", - "metadata": {}, - "source": [ - "Number of waves on time and spatial plot, dispersive vs non-dispersive etc.\n", - "- questions?" - ] - }, - { - "cell_type": "markdown", - "id": "6bb6c545-0439-4ac5-81b6-70a4fb1b577e", - "metadata": {}, - "source": [ - "After these questions comes an animation with waves propagating over space.\n", - "Can questions be asked about this?" - ] - }, - { - "cell_type": "markdown", - "id": "73d37211-208b-429a-8e54-306b5928ed45", - "metadata": {}, - "source": [ - "# Oude notities: Worden later verwijderd" - ] - }, - { - "cell_type": "raw", - "id": "0dcf251d-2664-458a-9f32-2860e1cd4b77", - "metadata": {}, - "source": [ - "# maak code veld\n", - "- commpute:\n", - "\n", - "+ delta w, delta k, omega gemmideld, k gemmideld\n", - "+ c_average, c_group, Lgroup, Tgroup\n", - "+ aantal golven in space (binnen golfgroep) <--\n", - "\n", - "- reflective question\n", - "\n", - "# c_average, reflective question (deze ook berekenen erboven = gemiddelde twee componenten). omega_gemiddeld/k_gemmiddeld. Ongeveer carierwave snelheid." - ] - }, - { - "cell_type": "markdown", - "id": "62bf6a94-2e34-4c9b-b6c2-b202572d69a6", - "metadata": {}, - "source": [ - "Hierna verder hoofdstuk 5, aanhaken Jaime bij Waves Notebook." - ] - }, - { - "cell_type": "raw", - "id": "2f797812-c9e0-4268-ba79-79766231817c", - "metadata": {}, - "source": [ - "ask questions on the code, what method is used...\n", - "\n", - "Volgende groups meeting: 3pm 19th Dec" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "97a5320d-f551-4283-869a-363d7cbae3ef", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "84df5754-bcdc-4179-9ea9-62c8c6d4baa3", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2470437f-5b38-41d7-be6e-9d956cb639f1", - "metadata": {}, - "outputs": [], - "source": [ - "\n" - ] - } - ], - "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.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/Chris/week_5.ipynb b/notebooks/Chris/week_5.ipynb deleted file mode 100644 index 7f8164f..0000000 --- a/notebooks/Chris/week_5.ipynb +++ /dev/null @@ -1,55 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "a57196e5-eb53-4eab-8bb2-b11c2a313e56", - "metadata": {}, - "source": [ - "
\n", - " \n", - "
\n", - "\n", - "Before you is the Jupiter Notebook of week 5." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fcad68ab-a006-40c5-93ad-5eb0a10e41e8", - "metadata": {}, - "outputs": [], - "source": [ - "%run Initialize/Week_5_Initialize.ipynb" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aa0f56bf-6489-413f-be44-fdf172466d8a", - "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.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/initialize/Week_2_Initialize.ipynb b/notebooks/initialize/2a_pre_knowledge_waves.ipynb similarity index 99% rename from notebooks/initialize/Week_2_Initialize.ipynb rename to notebooks/initialize/2a_pre_knowledge_waves.ipynb index ecce1d7..b491174 100644 --- a/notebooks/initialize/Week_2_Initialize.ipynb +++ b/notebooks/initialize/2a_pre_knowledge_waves.ipynb @@ -1891,13 +1891,14 @@ " for id in np.arange(1, number_of_questions+1,1):\n", " question = eval('Q'+ str(id))\n", " answer = eval('Ans' + str(id))\n", + " FG = eval('FG_'+ str(id))\n", + " FW = eval('FW_'+ str(id))\n", + " \n", " question_widget = pn.widgets.StaticText(value=question)\n", " set_button = pn.widgets.Button(name=\"Set graph\")\n", " checkbutton_group = pn.widgets.CheckButtonGroup(name='Check Button Group', value=[], options=all_answers)\n", " submit_button = pn.widgets.Button(name=\"Check\")\n", " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\")\n", - " FG = eval('FG_'+ str(id))\n", - " FW = eval('FW_'+ str(id))\n", " \n", " FV2 = {key: value for key, value in locals().items()}\n", " #FV2 = {key: value for key, value in {**globals(), **locals()}.items()}\n", diff --git a/notebooks/initialize/2b_wave_dispersion_and_grouping.ipynb b/notebooks/initialize/2b_wave_dispersion_and_grouping.ipynb new file mode 100644 index 0000000..03fa97e --- /dev/null +++ b/notebooks/initialize/2b_wave_dispersion_and_grouping.ipynb @@ -0,0 +1,1918 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "fd978930-bbc5-4c62-9218-96af5c11320f", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import panel as pn\n", + "pn.extension(\"ipywidgets\", 'katex', 'mathjax')\n", + "import ipywidgets as ipw\n", + "from matplotlib.animation import FuncAnimation\n", + "#from matplotlib.ticker import MultipleLocator\n", + "from matplotlib.figure import Figure\n", + "\n", + "from random import shuffle, uniform\n", + "\n", + "import sys\n", + "from inspect import signature\n", + "print(\"Packages succesfully loaded\")" + ] + }, + { + "cell_type": "markdown", + "id": "4a18b5d0-538b-45b9-a552-1a6c4865090b", + "metadata": {}, + "source": [ + "# Functions from cookbook" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "492dd19b-ad37-4a2d-94f9-de959183002b", + "metadata": {}, + "outputs": [], + "source": [ + "def multiple_selection(question, correct_statements, false_statements):\n", + " # Make an empty list to store the widgets (references), checkboxes, and true/false statements sorted.\n", + " check_boxes = [] # all the boxes to click\n", + " all_statements = [] # all the statements\n", + "\n", + " # question widget\n", + " question_widget = pn.widgets.StaticText(value=question)\n", + "\n", + " # for visualization, the maximum length/width of the question\n", + " max_length = max(len(item) for item in correct_statements + false_statements)\n", + " width_statement = max(250, max_length*7)\n", + " \n", + " # An empty list for visualization to store the HBoxes that contain the widgets, one statement and the corresponding checkbox\n", + " all_widgets = []\n", + "\n", + " for statement in correct_statements + false_statements:\n", + " add_statement = pn.widgets.StaticText(value=statement, width = width_statement)\n", + " check_box_widget = pn.widgets.Checkbox(value=False, width=120)\n", + " HBox1 = pn.Row(add_statement, check_box_widget)\n", + " \n", + " all_statements.append(add_statement)\n", + " check_boxes.append(check_box_widget)\n", + " all_widgets.append(HBox1)\n", + " \n", + " # randomize the order of statements\n", + " shuffle(all_widgets)\n", + " \n", + " # add submit button and output, which come on the bottom\n", + " submit_button = pn.widgets.Button(name='Check')\n", + " output_widget = pn.widgets.TextInput(value='', placeholder='', disabled=False)\n", + " \n", + " # make an additional HBox for aligning the submit button and the output widget\n", + " HBox2 = pn.Row(submit_button, output_widget)\n", + " all_widgets.append(HBox2)\n", + " \n", + " # align all the HBoxes beneath each other (oldest below if not randomized) and display them.\n", + " quiz_widget = pn.Column(question_widget, *all_widgets)\n", + " \n", + " # Check the checkbox for each statement and calculate the score.\n", + " def check_answers(event):\n", + " score = 0\n", + " \n", + " for i in range(len(check_boxes)):\n", + " check_box = check_boxes[i]\n", + " statement = all_statements[i].value\n", + " \n", + " if statement in correct_statements:\n", + " if check_box.value == True:\n", + " score += 1\n", + " else:\n", + " score -= 0\n", + " \n", + " if statement not in correct_statements:\n", + " if check_box.value == True:\n", + " score -= 1\n", + " else:\n", + " score -= 0\n", + " \n", + " score = np.max([score, 0])\n", + " output_widget.value = f'You have: {round(score/len(correct_statements)*100,0)}% of the points'\n", + " \n", + " submit_button.on_click(check_answers)\n", + " return quiz_widget # This is similar to .serve(), it can also be done through: display(quiz_widget)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2d0f15b-7968-4d79-9947-e158ce2c3575", + "metadata": {}, + "outputs": [], + "source": [ + "def check_code_function(fig, horizontal_axis, function_name, correct_function, par_x_axis, f_margin = 0.001):\n", + " # The error margin (f_margin) is set at 0.1% if it is not defined\n", + "\n", + " # make the graph and show the correct answer\n", + " ax = fig.subplots()\n", + " pane = pn.pane.Matplotlib(fig, dpi=100)\n", + " \n", + " # Plot the answer if the students function is found\n", + " try:\n", + " # Load the student-made function from globals\n", + " function = globals()[function_name]\n", + "\n", + " # Add the arguments to the name of the function, for eval()\n", + " # + Replace the argument on the x-axis so that it can be assessed with list comprehension.\n", + " # https://docs.python.org/3/library/inspect.html\n", + " # https://peps.python.org/pep-0362/ \n", + " sig = signature(function)\n", + " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", + " function = function_name + str(sig_function)\n", + " title = function_name + str(sig)\n", + " title = title.replace('_', ' ')\n", + "\n", + " sig = signature(correct_function) \n", + " sig_function = str(sig).replace(par_x_axis, 'par_x_axis')\n", + " correct_function2 = str(correct_function.__name__) + sig_function\n", + "\n", + " # calculate the student's answer through eval()\n", + " student_answer = [eval(function) for par_x_axis in horizontal_axis]\n", + "\n", + " #calculate the correct answer\n", + " correct_answer = []\n", + " for par_x_axis in horizontal_axis:\n", + " correct_answer.append(eval(correct_function2))\n", + " # This code should work, but it does not\n", + " #correct_answer = [eval(correct_function2) for par_x_axis in horizontal_axis]\n", + "\n", + " # check if the answer is correct and plot it before/below lines\n", + " changes = np.array(student_answer) - np.array(correct_answer)\n", + " inaccuracy = np.abs(1-np.array(correct_answer)/np.array(student_answer))\n", + " \n", + " if np.max(changes) == 0:\n", + " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", + " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Perfect!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", + "\n", + " if np.max(changes) != 0 and np.max(inaccuracy) < f_margin:\n", + " y_loc = (np.mean(correct_answer) + np.min(correct_answer))/2\n", + " text = ax.text(np.mean([horizontal_axis]), y_loc, 'Good!', fontsize=12, color = '#1b5a00', ha='center', va='center')\n", + " \n", + " # plot the answers\n", + " line = ax.plot(horizontal_axis, student_answer, label = 'Your answer')\n", + " line = ax.plot(horizontal_axis, correct_answer, label = 'Correct answer')\n", + " ax.legend()\n", + " ax.set_title(title)\n", + " \n", + " except:\n", + " text_failed = 'Almost there, \\n your function can not be plotted, \\n please try to fix the bug'\n", + " x_ticks = ax.get_xticks()\n", + " y_ticks = ax.get_yticks()\n", + " text = ax.text(np.average(x_ticks),np.average(y_ticks), text_failed, fontsize=16, color = 'r', ha='center', va='center')\n", + "\n", + " # update the graph\n", + " display(pane)" + ] + }, + { + "cell_type": "markdown", + "id": "c9adcb4e-6a5b-4681-b195-88026097f28e", + "metadata": {}, + "source": [ + "## new functions for cookbook" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44890bb2-981b-4779-a9f7-69502c81b0c4", + "metadata": {}, + "outputs": [], + "source": [ + "def check_code_function_RANGE(fig, function_name, correct_function, f_margin = 0.001):\n", + " # The error margin (f_margin) is set at 0.1% if it is not defined\n", + "\n", + " # make the graph and show the correct answer\n", + " ax = fig.subplots()\n", + " pane = pn.pane.Matplotlib(fig, dpi=100)\n", + " \n", + " # Plot the answer if the student function is found\n", + " try:\n", + " # Load the student-made function from globals\n", + " function = globals()[function_name]\n", + "\n", + " # Add the arguments to the name of the function, for eval()\n", + " # https://docs.python.org/3/library/inspect.html\n", + " # https://peps.python.org/pep-0362/ \n", + " sig = signature(function)\n", + " student_function = function_name + str(sig)\n", + "\n", + " student_answer = eval(student_function)\n", + " \n", + " sig = signature(correct_function) \n", + " correct_function2 = str(correct_function.__name__) + str(sig)\n", + " correct_answer = eval(correct_function2)\n", + "\n", + " # plot the answers\n", + " line = ax.plot(student_answer, label = 'Your answer', zorder = 1)\n", + " line = ax.plot(correct_answer, label = 'Correct answer', zorder = 1)\n", + "\n", + " # check if the answer is correct and plot it before/below lines\n", + " changes = np.array(student_answer) - np.array(correct_answer)\n", + " inaccuracy = np.abs(1-np.array(correct_answer)/np.array(student_answer))\n", + "\n", + " # Plot comment if the answer is correct\n", + " y_loc = (np.nanmean(correct_answer) + np.nanmin(correct_answer))/2\n", + " x_loc = np.mean(ax.get_xticks())\n", + " if np.nanmax(changes) == 0: \n", + " text = ax.text(x_loc, y_loc, 'Perfect!', fontsize=12, color = '#1b5a00', ha='center', va='center', zorder = 0)\n", + "\n", + " if np.nanmax(changes) != 0 and np.nanmax(inaccuracy) < f_margin:\n", + " text = ax.text(x_loc, y_loc, 'Good!', fontsize=12, color = '#1b5a00', ha='center', va='center', zorder = 0)\n", + "\n", + " # show legend, set title, and update figure\n", + " ax.legend()\n", + " title = student_function\n", + " title = title.replace('_', ' ')\n", + " ax.set_title(title)\n", + "\n", + " except:\n", + " text_failed = 'Almost there, \\n your function can not be plotted, \\n please try to fix the bug'\n", + " x_ticks = ax.get_xticks()\n", + " y_ticks = ax.get_yticks()\n", + " text = ax.text(np.average(x_ticks),np.average(y_ticks), text_failed, fontsize=16, color = 'r', ha='center', va='center')\n", + "\n", + " # update the graph\n", + " display(pane)" + ] + }, + { + "cell_type": "markdown", + "id": "add2043c-b3cd-4ef8-9da6-9310aee3f2bc", + "metadata": {}, + "source": [ + "# Commonly used functions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "74b6ba94-f4a6-46f4-8277-8b2e83d9266d", + "metadata": {}, + "outputs": [], + "source": [ + "def wave_length(T, h):\n", + " L = 9.81 * T**2 / (2 * np.pi)\n", + " L_all = [L]\n", + "\n", + " for i in range(1500):\n", + " L = 9.81 * T**2 / (2 * np.pi) * np.tanh(2 * np.pi * h / L)\n", + " L_all.append(L)\n", + "\n", + " # stop the iteration when the error is sufficiently small\n", + " if np.abs(L_all[-1] - L_all[-2]) < 0.0005:\n", + " break\n", + " \n", + " return round(L, 13)\n", + "\n", + "def group_stats(k1,k2,w1,w2):\n", + " Delta_k = np.abs(k2-k1)\n", + " Delta_w = np.abs(w2-w1)\n", + " L = 2*np.pi/Delta_k\n", + " T = 2*np.pi/Delta_w\n", + " cg = Delta_w/Delta_k\n", + " return L,T, cg" + ] + }, + { + "cell_type": "markdown", + "id": "fba1b418-519b-485f-82ac-88d44eac8c32", + "metadata": {}, + "source": [ + "# Part 1B" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "92294b6c-be30-4644-8253-488741f1ad7e", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_fig_5_13():\n", + " a1 = ipw.FloatText(value=1, min=0, max=20, step=0.01, description='a [m]')\n", + " a2 = ipw.FloatText(value=0.25, min=0, max=20, step=0.01, description='a [m]')\n", + "\n", + " # Setup widget layout (User Interface)\n", + " vbox1 = ipw.VBox([ipw.Label('Wave 1', layout=ipw.Layout(align_self='center')),a1])\n", + " vbox2 = ipw.VBox([ipw.Label('Wave 2', layout=ipw.Layout(align_self='center')),a2])\n", + " ui = ipw.HBox([vbox1, vbox2])\n", + " \n", + " def update_graph(a1, a2):\n", + " S_min = -0.25*np.pi\n", + " S_max = 2.25*np.pi\n", + " \n", + " S = np.linspace(S_min, S_max,60)\n", + " \n", + " eta1 = a1*np.cos(S)/a1\n", + " eta2 = a2*np.cos(S*2)/a1\n", + " eta = eta1 + eta2\n", + " \n", + " fig, axs = plt.subplots(nrows=1,ncols=1,figsize=(5,4), sharex=False, sharey = False)\n", + " axs.plot(S, eta1, label = '$\\eta_1$', color = 'gray', linestyle = 'dashed')\n", + " axs.plot(S, eta2, label = '$\\eta_2$', color = 'gray', linestyle = 'dashdot')\n", + " axs.plot(S, eta, label = '$\\eta_{1+2}$', color = 'k')\n", + " axs.legend(loc = 'best')\n", + " axs.set_ylabel('eta/eta_1 [-]')\n", + " axs.set_xlabel('phase [rad]')\n", + " axs.set_xlim(S_min,S_max)\n", + "\n", + " x_tick_min = S_min//(0.5*np.pi)*0.5*np.pi+0.5*np.pi\n", + " x_ticks = np.arange(x_tick_min, S_max, 0.5*np.pi)\n", + " x_ticks_labels = [f\"{angle/np.pi:.1f}Ï€\" for angle in x_ticks]\n", + " axs.set_xticks(x_ticks, x_ticks_labels)\n", + "\n", + " graph = ipw.interactive_output(update_graph, {'a1': a1,'a2': a2})\n", + " display(ui, graph)\n", + "\n", + "#W3_fig_5_13()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ea20504-3ad4-4332-a5fb-fc67a44f4673", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_fig_5_14():\n", + " a1 = ipw.FloatText(value=1, min=0, max=20, step=0.01, description='a1 [m]')\n", + " ui = a1\n", + " \n", + " def update_graph(a1):\n", + " S_min = -0.25*np.pi\n", + " S_max = 2.25*np.pi\n", + " \n", + " S = np.linspace(S_min, S_max,60)\n", + "\n", + " # left graph, sinusoidal to the power 1 and 3\n", + " eta1 = a1*np.cos(S)/a1\n", + " eta1_3 = eta1**3\n", + "\n", + " # right graph, 2 harmonic components (2nd order stokes) to the first and third order\n", + " a2 = 0.25*a1\n", + " eta = (a1*np.cos(S)+a2*np.cos(S*2))/a1\n", + " eta_3 = eta**3\n", + "\n", + " # set structure for graph\n", + " fig, axs = plt.subplots(nrows=1,ncols=2,figsize=(10,4), sharex=False, sharey = True)\n", + " fig.subplots_adjust(hspace=0)\n", + " fig.subplots_adjust(wspace=0.06)\n", + "\n", + " # plot lines\n", + " axs[0].plot(S, eta1, label = '$\\eta_1$', color = 'gray', linestyle = '-')\n", + " axs[0].plot(S, eta1_3, label = '$\\eta^3_1$', color = 'k', linestyle = '-')\n", + " axs[0].plot([S_min, S_max],[0,0], color = 'k', linewidth = 1, linestyle = 'dashed')\n", + " \n", + " axs[1].plot(S, eta, label = '$\\eta$', color = 'gray', linestyle = '-')\n", + " axs[1].plot(S, eta_3, label = '$\\eta^3$', color = 'k', linestyle = '-')\n", + " axs[1].plot([S_min, S_max],[0,0], color = 'k', linewidth = 1, linestyle = 'dashed')\n", + "\n", + " # set titles\n", + " axs[0].set_title('Harmonic component \\n (sinusoidal)')\n", + " axs[1].set_title('Second-order Stokes wave \\n (skewed)')\n", + " \n", + " # set legend and labels\n", + " axs[0].legend(loc = 'best')\n", + " axs[0].set_ylabel('$\\eta/\\eta_1$ [-]')\n", + " axs[0].set_xlabel('phase [rad]') \n", + " axs[0].set_xlim(S_min,S_max)\n", + "\n", + " axs[1].legend(loc = 'best')\n", + " axs[1].set_xlabel('phase [rad]') \n", + " axs[1].set_xlim(S_min,S_max)\n", + " \n", + " x_tick_min = S_min//(0.5*np.pi)*0.5*np.pi+0.5*np.pi\n", + " x_ticks = np.arange(x_tick_min, S_max, 0.5*np.pi)\n", + " x_ticks_labels = [f\"{angle/np.pi:.1f}Ï€\" for angle in x_ticks]\n", + " axs[0].set_xticks(x_ticks, x_ticks_labels)\n", + " axs[1].set_xticks(x_ticks, x_ticks_labels)\n", + "\n", + " graph = ipw.interactive_output(update_graph, {'a1': a1})\n", + " display(ui, graph)\n", + "\n", + "#W3_fig_5_14()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e55f27ce-85ab-450a-a118-2df5b91b6222", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_fig_5_15():\n", + " a1 = ipw.FloatText(value=1, min=0, max=20, step=0.01, description='a1 [m]')\n", + " ui = a1\n", + " \n", + " def update_graph(a1):\n", + " T1, depth, n_waves = 4,9999,1\n", + " T2 = 0.5* T1\n", + " a2 = 0.25 * a1\n", + "\n", + " L1 = wave_length(T1,depth)\n", + " L2 = 0.5*L1\n", + " \n", + " L_group, T_group, c_g = group_stats(\n", + " k1=2 * np.pi / L1,\n", + " k2=2 * np.pi / L2,\n", + " w1=2 * np.pi / T1,\n", + " w2=2 * np.pi / T2,\n", + " )\n", + " \n", + " t = np.arange(0,n_waves*T1+T1/60,T1/60)\n", + " x = np.arange(0,n_waves*L1+L1/60,L1/60)\n", + " \n", + " xp, tp = 0, 0\n", + " eta1_T = a1/a1*np.cos(2*np.pi/T1*t-2*np.pi/L1*xp)\n", + " eta2_T = a2/a1*np.cos(2*np.pi/T2*t-2*np.pi/L2*xp-0.5*np.pi)\n", + " eta_T = eta1_T + eta2_T\n", + "\n", + " eta1_x= a1/a1*np.cos(2*np.pi/T1*tp-2*np.pi/L1*x)\n", + " eta2_x= a2/a1*np.cos(2*np.pi/T2*tp-2*np.pi/L2*x-0.5*np.pi)\n", + " eta_x = eta1_x + eta2_x\n", + "\n", + "\n", + " # set structure for graph\n", + " fig, axs = plt.subplots(nrows=1,ncols=2,figsize=(10,4), sharex=False, sharey = True)\n", + " fig.subplots_adjust(hspace=0)\n", + " fig.subplots_adjust(wspace=0.06)\n", + "\n", + " # plot lines\n", + " axs[0].plot(t, eta1_T, label = '$\\eta_1$', color = 'gray', linestyle = 'dashed')\n", + " axs[0].plot(t, eta2_T, label = '$\\eta_2$', color = 'gray', linestyle = 'dashdot')\n", + " axs[0].plot(t, eta_T, label = '$\\eta$', color = 'k', linestyle = '-')\n", + "\n", + " axs[1].plot(x, eta1_x, label = '$\\eta_1$', color = 'gray', linestyle = 'dashed')\n", + " axs[1].plot(x, eta2_x, label = '$\\eta_2$', color = 'gray', linestyle = 'dashdot')\n", + " axs[1].plot(x, eta_x, label = '$\\eta$', color = 'k', linestyle = '-')\n", + "\n", + " # set x-lim\n", + " axs[0].set_xlim(0,T1*n_waves)\n", + " axs[1].set_xlim(0,L1*n_waves)\n", + " \n", + " # set x-ticks, 4 every wave period/length\n", + " x_ticks = np.arange(0,n_waves + 0.25,0.25 * n_waves)\n", + " x_values_1 = x_ticks * n_waves * T1\n", + " x_values_2 = x_ticks * n_waves * L1\n", + " \n", + " axs[0].set_xticks(x_values_1, x_ticks)\n", + " axs[1].set_xticks(x_values_2, x_ticks)\n", + "\n", + " # set labels\n", + " axs[0].set_xlabel('t/T1 [-]')\n", + " axs[1].set_xlabel('x/L1 [-]')\n", + "\n", + " axs[0].set_ylabel('$\\eta/\\eta_1$ [-]')\n", + " \n", + " # set legend\n", + " axs[0].legend(loc = 'best')\n", + " axs[1].legend(loc = 'best')\n", + "\n", + " # set title\n", + " axs[0].set_title('Time based')\n", + " axs[1].set_title('Space based')\n", + " \n", + " graph = ipw.interactive_output(update_graph, {'a1': a1})\n", + " display(ui, graph)\n", + "\n", + "#W3_fig_5_15()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "890b3e26-726a-4ce0-8f7b-fde3288d44f3", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_fig_5_16():\n", + " a1 = ipw.FloatText(value=1, min=0, max=20, step=0.01, description='a1 [m]')\n", + " ui = a1\n", + " \n", + " def update_graph(a1):\n", + " S_min = -0.25*np.pi\n", + " S_max = 2.25*np.pi\n", + "\n", + " # wave phases\n", + " S = np.linspace(S_min, S_max,60)\n", + " S2 = S*2-0.5*np.pi # twice as fast and 90 degrees phase forwarded\n", + "\n", + " # left graph, sinusoidal to the power 1 and 3\n", + " eta1 = a1*np.cos(S)/a1\n", + " eta1_3 = eta1**3\n", + "\n", + " # right graph, 2 harmonic components (2nd order stokes) to the first and third order\n", + " a2 = 0.25*a1\n", + " eta_1 = a1*np.cos(S)/a1\n", + " eta_2 = a2*np.cos(S2)/a1\n", + " eta = eta_1 + eta_2\n", + "\n", + " # set structure for graph\n", + " fig, axs = plt.subplots(nrows=1,ncols=1,figsize=(5,4), sharex=False, sharey = True)\n", + " fig.subplots_adjust(hspace=0)\n", + " fig.subplots_adjust(wspace=0.06)\n", + "\n", + " # plot lines\n", + " axs.plot(S, eta_1, label = '$\\eta_1$', color = 'gray', linestyle = 'dashed')\n", + " axs.plot(S, eta_2, label = '$\\eta_2$', color = 'gray', linestyle = 'dashdot')\n", + " axs.plot(S, eta, label = '$\\eta$', color = 'k', linestyle = '-')\n", + "\n", + " # set legend and labels\n", + " axs.legend(loc = 'best')\n", + " axs.set_ylabel('$\\eta/\\eta_1$ [-]')\n", + " axs.set_xlabel('phase [rad]') \n", + " axs.set_xlim(S_min,S_max)\n", + " \n", + " x_tick_min = S_min//(0.5*np.pi)*0.5*np.pi+0.5*np.pi\n", + " x_ticks = np.arange(x_tick_min, S_max, 0.5*np.pi)\n", + " x_ticks_labels = [f\"{angle/np.pi:.1f}Ï€\" for angle in x_ticks]\n", + " axs.set_xticks(x_ticks, x_ticks_labels)\n", + "\n", + " graph = ipw.interactive_output(update_graph, {'a1': a1})\n", + " display(ui, graph)\n", + "\n", + "#W3_fig_5_16()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c8b977d-edb3-4755-a71d-da5b435fdacb", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_fig_5_17():\n", + " a1 = ipw.FloatText(value=1, min=0, max=20, step=0.01, description='a1 [m]')\n", + " ui = a1\n", + " \n", + " def update_graph(a1):\n", + " S_min = -0.25*np.pi\n", + " S_max = 2.25*np.pi\n", + " \n", + " S = np.linspace(S_min, S_max,60)\n", + " S2 = S*2+0.5*np.pi # twice as fast and 90 degrees phase forwarded\n", + "\n", + " # left graph, 2 harmonic components (2nd order stokes) to the first and third order\n", + " a2 = 0.25*a1\n", + " eta = (a1*np.cos(S)+a2*np.cos(S*2))/a1\n", + " eta_3 = eta**3\n", + "\n", + " # right graph, assymetric\n", + " eta_As = (a1*np.cos(S)+a2*np.cos(S2))/a1\n", + " eta_As_3 = eta_As**3\n", + " \n", + " # set structure for graph\n", + " fig, axs = plt.subplots(nrows=1,ncols=2,figsize=(10,4), sharex=False, sharey = True)\n", + " fig.subplots_adjust(hspace=0)\n", + " fig.subplots_adjust(wspace=0.06)\n", + "\n", + " # plot lines \n", + " axs[0].plot(S, eta, label = '$\\eta$', color = 'gray', linestyle = '-')\n", + " axs[0].plot(S, eta_3, label = '$\\eta^3$', color = 'k', linestyle = '-')\n", + " axs[0].plot([S_min, S_max],[0,0], color = 'k', linewidth = 1, linestyle = 'dashed')\n", + "\n", + " axs[1].plot(S, eta_As, label = '$\\eta$', color = 'gray', linestyle = '-')\n", + " axs[1].plot(S, eta_As_3, label = '$\\eta^3$', color = 'k', linestyle = '-')\n", + " axs[1].plot([S_min, S_max],[0,0], color = 'k', linewidth = 1, linestyle = 'dashed')\n", + " \n", + " # set titles\n", + " axs[0].set_title('Skewed waves \\n (Second-order Stokes wave)')\n", + " axs[0].plot([S_min, S_max],[0,0], color = 'k', linewidth = 1, linestyle = 'dashed')\n", + " # set legend and labels\n", + " axs[0].legend(loc = 'best')\n", + " axs[0].set_ylabel('$\\eta/\\eta_1$ [-]')\n", + " axs[0].set_xlabel('phase [rad]') \n", + " axs[0].set_xlim(S_min,S_max)\n", + "\n", + " axs[1].set_title('Assymetric waves')\n", + " axs[1].legend(loc = 'best')\n", + " axs[1].set_xlabel('phase [rad]') \n", + " axs[1].set_xlim(S_min,S_max)\n", + " \n", + " x_tick_min = S_min//(0.5*np.pi)*0.5*np.pi+0.5*np.pi\n", + " x_ticks = np.arange(x_tick_min, S_max, 0.5*np.pi)\n", + " x_ticks_labels = [f\"{angle/np.pi:.1f}Ï€\" for angle in x_ticks]\n", + " axs[0].set_xticks(x_ticks, x_ticks_labels)\n", + " axs[1].set_xticks(x_ticks, x_ticks_labels)\n", + "\n", + " graph = ipw.interactive_output(update_graph, {'a1': a1})\n", + " display(ui, graph)\n", + "\n", + "#W3_fig_5_17()" + ] + }, + { + "cell_type": "markdown", + "id": "9f3f6326-6aee-42b3-aa6e-158adf01d2fd", + "metadata": {}, + "source": [ + "# Part 2" + ] + }, + { + "cell_type": "markdown", + "id": "4199dec8-45fd-4e12-a618-e7fe18e8cac7", + "metadata": {}, + "source": [ + "### Normal incident waves" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c3591426-2e42-436c-9571-ed82c930593a", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_interactiveplot_normal_indicent_waves(H0, T, d0, slope_in):\n", + " \n", + " slope = 1.0 / slope_in # bed slope [-]\n", + " d0 # offshore water depth [m]\n", + " x_max = round((d0+2)/slope)\n", + " x = np.arange(0, x_max + 1, 1) # cross-shore coordinate [m]\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # w is zero when h is 0, causing a divide by zero.\n", + " # shorten the lists if a water depth of 0 is reached.\n", + " x0_id = np.argwhere(h == 0)[0][0] # first location where water depth = 0\n", + " h = h[0:x0_id]\n", + " x_water = x[0:x0_id]\n", + " \n", + " # given:\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wave length\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", + " H = H0*Ksh # The wave height due to shoaling (only)\n", + " H_shoal = H.copy()\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # The wave height\n", + "\n", + " fig, axs = plt.subplots(nrows = 4, ncols = 2, figsize = (9,6), sharex=True, sharey = False)\n", + " fig.subplots_adjust(hspace=0)\n", + " fig.subplots_adjust(wspace=0.15)\n", + " \n", + " axs[0,0].plot(x, zbed, label=\"Bed level [m] (1:\" + str(round(slope_in,2)) + ')', color=\"k\")\n", + " axs[0,0].plot([0, x[x0_id]], [0, 0], color=\"gray\", label=\"Still water elevation [m]\") \n", + " axs[1,0].plot(x_water, L, label= 'Wavelength (L) [m]', color=\"k\")\n", + " axs[2,0].plot(x_water, k, label= 'Wave number (k) [rad/m]', color=\"k\")\n", + " axs[3,0].plot(x_water, c, label= 'Wave celerity (c) [m]', color=\"k\")\n", + " axs[0,1].plot(x_water, cg, label= 'Wavegroup celerity (cg) [m/s]', color=\"k\") \n", + " axs[1,1].plot(x_water, Ksh, label= 'shoaling factor (ksh) [-]', color=\"k\")\n", + " axs[2,1].plot(x_water, H_shoal, label= 'only shoaling wave height [m]', color=\"k\")\n", + " axs[3,1].plot(x_water, H, label= 'breaking wave height [m]', color=\"k\")\n", + " \n", + " for ax in axs:\n", + " ax[0].legend(loc=\"lower left\")\n", + " ax[0].xaxis.set_visible(False)\n", + " ax[1].legend(loc=\"best\")\n", + " ax[1].xaxis.set_visible(False)\n", + " axs[3,0].xaxis.set_visible(True)\n", + " axs[3,1].xaxis.set_visible(True)\n", + "\n", + " #print(f'max group celerity: {np.max(cg)}')\n", + " #print(f'wave height: {np.max(H)}')\n", + " #xid = np.where(H < H_shoal)[0][0]\n", + " #print(f'widht surfzone = from {x_water[xid]} to {x_water[-1]} is {- x_water[xid] + x_water[-1]}')\n", + "\n", + " \n", + "def W3_plot_normal_indicent_waves():\n", + " # Create interactive widgets, which require IPY Widgets, widgets from panel do not work\n", + " #H0 = pn.widgets.FloatInput(value=1.5, start=0, end=500, step=0.1, name=\"Offshore wave height (H0) [m]\")\n", + " #T = pn.widgets.FloatInput(value=5, start=0.05, end=500, step=0.01, name =\"Wave period (T) [s]\")\n", + " #slope = pn.widgets.FloatInput(value=30, start=0.1, end=50, step=0.1, name =\"slope 1:...\")\n", + " #d0 = pn.widgets.FloatInput(value=50, start=0.1, end =500, step=0.1, name=\"offshore depth (h0) [m]\")\n", + "\n", + "\n", + " H0 = ipw.FloatText(value=1.5, min=0, max=500, step=0.1, description=\"H0 [m]\", width = 75)\n", + " T = ipw.FloatText(value=6, min=0.05, max=500, step=0.01, description =\"T [s]\")\n", + " slope = ipw.FloatText(value=30, min=0.1, max=50, step=0.1, description =\"slope 1:...\")\n", + " d0 = ipw.FloatText(value=50, min=0.1, max =500, step=0.1, description=\"depth [m]\")\n", + "\n", + " Vbox1 = ipw.VBox([H0, T])\n", + " Vbox2 = ipw.VBox([d0, slope])\n", + " \n", + " widgets = ipw.HBox([Vbox1, Vbox2])\n", + " graph = ipw.interactive_output(W3_interactiveplot_normal_indicent_waves, {'H0': H0,'T':T, 'd0': d0, 'slope_in': slope})\n", + " \n", + " display(widgets, graph)\n", + "\n", + "#W3_plot_normal_indicent_waves()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e48cc49c-6e9b-447c-b972-cdd273894999", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_Q4():\n", + " W3_plot_normal_indicent_waves()\n", + " \n", + " Q1_text = 'Which method is used to calculate the wavelength?'\n", + " Q1_cor = ['Iterative approach']\n", + " Q1_false = ['Linear interpolating tables', 'An explicit formula']\n", + "\n", + " Q2_text = 'Which increases the width of the shoaling zone?'\n", + " Q2_cor = ['Increasing wave period (T)', 'Decreasing bed slope']\n", + " Q2_false = ['Increasing offshore wave height (H0)', 'Increasing deep water depth']\n", + "\n", + " Q3_text = 'Which increases the wave breaking height?'\n", + " Q3_cor = [' A larger offshore wave height (H0)', ' A larger wave period (T)']\n", + " Q3_false = ['A steeper bed slope', 'Increasing the deep water depth']\n", + "\n", + " #Q4_text = 'Which influences the set-down?'\n", + " #Q4_cor = ['The wave period (T)']\n", + " #Q4_false = ['The deepwater wave height (H0)', 'The bed slope', 'The deep water depth']\n", + "\n", + " Q4_text = 'Which of the following changes in parameters will increase the width of the surf zone'\n", + " Q4_cor = ['Increasing deepwater wave height (H0)', 'Increasing the wave period (T)', 'Decreasing the bed slope']\n", + " Q4_false = ['Decreasing the deep water depth']\n", + "\n", + " # Visualize it through a list (disabled, now uses eval())\n", + " #Q_texts = [Q1_text, Q2_text, Q3_text, Q4_text, Q5_text]\n", + " #Q_cor_all = [Q1_cor, Q2_cor, Q3_cor, Q4_cor, Q5_cor]\n", + " #Q_false_all = [Q1_false, Q2_false, Q3_false, Q4_false, Q5_false]\n", + "\n", + " #for Q_text, Q_cor, Q_false in zip(Q_texts, Q_cor_all, Q_false_all):\n", + " # display(multiple_selection(Q_text, Q_cor, Q_false))\n", + "\n", + " n_questions = 4\n", + " for i in np.arange(1,n_questions+1,1):\n", + " Q = eval('Q' + str(i) + '_text')\n", + " C = eval('Q' + str(i) + '_cor')\n", + " F = eval('Q' + str(i) + '_false')\n", + " display(multiple_selection(Q,C,F))\n", + "\n", + "#W3_Q4()" + ] + }, + { + "cell_type": "markdown", + "id": "f0731d23-7194-476e-aa2e-89d637487cf7", + "metadata": {}, + "source": [ + "## Oblique waves" + ] + }, + { + "cell_type": "markdown", + "id": "0e7f6ba7-ab3f-410a-be1d-2c4febf8f4f0", + "metadata": {}, + "source": [ + "### Wave height" + ] + }, + { + "cell_type": "raw", + "id": "f6db99c9-668f-44c8-aadd-ca52005afff6", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66a4ebaa-a39b-4747-8090-483f9544bbe7", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_plot_oblique_waves():\n", + " # define the name of the function that the students will make\n", + " function_name = \"W3_oblique_waves\"\n", + "\n", + " # define the correct function\n", + " def correct_function(x_range, H0, T, d0, slope, angle):\n", + " \n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " '''Completed the code here'''\n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " return H\n", + " \n", + " # set the acceptable computational error (ratio)\n", + " f_margin = 0.001 # 0.001 = 0.01%\n", + "\n", + " # set the size of the figure\n", + " fig = Figure((5,2.5))\n", + "\n", + " # call the function that builds the backend.\n", + " check_code_function_RANGE(fig, function_name, correct_function, f_margin)\n" + ] + }, + { + "cell_type": "raw", + "id": "cf7a58a8-ca7e-48fd-a57c-42ac9e528fa8", + "metadata": {}, + "source": [ + "# student answer, can be used to check the function\n", + "\n", + "H0, T, d0, slope, angle = 1.5, 6, 20, 1/30, 10\n", + "x_range = np.arange(0,900,1)\n", + "def W3_oblique_waves(x_range, H0, T, d0, slope, angle):\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", + "\n", + " '''Complete the code here'''\n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " \n", + " return H*0.9999\n", + " \n", + "W3_plot_oblique_waves()" + ] + }, + { + "cell_type": "markdown", + "id": "2faaece0-c349-4da2-8101-270ce1059e16", + "metadata": {}, + "source": [ + "### Radiation stress" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8e0f7cad-4fc5-4b1a-a370-48aba211b984", + "metadata": {}, + "outputs": [], + "source": [ + "def W3_plot_radiation_stres_Sxx():\n", + " # define the name of the function that the students will make\n", + " function_name = \"W3_radiation_stres_Sxx\"\n", + "\n", + " # define the correct function\n", + " def correct_function(x_range, H0, T, d0, slope, angle):\n", + " \n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " '''Completed the code here'''\n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + " Sxx = (n-0.5+n*np.cos(theta_radians)**2)*E # Radiant stresses\n", + "\n", + " return Sxx\n", + " \n", + " # set the acceptable computational error (ratio)\n", + " f_margin = 0.001 # 0.001 = 0.01%\n", + "\n", + " # set the size of the figure\n", + " fig = Figure((5,2.5))\n", + "\n", + " # call the function that builds the backend.\n", + " check_code_function_RANGE(fig, function_name, correct_function, f_margin)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "faa01e80-9123-4e3d-88f3-651e1e5ceca5", + "metadata": {}, + "outputs": [], + "source": [ + "# An example of the student's answer\n", + "H0, T, d0, slope, angle, rho = 1.5, 6, 20, 1/30, 10, 1025\n", + "x_range = np.arange(0,900,1)\n", + "\n", + "def W3_radiation_stres_Sxx(x_range, H0, T, d0, slope, angle, rho):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " '''Completed the code here'''\n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + "\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + " Sxx = (2*n-0.5)*E # Radiant stresses for oblique waves\n", + " Sxx = (n-0.5+n*np.cos(theta_radians)**2)*E # Radiant stresses\n", + "\n", + " return Sxx\n", + "\n", + "#W3_plot_radiation_stres_Sxx()" + ] + }, + { + "cell_type": "markdown", + "id": "2611bd6f-aa13-488c-9d6b-ff0352896bf6", + "metadata": {}, + "source": [ + "### Setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52843a0e-ea0a-4e35-b416-0496e3272fc4", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho = 1.5, 6, 20, 1/30, 10, 1025\n", + "x_range = np.arange(500,900,1)\n", + "\n", + "def W3_plot_wave_setup():\n", + " # define the name of the function that the students will make\n", + " function_name = \"W3_wave_setup\"\n", + "\n", + " # define the correct function\n", + " def correct_function(x_range, H0, T, d0, slope, angle):\n", + " \n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + " \n", + " #Sxx = (2*n-0.5)*E # Radiant stresses for oblique waves\n", + " Sxx = (n-0.5+n*np.cos(theta_radians)**2)*E # Radiant stresses\n", + " \n", + " setup = np.zeros(Sxx.shape) # here we create a vector for the mean water level \n", + " for i in range(len(setup)-1):# key here is that setup[0] = 0 \n", + " setup[i+1] = setup[i] - (Sxx[i+1]-Sxx[i])/(1000*g*h[i])\n", + " \n", + " return setup\n", + " \n", + " # set the acceptable computational error (ratio)\n", + " f_margin = 0.001 # 0.001 = 0.01%\n", + "\n", + " # set the size of the figure\n", + " fig = Figure((5,2.5))\n", + "\n", + " # call the function that builds the backend.\n", + " check_code_function_RANGE(fig, function_name, correct_function, f_margin)\n" + ] + }, + { + "cell_type": "markdown", + "id": "f78a3c94-ae40-429b-842f-9702db368287", + "metadata": {}, + "source": [ + "Possible student answer" + ] + }, + { + "cell_type": "raw", + "id": "7120fdc5-3d19-4059-b606-3c9e4b6d4954", + "metadata": {}, + "source": [ + "def W3_wave_setup(x_range, H0, T, d0, slope, angle):\n", + " \n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + "\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + " \n", + " Sxx = (2*n-0.5)*E # Radiant stresses\n", + " Sxx = (n-0.5+n*np.cos(theta_radians)**2)*E # radiant stresses for oblique waves\n", + "\n", + " ''' Adjusted the code to implement oblique waves, source: Computerlab by Jaime '''\n", + " setup = np.zeros(Sxx.shape) # here we create a vector for the mean water level \n", + " for i in range(len(setup)-1):# key here is that setup[0] = 0 \n", + " setup[i+1] = setup[i] - (Sxx[i+1]-Sxx[i])/(1000*g*h[i])\n", + " \n", + " return setup\n", + "\n", + "W3_plot_wave_setup()" + ] + }, + { + "cell_type": "markdown", + "id": "827d4e86-61e8-4dc1-a296-084b712544d6", + "metadata": {}, + "source": [ + "### Nearshore force (Fx)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ee469e6-2bfa-4675-9313-ce81ed7fc87d", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho = 1.5, 6, 20, 1/30, 10, 1025\n", + "x_range = np.arange(0,900,1)\n", + "\n", + "def W3_plot_Fx():\n", + " # define the name of the function that the students will make\n", + " function_name = \"W3_Fx\"\n", + "\n", + " # define the correct function\n", + " def correct_function(x_range, H0, T, d0, slope, angle):\n", + " \n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + " \n", + " #Sxx = (2*n-0.5)*E # Radiant stresses for oblique waves\n", + " Sxx = (n-0.5+n*np.cos(theta_radians)**2)*E # Radiant stresses\n", + " \n", + " setup = np.zeros(Sxx.shape) # here we create a vector for the mean water level \n", + " for i in range(len(setup)-1):# key here is that setup[0] = 0 \n", + " setup[i+1] = setup[i] - (Sxx[i+1]-Sxx[i])/(1000*g*h[i])\n", + "\n", + " Fx = np.zeros(setup.shape)\n", + " for i in range(len(Fx)-1):\n", + " Fx[i] = -(Sxx[i+1] - Sxx[i])/(x[i+1]-x[i])\n", + " \n", + " return Fx\n", + " \n", + " # set the acceptable computational error (ratio)\n", + " f_margin = 0.001 # 0.001 = 0.01%\n", + "\n", + " # set the size of the figure\n", + " fig = Figure((5,2.5))\n", + "\n", + " # call the function that builds the backend.\n", + " check_code_function_RANGE(fig, function_name, correct_function, f_margin)\n" + ] + }, + { + "cell_type": "raw", + "id": "876523de-7c56-4b87-ad38-bce1cd5d18ca", + "metadata": {}, + "source": [ + "# an possible student answer\n", + "def W3_Fx(x_range, H0, T, d0, slope, angle):\n", + " \n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + "\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking] = Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + "\n", + " Sxx = (n-0.5+n*np.cos(theta_radians)**2)*E # Radiant stresses\n", + " Sxx = (2*n-0.5)*E # Radiant stresses for oblique waves\n", + "\n", + " ''' Adjusted the code to implement oblique waves, source: Computerlab by Jaime '''\n", + " setup = np.zeros(Sxx.shape) # here we create a vector for the mean water level \n", + " for i in range(len(setup)-1):# key here is that setup[0] = 0 \n", + " setup[i+1] = setup[i] - (Sxx[i+1]-Sxx[i])/(1000*g*h[i])\n", + "\n", + " Fx = np.zeros(Sxx.shape)\n", + " for i in range(len(Fx)-1):\n", + " Fx[i] = -(Sxx[i+1] - Sxx[i])/(x[i+1]-x[i])\n", + " \n", + " return Fx\n", + " \n", + "W3_plot_Fx()" + ] + }, + { + "cell_type": "markdown", + "id": "fa4bf817-e50d-48eb-aab1-c27472275fd3", + "metadata": {}, + "source": [ + "### Syx" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1d59343-deff-4f0a-b42d-989c7a25cb80", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho = 1.5, 6, 20, 1/30, 10, 1025\n", + "x_range = np.arange(0,900,1)\n", + "\n", + "def W3_plot_Syx():\n", + " # define the name of the function that the students will make\n", + " function_name = \"W3_Syx\"\n", + "\n", + " # define the correct function\n", + " def correct_function(x_range, H0, T, d0, slope, angle):\n", + " \n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + "\n", + " Syx = n * np.cos(theta_radians) * np.sin(theta_radians)*E\n", + " \n", + " return Syx\n", + " \n", + " # set the acceptable computational error (ratio)\n", + " f_margin = 0.001 # 0.001 = 0.01%\n", + "\n", + " # set the size of the figure\n", + " fig = Figure((5,2.5))\n", + "\n", + " # call the function that builds the backend.\n", + " check_code_function_RANGE(fig, function_name, correct_function, f_margin)\n" + ] + }, + { + "cell_type": "raw", + "id": "34482917-ba98-44d3-9448-e3a19d1a2740", + "metadata": {}, + "source": [ + "def W3_Syx(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + "\n", + " Syx = n * np.cos(theta_radians) * np.sin(theta_radians)*E\n", + " \n", + " return Syx\n", + "\n", + "W3_plot_Syx()" + ] + }, + { + "cell_type": "markdown", + "id": "cee4089b-0605-496f-b4b8-b7ac1c70efc4", + "metadata": {}, + "source": [ + "### Fy " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "989b4476-a61c-44fd-97dd-196b80b92f80", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho = 1.5, 6, 20, 1/30, 10, 1025\n", + "x_range = np.arange(0,900,1)\n", + "\n", + "def W3_plot_Fy():\n", + " # define the name of the function that the students will make\n", + " function_name = \"W3_Fy\"\n", + "\n", + " # define the correct function\n", + " def correct_function(x_range, H0, T, d0, slope, angle):\n", + " \n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + "\n", + " Syx = n * np.cos(theta_radians) * np.sin(theta_radians)*E\n", + "\n", + " Fy = np.zeros(Syx.shape)\n", + " for i in range(len(Fy)-1):\n", + " Fy[i] = - (Syx[i+1] - Syx[i])/(x[i+1]-x[i])\n", + " \n", + " return Fy\n", + " \n", + " # set the acceptable computational error (ratio)\n", + " f_margin = 0.001 # 0.001 = 0.01%\n", + "\n", + " # set the size of the figure\n", + " fig = Figure((5,2.5))\n", + "\n", + " # call the function that builds the backend.\n", + " check_code_function_RANGE(fig, function_name, correct_function, f_margin)\n" + ] + }, + { + "cell_type": "raw", + "id": "7fdd9b55-e76f-4bd0-903b-e2d479e6e038", + "metadata": {}, + "source": [ + "def W3_Fy(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + "\n", + " Syx = n * np.cos(theta_radians) * np.sin(theta_radians)*E\n", + "\n", + " Fy = np.zeros(Syx.shape)\n", + " for i in range(len(Fy)-1):\n", + " Fy[i] = (Syx[i+1] - Syx[i])/(x[i+1]-x[i])\n", + " \n", + " return Fy\n", + "\n", + "#W3_plot_Fy()" + ] + }, + { + "cell_type": "markdown", + "id": "28a3c03c-96b8-4903-8564-a4237cfa8ec8", + "metadata": {}, + "source": [ + "### Longshore current V(x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "677a83d7-eee4-47b7-a6d4-2decf426b82f", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho, r = 1.5, 6, 20, 1/30, 10, 1025, 0.06\n", + "x_range = np.arange(0,900,1)\n", + "\n", + "def W3_plot_V():\n", + " # define the name of the function that the students will make\n", + " function_name = \"W3_V\"\n", + "\n", + " # define the correct function\n", + " def correct_function(x_range, H0, T, d0, slope, angle, r):\n", + " \n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + "\n", + " Syx = n * np.cos(theta_radians) * np.sin(theta_radians)*E\n", + " \n", + " dh_dx = np.zeros(len(h)) # dh/dx\n", + " for i in range(len(dh_dx)-1):\n", + " dh_dx[i] = (h[i+1] - h[i])/(x[i+1]-x[i])\n", + " \n", + " # page 198\n", + " #r = 0.06 # bottom roughness [m]\n", + " C = 18*np.log(12)*h/r # Chezy coeffcient\n", + " c_f = g/C**2 # friction factor\n", + " \n", + " # formula 5.82 on page 226 of the book\n", + " V = -5/16 * np.pi * gamma / c_f * g * np.sin(theta_radians[0])/c[0]*dh_dx\n", + " V[H != Hbreaking] = 0 # function valid where waves are breaking\n", + " \n", + " return V\n", + " \n", + " # set the acceptable computational error (ratio)\n", + " f_margin = 0.001 # 0.001 = 0.01%\n", + "\n", + " # set the size of the figure\n", + " fig = Figure((5,2.5))\n", + "\n", + " # call the function that builds the backend.\n", + " check_code_function_RANGE(fig, function_name, correct_function, f_margin)\n" + ] + }, + { + "cell_type": "raw", + "id": "f31f45cb-9cd3-40f5-a35d-d0dae2674a5d", + "metadata": {}, + "source": [ + "def W3_V(x_range, H0, T, d0, slope, angle, r):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + "\n", + " Syx = n * np.cos(theta_radians) * np.sin(theta_radians)*E\n", + "\n", + " dh_dx = np.zeros(len(h)) # dh/dx\n", + " for i in range(len(dh_dx)-1):\n", + " dh_dx[i] = (h[i+1] - h[i])/(x[i+1]-x[i])\n", + "\n", + " # page 198\n", + " C = 18*np.log(12)*h/r # Chezy coefcient\n", + " c_f = g/C**2 # friction factor\n", + " \n", + " # formula 5.82 at page 226 of the book\n", + " V = -5/16 * np.pi * gamma / c_f * g * np.sin(theta_radians[0])/c[0]*dh_dx\n", + " \n", + " return V\n", + " \n", + "W3_plot_V()" + ] + }, + { + "cell_type": "markdown", + "id": "0df74d08-7b20-43b7-8380-6cc44245fc25", + "metadata": {}, + "source": [ + "### Wave orbital velocity near bed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81a72627-3b88-4678-9dc0-0c023753c90f", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho, r = 1.5, 6, 20, 1/30, 10, 1025, 0.06\n", + "x_range = np.arange(0,900,1)\n", + "\n", + "def W3_plot_u0():\n", + " # define the name of the function that the students will make\n", + " function_name = \"W3_u0\"\n", + "\n", + " # define the correct function\n", + " def correct_function(x_range, H0, T, d0, slope, angle):\n", + " \n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + "\n", + " # page 199 of the book\n", + " omega = 2*np.pi/T\n", + " u0 = 0.5* omega * H / (np.sin(theta_radians)*k*h)\n", + " \n", + " return u0\n", + " \n", + " # set the acceptable computational error (ratio)\n", + " f_margin = 0.001 # 0.001 = 0.01%\n", + "\n", + " # set the size of the figure\n", + " fig = Figure((5,2.5))\n", + "\n", + " # call the function that builds the backend.\n", + " check_code_function_RANGE(fig, function_name, correct_function, f_margin)\n" + ] + }, + { + "cell_type": "raw", + "id": "51783393-6f00-479a-ac09-f066a07cf0b2", + "metadata": {}, + "source": [ + "def W3_u0(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + "\n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " '''Completed the code here'''\n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + "\n", + " # page 199 of the book\n", + " omega = 2*np.pi/T\n", + " u0 = 0.5* omega * H / (np.sin(theta_radians)*k*h)\n", + " \n", + " return u0\n", + "\n", + "W3_plot_u0()" + ] + }, + { + "cell_type": "markdown", + "id": "27e3485f-d547-43e4-92d6-996fac692246", + "metadata": {}, + "source": [ + "### Current underneath breaking waves" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f65931f-fd91-4001-b89c-a6f69e1c574e", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "0bbcdde1-5f9f-4ee6-aba1-5512a8b8a1c3", + "metadata": {}, + "source": [ + "### Include wave setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bde44dd9-6759-4d17-a29e-00ef294e8138", + "metadata": {}, + "outputs": [], + "source": [ + "H0, T, d0, slope, angle, rho = 1.5, 6, 20, 1/30, 10, 1025\n", + "x_range = np.arange(0,900,1)\n", + "\n", + "def W3_plot_wave_setup2():\n", + " # define the name of the function that the students will make\n", + " function_name = \"W3_wave_setup2\"\n", + "\n", + " # define the correct function\n", + " def correct_function(x_range, H0, T, d0, slope, angle):\n", + " # The environmental conditions\n", + " x = x_range.copy() # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " setup = np.zeros(len(h)) # initial wave-induced setup\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + "\n", + " for i in range(3): # number of iterations considered\n", + " #print(h) # for debugging: print initial water depth each iteration\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + " \n", + " Sxx = (n-0.5+n*np.cos(theta_radians)**2)*E # Radiant stresses\n", + " \n", + " setup = np.zeros(Sxx.shape) # here we create a vector for the mean water level \n", + " for i in range(len(setup)-1):# key here is that setup[0] = 0 \n", + " setup[i+1] = setup[i] - (Sxx[i+1]-Sxx[i])/(1000*g*h[i])\n", + "\n", + " setup_shoreline = setup[~np.isnan(setup)][-1] # get setup at the shoreline, which is the last non-Nan value\n", + " setup[np.isnan(setup)] = setup_shoreline # apply the setup to the water level\n", + "\n", + " h = -zbed + setup # the still water depth + setup\n", + " h[np.isnan(h)] = 0\n", + " h[h < 0] = 0\n", + " #plt.plot(h) # for debugging: plot the final water depth each iteration\n", + " \n", + " return setup\n", + " \n", + " # set the acceptable computational error (ratio)\n", + " f_margin = 0.01 # 0.001 = 0.01%\n", + "\n", + " # set the size of the figure\n", + " fig = Figure((5,2.5))\n", + "\n", + " # call the function that builds the backend.\n", + " check_code_function_RANGE(fig, function_name, correct_function, f_margin)\n" + ] + }, + { + "cell_type": "markdown", + "id": "73293ef1-7410-4f62-ad31-2342db0c5b9b", + "metadata": {}, + "source": [ + "Possible student answer" + ] + }, + { + "cell_type": "raw", + "id": "869b0b78-ce2e-4a60-b9ee-d7d5f898c163", + "metadata": {}, + "source": [ + "def W3_wave_setup2(x_range, H0, T, d0, slope, angle):\n", + "\n", + " # The environmental conditions\n", + " x = x_range.copy() # the horizontal axis\n", + " zbed = -(d0 - slope * x) # bed elevation [m]\n", + " h = -zbed # still water depth [m]\n", + " h[h < 0] = 0 # no negative depths\n", + " setup = np.zeros(len(h)) # initial wave-induced setup\n", + " \n", + " # given conditions\n", + " gamma = 0.8 # wave breaking ratio\n", + "\n", + " for i in range(3): # number of iterations considered\n", + " #print(h)\n", + " \n", + " # The wave characteristics at every location in the cross-section\n", + " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", + " c = L/T # The wave celerity\n", + " k = 2*np.pi/L # The wave number\n", + " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", + " cg = n*c # The wave group celerity\n", + " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter\n", + " \n", + " snell_constant = np.sin(np.deg2rad(angle))/c[0] # apply snell's law\n", + " theta_radians = np.arcsin(snell_constant * c)\n", + " Kr = (np.cos(theta_radians[0])/np.cos(theta_radians))**0.5\n", + " \n", + " H = H0*Ksh*Kr # The wave height due to shoaling and refraction\n", + " Hbreaking = gamma * h # The wave-breaking height\n", + " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", + " g = 9.81\n", + " E = 1/8*rho*g*H**2 # The wave energy\n", + " \n", + " Sxx = (n-0.5+n*np.cos(theta_radians)**2)*E # Radiant stresses\n", + " \n", + " setup = np.zeros(Sxx.shape) # here we create a vector for the mean water level \n", + " for i in range(len(setup)-1):# key here is that setup[0] = 0 \n", + " setup[i+1] = setup[i] - (Sxx[i+1]-Sxx[i])/(1000*g*h[i])\n", + "\n", + " setup_shoreline = setup[~np.isnan(setup)][-1] # get setup at the shoreline, which is the last non-Nan value\n", + " setup[np.isnan(setup)] = setup_shoreline # apply the setup to the water level\n", + "\n", + " h = -zbed + setup # the still water depth + setup\n", + " h[np.isnan(h)] = 0\n", + " h[h < 0] = 0\n", + " #plt.plot(h)\n", + " \n", + " return setup\n", + " \n", + "W3_plot_wave_setup2()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a194cd8b-739b-4653-bbd7-29578404b5c8", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "65c0fd22-3da1-4b55-a110-1f634adfe0fc", + "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.11.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/initialize/Week_3_Initialize.ipynb b/notebooks/initialize/Week_3_Initialize.ipynb deleted file mode 100644 index 5973b51..0000000 --- a/notebooks/initialize/Week_3_Initialize.ipynb +++ /dev/null @@ -1,305 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "fd978930-bbc5-4c62-9218-96af5c11320f", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "import panel as pn\n", - "pn.extension(\"ipywidgets\", 'katex', 'mathjax')\n", - "import ipywidgets as ipw\n", - "from matplotlib.animation import FuncAnimation\n", - "#from matplotlib.ticker import MultipleLocator\n", - "from matplotlib.figure import Figure\n", - "\n", - "from random import shuffle, uniform\n", - "\n", - "import sys\n", - "from inspect import signature\n", - "print(\"Packages succesfully loaded\")" - ] - }, - { - "cell_type": "markdown", - "id": "4a18b5d0-538b-45b9-a552-1a6c4865090b", - "metadata": {}, - "source": [ - "# Functions from cookbook" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "492dd19b-ad37-4a2d-94f9-de959183002b", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "add2043c-b3cd-4ef8-9da6-9310aee3f2bc", - "metadata": {}, - "source": [ - "# Commonly used functions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "74b6ba94-f4a6-46f4-8277-8b2e83d9266d", - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length(T, h):\n", - " L = 9.81 * T**2 / (2 * np.pi)\n", - " L_all = [L]\n", - "\n", - " for i in range(1500):\n", - " L = 9.81 * T**2 / (2 * np.pi) * np.tanh(2 * np.pi * h / L)\n", - " L_all.append(L)\n", - "\n", - " # stop the iteration when the error is sufficiently small\n", - " if np.abs(L_all[-1] - L_all[-2]) < 0.0005:\n", - " break\n", - " \n", - " return round(L, 13)" - ] - }, - { - "cell_type": "markdown", - "id": "9f3f6326-6aee-42b3-aa6e-158adf01d2fd", - "metadata": {}, - "source": [ - "# Questions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c3591426-2e42-436c-9571-ed82c930593a", - "metadata": {}, - "outputs": [], - "source": [ - "def W3_interactiveplot_normal_indicent_waves(H0, T, d0, slope_in):\n", - " \n", - " slope = 1.0 / slope_in # bed slope [-]\n", - " d0 # offshore water depth [m]\n", - " x_max = round((d0+2)/slope)\n", - " x = np.arange(0, x_max + 1, 1) # cross-shore coordinate [m]\n", - " zbed = -(d0 - slope * x) # bed elevation [m]\n", - " h = -zbed # still water depth [m]\n", - " h[h < 0] = 0 # no negative depths\n", - "\n", - " # w is zero when h is 0, causing a divide by zero.\n", - " # shorten the lists if a water depth of 0 is reached.\n", - " x0_id = np.argwhere(h == 0)[0][0] # first location where water depth = 0\n", - " h = h[0:x0_id]\n", - " x_water = x[0:x0_id]\n", - " \n", - " # given:\n", - " gamma = 0.8 # wave breaking ratio\n", - " \n", - " # The wave characteristics at every location in the cross-section\n", - " L = np.array([wave_length(T, h) for h in h]) # The wave length\n", - " c = L/T # The wave celerity\n", - " k = 2*np.pi/L # The wave number\n", - " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", - " cg = n*c # The wave group celerity\n", - " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", - " H = H0*Ksh # The wave height due to shoaling (only)\n", - " H_shoal = H.copy()\n", - " Hbreaking = gamma * h # The wave-breaking height\n", - " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # The wave height\n", - "\n", - " fig, axs = plt.subplots(nrows = 4, ncols = 2, figsize = (9,6), sharex=True, sharey = False)\n", - " fig.subplots_adjust(hspace=0)\n", - " fig.subplots_adjust(wspace=0.15)\n", - " \n", - " axs[0,0].plot(x, zbed, label=\"Bed level [m] (1:\" + str(round(slope_in,2)) + ')', color=\"k\")\n", - " axs[0,0].plot([0, x[x0_id]], [0, 0], color=\"gray\", label=\"Still water elevation [m]\") \n", - " axs[1,0].plot(x_water, L, label= 'Wavelength (L) [m]', color=\"k\")\n", - " axs[2,0].plot(x_water, k, label= 'Wave number (k) [rad/m]', color=\"k\")\n", - " axs[3,0].plot(x_water, c, label= 'Wave celerity (c) [m]', color=\"k\")\n", - " axs[0,1].plot(x_water, cg, label= 'Wavegroup celerity (cg) [m/s]', color=\"k\") \n", - " axs[1,1].plot(x_water, Ksh, label= 'shoaling factor (ksh) [-]', color=\"k\")\n", - " axs[2,1].plot(x_water, H_shoal, label= 'shoaling wave height [m]', color=\"k\")\n", - " axs[3,1].plot(x_water, H, label= 'breaking wave height [m]', color=\"k\")\n", - " \n", - " for ax in axs:\n", - " ax[0].legend(loc=\"lower left\")\n", - " ax[0].xaxis.set_visible(False)\n", - " ax[1].legend(loc=\"best\")\n", - " ax[1].xaxis.set_visible(False)\n", - " axs[3,0].xaxis.set_visible(True)\n", - " axs[3,1].xaxis.set_visible(True)\n", - "\n", - " \n", - "def W3_plot_normal_indicent_waves():\n", - " # Create interactive widgets, which require IPY Widgets, widgets from panel do not work\n", - " #H0 = pn.widgets.FloatInput(value=1.5, start=0, end=500, step=0.1, name=\"Offshore wave height (H0) [m]\")\n", - " #T = pn.widgets.FloatInput(value=5, start=0.05, end=500, step=0.01, name =\"Wave period (T) [s]\")\n", - " #slope = pn.widgets.FloatInput(value=30, start=0.1, end=50, step=0.1, name =\"slope 1:...\")\n", - " #d0 = pn.widgets.FloatInput(value=50, start=0.1, end =500, step=0.1, name=\"offshore depth (h0) [m]\")\n", - "\n", - "\n", - " H0 = ipw.FloatText(value=1.5, min=0, max=500, step=0.1, description=\"H0 [m]\", width = 75)\n", - " T = ipw.FloatText(value=6, min=0.05, max=500, step=0.01, description =\"T [s]\")\n", - " slope = ipw.FloatText(value=30, min=0.1, max=50, step=0.1, description =\"slope 1:...\")\n", - " d0 = ipw.FloatText(value=25, min=0.1, max =500, step=0.1, description=\"depth [m]\")\n", - "\n", - " Vbox1 = ipw.VBox([H0, T])\n", - " Vbox2 = ipw.VBox([d0, slope])\n", - " \n", - " widgets = ipw.HBox([Vbox1, Vbox2])\n", - " graph = ipw.interactive_output(W3_interactiveplot_normal_indicent_waves, {'H0': H0,'T':T, 'd0': d0, 'slope_in': slope})\n", - " \n", - " display(widgets, graph)\n", - "\n", - "W3_plot_normal_indicent_waves()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "501d744f-602e-49cd-b74b-457fe4279df5", - "metadata": {}, - "outputs": [], - "source": [ - "1) Which method is used to calculate the wave length? --> iterative approach\n", - "2) Various questions on the influence of H when parameters change." - ] - }, - { - "cell_type": "markdown", - "id": "b67e52d8-d473-435a-a8b3-64830cb1cb44", - "metadata": {}, - "source": [ - "Building for cookbook, having a number of multiple-selection questions, with one score.\n", - "todo:\n", - "- change widget to multiple selection\n", - "- calculate score, up to 1 pt per question." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e48cc49c-6e9b-447c-b972-cdd273894999", - "metadata": {}, - "outputs": [], - "source": [ - "def multiple_selection_questions(questions, choices, answer_ids):\n", - " # define the widgets for visualization, make for each a row with a question and answer\n", - " # Store all the rows with statements in a list for visualization, and in toggle_widgets for checking the answer\n", - " Rows = []\n", - " toggle_widgets = []\n", - " for i in range(len(choices)):\n", - " question_widget = pn.widgets.StaticText(value=questions[i], width = 150) #statement\n", - " \n", - " radio_group_widget = pn.widgets.RadioButtonGroup(name='Radio Button Group', options=choices[i], button_type='default')\n", - " toggle_widgets.append(radio_group_widget)\n", - " \n", - " add_row = pn.Row(question_widget, radio_group_widget)\n", - " Rows.append(add_row)\n", - "\n", - " # randomize the order of statements\n", - " shuffle(Rows)\n", - "\n", - " # Add a submit button with a feedback option next to it\n", - " submit_button = pn.widgets.Button(name=\"Check\")\n", - " feedback_widget = pn.widgets.StaticText(value=\"\", name=\"\", width=500)\n", - " submit_row = pn.Row(submit_button, feedback_widget)\n", - " \n", - " UI = *Rows, submit_row\n", - "\n", - " # check the answer and give feedback\n", - " def check_answers(button):\n", - " score = 0\n", - "\n", - " for i in range(len(questions)):\n", - " for j in range (len(answer_ids[i])):\n", - " print(answer_ids[i][j])\n", - " #if toggle_widgets[i].value == answers[correct_answers_id[i]]:\n", - " # score += 1\n", - "\n", - " # print(toggle_widget.value)\n", - " #feedback_widget.value = \"Your score is \" + str(score) + \"/\" + str(len(correct_answers_id))\n", - "\n", - " submit_button.on_click(check_answers)\n", - "\n", - " return UI\n", - "\n", - "def ask_select_statement():\n", - "\n", - " # define the questions and store them in a list\n", - " question_1 = 'Question 1'\n", - " choices_1 = [\"good\", \"wrong\", \"false\"]\n", - " answer_id_1 = [0]\n", - " \n", - " question_2 = 'Question 2'\n", - " choices_2 = [\"good\", \"good\", \"wrong\"]\n", - " answer_id_2 = [0,1]\n", - " \n", - " question_3 = 'Question 3'\n", - " choices_3 = [\"You should select this answer\", \"this answer is wrong\", \"This answer is also incorrect\"]\n", - " answer_id_3 = [0]\n", - " \n", - " questions = [question_1, question_2, question_3]\n", - " choices = [choices_1, choices_2, choices_3]\n", - " answer_ids = [answer_id_1,answer_id_2,answer_id_3]\n", - "\n", - " # Add the text of the question on top\n", - " text_general = \"Select if the wave described on the right experiences shallow\"\n", - " \n", - " # make the widgets and display them\n", - " text_widget = pn.widgets.StaticText(value=text_general)\n", - " question_widgets = multiple_selection_questions(questions, choices, answer_ids)\n", - " display(text_widget, *question_widgets)\n", - "\n", - "\n", - "ask_select_statement()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a255bfe8-6261-42bf-8d3d-f47713f078ae", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bf094d1b-7fca-475d-95c0-6d8d648155f8", - "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.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/week_3.ipynb b/notebooks/week_3.ipynb deleted file mode 100644 index 794610b..0000000 --- a/notebooks/week_3.ipynb +++ /dev/null @@ -1,419 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "a57196e5-eb53-4eab-8bb2-b11c2a313e56", - "metadata": {}, - "source": [ - "
\n", - " \n", - "
\n", - "\n", - "Before you is the Jupiter Notebook of week 3." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fcad68ab-a006-40c5-93ad-5eb0a10e41e8", - "metadata": {}, - "outputs": [], - "source": [ - "%run Initialize/Week_3_Initialize.ipynb" - ] - }, - { - "cell_type": "markdown", - "id": "9a3b7540-f1c8-4188-9321-c162619892e7", - "metadata": {}, - "source": [ - "## 3.1) Wave and tidal information" - ] - }, - { - "cell_type": "raw", - "id": "68cd6b43-9174-4862-a334-331835a54184", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "52d1bbf4-31a0-448d-86d8-a627eb550d41", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "raw", - "id": "d72a92e9-7ffb-45a9-9422-b41e2890ef77", - "metadata": {}, - "source": [ - "part 1 wel, figure pagian 186,187etc/. Hoeven nog niet te progermeeren, moeten wel snappen met tootlje." - ] - }, - { - "cell_type": "markdown", - "id": "55200145-c32c-444e-b925-9e73e173cf2a", - "metadata": {}, - "source": [ - "## 3.2) Wave transformation in the near-shore " - ] - }, - { - "cell_type": "markdown", - "id": "75e92d3c-f6b9-40c4-9fe1-3082483a6ff7", - "metadata": {}, - "source": [ - "We continue on the application of the dispersion relationship in the cross-shore, which was discussed in week 3 and the Wave course." - ] - }, - { - "cell_type": "markdown", - "id": "17205ca8-dfd6-4622-8482-c64cfd445857", - "metadata": {}, - "source": [ - "### 3.2.1) Normal indicent waves" - ] - }, - { - "cell_type": "markdown", - "id": "e5dd8576-5be2-40dd-916d-32d9d8453cfc", - "metadata": {}, - "source": [ - "In Waves you should have programmed a function to compute the wave height along the cross-shore for normal incident waves. A similar function is defined below, together with a function to calculate the wavelength. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a903c1f3-af10-46e5-bfc3-8d086a18923b", - "metadata": {}, - "outputs": [], - "source": [ - "def wave_length(T, h):\n", - " L = 9.81 * T**2 / (2 * np.pi)\n", - " L_all = [L]\n", - "\n", - " for i in range(1500):\n", - " L = 9.81 * T**2 / (2 * np.pi) * np.tanh(2 * np.pi * h / L)\n", - " L_all.append(L)\n", - "\n", - " # stop the iteration when the error is sufficiently small\n", - " if np.abs(L_all[-1] - L_all[-2]) < 0.0005:\n", - " break\n", - " \n", - " return round(L, 13)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6b4b0fb3-dbac-40cd-b494-57a143b711b4", - "metadata": {}, - "outputs": [], - "source": [ - "def W3_normal_indicent_waves(H0, T, d0, slope):\n", - "\n", - " # The environmental conditons\n", - " x = np.arange(0,900,.1) # the horizontal axis\n", - " zbed = -(d0 - slope * x) # bed elevation [m]\n", - " h = -zbed # still water depth [m]\n", - " h[h < 0] = 0 # no negative depths\n", - "\n", - " # given:\n", - " gamma = 0.8 # wave breaking ratio\n", - " \n", - " # The wave characteristics at every location in the cross-section\n", - " L = np.array([wave_length(T, h) for h in h]) # The wave length\n", - " c = L/T # The wave celerity\n", - " k = 2*np.pi/L # The wave number\n", - " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", - " cg = n*c # The wave group celerity\n", - " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", - " H = H0*Ksh # The wave height due to shoaling (only)\n", - " Hbreaking = gamma * h # The wave-breaking height\n", - " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # The wave height" - ] - }, - { - "cell_type": "markdown", - "id": "d226190a-a118-413e-87cb-8237866feeff", - "metadata": {}, - "source": [ - "The graph below shows the outcome of the calculaten. Can you use it to answer the questions?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aadfa6d4-c1f3-4cd6-a67e-0fba4638c737", - "metadata": {}, - "outputs": [], - "source": [ - "W3_plot_normal_indicent_waves()" - ] - }, - { - "cell_type": "raw", - "id": "cf5f44d1-465a-46bb-924e-6d49f470117e", - "metadata": {}, - "source": [ - "1) Which method is used to calculate the wave length? --> iterative approach\n", - "2) Various questions on the influence of H when parameters change." - ] - }, - { - "cell_type": "markdown", - "id": "42bbaabc-6522-4f63-91ef-82616506db41", - "metadata": {}, - "source": [ - "### 3.2.2) Wave height for oblique waves" - ] - }, - { - "cell_type": "markdown", - "id": "7514bb6e-75a4-4580-aff7-6809c44fc018", - "metadata": {}, - "source": [ - "The influence of refraction should also be considered for waves that approach the shore under an angle. This process should be considered in the functions for oblique waves, in contrast to normal incident waves. Can you include the impact of refraction on the wave height in the function below by completing the code at the dots? The function W3_plot_oblique_waves() will plot the wave height calculated in W3_oblique_waves(), together with the answer." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "29643432-a621-4d60-97ff-567b4226f77f", - "metadata": {}, - "outputs": [], - "source": [ - "def W3_oblique_waves(H0, T, d0, slope):\n", - "\n", - " # The environmental conditions\n", - " x = np.arange(0,900,.1) # the horizontal axis\n", - " zbed = -(d0 - slope * x) # bed elevation [m]\n", - " h = -zbed # still water depth [m]\n", - " h[h < 0] = 0 # no negative depths\n", - "\n", - " # given conditions\n", - " gamma = 0.8 # wave breaking ratio\n", - " \n", - " # The wave characteristics at every location in the cross-section\n", - " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", - " c = L/T # The wave celerity\n", - " k = 2*np.pi/L # The wave number\n", - " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", - " cg = n*c # The wave group celerity\n", - " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", - " H = H0*Ksh # The wave height due to shoaling (only)\n", - " Hbreaking = gamma * h # The wave-breaking height\n", - " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", - " H = ..." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3d5fc846-4186-4c8a-95dc-56d587e73d3d", - "metadata": {}, - "outputs": [], - "source": [ - "#W3_plot_oblique_waves() " - ] - }, - { - "cell_type": "markdown", - "id": "a6835219-6247-45d7-a269-752c309909a5", - "metadata": {}, - "source": [ - "### 3.2.3) radiation stresses for oblique waves" - ] - }, - { - "cell_type": "raw", - "id": "7f7921f8-b65e-47ef-b205-2f7bfa926993", - "metadata": {}, - "source": [ - "Describe parameter, give how to calculate, ask to finish the function.\n", - " E= 1/8*1000*g*H**2 # The wave energy\n", - " Sxx = (2*n-0.5)*E # ? Radiant stesses?\n", - "\n", - "adjust for oblique incident waves" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1b8e0c4f-f24d-4311-a366-42f84cbe41f9", - "metadata": {}, - "outputs": [], - "source": [ - "def W3_oblique_waves(H0, T, d0, slope):\n", - "\n", - " # The environmental conditions\n", - " x = np.arange(0,900,.1) # the horizontal axis\n", - " zbed = -(d0 - slope * x) # bed elevation [m]\n", - " h = -zbed # still water depth [m]\n", - " h[h < 0] = 0 # no negative depths\n", - "\n", - " # given conditions\n", - " gamma = 0.8 # wave breaking ratio\n", - " \n", - " # The wave characteristics at every location in the cross-section\n", - " L = np.array([wave_length(T, h) for h in h]) # The wavelength\n", - " c = L/T # The wave celerity\n", - " k = 2*np.pi/L # The wave number\n", - " n = 0.5 + (k*h/np.sinh(2*k*h)) \n", - " cg = n*c # The wave group celerity\n", - " Ksh = np.sqrt(cg[0]/cg) # The shoaling parameter \n", - " H = H0*Ksh # The wave height due to shoaling (only)\n", - " Hbreaking = gamma * h # The wave-breaking height\n", - " H[H>Hbreaking]=Hbreaking[H>Hbreaking] # Adjusting the wave height\n", - " \n", - " '''Complete the code here'''" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b1aa6e2b-95bd-4e49-b39e-2ee263be2339", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "raw", - "id": "4877cc39-712f-4539-8d3f-1eeb94cf1d36", - "metadata": {}, - "source": [ - "1.\tContinue from the notebook in week 3 on Application of dispersion relationship in the cross-shore (could make sense to have it in the same notebook). \n", - "2.\tProvide students with the code that they should have programmed in Waves to obtain the cross-shore distribution of H for normally incident waves.\n", - "3.\tVisualize their answers\n", - "4.\tAsk students to reflect on H(x) for different variables (slope, H0, gamma, T, mean water level) by means of multiple choice or multiple selection questions. In order to answer these questions let students vary their code in step 2. and redo step 3 (or if handier with sliders). For instance: which of the following changes in parameters will increase the width of the surf zone (give multiple answer options, more than one can be correct, for instance increase H, decrease slope)\n", - "5.\tNow change to obliquely incident waves and ask students to add the effect of refraction in the code and replot H(x). \n", - "6.\tAsk a reflective question about the effect of refraction (we will add this later)\n", - "7.\tContinue with same wave conditions or repeat. Provide students with the code that they should have programmed in Waves to obtain the cross-shore distribution of Sxx. Ask them to adjust this for obliquely incident waves. \n", - "8.\tVisualize their code\n", - "9.\tProvide students with the code that they should have programmed to obtain the cross-shore distribution of mean water level eta (discretizing forward Euler from week 1.5 MUDE) and visualize this. Ask them to:\n", - "a.\tAdjust this for obliquely incident waves. \n", - "b.\tAdd the computation and plot of Fx as intermediate step. \n", - "10.\tAsk some reflective questions (we will add these later)\n", - "11.\tLet students add code and plot Syx and Fy (varying on the above code)\n", - "12.\tAdd reflective questions\n", - "13.\tLet students add code and plot V(x) according to Eq. 5.82\n", - "14.\tAdd reflective question\n", - "15.\tLet students add code and plot orbital velocity magnitude in cross-shore direction \n", - "16.\tTo be detailed: provide code and plot for undertow velocity (Eq. 5.41, the latter needs an estimation of the roller energy dissipation and roller area, I will provide the equations)\n", - "--> 16 Doet Judith uitdenken (dus voor nu evne)" - ] - }, - { - "cell_type": "raw", - "id": "c9b72d80-1bf1-4693-a1ef-a152395802c1", - "metadata": {}, - "source": [ - "tot 2.7 computerlab, maak functies, daarna reflective . what is the effect of ...\n", - "refraction coeficient for refraction. refractie op golfhoogte --> wordt kleiner\n", - "3.1 gegeven, aanpassen... \n", - "8) plot tegenoverelkaar.\n", - "9) 3.3 (computerlab) eta --> pagina 215. Fx --> daarna eta uitrekenenen.\n", - "11) uit boek/slides. (voorkeur zelfde waar als slides.)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5588ebbe-8d34-4bb9-8cd6-2350c0fb5b9d", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "raw", - "id": "3bf06788-3e36-43b1-8380-697d2988057e", - "metadata": {}, - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b3b47a7c-339f-46db-a607-8293c85de50b", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "raw", - "id": "2f134374-25cf-4d5e-b889-989ed4bb5ec6", - "metadata": {}, - "source": [ - "gebouw gesloten tot ma 8 januari" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "271860fc-886d-4193-a3b7-a4adf2ea1715", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "id": "1091162f-11da-40d7-84ed-e21911f92e6a", - "metadata": {}, - "source": [ - "# week 4" - ] - }, - { - "cell_type": "raw", - "id": "53970be3-e2b2-4f18-89df-14b0e7d18971", - "metadata": {}, - "source": [ - "slide 21\n", - "slide 22-> fluctuatie enegery golfgroepschaal (zelf codereren --> plot)\n", - "slide 26, sxx --> formule foto\n", - "als voor 9 januari klaar, laat weten.\n", - "\n", - "deze week status update. op 9e " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3206e466-84e8-450e-9552-eb5937bbf349", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "211e97dc-15ac-40c1-a459-e5445e8cdf26", - "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.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}