{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "markdown-000",
      "metadata": {},
      "source": [
        "# Your first physics notebook\n",
        "\n",
        "[Read the lesson](https://tirta.co/a-byte-of-physics/your-first-physics-notebook). This notebook includes the lesson text and its Python examples in reading order. Run the setup cell, then run the remaining cells from top to bottom. In Colab, use File → Save a copy in Drive to keep your edits. You can run and edit this view without changing the course source. Examples use illustrative data unless stated otherwise."
      ]
    },
    {
      "cell_type": "code",
      "id": "code-001",
      "metadata": {},
      "source": [
        "import importlib.metadata\n",
        "import subprocess\n",
        "import sys\n",
        "\n",
        "requirements = {\"numpy\": (2, 0), \"sympy\": (1, 13), \"matplotlib\": (3, 8)}\n",
        "to_install = []\n",
        "for package, minimum in requirements.items():\n",
        "    try:\n",
        "        installed = importlib.metadata.version(package)\n",
        "        version = tuple(int(part) for part in installed.split(\".\")[:2])\n",
        "    except importlib.metadata.PackageNotFoundError:\n",
        "        version = (0, 0)\n",
        "    if version < minimum:\n",
        "        to_install.append(f\"{package}>={minimum[0]}.{minimum[1]}\")\n",
        "if to_install:\n",
        "    subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", *to_install])\n",
        "\n",
        "import numpy as np\n",
        "import sympy as sp\n",
        "import matplotlib\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "print(\"Python\", sys.version.split()[0])\n",
        "print(\"NumPy\", np.__version__)\n",
        "print(\"SymPy\", sp.__version__)\n",
        "print(\"Matplotlib\", matplotlib.__version__)\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-002",
      "metadata": {},
      "source": [
        "Python is the language we will use to calculate numbers, work with equations, and draw\n",
        "graphs throughout this course. You do not need to install it before starting. Google\n",
        "Colab runs Python in your browser and gives you a notebook: a document that keeps your\n",
        "explanation, code, and results together. This lesson takes you from an empty cell to a\n",
        "small, complete physics calculation.\n",
        "\n",
        "A Jupyter notebook is a file with the ending `.ipynb`. It contains cells rather than one\n",
        "long program. Some cells contain text. Others contain Python code. The result of running\n",
        "a code cell appears underneath it. Colab is a hosted notebook service built on the\n",
        "Jupyter project. You can download the same notebook and open it in Jupyter on your own\n",
        "computer later. The physics and Python examples do not depend on a special Colab API.\n",
        "\n",
        "We will calculate the energy needed to raise a book, change the inputs, and plot how the\n",
        "answer changes. Along the way you will meet variables, functions, arrays, symbolic\n",
        "expressions, and a few useful checks. Each idea starts small. Run one cell and read its\n",
        "output before moving to the next.\n",
        "\n",
        "## Open your copy in Google Colab\n",
        "\n",
        "Use **Open in Google Colab** above this lesson. The notebook opens with this lesson's\n",
        "text and Python cells already included. You can edit and run that view without changing\n",
        "the original course notebook. Sign in to your Google account when Colab asks you to\n",
        "connect to a runtime. To keep your work, choose **File → Save a copy in Drive**, give the\n",
        "copy a name you recognise, and continue there.\n",
        "\n",
        "The **Download notebook** link gives you the `.ipynb` file directly. This is useful if\n",
        "you prefer a local Jupyter installation, or if you want to upload the file using Colab's\n",
        "**File → Upload notebook** menu. Both routes use the same lesson and code. Keep a downloaded\n",
        "copy if you want a separate record of your work. Your saved file contains the text and\n",
        "code, and can also include the outputs from your last run.\n",
        "\n",
        "A runtime is the computer that executes your cells. Your browser displays the notebook,\n",
        "while the Python calculation runs on that computer. A normal CPU runtime is enough for\n",
        "these examples. No graphics processor is needed. If the runtime disconnects, your saved\n",
        "notebook remains, but values held in the runtime's memory may disappear. Running the\n",
        "cells again recreates those values.\n",
        "\n",
        "## Run your first Python cell\n",
        "\n",
        "Click the triangular run button beside a code cell, or select the cell and press\n",
        "**Shift + Enter**. This executes the cell and moves to the next one. The first run may\n",
        "take a little longer while Colab connects. Start with this calculation:"
      ]
    },
    {
      "cell_type": "code",
      "id": "code-003",
      "metadata": {},
      "source": [
        "print(2 + 3)\n",
        "print(2 * 3)\n",
        "print(10 / 4)\n",
        "print(3**2)\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-004",
      "metadata": {},
      "source": [
        "The output is 5, 6, 2.5, and 9, on separate lines. Python uses `*` for multiplication,\n",
        "`/` for division, and `**` for a power. The last line means three squared. Do not use\n",
        "`^` for powers: in Python that symbol performs a different operation on integers.\n",
        "Parentheses work as they do in ordinary arithmetic. For example, `(2 + 3) * 4` evaluates\n",
        "the addition first.\n",
        "\n",
        "`print` asks Python to show a value. You will also see notebooks that put an expression\n",
        "on the last line without `print`. Jupyter displays that final expression automatically.\n",
        "Using `print` is useful when a cell should show several results or attach a label to a\n",
        "number. The text inside quotation marks is a string; Python displays it as text rather\n",
        "than treating it as the name of a variable.\n",
        "\n",
        "## Use text cells for the explanation\n",
        "\n",
        "Choose **+ Text** to add a text cell and **+ Code** to add a code cell. In a text cell,\n",
        "write the question in your own words. You can use Markdown, a simple way to format text.\n",
        "A line starting with a single `#` becomes a large heading. Two `#` symbols make a smaller\n",
        "heading. Surround a word with two asterisks on each side to make it bold.\n",
        "\n",
        "Text cells do not run Python. They are where you describe what the calculation means,\n",
        "where the inputs came from, and which assumptions you made. In a code cell, a `#` starts\n",
        "a comment. Python ignores the rest of that line. A comment can explain a working line of\n",
        "code, but commenting out an entire calculation means the calculation does nothing.\n",
        "\n",
        "For this notebook, write: “How much does the gravitational potential energy change when\n",
        "I move a book to a higher shelf?” Then record that we use metres for height, kilograms\n",
        "for mass, and an upward positive direction. These choices should remain visible beside\n",
        "the calculation. You should not have to remember them from an earlier conversation.\n",
        "\n",
        "## Give numbers useful names\n",
        "\n",
        "A variable is a name that refers to a value. The equals sign stores the value on its\n",
        "right under the name on its left. Run this cell:"
      ]
    },
    {
      "cell_type": "code",
      "id": "code-005",
      "metadata": {},
      "source": [
        "mass_kg = 2.0\n",
        "initial_height_m = 0.8\n",
        "final_height_m = 1.6\n",
        "gravity_m_per_s2 = 9.81\n",
        "\n",
        "height_change_m = final_height_m - initial_height_m\n",
        "print(\"Height change in metres:\", height_change_m)\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-006",
      "metadata": {},
      "source": [
        "The height change is 0.8 metres. Names such as `mass_kg` make the unit visible. The\n",
        "underscore joins words into one Python name; spaces cannot appear inside a variable\n",
        "name. Python distinguishes upper and lower case, so `mass_kg` and `Mass_kg` would be\n",
        "different names. Pick one spelling and use it consistently.\n",
        "\n",
        "A name does not enforce a unit. If you store centimetres under a name ending in `_m`,\n",
        "Python will still do the arithmetic. That is why we convert values before calculating\n",
        "and explain the units in text. Later lessons introduce stronger ways to check units.\n",
        "For a first notebook, explicit names and a small worked example make mistakes easier\n",
        "to notice.\n",
        "\n",
        "Assignment also does not create a permanently linked equation. If you change\n",
        "`final_height_m` later, `height_change_m` keeps its old value until you run the line that\n",
        "calculates it again. This is one reason execution order matters. After changing an\n",
        "input, rerun the calculation cells below it.\n",
        "\n",
        "## Turn the physics equation into Python\n",
        "\n",
        "The system is the book together with Earth. Gravitational potential energy describes\n",
        "their interaction. Near Earth's surface, a small change in height gives\n",
        "\n",
        "\n",
        "$$\n",
        "\\Delta U_g=mg(y_f-y_i)\n",
        "$$\n",
        "\n",
        "\n",
        "The symbol $\\Delta$ means a change:\n",
        "final value minus initial value. Mass is measured in kilograms, gravitational\n",
        "acceleration in metres per second squared, and height in metres. Their product is\n",
        "measured in joules. We use 9.81 as an approximate near-Earth value of gravitational\n",
        "acceleration, not as a measured value for every location on Earth."
      ]
    },
    {
      "cell_type": "code",
      "id": "code-007",
      "metadata": {},
      "source": [
        "energy_change_j = mass_kg * gravity_m_per_s2 * height_change_m\n",
        "print(\"Unrounded energy change in joules:\", energy_change_j)\n",
        "print(f\"Energy change: {energy_change_j:.1f} J\")\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-008",
      "metadata": {},
      "source": [
        "The unrounded product is 15.696 joules. The last line displays 15.7 J. An `f` before a\n",
        "quoted string lets you insert values inside braces. The `:.1f` part asks for one digit\n",
        "after the decimal point. It changes the displayed text, while `energy_change_j` keeps\n",
        "its unrounded value for later calculations.\n",
        "\n",
        "A positive answer means the book-Earth system gained gravitational potential energy.\n",
        "If you lower the book, the height change is negative and the energy change is negative.\n",
        "The calculation does not give the exact energy used by a person's muscles. It describes\n",
        "one energy change in a deliberately simple physical model.\n",
        "\n",
        "The chosen zero of height does not change this answer. Measuring both shelves from a\n",
        "point one metre below the floor adds one metre to both heights. Their difference stays\n",
        "the same. Measuring one shelf from the floor and the other from the ceiling would mix\n",
        "origins and produce an answer with no useful physical meaning.\n",
        "\n",
        "## Make the calculation reusable with a function\n",
        "\n",
        "A function groups instructions that you want to repeat with different inputs. The\n",
        "`def` line names the function and its inputs. The indented lines belong to it. Python\n",
        "uses indentation to mark this grouping, so keep the four spaces at the start of each\n",
        "line inside the function."
      ]
    },
    {
      "cell_type": "code",
      "id": "code-009",
      "metadata": {},
      "source": [
        "def energy_change(mass_kg, initial_height_m, final_height_m, gravity=9.81):\n",
        "    if mass_kg < 0:\n",
        "        raise ValueError(\"Mass must not be negative.\")\n",
        "    if gravity <= 0:\n",
        "        raise ValueError(\"Use a positive gravity magnitude.\")\n",
        "    height_change_m = final_height_m - initial_height_m\n",
        "    return mass_kg * gravity * height_change_m\n",
        "\n",
        "\n",
        "print(f\"Raise the book: {energy_change(2.0, 0.8, 1.6):.1f} J\")\n",
        "print(f\"Lower the book: {energy_change(2.0, 1.6, 0.8):.1f} J\")\n",
        "print(f\"Leave it in place: {energy_change(2.0, 0.8, 0.8):.1f} J\")\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-010",
      "metadata": {},
      "source": [
        "`return` sends the calculated value back to the caller. It belongs inside the function.\n",
        "Defining a function does not run its calculation immediately; the calls in the three\n",
        "`print` lines supply the inputs and run it. The default `gravity=9.81` means that the\n",
        "caller can omit that fourth input, or supply a different positive value explicitly.\n",
        "\n",
        "The two `if` statements check whether inputs fit our model. Negative mass is excluded,\n",
        "but negative height change is allowed. The distinction matters: a check should reject\n",
        "an invalid physical situation, not a perfectly valid downward motion. A `ValueError`\n",
        "stops that call and explains what needs to change.\n",
        "\n",
        "## Ask Python to check a few predictions\n",
        "\n",
        "Before making a graph, check simple cases whose answers you can predict. Keeping the\n",
        "book on the same shelf must give zero. Doubling its mass must double the energy change.\n",
        "Moving it back must reverse the sign. Use `assert` to make these expectations executable:"
      ]
    },
    {
      "cell_type": "code",
      "id": "code-011",
      "metadata": {},
      "source": [
        "import math\n",
        "\n",
        "up = energy_change(2.0, 0.8, 1.6)\n",
        "down = energy_change(2.0, 1.6, 0.8)\n",
        "double_mass = energy_change(4.0, 0.8, 1.6)\n",
        "shifted_origin = energy_change(2.0, 1.8, 2.6)\n",
        "\n",
        "assert energy_change(2.0, 0.8, 0.8) == 0.0\n",
        "assert math.isclose(down, -up)\n",
        "assert math.isclose(double_mass, 2 * up)\n",
        "assert math.isclose(shifted_origin, up)\n",
        "print(\"All four physical checks passed.\")\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-012",
      "metadata": {},
      "source": [
        "`import math` loads Python's standard mathematics module. `math.isclose` compares\n",
        "numbers while allowing a small rounding difference. Many decimal fractions cannot be\n",
        "stored exactly in a computer's binary number format. A tiny discrepancy after\n",
        "subtraction is therefore not automatically a physics error.\n",
        "\n",
        "An assertion that fails raises an error. Read it as a clue: either the calculation or\n",
        "the expectation is wrong. Passing these checks gives useful evidence about the code,\n",
        "but does not prove that the uniform-gravity approximation works at every possible\n",
        "height. Checking implementation and checking physical assumptions are related tasks,\n",
        "and both are needed.\n",
        "\n",
        "## Calculate many cases with NumPy\n",
        "\n",
        "NumPy helps Python work with collections of numbers. Instead of entering five final\n",
        "heights separately, put them in an array. An array is an ordered collection whose\n",
        "arithmetic can act on every element at once."
      ]
    },
    {
      "cell_type": "code",
      "id": "code-013",
      "metadata": {},
      "source": [
        "import numpy as np\n",
        "\n",
        "final_heights_m = np.array([0.0, 0.4, 0.8, 1.2, 1.6])\n",
        "energy_changes_j = 2.0 * 9.81 * (final_heights_m - 0.8)\n",
        "\n",
        "for height, energy in zip(final_heights_m, energy_changes_j):\n",
        "    print(f\"Final height {height:.1f} m -> change {energy:.3f} J\")\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-014",
      "metadata": {},
      "source": [
        "Subtracting 0.8 subtracts it from every height. Multiplication then applies to each\n",
        "height change. The output array has the same number of entries and the same order.\n",
        "The first energy is negative, the middle energy is zero, and the last is positive.\n",
        "These signs describe shelves below, level with, and above the initial shelf.\n",
        "\n",
        "The `for` loop prints one pair at a time. `zip` pairs corresponding entries in the two\n",
        "arrays. The calculation itself already happened before the loop. Keeping calculation\n",
        "and display separate lets you reuse the numerical values in a table, a graph, or a\n",
        "later calculation without reading numbers back from formatted text.\n",
        "\n",
        "## Draw your first graph with Matplotlib\n",
        "\n",
        "Matplotlib turns numerical results into figures. NumPy's `linspace` gives us 101 evenly\n",
        "spaced heights from zero to two metres, including both endpoints. This makes a smooth\n",
        "looking line without typing every height ourselves."
      ]
    },
    {
      "cell_type": "code",
      "id": "code-015",
      "metadata": {},
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "heights_m = np.linspace(0.0, 2.0, 101)\n",
        "energies_j = 2.0 * 9.81 * (heights_m - 0.8)\n",
        "\n",
        "fig, ax = plt.subplots(figsize=(7, 4))\n",
        "ax.plot(heights_m, energies_j, label=\"2 kg book\")\n",
        "ax.axhline(0, color=\"grey\", linewidth=0.8)\n",
        "ax.axvline(0.8, color=\"grey\", linewidth=0.8, linestyle=\"--\")\n",
        "ax.set_xlabel(\"Final height (m)\")\n",
        "ax.set_ylabel(\"Potential energy change (J)\")\n",
        "ax.set_title(\"Moving a book from a shelf at 0.8 m\")\n",
        "ax.legend()\n",
        "ax.grid(alpha=0.25)\n",
        "fig.tight_layout()\n",
        "plt.show()\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-016",
      "metadata": {},
      "source": [
        "`fig` is the whole figure and `ax` is the plotting area with its axes. `ax.plot` receives\n",
        "horizontal values first and vertical values second. The axis labels say which\n",
        "quantities are shown and give their units. `plt.show()` displays the completed figure\n",
        "under the cell.\n",
        "\n",
        "Read the graph before changing it. The line crosses zero at 0.8 metres because that is\n",
        "the starting shelf. Its positive slope says that a higher final shelf gives a larger\n",
        "energy change. The slope is 19.62 joules per metre, equal to mass times gravity. A\n",
        "straight line is expected because the model uses a constant gravitational field.\n",
        "\n",
        "More plotted points make the line look smoother. They do not make the input mass more\n",
        "accurate or improve the gravity approximation. This is a useful distinction to keep\n",
        "throughout the course: the detail in a picture and the accuracy of a physical model are\n",
        "different things.\n",
        "\n",
        "## Use SymPy to work with the equation itself\n",
        "\n",
        "NumPy calculates with numerical values. SymPy can keep quantities symbolic and\n",
        "rearrange an equation before we choose values. Start by declaring symbols. These are\n",
        "mathematical objects, not measurements already stored in kilograms or metres."
      ]
    },
    {
      "cell_type": "code",
      "id": "code-017",
      "metadata": {},
      "source": [
        "import sympy as sp\n",
        "\n",
        "m, g = sp.symbols(\"m g\", positive=True)\n",
        "y_i, y_f = sp.symbols(\"y_i y_f\", real=True)\n",
        "delta_u = m * g * (y_f - y_i)\n",
        "\n",
        "print(\"Energy expression:\", delta_u)\n",
        "print(\"Slope with respect to final height:\", sp.diff(delta_u, y_f))\n",
        "value = delta_u.subs({m: 2, g: 9.81, y_i: 0.8, y_f: 1.6})\n",
        "print(f\"Numerical energy change: {float(value):.3f} J\")\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-018",
      "metadata": {},
      "source": [
        "`sp.diff` differentiates the expression. Its result is `g*m`, which matches the slope we\n",
        "read from the graph. `subs` substitutes values into the symbolic expression. The call\n",
        "to `float` converts the resulting SymPy number to an ordinary Python floating-point\n",
        "value so that the familiar formatting works.\n",
        "\n",
        "SymPy can also answer the reverse question. Suppose the energy increase is 20 joules.\n",
        "At what height should the final shelf be, under the same assumptions?"
      ]
    },
    {
      "cell_type": "code",
      "id": "code-019",
      "metadata": {},
      "source": [
        "target_energy = sp.Symbol(\"target_energy\", real=True)\n",
        "height_solution = sp.solve(sp.Eq(delta_u, target_energy), y_f)[0]\n",
        "print(\"Final height formula:\", height_solution)\n",
        "height_value = height_solution.subs(\n",
        "    {\n",
        "        m: 2,\n",
        "        g: 9.81,\n",
        "        y_i: 0.8,\n",
        "        target_energy: 20,\n",
        "    }\n",
        ")\n",
        "print(f\"Final shelf height: {float(height_value):.3f} m\")\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-020",
      "metadata": {},
      "source": [
        "The result is approximately 1.819 metres. `sp.Eq` states a mathematical equality and\n",
        "`sp.solve` asks for the specified unknown. The `[0]` selects the first solution from\n",
        "the returned list; this particular equation has one solution for positive mass and\n",
        "gravity. Other physics equations can have several solutions, and you must decide which\n",
        "ones fit the physical question.\n",
        "\n",
        "## Change one input and explain what happened\n",
        "\n",
        "Try a one-kilogram book while keeping the two shelves fixed. Predict the answer first:\n",
        "the energy change should be half of 15.696 joules. Then run the cell. Next keep the\n",
        "mass at two kilograms but move the final shelf down to 0.4 metres. Predict the sign\n",
        "before calculating the value."
      ]
    },
    {
      "cell_type": "code",
      "id": "code-021",
      "metadata": {},
      "source": [
        "cases = [\n",
        "    (\"Lighter book\", 1.0, 0.8, 1.6),\n",
        "    (\"Lower shelf\", 2.0, 0.8, 0.4),\n",
        "    (\"Same shelf\", 2.0, 0.8, 0.8),\n",
        "]\n",
        "for label, mass, start, finish in cases:\n",
        "    result = energy_change(mass, start, finish)\n",
        "    print(f\"{label}: {result:.3f} J\")\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-022",
      "metadata": {},
      "source": [
        "The results are 7.848 J, −7.848 J, and 0 J. Write a text cell below the outputs saying\n",
        "why each answer has that size and sign. This is more useful than saving a notebook full\n",
        "of unexplained numbers. It also gives you a way to catch a mistake that produces a\n",
        "plausible looking number with the wrong interpretation.\n",
        "\n",
        "To compare masses on one graph, reuse the same height array for every curve. Keep the\n",
        "starting shelf fixed, so that mass is the only changing input. The shared zero crossing\n",
        "then tells you something that a table of unrelated calculations might hide."
      ]
    },
    {
      "cell_type": "code",
      "id": "code-023",
      "metadata": {},
      "source": [
        "fig, ax = plt.subplots(figsize=(7, 4))\n",
        "for mass in [1.0, 2.0, 3.0]:\n",
        "    changes = mass * 9.81 * (heights_m - 0.8)\n",
        "    ax.plot(heights_m, changes, label=f\"{mass:.0f} kg\")\n",
        "ax.set_xlabel(\"Final height (m)\")\n",
        "ax.set_ylabel(\"Potential energy change (J)\")\n",
        "ax.legend(title=\"Book mass\")\n",
        "ax.grid(alpha=0.25)\n",
        "fig.tight_layout()\n",
        "plt.show()\n"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "markdown-024",
      "metadata": {},
      "source": [
        "Every curve passes through zero at the starting height. Heavier books have steeper\n",
        "curves, but the sign still depends on whether the final shelf is above or below the\n",
        "starting shelf. Try stating that conclusion without referring to line colours. A good\n",
        "explanation should remain understandable when the figure is printed in black and white.\n",
        "\n",
        "## Read errors without losing your place\n",
        "\n",
        "A `NameError` often means that a name was misspelled or that the cell defining it has\n",
        "not run. If `np` is unknown, run the `import numpy as np` cell. If `heights_m` is unknown,\n",
        "run the cell that creates the height array. Start at the earliest missing definition\n",
        "rather than adding a new value in a random cell near the error.\n",
        "\n",
        "A `SyntaxError` means Python could not read the code. Check for missing parentheses,\n",
        "unclosed quotes, and a missing colon after `def`, `if`, or `for`. An `IndentationError`\n",
        "points to the spacing that groups statements. The error message usually includes a\n",
        "line number and shows the location where Python became unable to continue.\n",
        "\n",
        "A `ModuleNotFoundError` means a required library is absent from the runtime. The course\n",
        "notebooks include a setup cell for NumPy, SymPy, and Matplotlib. Run that cell first in a\n",
        "fresh environment. If you are using your own Jupyter installation, select the Python\n",
        "environment where those libraries are installed. Installing a package in a different\n",
        "Python environment will not make it appear in the current notebook.\n",
        "\n",
        "You do not need to erase the whole notebook after an error. Correct the relevant cell,\n",
        "run it again, and rerun the cells that depend on its result. Keep a short note if the\n",
        "error revealed a misunderstanding about the physics. That note may be more valuable\n",
        "than the final decimal answer.\n",
        "\n",
        "## Finish with a clean run\n",
        "\n",
        "Before sharing, restart the runtime and run all cells from the top. In Colab, use the\n",
        "restart-and-run-all option in the Runtime menu. This removes values left over from\n",
        "experiments and shows whether the saved cell order really reproduces the calculation.\n",
        "The setup and import cells should run before any cell that needs their libraries.\n",
        "\n",
        "Save the notebook after reviewing the fresh outputs. Add your name or a short purpose\n",
        "statement, label the values as illustrative rather than measured, and record the\n",
        "assumptions: a small lift near Earth's surface, uniform gravity, and both heights\n",
        "measured from the same origin. If you later replace the sample inputs with measurements,\n",
        "record how you measured them and what their uncertainties mean.\n",
        "\n",
        "The finished notebook now contains a question, inputs, a working Python calculation,\n",
        "checks, graphs, and an explanation. You can change one value and follow its effect all\n",
        "the way to the conclusion. That is the basic workflow we will reuse when the equations\n",
        "become more involved: keep the reasoning visible, calculate with code, and use the\n",
        "result to answer a physical question.\n",
        "\n",
        "## Sources\n",
        "\n",
        "- [Google Colab, frequently asked questions](https://research.google.com/colaboratory/faq.html).\n",
        "- [Google Colab, using notebooks from GitHub](https://github.com/googlecolab/colabtools/blob/main/notebooks/colab-github-demo.ipynb).\n",
        "- [NumPy, absolute basics for beginners](https://numpy.org/doc/stable/user/absolute_beginners.html).\n",
        "- [Matplotlib, quick start guide](https://matplotlib.org/stable/users/explain/quick_start.html).\n",
        "- [SymPy, introductory tutorial](https://docs.sympy.org/latest/tutorials/intro-tutorial/index.html).\n",
        "- [OpenStax, University Physics Volume 1, potential energy of a system](https://openstax.org/books/university-physics-volume-1/pages/8-1-potential-energy-of-a-system).\n",
        "- [OpenStax, University Physics Volume 1, significant figures](https://openstax.org/books/university-physics-volume-1/pages/1-6-significant-figures)."
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "name": "python",
      "version": "3.12"
    },
    "colab": {
      "name": "your-first-physics-notebook.ipynb",
      "provenance": []
    },
    "tirta": {
      "lesson": "https://tirta.co/a-byte-of-physics/your-first-physics-notebook",
      "source_sha256": "e50beef2be012194db7a784ef1e832ba3d1e0d893b3edcf129ef99befd08a3b5"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
