` space separating it from the left side of the screen.\n",
+ "\n",
+ "When we write indented code below a line starting with `for`, all of that code gets repeated within the `for` loop. Conveniently, while we are within this loop, the variable `mol` will adopt the values of each element of `molecules` in sequence, changing at every loop.\n",
+ "\n",
+ "This is why `print(mol)` is effectively becoming `print(\"H2\")`, `print(\"H2O\")`, and `print(\"CH4\")` one after another.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Task 1: Understanding a Loop \n",
+ "\n",
+ " Take a look at this code block and answer the questions\n",
+ "\n",
+ ">``` Python\n",
+ ">\n",
+ ">gas_list = ['Nitrogen', 'Oxygen', 'Fluorine']\n",
+ ">for gas in gas_list:\n",
+ "> print(gas)\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "MULTIPLE CHOICE QUESTION\n",
+ "\n",
+ "- The loop will print the name of each gas on a separate line (correct)\n",
+ "- the loop will print one line with all the gas names\n",
+ "- The variable gas takes on each value of gas list in turn (correct)\n",
+ "- the variable gas_list changes each time around the loop"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Task 2: Understanding a Loop \n",
+ "\n",
+ "In the lab you are making methyl benzoate according using the scheme below. Five scientists repeat the reaction starting with 3 g of benzoic acid and get of 2.5 g, 2.7 g, 3.1 g, 1.6 g and 4 g of product.\n",
+ "\n",
+ "

\n",
+ "\n",
+ "Lets use your understanding of for loops to quickly work out the yields for each person.\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "product_mass_list = [2.5,2.7,3.1,1.6,4]\n",
+ "starting_mass = 3 # grams\n",
+ "theoretical_yield = (_______/_______)*136\n",
+ "for product_mass in product_mass_list:\n",
+ " percent_yield = (_________ / ___________) * 100\n",
+ " # the print statement uses the f-string syntax to format the output of the calculation\n",
+ " print(f\"Percent yield: {percent_yield:.0f}%\")\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ " Click here to see a possible solution to Task 2
\n",
+ "\n",
+ ">```Python\n",
+ ">product_mass_list = [2.5,2.7,3.1,1.6,4]\n",
+ ">starting_mass = 3 # grams\n",
+ ">theoretical_yield = (starting_mass/122)*136\n",
+ ">for product_mass in product_mass_list:\n",
+ "> percent_yield = (product_mass / theoretical_yield) * 100\n",
+ "> # the print statement uses the f-string syntax to format the output of the calculation\n",
+ "> print(f\"Percent yield: {percent_yield:.0f}%\")\n",
+ ">```\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Task 3: Understanding a Loop \n",
+ "\n",
+ "You have a series of temperature measurements from your reaction in degrees C and you want to print them out in Kelvin. One of your colleagues writes some code but it doesn't seem to be working. Can you identify the problem and fix the code?\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Your colleagues code\n",
+ "\n",
+ "# The code below is not working. Can you fix it?\n",
+ "temperature_measurements = [27.3, 28.1, 26.9, 27.5, 28.0]\n",
+ "for temperature_C in temperature_measurements:\n",
+ " temperature_K = temperature_C + 273.15\n",
+ "print(f\"Temperature: {temperature_K}°C\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ " Click here to see a possible solution to Task 3
\n",
+ "\n",
+ "Remember that the items that you want to do each time you go around the for loop need to be indented. In the example the print statement is not indented and so is not executed each time, but only one the loop is finished. You can fix the code just by indenting the print statement with the tab key.\n",
+ ">```Python\n",
+ ">temperature_measurements = [27.3, 28.1, 26.9, 27.5, 28.0]\n",
+ ">for temperature_C in temperature_measurements:\n",
+ "> temperature_K = temperature_C + 273.15\n",
+ "> print(f\"Temperature: {temperature_K}°C\")\n",
+ ">```\n",
+ "\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "When we are talking about for loops more generally we use the following terms:\n",
+ "- **Discrete variable**: a variable changes its value at every iteration of the loop (e.g. `temperature_C` in task 3).\n",
+ "- **Iterator**: a variable containing multiple values, which can be used in a `for` loop (e.g. `temperature_measurements` in the last example). Lists are the most common kind of operator in Python.\n",
+ "\n",
+ "The general syntax of a `for` loop is:\n",
+ "```python\n",
+ "for discrete_variable in iterator:\n",
+ " # some\n",
+ " # indented\n",
+ " # code\n",
+ "```\n",
+ "\n",
+ "You can write Python code of any length inside a `for` loop as long as you respect its indentation. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Task 4: Understanding a Loop \n",
+ "\n",
+ "Run the code below and answer the following questions to check your understanding\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "natural_amino_acids = [\"Alanine\", \"Arginine\", \"Asparagine\", \"Aspartic acid\", \"Cysteine\", \"Glutamic acid\", \"Glutamine\", \"Glycine\", \"Histidine\", \"Isoleucine\", \"Leucine\", \"Lysine\", \"Methionine\", \"Phenylalanine\", \"Proline\", \"Serine\", \"Threonine\", \"Tryptophan\", \"Tyrosine\", \"Valine\"]\n",
+ "for amino_acid in natural_amino_acids:\n",
+ " print(f\"Amino acid: {amino_acid}\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "MULTIPLE CHOICE QUESTION\n",
+ "\n",
+ "- The discrete variable is amino acid (correct)\n",
+ "- The discrete variable is amino acids\n",
+ "- The iterator is amino_acid\n",
+ "- The iterator is amino_acids (correct)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Task 5: Using loops \n",
+ "\n",
+ "When chemists want to store molecular structures in a computer they can store in an encoded way called SMILES strings. You don't need to understand what these mean but just to know that libraries that can handle chemical structure can read them. The code below will import the RDKit package and print the structure of benzoic acid. The code works by passing the SMILES string for benzoic acid to the function display_molecule that then draws it out. Check out the functions lesson if you want to understand more about functions, but for this lesson all you just need to follow the syntax below.\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from rdkit import Chem\n",
+ "\n",
+ "def display_molecule (smiles_string):\n",
+ " mol = Chem.MolFromSmiles(smiles_string)\n",
+ " img=Chem.Draw.MolToImage(mol)\n",
+ " display(img)\n",
+ "\n",
+ "# display the structure of benzoic acid\n",
+ "benzoic_acid_miles = \"C1=CC=C(C=C1)C(=O)O\"\n",
+ "display_molecule(benzoic_acid_miles)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Task 5 continued \n",
+ "Now you know how to print one molecule lets use your understanding of for loops to print lots of molecules. Here are a list of the smiles strings of some interesting molecules, Dolutegravir a very important anti-hiv drug, Atovaquone which treats malaria and paclitaxol that is an important \n",
+ "\n",
+ "\n",
+ "
\n",
+ "
\n",
+ "- C[C@@H]1CCO[C@@H]2N1C(=O)C3=C(C(=O)C(=CN3C2)C(=O)NCC4=C(C=C(C=C4)F)F)O\n",
+ "
- C1CC(CCC1C2=CC=C(C=C2)Cl)C3=C(C4=CC=CC=C4C(=O)C3=O)O\n",
+ "
- CC1=C2[C@H](C(=O)[C@@]3([C@H](C[C@@H]4[C@]([C@H]3[C@@H]([C@@](C2(C)C)(C[C@@H]1OC(=O)[C@@H]([C@H](C5=CC=CC=C5)NC(=O)C6=CC=CC=C6)O)O)OC(=O)C7=CC=CC=C7)(CO4)OC(=O)C)O)C)OC(=O)C\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "\n",
+ "Using the concepts above write a for loop that will draw the structrue of all 3 molecules. You need to:\n",
+ "- define a list of the smiles strings\n",
+ "- loop through the list and display each molecule using the display_molecule function\n",
+ "\n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "molecule_smiles = ['C[C@@H]1CCO[C@@H]2N1C(=O)C3=C(C(=O)C(=CN3C2)C(=O)NCC4=C(C=C(C=C4)F)F)O',\n",
+ "'C1CC(CCC1C2=CC=C(C=C2)Cl)C3=C(C4=CC=CC=C4C(=O)C3=O)O',\n",
+ "'CC1=C2[C@H](C(=O)[C@@]3([C@H](C[C@@H]4[C@]([C@H]3[C@@H]([C@@](C2(C)C)(C[C@@H]1OC(=O)[C@@H]([C@H](C5=CC=CC=C5)NC(=O)C6=CC=CC=C6)O)O)OC(=O)C7=CC=CC=C7)(CO4)OC(=O)C)O)C)OC(=O)C']\n",
+ "\n",
+ "for smiles in molecule_smiles:\n",
+ " display_molecule(smiles)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ " Click here to see a possible solution to Task 5
\n",
+ "\n",
+ "\n",
+ "```Python\n",
+ "molecule_smiles = ['C[C@@H]1CCO[C@@H]2N1C(=O)C3=C(C(=O)C(=CN3C2)C(=O)NCC4=C(C=C(C=C4)F)F)O',\n",
+ "'C1CC(CCC1C2=CC=C(C=C2)Cl)C3=C(C4=CC=CC=C4C(=O)C3=O)O',\n",
+ "'CC1=C2[C@H](C(=O)[C@@]3([C@H](C[C@@H]4[C@]([C@H]3[C@@H]([C@@](C2(C)C)(C[C@@H]1OC(=O)[C@@H]([C@H]>>>(C5=CC=CC=C5)NC(=O)C6=CC=CC=C6)O)O)OC(=O)C7=CC=CC=C7)(CO4)OC(=O)C)O)C)OC(=O)C']\n",
+ "\n",
+ "for smiles in molecule_smiles:\n",
+ " display_molecule(smiles)\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Ejspk6fFPXFK"
+ },
+ "source": [
+ "## TODO\n",
+ "A second part of the lesson covering the following.\n",
+ "- Iterating with `range()`\n",
+ "- Using a loop to generate a list (and maybe list comprehension)\n",
+ "- Nesting loops\n",
+ "- Continue statements"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "tpuihpop9duI"
+ },
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "provenance": []
+ },
+ "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.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/lessons/loops_functions/PyinC_while_loops_WIP.ipynb b/lessons/loops_functions/PyinC_while_loops_WIP.ipynb
new file mode 100644
index 0000000..40512e6
--- /dev/null
+++ b/lessons/loops_functions/PyinC_while_loops_WIP.ipynb
@@ -0,0 +1,260 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# The ```while``` Loop"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Learning Objectives\n",
+ "- Understand how to construct and use `while` loops\n",
+ "- Know how to control iterations in a loop\n",
+ "\n",
+ "## Prerequisites: \n",
+ "- boolean variables\n",
+ "- comparisons\n",
+ "- `if` statements\n",
+ "- `for` loops\n",
+ "\n",
+ "## The `while` Loop\n",
+ "\n",
+ "A `while` loop is similar to a [`for` loop](./For_Loop_Lesson.ipynb), with the main difference being it will continuosly loop while a condition is `True`. As as example see the cell below which prints integers from 1 to 5."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "count=0\n",
+ "while count<5:\n",
+ " count+=1\n",
+ " print(count)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The first line defines our starting value and the second initiates our loop.\n",
+ "```python\n",
+ "while(count<5):\n",
+ "```\n",
+ "Looking closer at this line we have:\n",
+ "\n",
+ "- `while` which signals we want to start a loop\n",
+ "- `count<5` is our conditional statement\n",
+ "- `:` signals the start of the loop\n",
+ "\n",
+ "If our conditional statement is `True` the code prints out the current value of `count` and increases it by one for each iteration using the `+=` operator.\n",
+ "\n",
+ "In general `while` loops take the structure:\n",
+ "\n",
+ "```python\n",
+ "while condition:\n",
+ " code \n",
+ " block\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Exercise\n",
+ "Create a `while` loop to print even integers from zero to 10."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Answer"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "count = 0\n",
+ "while count < 11:\n",
+ " print(count)\n",
+ " count += 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Infinite `while` loops\n",
+ "\n",
+ "When constructing `while` loops be wary of your condition, one common bug can be creating a loop that runs forever:\n",
+ "\n",
+ "```python\n",
+ "count = 5\n",
+ "while count != 0:\n",
+ " print(count)\n",
+ " count -= 2\n",
+ "```\n",
+ "\n",
+ "In this example, `count` is never exactly equal to 0: it counts down from 5, to 3, 1, -1, and continues down. As a result, the condition `count != 0` is never `False`, and the loop will run forever."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Break Clauses\n",
+ "\n",
+ "In order to avoid infinite loops, we might want to add a second condition under which the code should stop looping. We can do this using the keyword `break`.\n",
+ "\n",
+ "For example, the code below is the same as the code above, but with an additional `break` added, which will be executed once `count < 9`.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "count = 5\n",
+ "while count != 0:\n",
+ " print(count)\n",
+ " count -= 2\n",
+ " if count < -9:\n",
+ " break"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Password Example\n",
+ "\n",
+ "The code below pulls together everything we've seen in this lesson. It continuially asks the user for a password, while counting the number of attempts the user has made. If the user either enters the password correctly, or reaches the maximum permitted number of attempts, we use `break` to exit the loop."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Password example\n",
+ "\n",
+ "MAX_ATTEMPTS = 3\n",
+ "\n",
+ "correct_password = \"secret123\"\n",
+ "attempts = 0\n",
+ "\n",
+ "while True:\n",
+ " password = input(\"Password: \").strip()\n",
+ " attempts += 1\n",
+ "\n",
+ " if password == correct_password:\n",
+ " print(\"Login successful! Welcome!\")\n",
+ " break\n",
+ "\n",
+ " if attempts >= MAX_ATTEMPTS:\n",
+ " print(\"Too many failed attempts.\")\n",
+ " break\n",
+ " else:\n",
+ " print(f\"Incorrect password. {MAX_ATTEMPTS - attempts} attempts left.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Exercise\n",
+ "\n",
+ "The code above uses two `break` statements, and begins `while True:`. Rewrite the code so that only one `break` statement is used."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Answer"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "MAX_ATTEMPTS = 3\n",
+ "\n",
+ "correct_password = \"secret123\"\n",
+ "attempts = 0\n",
+ "\n",
+ "while attempts < MAX_ATTEMPTS:\n",
+ " password = input(\"Password: \").strip()\n",
+ " attempts += 1\n",
+ "\n",
+ " if password == correct_password:\n",
+ " print(\"Login successful! Welcome!\")\n",
+ " break\n",
+ " else:\n",
+ " if attempts < MAX_ATTEMPTS:\n",
+ " print(f\"Incorrect password. {MAX_ATTEMPTS - attempts} attempts left.\")\n",
+ " else:\n",
+ " print(\"Too many failed attempts.\")\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## TO DO\n",
+ "- Useful chemical applications/exercises?\n",
+ "- applications? -> program inputs, chemical?"
+ ]
+ }
+ ],
+ "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.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/lessons/loops_functions/StockCake-Overwhelmed with paperwork_1744290627.jpg b/lessons/loops_functions/StockCake-Overwhelmed with paperwork_1744290627.jpg
new file mode 100644
index 0000000..8453085
Binary files /dev/null and b/lessons/loops_functions/StockCake-Overwhelmed with paperwork_1744290627.jpg differ
diff --git a/lessons/loops_functions/functions_and_scope.ipynb b/lessons/loops_functions/functions_and_scope.ipynb
new file mode 100644
index 0000000..ad79c3f
--- /dev/null
+++ b/lessons/loops_functions/functions_and_scope.ipynb
@@ -0,0 +1,508 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "db5b621f-285a-4035-86f8-129e4382a8b5",
+ "metadata": {},
+ "source": [
+ "# Functions and Scope\n",
+ "\n",
+ "## Prerequisites\n",
+ "\n",
+ "- Variables and Data Types\n",
+ "- Mathematical Operators\n",
+ "- Conditional Execution\n",
+ "- Loops\n",
+ "- Lists, Dictionaries and Tuples\n",
+ "\n",
+ "## Learning Objectives\n",
+ "\n",
+ "- To learn how to re-run code which will be used often\n",
+ "- To understand the purpose of a function\n",
+ "- Understanding how function arguments work\n",
+ "- Understanding how functions return information\n",
+ " \n",
+ " \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1c23b1aa-bffb-4400-867c-042a065273f8",
+ "metadata": {},
+ "source": [
+ "## Functions\n",
+ "\n",
+ "When running Python scripts, we often use statements to change or read the values of variables. Take the following code, for instance, which looks to add up the values of numbers in a list. Here we can use it to generate the molecular mass of a molecule from a list of atomic masses. The script iterates through the list, and adds each element in it to a running total variable, which at the end of the function contains the total:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9c60cd53-dab9-4810-9ec2-6bc1914af9a9",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "m1 = [12,12,1,1,1,1]\n",
+ "\n",
+ "m1_total = 0\n",
+ "\n",
+ "for i in m1:\n",
+ " m1_total += i\n",
+ "\n",
+ "print(m1_total)\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6603b780-f776-4993-9f61-b02a303b5e8a",
+ "metadata": {},
+ "source": [
+ "This code works well to add up the numbers in the list, and if we're just doing this once, will serve perfectly fine. However, if you had another list you needed to add up this way, you would need to write this expression again. If you had 100 lists, you could be spending a lot of time writing just these few lines of code."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c4c048a9-fa8c-43d2-ae91-48939a2d02c2",
+ "metadata": {},
+ "source": [
+ "## Defining functions\n",
+ "\n",
+ "Functions offer a way for us to package up frequently-run code into an easily callable package, which we can call repeatedly. Let's now wrap the code used to add up the list elements in a function, called `add_up`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d03d38ba-1447-4f03-86b0-6c09c09297af",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def add_up(list_of_atoms):\n",
+ " total = 0\n",
+ " for atom in list_of_atoms:\n",
+ " total += atom\n",
+ " return total"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "59c43866-bfd3-4e98-abe9-fa41d97cf5b1",
+ "metadata": {},
+ "source": [
+ "To turn our adding up into a function, we have added a line before and after. To start with, we have added the statement `def add_up(list_of_atoms):` to the start of our code. This tells Python that we are defining a function, and that it is to treat all indented lines following the colon as part of the function. The `list_of_atoms` contained within the brackets is the function's _argument_. Functions can take many arguments (or none) but this function just takes one: the list which we are asking it to add up. \n",
+ "\n",
+ "Following the iterative adding process, the function is asked to `return` the total. Whatever is on the return line of the function is what the function will give back once it has finished running. Occasionally functions will just end in `return`, but generally something should be put on this line so that the function can pass it back. \n",
+ "\n",
+ "Below, we use our new `add_up` function to perform the same operation on `l1` and store the result in variable `x`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4a30ee2e-a69a-4495-b681-cb58d2437655",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = add_up(m1)\n",
+ "\n",
+ "print(x)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d417fb60-282e-4457-a911-c0f94b50afb4",
+ "metadata": {},
+ "source": [
+ "## Scope\n",
+ "\n",
+ "You may be asking why we need the `return total` statement in our code, as we should be able to reference just the variable `total` which is used as our running total during the function's loop. However, if we try that, we will get an error:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f8a25e4c-cd67-4ae3-a784-a8f7b1078cd2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = add_up(m1)\n",
+ "\n",
+ "print(total)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "fc7bffb4-2db8-4ab2-9570-49a13975ec9b",
+ "metadata": {},
+ "source": [
+ "Here, Python is telling us that the variable named `total` is not defined. This highlights an important feature of functions in Python. Functions only return what is included on the return line, and any other variables which we define in them which are not returned are lost once the function has finished executing. However, this is not true the other way around. Functions *do* have access to variables which have not been given to them explicitly as arguments.\n",
+ "\n",
+ "In the code below we can initialise a variable `string` which contains the string `\"benzene\"`. We can then also initialise the function `read_string()` which takes no arguments, but prints the value of the variable `string`. If we call this function, we can see that the string `benzene` is printed to the console."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b8cdf670-d3e8-4504-8240-4628fd65ec6a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "string = \"benzene\"\n",
+ "\n",
+ "def read_string():\n",
+ " print(string)\n",
+ " return\n",
+ "\n",
+ "read_string() # Note here that even though readfoo takes no arguments, we still need to supply some empty brackets after it when calling it.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ba898076-1abc-45bc-9198-2d22fc0bbdb3",
+ "metadata": {},
+ "source": [
+ "This shows us that even though we did not explicitly pass the variable `string` to our function, it was able to find the value of the variable `string` and output it to the console anyway. This is because `string`, having been declared in our main script, is a *global* variable, in contrast to function variables, which are *local* variables. Remember that if you see an error like this in your code output, it is likely because you have referenced a value which only exists inside the function from outside of it. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "10374f42-0a0f-4457-b3b7-b0f8443d3fd9",
+ "metadata": {},
+ "source": [
+ "## Calling functions\n",
+ "\n",
+ "Now that we've written our `add_up` function, we can use it to add up lots of lists very quickly. Here, we can define a list of lists, then iterate through that to provide a list of their totals. To call the function, all we need to do is write the name of the function, and enclose the list on which we would like it to operate in the brackets:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6f554c80-ff5a-4db0-b432-ef087b1d5252",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "many_sets_of_atoms = [\n",
+ " [12,12,1,1,1,1],\n",
+ " [12,1,1,1,12,16,12,1,1,1],\n",
+ " [12,12,12,12,12,12]\n",
+ "]\n",
+ "\n",
+ "totals = []\n",
+ "\n",
+ "for atom_set in many_sets_of_atoms:\n",
+ " set_total = add_up(atom_set)\n",
+ " totals.append(set_total)\n",
+ "\n",
+ "\n",
+ "print(totals)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8e0c9d02-ebf4-4467-83d5-d281a3cb6132",
+ "metadata": {},
+ "source": [
+ "In the example above, we are actually calling two functions every time our loop executes. The first is the `add_up` function which we have written, but the second is a the function we use to add the number to the list. We call the `list.append()` function, which adds its argument to the end of the list. Note here that the dot `.` between the `totals` and `append` is a signifier that this append function is specifically associated with the list stored in the `totals` variable. This type of function is quite common in Python, but is outside the scope of this lesson. For now, the important thing to note from this is that due to this association, we do not need to pass the `totals` list as an argument to the `append` function. \n",
+ "\n",
+ "Python has a huge amount of pre-set functions, many of which you may have already used. The `print()` and `type()` functions are ubiquitious in Python programming, but there are also many other functions which are pre-packaged in Python. In fact, the function that we have been looking at so far, `add_up()` replicates the behaviour of the inbuilt Python `sum()` function. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cfd2f639-6eb3-4378-8c31-8f391d7ddbdf",
+ "metadata": {},
+ "source": [
+ "## More complex functions\n",
+ "\n",
+ "So far we have covered basic functions, but there are more complex facets to function writing. In this section, we will look at a few of these facets, although there are many more which will not be covered here.\n",
+ "\n",
+ "### Multiple return conditions\n",
+ "\n",
+ "Functions we have written so far have only had one return statment right at the end. This is not, however, a requirement. A function can have a return statement anywhere, and it can have more than one. This is usually encountered when functions contain if statements, where depending on whether the conditions are met, different sections of the function may execute. Take the below function, for example, which checks if a supplied letter is a vowel or not:\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "03ea1aea-92db-4b13-97cd-ef159f206cd3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def is_vowel(letter):\n",
+ " if type(letter) is not str or len(letter) != 1:\n",
+ " print(\"Input must be a single character\")\n",
+ " return None\n",
+ " elif letter in [\"a\",\"e\",\"i\",\"o\",\"u\"]:\n",
+ " return True\n",
+ " else:\n",
+ " return False"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "354eeb4c-e5bf-42ed-b877-9550d7f30730",
+ "metadata": {},
+ "source": [
+ "There are three different possible return values in the above function, depending on the input. The first conditional statement confirms that a single character has been passed to the function as a string. There are more sophisticated ways to handle this sort of error, but these are outside the scope of this lesson. The key thing here is that based on this test, we can have the function return `None` rather than true or false, if an incompatible input is supplied. Following this, the function uses if/else tests to check if the single character is a vowel or not. \n",
+ "\n",
+ "We can also return more than one thing on the return line. If we define a function which uses the inbuilt python `min()` and `max()` functions to return the largest and smallest items in a list, we can return both in one statement:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1fcf6271-7e69-43c3-bd14-de1e1aff0c7f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def minmax(number_list):\n",
+ " min_value = min(number_list)\n",
+ " max_value = max(number_list)\n",
+ " return min_value, max_value"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "279c982c-39a6-4414-a5e9-487979d3eb8e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = minmax([1,2,3])\n",
+ "\n",
+ "print(x)\n",
+ "\n",
+ "y,z = minmax([1,2,3])\n",
+ "\n",
+ "print(y)\n",
+ "print(z)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9db49603-743b-43ee-9351-3a3cf09a8008",
+ "metadata": {},
+ "source": [
+ "As you can see above, when we store the output of this function in a single variable, it is returned as a tuple containing both values. However, in Python we can also use the multiple assignment variable unpacking feature to assign the minimum and maximum values in a single statement. This sort of behaviour is useful when we want to return and assign multiple variables from a single function."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "14e8bd90-4002-4b5e-92eb-8a6fe0eee1a1",
+ "metadata": {},
+ "source": [
+ "# Debugging\n",
+ "\n",
+ "The following code chunks have errors in them. See what output the chunk is currently providing, and fix the errors to give the expected output"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "47374f25-3d38-4b46-9442-acdff2dddd26",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def say_hello():\n",
+ " print(\"Hello World\")\n",
+ "\n",
+ "\n",
+ "say_hello\n",
+ "\n",
+ "#Expected output: Hello World"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4b8b293d-cc79-40e9-a83c-b8f75454aec2",
+ "metadata": {},
+ "source": [
+ "# Exercises\n",
+ "\n",
+ "1. Write a function that takes one argument, `num`, and returns `True` if it is even and `False` if it is odd. \n",
+ "\n",
+ "Remember that the modulo operator (%) returns the remainder of the left hand quantity when divided by the right hand quantity."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "950d5308-327c-41f4-9560-3d96e72996e2",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "id": "abf7a379-edaf-4d04-857a-ba5ffccd01c4",
+ "metadata": {},
+ "source": [
+ "2. Using your function above, write a function which takes a list of integers, and returns only the even integers of this list"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "cbe33a5b-28c9-433f-a8ab-8bea29570d2b",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7a07fe93-0917-4b8b-ac26-cc6c13353b25",
+ "metadata": {},
+ "source": [
+ "3. The function `add_up()` defined earlier in the document accepted a list of atomic masses. However, molecules are more generally referred to using formulae rather than lists of masses. Write a series of functions as directed in the comments in the cell below to allow calculation of molecular masses from molecular formulae for simple organic molecules:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "991aba54-f8ab-4431-aba2-36fd3843401a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# The dictionary below can be used to look up an atom's mass from its symbol. \n",
+ "# Don't worry about other elements for the time being, you can assume that these are the only elements that matter.\n",
+ "# (pretend you're an organic chemist)\n",
+ "\n",
+ "atom_masses = {\n",
+ " \"C\" : 12,\n",
+ " \"H\" : 1,\n",
+ " \"O\" : 16,\n",
+ " \"N\" : 14\n",
+ "}\n",
+ "\n",
+ "# Write a function to take a list of atomic symbols, and return a list of masses.\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "203732e1-509b-436a-a2b3-fcde5b3d8ab6",
+ "metadata": {},
+ "source": [
+ "# Answers"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e14aea88-2dd4-486a-8a11-ba69db8256c5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Exercise 1:\n",
+ "\n",
+ "def is_even(num):\n",
+ " if num%2 == 0:\n",
+ " return True\n",
+ " else:\n",
+ " return False\n",
+ "\n",
+ "print(is_even(9))\n",
+ "print(is_even(42))\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "df8b0816-99f6-4f09-8636-96c14fa153e7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Exercise 2:\n",
+ "\n",
+ "def keep_evens(num_list):\n",
+ " evens = []\n",
+ " for num in num_list:\n",
+ " if is_even(num):\n",
+ " evens.append(num)\n",
+ "\n",
+ " return evens\n",
+ "\n",
+ "print(keep_evens([1,2,3,4,5,6,7,8,9,10]))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "22ba62ed-7af1-42f9-90dd-93cf68f828ca",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Exercise 3:\n",
+ "\n",
+ "# The dictionary below can be used to look up an atom's mass from its symbol. \n",
+ "# Don't worry about other elements for the time being, you can assume that these are the only elements that matter.\n",
+ "# (pretend you're an organic chemist)\n",
+ "\n",
+ "atom_masses = {\n",
+ " \"C\" : 12,\n",
+ " \"H\" : 1,\n",
+ " \"O\" : 16,\n",
+ " \"N\" : 14\n",
+ "}\n",
+ "\n",
+ "# Write a function to take a list of atomic symbols, and return a list of masses.\n",
+ "\n",
+ "def get_masses(atoms):\n",
+ " mass_list = []\n",
+ " for atom in atoms:\n",
+ " mass_list.append(atom_masses[atom])\n",
+ " return mass_list\n",
+ "\n",
+ "print(get_masses([\"C\",\"H\",\"H\",\"H\",\"C\",\"H\",\"O\"]))\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "26af8885-0ec8-4421-8947-89f371cf350f",
+ "metadata": {},
+ "source": [
+ "## TODO:\n",
+ "\n",
+ "- More relation to chemistry in some of the later examples?\n",
+ "- Discussion of use of *args etc to supply multiple arguments to a function\n",
+ "- More debugging exercises\n",
+ "- Default arguments\n",
+ "\n",
+ "Please email theo.tanner@chem.ox.ac.uk with any questions"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a571c19b-6970-4073-8a24-4cf10d757fb8",
+ "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.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/lessons/loops_functions/methyl_benzoate.png b/lessons/loops_functions/methyl_benzoate.png
new file mode 100644
index 0000000..0fffd08
Binary files /dev/null and b/lessons/loops_functions/methyl_benzoate.png differ
diff --git a/lessons/manipulating_variables.md b/lessons/manipulating_variables.md
new file mode 100644
index 0000000..f588326
--- /dev/null
+++ b/lessons/manipulating_variables.md
@@ -0,0 +1,3 @@
+# Manipulating Variables
+
+These lessons introduce different types of variables used in Python, and how to manipulate them.
diff --git a/lessons/organising_code.md b/lessons/organising_code.md
new file mode 100644
index 0000000..b461946
--- /dev/null
+++ b/lessons/organising_code.md
@@ -0,0 +1,3 @@
+# Organising Code
+
+These lessons describe how to write code efficiently by organising it into loops and functions.
diff --git a/lessons/variables/Conditions.ipynb b/lessons/variables/Conditions.ipynb
new file mode 100644
index 0000000..748ae87
--- /dev/null
+++ b/lessons/variables/Conditions.ipynb
@@ -0,0 +1,598 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "bfa23092-475a-4e40-a6cd-538bb1b16a5a",
+ "metadata": {},
+ "source": [
+ "# Comparisons and Conditional Statements"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "63b3ed50-5670-4fcb-9cd4-81e0a50b650e",
+ "metadata": {},
+ "source": [
+ "## Learning Objectives"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b56ee7b2-cffc-471e-9fd2-bf2b2c437894",
+ "metadata": {},
+ "source": [
+ "- To understand how in python we can compare objects to each other.\n",
+ "- To understand how we can use logical statements to perform multiple comparisons or assessments at once.\n",
+ "- To see how we can build statements and scripts that can do different tasks when we give them different inputs."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "27111d44-d8ba-4393-bb63-7a1521a4a956",
+ "metadata": {},
+ "source": [
+ "## Prerequisites"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "98242630-1ee1-4fe6-bd25-d3bd5805c7ee",
+ "metadata": {},
+ "source": [
+ "- Variables\n",
+ "- Mathematical Operations\n",
+ "- Data types"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dad9b34b-ec0d-4b52-abd3-07b9efc631f4",
+ "metadata": {},
+ "source": [
+ "## Comparisons in Python"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "423201d6-956c-4bab-973f-981ee93188ef",
+ "metadata": {},
+ "source": [
+ "Python has a number of different ways in which it can compare objects, variables or just numbers. These can be very useful both for simple tasks (\"Does H$_2$O have a higher molecular weight than NH$_3$\") and more complex ones (\"How do we know that this peak in an IR spectrum is above the baseline noise?\")\n",
+ "\n",
+ "Let us start by looking at a few of the [mathematical operators](./mathematical-operators.ipynb) (either identical or very similar to how you would write them down on paper) that can be used directly in Python:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "de0e669c-1c14-47ad-b12d-5b25c0fdaf35",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "4 > 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e5d946d4-36d2-4114-bb2f-326975ee0ee5",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "5.1 < 3.6"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "dd5f7b7d-b9c7-411c-bef6-af9154332c9f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "4 >= 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "732d5859-56b9-48d6-beff-8ee521d46879",
+ "metadata": {},
+ "source": [
+ "We can see a few things from the above statements: firstly that running a comparison between two numbers in a code block outputs either 'True' if the expression is indeed true, or 'False' if not. Second, both the greater than (\">\") and less than (\"<\") mathematical operations are identical to those used in standard mathematical notation, while the greater than or equal to operation (\"≥\") is written with two characters, ```>=```. Finally, we can see that this works for both numerical [data types](Variable_data_types.ipynb) in Python - integers and floats. \n",
+ "\n",
+ "What if we wanted to check if two values were exactly equal? **Be careful!** We know from our understanding of [variables](Variable_data_types.ipynb) that ```=``` is used in python to assign values to variable names, we cannot use it here. Instead:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "962ed8f3-bb4e-4826-a348-4d42fb38025e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "4 == 4"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "39103a6d-8149-4a1e-9be8-f59f8231d729",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "4 != 4"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ff5c56d6-1170-4464-8ce3-23395e43a744",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "3.1 != 3"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c42c319f-594f-47a2-bb2f-535840c1ed80",
+ "metadata": {},
+ "source": [
+ "The double equals ```==``` is used to compare if two values are equal, while ```!=``` checks if two values are not equal.\n",
+ "\n",
+ "What about other data types? Let us try some examples of strings or lists using the ```==``` comparison operator."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2331f446-16b9-4b2a-82ca-c7c90aeaa15c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\"CH4\" == \"CH4\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3c29dc42-ae21-4a7f-beff-8c115fb3a7c8",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "\"NH3\" == \"H3N\""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "be03a6a0-eff3-4135-9258-97c46a00221d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "[2, 3, 5] == [2, 3, 4]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a6d6ecf7-38ca-4fc8-998f-9987dbd5a024",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "[\"helium\", \"neon\", \"argon\"] == [\"helium\", \"neon\", \"argon\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4239a695-24f5-4120-b42e-f7ca60b27aa2",
+ "metadata": {},
+ "source": [
+ "Note that all parts of the string (including order of characters) and all parts of a list (including the order of elements) need to be identical for the ```==``` operation to return 'True'. There is an alternative comparison that can be made - what if we wanted to distinguish if those two lists not only contained the same elements in the same order but if they are exactly the same object (i.e. rather than just looking the same, they are actually the same 'behind the scenes')? For this, we can use ```is```:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6ebfdf09-34fa-4919-a6f1-ba1b6fc12e1f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = [\"N\", \"H\", 3] \n",
+ "y = [\"N\", \"H\", 3]\n",
+ "x is y"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "462ee079-297b-4e00-bd37-6ff3df21a4af",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = y\n",
+ "x is y"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "be246bfa-183d-42a7-8388-b0af6bbba21c",
+ "metadata": {},
+ "source": [
+ "In the first example, despite the two variables, ```x``` and ```y```, containing the same chemical formula (in the same order), the two are currently two separate instances of variables according to python, so ```x is y``` is False. In the second block, we specifically reassign the variable x to the value of y, and so now the ```is``` comparison is true."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0deade2a-b54e-4888-8b22-48f902c82ce4",
+ "metadata": {},
+ "source": [
+ "## Boolean logic operators"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b818dedf-8ad5-4926-89d1-76e721599366",
+ "metadata": {},
+ "source": [
+ "Can we make multiple comparisons at the same time - i.e. to check if a number is within a certain range of bounds? Yes, and for this, we need Boolean operators - these are written as words but still make comparisons between two things: ```and```, ```or``` and ```not```. \n",
+ "\n",
+ "What does these mean? Let's start with ```and```:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "302f094f-766c-47fb-95ae-a89eed39ffac",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "z = 2\n",
+ "z < 3 and z > 0.1 "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b15d57e0-6260-42a5-94d8-f1e45743a40b",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "z < 3 and z != 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "06f35439-6217-4e18-a68b-e4375fa3025a",
+ "metadata": {},
+ "source": [
+ "When using ```and```, the statement before the ```and``` and after the ```and``` must **both** be 'True' for the overall result to be 'True', hence why the two code blocks give different answers: while z is less than 3 in both statements, z is equal to 2, and so the second statement in the second code block is 'False'. I.e. if Statement A is True AND Statement B is False, ```A and B``` is False.\n",
+ "\n",
+ "What about ```or```?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f016ee5a-25b4-4547-857f-b10f5c1d48d3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "z < 3 or z > 0.1"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "e97f2cdf-8e79-4eba-b5dc-2cbca8710b8e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "z < 3 or z !=2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d14bcff8-6b5f-4055-88bb-e18e3a0cafd0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "z > 3 or z != 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "448d821c-a069-455f-9992-a6320bbea6fb",
+ "metadata": {},
+ "source": [
+ "With ```or``` - only one of the two comparisons must be true for the whole statement to be 'True', though the whole statement will still be False if neither of the two are True. \n",
+ "\n",
+ "You don't need to just have two comparisons!"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "87ab9e7d-dc0d-44d5-9f0e-94b79f728f5f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "w = \"tungsten\"\n",
+ "w == \"neon\" or w == \"iron\" or w == \"bismuth\" or w == \"tungsten\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1c2b08e0-672b-46e7-b92e-ee9a35d69907",
+ "metadata": {},
+ "source": [
+ "Finally, not is slightly different - first it should be used before a statement, i.e. (```not a == 2```). \n",
+ "It can then be used to 'invert' the True or False behaviour of a statement: if a statement was true, not will make it False, or vice versa:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f30a769e-184f-4d13-aab3-ccd2a91f6470",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "z = 2\n",
+ "not z <= 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "40c9c586-0376-48c5-97cb-279d31c26067",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "not z > 3 or z != 2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "79597a1c-5dd4-4426-b21e-eb8135a9bfd0",
+ "metadata": {},
+ "source": [
+ "## Conditional Statements"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "feabf370-ad13-48b3-aba6-16dc7c74d5c0",
+ "metadata": {},
+ "source": [
+ "How are these comparisons useful for working with actual chemical data though? Writing a comparison for every data point would get very tiring! This is where conditional statements come in -- we can use the comparisons we have learned about to check **if** certain relationships are true as part of our data sets, and then either do something or not, or even doing a different operation...\n",
+ "\n",
+ "We do this, sensibly, through what are called **if statements**. If statements require specific syntax:\n",
+ "```\n",
+ "if :\n",
+ " what to do if statement is true\n",
+ "```\n",
+ "\n",
+ "When the statement after ```if``` is True, then Python will go on to do whatever is **indented**. Indentation is the way for python to know what is part of the ```if``` statement and what is the rest of the code. As a standard, indenting is 4 spaces before the code line -- many programs like jupyter notebook will automatically do this indenting after the if statement is written and you move to a new line with the Return key.\n",
+ "\n",
+ "If what comes after the ```if``` is False, then the indented code is ignored, and Python continues onto whatever the next non-indented lines are. As with loops, there must be a colon after the if statement, and there must be an indentation -- not having these can cause errors."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "3761936a-7163-41de-813b-580ce83a8857",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = \"CH4\"\n",
+ "y = \"CH4\"\n",
+ "if x == y:\n",
+ " ### This part of the code is indented, and is run if the if statement is true\n",
+ " print(\"Yes, these are the same formula\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "34f11095-cd62-40a6-ad7a-9df4dd7eae14",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = \"CH4\"\n",
+ "y = \"NH3\"\n",
+ "if x == y:\n",
+ " print(\"Yes, these are the same formula\")\n",
+ "\n",
+ "print(\"The indentation has ended, this code line is always run\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "f5e0ff28-eec5-4508-b5dd-ccea6d24c9e9",
+ "metadata": {},
+ "source": [
+ "What if we want to have multiple different categories and multiple different outcomes? We can use ```if... else``` if we want to do one thing if a statement is true, and then something else for for all other possibilities. Alternatively, if we have more than two decisions to make, we can use ```if... elif... else```, with ```elif``` statements acting like other if statements that are checked if they are true. For example:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b66d509c-f888-45a0-ab94-7c8115852979",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = \"CH4\"\n",
+ "if x == \"CH4\":\n",
+ " print(\"This compound is methane\")\n",
+ "elif x != \"NH3\":\n",
+ " print(\"This compound is not ammonia\")\n",
+ "else:\n",
+ " print(\"This compound is ammonia\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "64bfd426-81a8-44f3-a54e-cd2f9e99687e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = \"H2O\"\n",
+ "if x == \"CH4\":\n",
+ " print(\"This compound is methane\")\n",
+ "elif x != \"NH3\":\n",
+ " print(\"This compound is not ammonia\")\n",
+ "else:\n",
+ " print(\"This compound is ammonia\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "46cb8ac8-5627-4ddb-a169-552794c2099d",
+ "metadata": {},
+ "source": [
+ "Be careful, conditional statements are strictly ordered - if the first ```if``` is True, then that code block will be run, while all further ```elif``` statements are ignored, even if they would also be true!"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0a5899ad-5b4a-439b-bd47-df83a6d37136",
+ "metadata": {},
+ "source": [
+ "### Exercise"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "116cf3f9-74fa-46fe-a06c-ee5cec0a4b9b",
+ "metadata": {},
+ "source": [
+ "The following commented code needs the correct operator to replace the **BLANK** in the code to output True - what replaces the **BLANK**?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a827cfbe-7726-4a42-bb38-0468a752b998",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "atomic_no_of_nitrogen = 7\n",
+ "atomic_no_of_fluorine = 9"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "81dd9a93-9296-4da8-be41-3f4fc6989d56",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "atomic_no_of_nitrogen BLANK atomic_no_of_fluorine"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "bcdbba50-4cb5-4556-bc5c-39a16db6113f",
+ "metadata": {},
+ "source": [
+ "### Answer"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b3f749e9-674c-428c-8eeb-df549473ebae",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "### ANSWER: <, <= or != all work\n",
+ "atomic_no_of_nitrogen < atomic_no_of_fluorine"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "120f98bf-03ef-49b8-8338-6081411d2b10",
+ "metadata": {},
+ "source": [
+ "## Learning Outcomes"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b2a04b94-1118-422a-a616-ff6983cb92fa",
+ "metadata": {},
+ "source": [
+ "In this lesson we have learned:\n",
+ "- How to make comparisons between numbers and variables of different data types, getting Boolean True or False statements\n",
+ "- How we can use Boolean operators such as ```and``` and ```or``` to compare if multiple statements are True or False in different combinations.\n",
+ "- How to use ```if...elif...else``` statements to do certain tasks only if certain comparisons or statements are the case."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "be1e9adf-0c99-47e1-8fe7-01f461d77e08",
+ "metadata": {},
+ "source": [
+ "## TODO"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "4163f470-0898-4966-aa1e-fa2f9644278b",
+ "metadata": {},
+ "source": [
+ "- add comments to code?\n",
+ "- note different data type behaviour for is (e.g. strings versus lists as given)\n",
+ "- Think of more imaginative chemistry related examples for the lessons\n",
+ "- More tasks!\n",
+ "- Debugging task - maybe using a complex if not statement giving the wrong output?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "105d0079-c0ae-4a11-bccd-4d84b32ad1b5",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "32454202-0869-460c-b3e6-ed6fec5bf114",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "73a9fb7c-8930-4091-89a3-283eda6f41c1",
+ "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.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/lessons/variables/Variable_data_types.ipynb b/lessons/variables/Variable_data_types.ipynb
new file mode 100644
index 0000000..1c14824
--- /dev/null
+++ b/lessons/variables/Variable_data_types.ipynb
@@ -0,0 +1,454 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "VqjrEzS2z1Ol"
+ },
+ "source": [
+ "# Variable Data Types\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "_iRikrUPUjs3"
+ },
+ "source": [
+ "## Learning Outcomes\n",
+ "\n",
+ "* Understand how to print basic outputs in python\n",
+ "* Understand how to identify and verify the type of variable\n",
+ "* Create and print lists in python\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "Kup00O1iVHOG"
+ },
+ "source": [
+ "## Prerequisites\n",
+ "\n",
+ "- Be able to access Google Colab, Jupyter Notebooks or another piece of software that will allow you to interact and practice the code"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "IxCxV-mv0Svg"
+ },
+ "source": [
+ "## What is a variable?\n",
+ "\n",
+ "A variable is a place where some information can be held in the code, and can be used in many different ways within a programme."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "pNvKKeprCw5V"
+ },
+ "source": [
+ "## Print Function\n",
+ "\n",
+ "Python can specified message using the print function. This is one of the main ways you will output information."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "VQe62zJIDd2s"
+ },
+ "outputs": [],
+ "source": [
+ "message = \"we love chemistry\"\n",
+ "print(message)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "6K3DweS-SJkH"
+ },
+ "source": [
+ "You can use the print function to print answers to calculations. Remember to use brackets for more complex calculations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "mIQZr9GdSSzy"
+ },
+ "outputs": [],
+ "source": [
+ "print(54*145)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "E7oo-rhqLU4O"
+ },
+ "source": [
+ "## Type Function\n",
+ "\n",
+ "This is a built-in function that is used to return the type of data stored in the objects or variables in the program. For example, if a variable contains a value of 45.5 then the type of that variable is float.\n",
+ "\n",
+ "This is useful as when you have multiple data a variable types you check that they are functioning in the correct way."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "e1jc0uR_LXLE"
+ },
+ "outputs": [],
+ "source": [
+ "number = 45.5\n",
+ "print(type(number))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "0LF3cDZLBe82"
+ },
+ "source": [
+ "## Naming of variables\n",
+ "\n",
+ "There are few strict conventions with the naming of variables, and Python can be incredibly tolerant in this regard. However, for the ease of legibility of code, and minimise mistakes caused by referring to the wrong variable, it is sensible to try and use a clear and consistent approach to variable naming.\n",
+ "\n",
+ "### Variable name requirements\n",
+ "\n",
+ "\n",
+ "* Variables can only start with a letter or underscore (_) character and may not start with a number.\n",
+ "* Can only include upper and lowercase letters, numbers and the underscore character (A-z, 0-9, and _ )\n",
+ "* Names must not be a [Python keyword](https://docs.python.org/3/reference/lexical_analysis.html#keywords)\n",
+ "\n",
+ "### Choosing variable names\n",
+ "Short, descriptive names are generally a good choice, as variables will often need to be entered in multiple places in code. Names can consist of multiple words, but cannot use spaces. Commonly 'camelCase' is used with variable naming, where a multiple word variable name is closed together (ie the spaces are removed), and the initial letter of each word being capitalised (excluding the first word).\n",
+ "\n",
+ "Examples might include:\n",
+ "\n",
+ "\n",
+ "* elementMass\n",
+ "* elementAtomicNumber\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "VPhG17aLAcBb"
+ },
+ "source": [
+ "## Types of variables\n",
+ "Some of the common variable types are outlined below. For each type of variable, there is a code snippet which shows how the variable is set, the variable is printed, and the type of variable is printed."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "PwADJGso9G7-"
+ },
+ "source": [
+ "### Strings\n",
+ "\n",
+ "These store a sequence of characters. You signify what characters are stored by writing quotation marks either side of the characters. These can be double or single quotation marks, as long as they are consistent - it will still work either way."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "ObD5OOJswqse"
+ },
+ "outputs": [],
+ "source": [
+ "subject = \"Chemistry\"\n",
+ "print(subject)\n",
+ "print(type(subject))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "j2LOZu_qAnti"
+ },
+ "source": [
+ "### Numbers"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "5Zzn3B9OAq1d"
+ },
+ "source": [
+ "#### Integer\n",
+ "\n",
+ "This is any whole number, e.g. 5. If your variable type is an integer you will see the class output will be displayed as 'int'."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "FV_SqZ4N3edG"
+ },
+ "outputs": [],
+ "source": [
+ "number = 5\n",
+ "print(number)\n",
+ "print(type(number))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "tmlYe2PTAzCN"
+ },
+ "source": [
+ "#### Float\n",
+ "\n",
+ "This is any number where you have a decimal place. If your variable type is a float, you will see the class output will be displayed at 'float'."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "axf-MWdz4V03"
+ },
+ "outputs": [],
+ "source": [
+ "number = 5.3\n",
+ "print(number)\n",
+ "print(type(number))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "xuiU47q1A3qV"
+ },
+ "source": [
+ "#### Standard form\n",
+ "\n",
+ "When writing standard form, as is the case for Avogadro's constant you would use the letter `e` to show that the number is multipled by 10 to the power of 23."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "SF6MngZc5LTw"
+ },
+ "outputs": [],
+ "source": [
+ "avogadro = 6.023e23\n",
+ "print(avogadro)\n",
+ "print(type(avogadro))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "aWRMOW5M7b2D"
+ },
+ "source": [
+ "#### Complex Numbers\n",
+ "\n",
+ "You may not use complex numbers when you first start out on your chemistry course, but you will come across them later in maths and potentially when you study crystallography, quantum mechanics and other areas of science.\n",
+ "\n",
+ "When using complex numbers in python, you cannot use $i$ for imaginary numbers, you must use $j$ instead. In the example below we have the complex number $3+3i$, which is written as $3+3j$ in the code."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "v6df_hB45r-3"
+ },
+ "outputs": [],
+ "source": [
+ "complexNumber=(3+3j)\n",
+ "print(complexNumber)\n",
+ "print(type(complexNumber))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "K6aMfYCUAjMn"
+ },
+ "source": [
+ "#### Boolean\n",
+ "\n",
+ "Boolean variables are the simplest type of variable - they can be equal to either `True` or `False`, and nothing else."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "gkuDEMKq4csJ"
+ },
+ "outputs": [],
+ "source": [
+ "Answer = True\n",
+ "print(Answer)\n",
+ "print(type(Answer))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "HFWW4cO9BlLI"
+ },
+ "source": [
+ "#### Lists\n",
+ "\n",
+ "Lists are collections of variables, separated by commas. These variables do not have to be of the same type."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "3C66kNgC4sQf"
+ },
+ "outputs": [],
+ "source": [
+ "elements = ['H', 'He', 'Li']\n",
+ "print(elements)\n",
+ "print(type(elements))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Individual entries within a list can be accessed by writing the variable name, followed by the index of the entry within the list inside square brackets. Note that Python starts counting at 0, not 1, so the first entry in a list has index 0."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(elements[0])\n",
+ "print(elements[2])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "98-HGTmIBoSl"
+ },
+ "source": [
+ "#### Dictionaries\n",
+ "\n",
+ "Dictionaries are similar to lists, in that they are a collection of information. However, instead of using numbers to index each entry in a dictionary, we use \"keys\" instead, which are usually strings. In the example below the names of the elements are the keys. Using dictionaries usually leads to more human readable code."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "lAOQrm4LE24D"
+ },
+ "outputs": [],
+ "source": [
+ "ram = {\n",
+ " \"Hydrogen\": 1.008,\n",
+ " \"Helium\": 4.002602,\n",
+ " \"Lithium\": 6.941\n",
+ "}\n",
+ "\n",
+ "print(ram)\n",
+ "print(ram['Hydrogen'])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Exercise** Can you work out what the data dictionary below is describing?"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "id": "EbJjHF7fD0Ea"
+ },
+ "outputs": [],
+ "source": [
+ "data = {\n",
+ "'H' : [(1.007825, 99.9885),(2.014102, 0.0115)],\n",
+ "'C' : [ (12.0, 98.93),(13.003355, 1.07)],\n",
+ "'N' : [(14.003074, 99.632),(15.000109, 0.368)],\n",
+ "'O' : [(15.994915 , 99.757),(16.999132, 0.038),(17.999160,0.205)]\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "id": "5Rep96srUZeM"
+ },
+ "source": [
+ "### TO-DO List\n",
+ "\n",
+ "Add in an introduction to f-strings.\n",
+ "\n",
+ "Add in some more worked examples\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "provenance": []
+ },
+ "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.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/lessons/variables/mathematical-operators.ipynb b/lessons/variables/mathematical-operators.ipynb
new file mode 100644
index 0000000..2504642
--- /dev/null
+++ b/lessons/variables/mathematical-operators.ipynb
@@ -0,0 +1,231 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Mathematical Operators \n",
+ "\n",
+ "## Learning outcomes\n",
+ "\n",
+ "- Develop familiarity with basic mathematical operations in Python\n",
+ "- Understand how to access some additional, more complex mathematical operations. \n",
+ "\n",
+ "## Prerequisites\n",
+ "\n",
+ "- Variables and their data types\n",
+ "- Printing f-strings\n",
+ "\n",
+ "## Arithmetic\n",
+ "\n",
+ "Python is extremely relevant to numerical computing. Therefore, it is useful to outline some of the mathematical operations that can be natively performed with Python. "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Python natively supports some mathematical operations. \n",
+ "\n",
+ "| Operation | Mathematical Notation | Pythonic Notation |\n",
+ "| -------- | ------- | ------- |\n",
+ "| Addition | $a + b$ | `a + b` |\n",
+ "| Subtraction | $a - b$ | `a - b` |\n",
+ "| Multiplication | $a \\times b$ | `a * b` |\n",
+ "| Division | $a \\div b$ | `a / b` |\n",
+ "| Exponent | $a ^ b$ | `a ** b` |\n",
+ "| Modulo | $a \\textrm{ mod } b$ | `a % b` |\n",
+ "\n",
+ "The modulo operation may be new to you, you may know it as the remainder from the division of two numbers. Other, more complicated operations are typically also available through Python libraries such as [NumPy](https://numpy.org/), which is introduced below.\n",
+ "\n",
+ "A single line of code may have many mathematical operations. In this event, Python will follow the standard order of operations for mathematical operations: you might know this as [BODMAS](https://en.wikipedia.org/wiki/Order_of_operations#Mnemonics)."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Example: The Quadratic Formula\n",
+ "\n",
+ "The quadratic formula is an expression to solve quadratic equations with the form, \n",
+ "\n",
+ "$$\n",
+ "ax^2 + bx + c = 0,\n",
+ "$$\n",
+ "\n",
+ "where, $x$ is an unknown value that we want to find, and $a$, $b$, and $c$ are fixed parameters. \n",
+ "The quadratic formula states that the value of $x$ is, \n",
+ "\n",
+ "$$\n",
+ "x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}.\n",
+ "$$\n",
+ "\n",
+ "This becomes relevant in chemistry if we consider the following problem: Formic acid is a weak acid with a dissociation constant $K_a$ of 1.8×10-4. \n",
+ "The dissociation constant relates the concentration of the H+ ions and the amount of acid dissolve, $N$, by the equation: \n",
+ "\n",
+ "$$\n",
+ "K_a = \\frac{[\\textrm{H}^+]^2}{N - [\\textrm{H}^+]}.\n",
+ "$$\n",
+ "\n",
+ "This equation can be reformulated as a quadratic equation, \n",
+ "\n",
+ "$$\n",
+ "[\\textrm{H}^+]^2 + K_a[\\textrm{H}^+] - K_aN = 0.\n",
+ "$$\n",
+ "\n",
+ "Therefore, we can use the quadratic formula to solve for the concentration of hydrogen ions for a given amount of dissolved acid, where $a = 1$, $b=K_a$ and $c=-K_aN$. \n",
+ "\n",
+ "We can write Python code to compute [H+] for 0.1 moles of dissolved acid. \n",
+ "Don't worry if some of the code specifics below are a bit new, the table below explains each of the operators. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "K_a = 1.8e-4\n",
+ "N = 0.1\n",
+ "\n",
+ "a = 1.\n",
+ "b = K_a\n",
+ "c = -K_a * N\n",
+ "\n",
+ "H_conc_plus = (-b + (b ** 2 - 4 * a * c) ** (1 / 2)) / (2 * a)\n",
+ "H_conc_minus = (-b - (b ** 2 - 4 * a * c) ** (1 / 2)) / (2 * a)\n",
+ "\n",
+ "print(f'H_conc_plus = {H_conc_plus:.5f} M')\n",
+ "print(f'H_conc_minus = {H_conc_minus:.5f} M')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Clearly, the variable `H_conc_minus` should be disregarded as it is not possible to have a negative concentration. \n",
+ "This means that the concentration of [H+] is 0.00415 (to 5 decimal places). "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## More Complex Mathematical Operations\n",
+ "\n",
+ "The `numpy` module provides access to a range of advanced mathematical functions. \n",
+ "Information about all of the functions that the `numpy` module has can be found in the [numpy lesson](../common_libs/Introduction_to_NumPy.ipynb). To access a given function, we must `import` it `from` the module. Below, we import the base 10 logarithm function, `log10`. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from numpy import log10"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We can then use this with the result from above to compute the pH of the solution. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "pH = log10(H_conc_plus)\n",
+ "print(f'pH = {pH:.2f}')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Exercise\n",
+ "Find the velocity, $v$, of a N2 molecule with a mass, $m$ of 4.6×10-26 kg at a temperature, $T$, of 293 K, given the following equation,\n",
+ "\n",
+ "$$\n",
+ "v = \\sqrt{\\frac{3k_bT}{m}}, \n",
+ "$$\n",
+ "\n",
+ "where, $k_b$ is 1.38×10−23 J/K."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### Answer"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "k_b = 1.38e-23\n",
+ "T = 293\n",
+ "m = 4.6e-26\n",
+ "\n",
+ "v = ((3 * k_b * T) / m) ** (1 / 2)\n",
+ "print(f'velocity = {v:.1f} m/s')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Learning outcomes\n",
+ "\n",
+ "- Develop familiarity with basic mathematical operations in Python, including `+`, `-`, `*`, `/`. \n",
+ "- Understand how to access some additiona, more complex mathematical function using the `numpy` module. \n",
+ "\n",
+ "## TODO \n",
+ "\n",
+ "- Add more exercises for other operations"
+ ]
+ },
+ {
+ "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.10.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}