Skip to content

Commit 1f2fa32

Browse files
committed
Added week 5 model answers
1 parent 3c98a54 commit 1f2fa32

14 files changed

Lines changed: 3831 additions & 33 deletions

CH40208/_toc.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ parts:
7070
- file: model_fitting/empirical_and_theoretical_models.md
7171
- file: model_fitting/model_complexity.md
7272
- file: model_fitting/finding_the_best_fit.md
73+
- file: model_fitting/understanding_residuals
74+
- file: model_fitting/fitting_with_minimize
7375
- file: model_fitting/exercises.ipynb
7476
## - file: working_with_data/ideal_gas_law
7577
# - file: working_with_data/curve_fitting
@@ -155,3 +157,17 @@ parts:
155157
title: NumPy
156158
- file: worked_examples/week_3_synoptic_exercises
157159
title: Synoptic Exercises
160+
- file: worked_examples/week_5/week_5_index
161+
title: Week 5
162+
sections:
163+
- file: worked_examples/week_5/grid_search_method
164+
title: Grid Search
165+
- file: worked_examples/week_5/gradient_descent_method
166+
title: Gradient Descent
167+
- file: worked_examples/week_5/newton_raphson_method
168+
title: Newton-Raphson
169+
- file: worked_examples/week_5/Lennard_Jones_optimisation
170+
title: Lennard-Jones optimisation
171+
- file: worked_examples/week_5/scipy_optimize_minimize
172+
title: scipy.optimize.minimize
173+

CH40208/course_contents/week_6.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
- [Empirical and Theoretical Models](../model_fitting/empirical_and_theoretical_models.md)
88
- [Model Complexity](../model_fitting/model_complexity.md)
99
- [Finding the Best Fit](../model_fitting/finding_the_best_fit.md)
10+
- [Understanding Residuals and Quality of Fit](../model_fitting/understanding_residuals.ipynb)
11+
- [Linear Model Fitting with scipy.optimize.minimize()](../model_fitting/fitting_with_minimize.ipynb)
1012
- [Exercises](../model_fitting/exercises.ipynb)

CH40208/model_fitting/exercises.ipynb

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
"source": [
88
"# Exercises\n",
99
"\n",
10+
"You have now worked through the complete workflow for fitting linear models to data. The exercises here give you practice applying this workflow to real chemical systems, where the context adds physical interpretation and requires data handling skills like file reading and coordinate transformations.\n",
11+
"\n",
1012
"## 1. Linear fitting of the Van't Hoff equation\n",
1113
"\n",
1214
"The Van’t Hoff equation relates the change in the equilibrium constant, $K$, for a reaction, to a change in temperature, $T$, given that the enthalpy change for the reaction, $\\Delta H$, is constant over the temperature range of interest.\n",
@@ -37,7 +39,9 @@
3739
"id": "7d286fc9-c672-492d-af95-037d818e3968",
3840
"metadata": {},
3941
"source": [
40-
"**(a)** The data file [exercise_1.dat](data/exercise_1.dat) gives the following experimentally measured equilibrium constants at different temperatures for the reaction\n",
42+
"### (a) Read the data\n",
43+
"\n",
44+
"The data file [exercise_1.dat](data/exercise_1.dat) gives the following experimentally measured equilibrium constants at different temperatures for the reaction\n",
4145
"\n",
4246
"$$2\\mathrm{NO}_2 \\rightleftharpoons 2\\mathrm{N}_2\\mathrm{O}_4$$\n",
4347
"\n",
@@ -60,7 +64,9 @@
6064
"id": "3e6cf158-eb1a-4c24-9c07-bc046f360d54",
6165
"metadata": {},
6266
"source": [
63-
"**(b)** Convert your raw data into $1/T$ (in inverse Kelvin) and $\\ln K$.\n",
67+
"### (b) Transform and plot the data\n",
68+
"\n",
69+
"Convert your raw data into $1/T$ (in inverse Kelvin) and $\\ln K$.\n",
6470
"\n",
6571
"Plot these transformed data and confirm that they approximately show a linear relationship."
6672
]
@@ -70,51 +76,39 @@
7076
"id": "6bafffeb-00b4-4414-b7eb-a158acb10ecc",
7177
"metadata": {},
7278
"source": [
73-
"**(c)** Write a function called `linear_model()` with arguments $x$, $m$, and $c$, that returns the corresponding $y$ value for the function\n",
74-
"\n",
75-
"$$y = mx + c$$"
76-
]
77-
},
78-
{
79-
"cell_type": "markdown",
80-
"id": "23bbafeb-c1ad-44c7-beaa-05836a84e64a",
81-
"metadata": {},
82-
"source": [
83-
"**(d)** Replot your raw $1/T$ versus $\\ln K$ data and show that for $m$ = 7400 and $c$ = -22.5 your model approximately described your data."
84-
]
85-
},
86-
{
87-
"cell_type": "markdown",
88-
"id": "a7465c35",
89-
"metadata": {},
90-
"source": [
91-
"**(e)** Write a function `error_function()` that takes as arguments a list of model parameters, a list of $x$ values, and a list of observed $y$ values. This function should call your `linear_model()` function to calculate the sum-of-squares error for a given pair of model parameters, $m$ and $c$.\n",
79+
"### (c) Define model and error functions\n",
9280
"\n",
93-
"$$\\chi^2 = \\sum_i\\left[y_i - f(x_i)\\right]^2$$"
81+
"You have already written functions for a linear model and error function in the previous section. Write similar functions here, or adapt your previous code. Test your model by plotting the data alongside the model prediction for $m$ = 7400 and $c$ = -22.5 to verify your model approximately describes the data."
9482
]
9583
},
9684
{
9785
"cell_type": "markdown",
9886
"id": "cae94c54",
9987
"metadata": {},
10088
"source": [
101-
"**(f)** Use `scipy.optimize.minimize()` to find the “best fit” values of $m$ and $c$ for your data. Use the previous values of $m$ = 7400 and $c$ = -22.5 as your starting guess."
89+
"### (d) Find best-fit parameters using `minimize()`\n",
90+
"\n",
91+
"Use `scipy.optimize.minimize()` to find the “best fit” values of $m$ and $c$ for your data. Use the previous values of $m$ = 7400 and $c$ = −22.5 as your starting guess."
10292
]
10393
},
10494
{
10595
"cell_type": "markdown",
10696
"id": "ac96378e",
10797
"metadata": {},
10898
"source": [
109-
"**(g)** Replot your data, now including your “line of best fit”"
99+
"### (e) Plot the fitted model\n",
100+
"\n",
101+
"Plot the fitted model Replot your data, now including your “line of best fit”"
110102
]
111103
},
112104
{
113105
"cell_type": "markdown",
114106
"id": "a1fd5beb",
115107
"metadata": {},
116108
"source": [
117-
"**(h)** One way to perform linear regression in Python without writing your own model function and error function is to use `scipy.stats.linregress()`:\n",
109+
"### (f) Compare with `scipy.stats.linregress()`\n",
110+
"\n",
111+
"One way to perform linear regression in Python without writing your own model function and error function is to use `scipy.stats.linregress()`:\n",
118112
"\n",
119113
"```python\n",
120114
"from scipy.stats import linregress\n",
@@ -157,21 +151,33 @@
157151
"id": "e693f423",
158152
"metadata": {},
159153
"source": [
160-
"**(a)** Read these data to numpy arrays, and plot time versus the concentration of H<sub>2</sub>O<sub>2</sub>.\n",
154+
"### (a) Read and plot the data\n",
155+
"\n",
156+
"Read these data to numpy arrays, and plot time versus the concentration of H<sub>2</sub>O<sub>2</sub>.\n",
161157
"\n",
162-
"**(b)** This reaction has a first-order rate equation, which will be our \"model\" for fitting the data:\n",
158+
"### (b) Define the kinetic model function\n",
159+
"\n",
160+
"This reaction has a first-order rate equation, which will be our \"model\" for fitting the data:\n",
163161
"\n",
164162
" $$[\\mathrm{A}](t) = [\\mathrm{A}]_0\\exp(-kt)$$ \n",
165163
"\n",
166164
" Write a function `first_order()` to calculate this $[\\mathrm{A}](t)$ as a function of $t$, $[\\mathrm{A}]_0$, and $k$.\n",
167165
"\n",
168-
"**(c)** Write a function `error_function()` that calculates the least-squares error for your first-order kinetic model compared to the experimental data.\n",
166+
"### (c) Write an error function\n",
167+
"\n",
168+
"Write a function `error_function()` that calculates the least-squares error for your first-order kinetic model compared to the experimental data.\n",
169+
"\n",
170+
"### (d) Find best-fit parameters using minimize()\n",
171+
"\n",
172+
"Use `scipy.optimize.minimize()` to find the best-fit values of $[\\mathrm{A}]_0$, and $k$. Use starting guesses of $k$ = 0.1 and $A$ = 7.\n",
169173
"\n",
170-
"**(d)** Use `scipy.optimize.minimize()` to find the best-fit values of $[\\mathrm{A}]_0$, and $k$. Use starting guesses of $k$ = 0.1 and $A$ = 7.\n",
174+
"### (e) Plot the fitted model\n",
171175
"\n",
172-
"**(e)** Replot the raw data, plus the curve for your best-fit parameters.\n",
176+
"Replot the raw data, plus the curve for your best-fit parameters.\n",
173177
"\n",
174-
"**(f)** You can also perform non-linear least-squares fitting, without having to define an error function, using `scipy.optimize.curve_fit()`.\n",
178+
"### (f) Use `scipy.optimize.curve_fit()`\n",
179+
"\n",
180+
"You can also perform non-linear least-squares fitting, without having to define an error function, using `scipy.optimize.curve_fit()`.\n",
175181
"\n",
176182
"```python\n",
177183
"from scipy.optimize import curve_fit\n",
@@ -184,7 +190,24 @@
184190
"\n",
185191
"`curve_fit()` returns two numpy arrays. The first array contains the optimised model parameters. The second array contains a 2D array that describes the *covariance* of these parameters. The diagonal elements of this array are estimated uncertainties (standard deviations) for each model parameter, and the off-diagonal elements give information about the degree of correlation between the model parameters.\n",
186192
"\n",
187-
"Use `curve_fit()` to confirm your best fit model parameters for your first-order kinetic model. You will need to pass in as arguments your model function, the observed $x$ data (time), and the observed $y$ data (H<sub>2</sub>O<sub>2</sub> concentration)."
193+
"Use `curve_fit()` to confirm your best fit model parameters for your first-order kinetic model. You will need to pass in as arguments your model function, the observed $x$ data (time), and the observed $y$ data (H<sub>2</sub>O<sub>2</sub> concentration).\n",
194+
"\n",
195+
"### (g) Optional: Using linearization to find initial guesses\n",
196+
"\n",
197+
"Non-linear fitting can sometimes be sensitive to initial parameter guesses, particularly for exponential models. One useful strategy when you encounter convergence difficulties is to linearise the model first.\n",
198+
"\n",
199+
"For a first-order kinetic model, taking the natural logarithm of both sides gives:\n",
200+
"\n",
201+
"$$\\ln[\\mathrm{A}](t) = \\ln[\\mathrm{A}]_0 - kt$$\n",
202+
"\n",
203+
"This is linear in $t$ with slope $-k$ and intercept $\\ln[\\mathrm{A}]_0$.\n",
204+
"\n",
205+
"1. Transform your data: calculate $\\ln[\\mathrm{H}_2\\mathrm{O}_2]$\n",
206+
"2. Use `scipy.stats.linregress()` to fit this linearised form\n",
207+
"3. Extract approximate values for $k$ and $[\\mathrm{A}]_0$ from the fit\n",
208+
"4. Use these as initial guesses in your non-linear fit from part (d)\n",
209+
"\n",
210+
"Compare the fitted parameters from the linearised fit with those from the non-linear fit. You should find they are slightly different. The non-linear fit (fitting the exponential model directly) is generally preferred and gives more accurate parameter estimates. The linearised fit is more numerically stable with respect to the starting guess though, so is useful primarily as a way to obtain good initial guesses for the non-linear fit."
188211
]
189212
},
190213
{
@@ -212,7 +235,7 @@
212235
"name": "python",
213236
"nbconvert_exporter": "python",
214237
"pygments_lexer": "ipython3",
215-
"version": "3.11.1"
238+
"version": "3.12.9"
216239
}
217240
},
218241
"nbformat": 4,

0 commit comments

Comments
 (0)