---
jupytext:
  text_representation:
    extension: .md
    format_name: myst
kernelspec:
  display_name: Python 3
  language: python
  name: python3
---
<!-- GENERATED by scripts/build_problems.py from
     DA4CHE-admin/problems/Topic1.1-Python_Basics/master.md
     Do not edit this file directly — your changes will be overwritten.
     Solutions and rubrics live in the private repo and must never appear here. -->

```{contents}
:local:
:depth: 2
```

# Problems: Python Basics

:::{admonition} Get this problem set
:class: seealso

{download}`Download everything (Topic1.1-Python_Basics_Problems.zip) <archives/Topic1.1-Python_Basics_Problems.zip>` — the notebook and
`water_properties.csv`, in a folder that is ready to run as-is.

The Download badge at the top of the page will also give you the notebook on its own.
On Vocareum everything is already set up for you.
:::

This is the first problem set of the course, so it does two jobs: it gets you started with
Python and data files, and it shows you how every later problem set will be arranged.

:::{admonition} How these problem sets work
:class: tip

Every set is worth **100 points** and has the same three parts.

**Part A — Skill Checks (30 pts).** Short questions with one right answer. You assign your
answer to a named variable, then run a check cell that tells you immediately whether it is
accepted. **You may resubmit as often as you like**, so there is no reason to hand in a
Part A that does not pass. The check cells look like this:

```
grader.check("q1")
```

**Part B — Visualization (35 pts).** Make a plot and say what it shows. Graded by your
peers against a rubric, so there is usually more than one acceptable answer — but the
rubric is specific about what has to be there.

**Part C — Open Ended (35 pts).** A question with no single right answer, where you are
graded on the reasoning and on whether your conclusions follow from what you actually
computed. Also peer graded.

The parts build on each other. Part A works out the syntax you need for Part B, and Part B
produces the evidence you argue from in Part C, so do them in order.

Cells marked `# YOUR CODE HERE` are yours to fill in. Everything else is provided and
should run as-is.

One convention worth knowing before you start: a line like `n_rows = ...` is a placeholder.
The three dots are real Python — an object called `Ellipsis` that does nothing — and they
mark the spot where your answer goes. Replace the `...`, do not add to it. If you run the
check cell without replacing it, the autograder will tell you the question is unanswered
rather than marking it wrong.
:::

## Setup

The dataset is `data/water_properties.csv`: four thermophysical properties of liquid
water, tabulated every 2 °C from the freezing point to the boiling point at 1 bar. It
comes from the NIST Chemistry WebBook, which serves the IAPWS-95 formulation — the
international standard for the properties of water.

Water is worth starting with because you already know roughly how it behaves, so when the
code gives you a number you can tell whether it is sensible. That is a habit worth forming
now: the first question to ask of any computed result is whether it could possibly be right.

```{code-cell} ipython3
import numpy as np
import pandas as pd

df = pd.read_csv('data/water_properties.csv')
df.head(10)
```

`import numpy as np` loads a library and gives it a short nickname, so every function from
it is written `np.something` — this is why NumPy calls all look alike. `pd.read_csv` reads
a comma-separated file into a **DataFrame**, a table that remembers its column names.
`df.head(10)` shows the first ten rows, which is always worth doing before anything else:
it is how you find out that the file is what you thought it was.

As in the chapter, `.values` converts the table into a NumPy array, and columns are
selected by slicing:

```{code-cell} ipython3
X = df.values
print(X.shape)
print(df.columns.values)      # which column is which
```

The five columns are, in order: temperature (°C), density (kg/m³), heat capacity
$C_p$ (J/g/K), viscosity (Pa·s), and thermal conductivity (W/m/K).

:::{admonition} Reading NumPy indexing
:class: note

An array is indexed `X[row, column]`, in that order, and **counting starts at zero**. So
the first column is `X[:, 0]` and the second is `X[:, 1]` — which is why density, the
second column in the file, is index 1. Off-by-one errors here are the single most common
mistake in this problem set.

The colon means "everything along this axis", so:

- `X[:, 1]` is *all rows, column 1* — a whole column.
- `X[10, :]` is *row 10, all columns* — a whole row.
- `X[10, 3]` is one single number, the value at row 10 and column 3.

Negative indices count backwards from the end, so `X[-1, 0]` is the last temperature in the
table without your needing to know how many rows there are.

To print a number readably, use an f-string: putting `f` in front of a string lets you drop
a variable into it inside braces, and `:.4f` after the name rounds it to four decimals.

```
print(f"mean density = {mean_density:.4f} kg/m^3")
```
:::

Run this once to load the autograder. It will not run inside the book — use the
downloaded notebook.

```{code-cell} ipython3
:tags: [skip-execution]

import otter
grader = otter.Notebook()
```

---

## Part A — Skill Checks (30 pts)

Three questions, 10 points each. Each asks for a single number.

### A1. Load the data and measure it (10 pts)

:::{exercise}
:label: pr-nm-water-rows

Using the array `X` created above, find how many rows the dataset has — that is, how many
temperatures are tabulated.

`X.shape` returns the array's dimensions as `(rows, columns)`, so the number of rows is
its first element.

Assign the number of rows to `n_rows`.
:::

```{code-cell} ipython3
:tags: [skip-execution]

# YOUR CODE HERE
n_rows = ...
```

```{code-cell} ipython3
:tags: [skip-execution]

grader.check("q1")
```


### A2. Average a column (10 pts)

:::{exercise}
:label: pr-nm-water-mean-density

Compute the **mean density** over all the tabulated temperatures, using `np.mean`.

Density is the second column, which is index 1, so `X[:, 1]` selects it — every row, column
one.

Assign the result to `mean_density`. The units are kg/m³.
:::

```{code-cell} ipython3
:tags: [skip-execution]

# YOUR CODE HERE
mean_density = ...
```

```{code-cell} ipython3
:tags: [skip-execution]

grader.check("q2")
```


### A3. Convert units and check against something you know (10 pts)

:::{exercise}
:label: pr-nm-water-viscosity-cp

Viscosity is the fourth column (index 3), and NIST reports it in Pa·s. Chemical engineers
more often quote the **centipoise**, where 1 Pa·s = 1000 cP.

Row index 10 of the table is 20.01 °C. Take the viscosity there and convert it to
centipoise.

Assign the result to `viscosity_cP`.
:::

```{code-cell} ipython3
:tags: [skip-execution]

# YOUR CODE HERE
viscosity_cP = ...
```

```{code-cell} ipython3
:tags: [skip-execution]

grader.check("q3")
```


---

## Part B — Visualization (35 pts)

This is where `matplotlib` gets introduced properly. The chapter's pattern is:

```
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(x, y, marker='o', ls='none')
ax.set_xlabel('...')
ax.set_ylabel('...')
```

`plt.subplots` makes two things at once and hands them back together: a figure (`fig`), the
canvas as a whole, and an axes (`ax`), the panel with the data on it. Everything after that
is a *method* — a function that belongs to `ax` and is called by writing a dot after it, in
the same way `df.head()` belonged to the DataFrame. So `ax.plot` draws on that panel and
`ax.set_xlabel` labels it, and if you had two panels each would take its own commands.
Axis limits work the same way, through `ax.set_xlim` and `ax.set_ylim`.

`marker='o', ls='none'` is how you get points with no connecting line. Arguments passed by
name like this can be given in any order, and any you leave out fall back to a default.

:::{exercise}
:label: pr-nm-water-first-plots

1. **Viscosity against temperature.** Plot column 3 against column 0, with points rather
   than a connecting line. Label both axes, **including units**.
2. **Thermal conductivity against temperature.** The same again for column 4, as a second
   figure.
3. **Zoom in.** Redraw the viscosity plot restricted to 0–40 °C using `ax.set_xlim`, and
   choose a `ax.set_ylim` that makes that range fill the axes.
4. In **3–4 sentences**, describe the two curves. Is either one a straight line? Which
   changes more over the range, and roughly by what factor? Read the factor off your own
   plot rather than computing it — Part C will do the arithmetic.

An unlabeled axis is the most common way to lose points on a plot, in this course and
afterwards. Units belong on both axes of every figure you make from here on.
:::

```{code-cell} ipython3
:tags: [skip-execution]

# YOUR CODE HERE
fig, ax = plt.subplots(figsize=(10, 5))
```


---

## Part C — Open Ended (35 pts)

This part asks you to write your first function and your first loop, so here is the shape
of each.

A function is defined with `def`, takes whatever you list in the parentheses, and hands a
value back with `return`. The indented block underneath is the body — indentation is how
Python knows where a block starts and stops, so it is not optional. A string on the first
line of the body is the **docstring**, which is where you say what the function does.

```
def double(x):
    """Return twice x."""
    return 2 * x
```

A `for` loop repeats the indented block once for each item in a list, with the loop
variable taking each value in turn. `zip` pairs up two lists so you can walk through both
at once — useful when each column index has a name you want to print alongside it.

```
for j, name in zip([1, 2], ['density', 'Cp']):
    print(j, name)
```

:::{exercise}
:label: pr-nm-water-sensitivity

Part B suggested that water's properties are not equally sensitive to temperature. Settle
it for all four.

1. **Write a function.** Define a function that takes a column index and returns the
   fractional change in that column from the first row to the last:

   $$
   \text{fractional change} \;=\; \frac{v_\text{last} - v_\text{first}}{v_\text{first}}
   $$

   Give it a docstring saying what it does. `X[0, j]` is the first row of column `j` and
   `X[-1, j]` is the last.

2. **Apply it** to all four property columns (1 through 4) and report the results as
   percentages. A `for` loop over the column indices is the natural way; `zip` is useful if
   you want to pair each index with a name.

3. **Plot all four together.** Divide each property column by its own value in the first
   row, so every curve starts at 1, and plot all four against temperature on one set of
   axes. Curves on one axes need a legend: pass `label='...'` to each `ax.plot` call and
   then call `ax.legend()`.

4. **One paragraph.** Which property is most sensitive to temperature, and by how much?
   Pick one of these and say what your numbers imply for it:
   - pumping the water through a pipe, where pressure drop rises with viscosity;
   - heating it in a heat exchanger, where the heat needed depends on $C_p$;
   - and say whether treating that property as constant over 0–100 °C would be reasonable.

You are not expected to know heat exchanger design. A sensible argument from your own
numbers is the whole requirement.
:::

```{code-cell} ipython3
:tags: [skip-execution]

# YOUR CODE HERE
```


---

## Summary

- Part A loaded a real dataset, measured its size, averaged a column and converted units —
  and every answer was checkable against something already known about water, which is how
  you catch mistakes before they propagate.
- Part B introduced `matplotlib` through the chapter's pattern: `plt.subplots` to make a
  figure and axes, `ax.plot` to draw, `ax.set_xlabel` and `ax.set_ylabel` to label, and
  `ax.set_xlim` / `ax.set_ylim` to zoom.
- Part C turned an impression from a plot into numbers by writing a function and looping
  over the columns, and found that water's viscosity changes by 84% over the liquid range
  while its heat capacity changes by almost nothing.

## Additional Reading

1. NIST Chemistry WebBook, [Thermophysical Properties of Fluid Systems](https://webbook.nist.gov/chemistry/fluid/)
   — the source of this dataset, and a useful reference throughout the course.
2. [The matplotlib gallery](https://matplotlib.org/stable/gallery/index.html) — worked
   examples with source code, the fastest way to find out how to draw something.
