← Back to Assignments|↑ Back to Module

Van der Waals Gas Analysis: Evaluating Equation-of-State Models with Real Experimental Data

Module: Modules_File_I

Assignment Type: Problem-Solving Exercise

Estimated Time: 2–4 hours

Submission Format: Google Colab notebook link (shared with comment access) or a written report (PDF or DOCX) that includes all code, outputs, and written responses


Purpose

Why This Assignment Matters

Real engineering and scientific data is rarely perfect. Instruments introduce measurement error, and theoretical models make simplifying assumptions that may or may not hold under real conditions. In this exercise, you will act as a data analyst examining experimentally collected gas-behavior data to evaluate two models of gas behavior:

1. The Ideal Gas Law: PV = nRT (simplified model)

2. The Van der Waals Equation: (P + a·n²/V²)(V − n·b) = nRT (corrected model)

You will use Python's standard library modules and file I/O tools — exactly the skills covered in this module — to load, process, analyze, and report on the data. This workflow mirrors what engineers and scientists do daily: load data from instruments, apply a model, visualize agreement (or disagreement), and draw defensible conclusions.

Connection to Course Goals

This assignment directly practices:


Learning Outcomes

By the end of this assignment, you will be able to:

1. Construct a Python script that reads experimental data from a CSV file and writes processed results to a new CSV file using csv.DictReader, csv.DictWriter, and the with statement.

2. Apply standard library modules (math, os, datetime, pathlib) to perform engineering calculations and manage file paths in a cross-platform manner.

3. Analyze the deviation between experimentally observed pressure values and values predicted by both the Ideal Gas Law and the Van der Waals equation by calculating and interpreting percent error.

4. Evaluate the relative accuracy of the Ideal Gas Law versus the Van der Waals equation for a real gas by interpreting visual plots and numerical error metrics, and construct a written argument that accounts for measurement uncertainty.


Background

The Ideal Gas Law

The Ideal Gas Law assumes gas molecules have no volume and exert no intermolecular forces:

`

P = nRT / V

`

Where:

The Van der Waals Equation

The Van der Waals equation corrects the Ideal Gas Law for two physical realities:

`

P = nRT / (V - nb) - a n² / V²

`

Where a and b are gas-specific constants. For this assignment, you are analyzing carbon dioxide (CO₂):

| Constant | Value | Units |

|---|---|---|

| a | 0.3658 | Pa·m⁶·mol⁻² |

| b | 4.286 × 10⁻⁵ | m³·mol⁻¹ |

> Note: These constants have known accepted (theoretical/literature) values. Your job is to use these accepted values to generate predicted pressures and compare them to the measured data.

Percent Error

Percent error quantifies how far a model's prediction is from the measured value:

`

percent_error = ((predicted - measured) / measured) * 100

`

A value near zero indicates good agreement. A positive value means the model over-predicts; a negative value means it under-predicts.

Measurement Error

No instrument reads perfectly. Assume the pressure sensor used to collect this data has a manufacturer-specified accuracy of ±1.5% of the measured value. This means any percent error within ±1.5% may be attributable to instrument error alone, not model error. You must address this in your conclusions.


Provided Data and Starter Materials

Experimental CSV Data File

Copy the data below into a plain text file named co2_experimental_data.csv and upload it to your Colab session, or create the file programmatically in Colab using the code provided in the starter notebook.

The dataset contains measurements taken on CO₂ at varying temperatures and volumes. The number of moles n = 1.000 mol is held constant across all trials.

`

trial_id,temperature_K,volume_m3,n_mol,measured_pressure_Pa

1,300.0,0.002500,1.000,997200

2,300.0,0.003000,1.000,829500

3,300.0,0.004000,1.000,621800

4,300.0,0.005000,1.000,497100

5,325.0,0.002500,1.000,1079600

6,325.0,0.003000,1.000,898200

7,325.0,0.004000,1.000,673100

8,325.0,0.005000,1.000,538500

9,350.0,0.002500,1.000,1161400

10,350.0,0.003000,1.000,967100

11,350.0,0.004000,1.000,724700

12,350.0,0.005000,1.000,579600

13,375.0,0.002500,1.000,1243700

14,375.0,0.003000,1.000,1036100

15,375.0,0.004000,1.000,776600

16,375.0,0.005000,1.000,620900

17,400.0,0.002500,1.000,1325600

18,400.0,0.003000,1.000,1105300

19,400.0,0.004000,1.000,828700

20,400.0,0.005000,1.000,662400

`

Starter Colab Notebook

A starter notebook with scaffolding code is provided below. Copy this into a new Colab notebook. Sections marked # TODO require you to write the code. Sections marked # PROVIDED are complete and should not be modified.

`python

============================================================

Van der Waals Gas Analysis — Starter Notebook

Module: Modules_File_I

============================================================

── PROVIDED: Import all required modules ───────────────────

import math

import csv

import os

import sys

from datetime import datetime

from pathlib import Path

import matplotlib.pyplot as plt # available in Colab by default

── PROVIDED: Constants ──────────────────────────────────────

R = 8.314 # Universal gas constant, J·mol⁻¹·K⁻¹

A_VDW = 0.3658 # Van der Waals constant a for CO₂, Pa·m⁶·mol⁻²

B_VDW = 4.286e-5 # Van der Waals constant b for CO₂, m³·mol⁻¹

SENSOR_ERROR_PCT = 1.5 # Instrument accuracy ±1.5%

── PROVIDED: File paths using pathlib ──────────────────────

INPUT_FILE = Path("co2_experimental_data.csv")

OUTPUT_FILE = Path("co2_analysis_results.csv")

============================================================

STEP 1 — Create the input CSV file (run this cell once)

PROVIDED: You do not need to modify this cell.

============================================================

raw_data = """trial_id,temperature_K,volume_m3,n_mol,measured_pressure_Pa

1,300.0,0.002500,1.000,997200

2,300.0,0.003000,1.000,829500

3,300.0,0.004000,1.000,621800

4,300.0,0.005000,1.000,497100

5,325.0,0.002500,1.000,1079600

6,325.0,0.003000,1.000,898200

7,325.0,0.004000,1.000,673100

8,325.0,0.005000,1.000,538500

9,350.0,0.002500,1.000,1161400

10,350.0,0.003000,1.000,967100

11,350.0,0.004000,1.000,724700

12,350.0,0.005000,1.000,579600

13,375.0,0.002500,1.000,1243700

14,375.0,0.003000,1.000,1036100

15,375.0,0.004000,1.000,776600

16,375.0,0.005000,1.000,620900

17,400.0,0.002500,1.000,1325600

18,400.0,0.003000,1.000,1105300

19,400.0,0.004000,1.000,828700

20,400.0,0.005000,1.000,662400"""

with open(INPUT_FILE, 'w', newline='') as f:

f.write(raw_data)

print(f"Input file created: {INPUT_FILE}")

print(f"File exists: {INPUT_FILE.exists()}")

print(f"Working directory: {os.getcwd()}")

print(f"Python version: {sys.version}")

============================================================

STEP 2 — Define calculation functions

============================================================

def ideal_gas_pressure(n, R, T, V):

"""

PROVIDED: Calculate pressure using the Ideal Gas Law.

Returns pressure in Pa.

"""

return (n R T) / V

def vdw_pressure(n, R, T, V, a, b):

"""

TODO: Calculate pressure using the Van der Waals equation.

The Van der Waals equation solved for P is:

P = nRT / (V - nb) - a n2 / V2

Parameters

----------

n : float — moles of gas

R : float — gas constant (8.314 J/mol/K)

T : float — temperature in Kelvin

V : float — volume in m³

a : float — Van der Waals constant a

b : float — Van der Waals constant b

Returns

-------

float — predicted pressure in Pa

"""

# TODO: Replace 'pass' with your implementation.

pass

def percent_error(predicted, measured):

"""

TODO: Calculate percent error between predicted and measured values.

Formula: ((predicted - measured) / measured) * 100

Parameters

----------

predicted : float

measured : float

Returns

-------

float — percent error (positive = over-prediction)

"""

# TODO: Replace 'pass' with your implementation.

pass

============================================================

STEP 3 — Read the CSV file and compute results

============================================================

results = [] # This list will hold one dictionary per trial.

TODO: Open INPUT_FILE for reading using the with statement and

csv.DictReader. For each row:

1. Convert temperature_K, volume_m3, and n_mol to float,

and measured_pressure_Pa to float.

2. Call ideal_gas_pressure() to get p_ideal.

3. Call vdw_pressure() to get p_vdw.

4. Call percent_error() twice: once for ideal vs measured,

once for vdw vs measured.

5. Append a dictionary to results containing all original

fields plus: p_ideal, p_vdw, pct_err_ideal, pct_err_vdw.

#

HINT: Remember that csv.DictReader returns all values as strings.

You must explicitly convert numeric columns with float().

--- Write your Step 3 code below this line ---

============================================================

STEP 4 — Write results to a new CSV file

============================================================

TODO: Define the fieldnames list for csv.DictWriter.

It should include all original column names plus:

'p_ideal_Pa', 'p_vdw_Pa', 'pct_err_ideal', 'pct_err_vdw'

fieldnames = [] # TODO: Replace [] with your list of field names.

TODO: Open OUTPUT_FILE for writing using the with statement,

newline='', and csv.DictWriter.

Write the header row, then write all rows from results.

After writing, print a confirmation message that includes

the output file name and the current date/time using datetime.now().

--- Write your Step 4 code below this line ---

============================================================

STEP 5 — Visualize the results

============================================================

PROVIDED: Extract data for plotting once results list is populated.

Run this cell after Step 3 and 4 are complete.

trial_ids = [r['trial_id'] for r in results]

measured = [r['measured_pressure_Pa'] for r in results]

p_ideal_list = [r['p_ideal_Pa'] for r in results]

p_vdw_list = [r['p_vdw_Pa'] for r in results]

pct_ideal_list = [r['pct_err_ideal'] for r in results]

pct_vdw_list = [r['pct_err_vdw'] for r in results]

── Plot 1: Predicted vs Measured Pressure ───────────────────

fig, ax = plt.subplots(figsize=(10, 5))

ax.plot(trial_ids, measured, 'ko-', label='Measured', linewidth=2)

ax.plot(trial_ids, p_ideal_list, 'b^--', label='Ideal Gas Law')

ax.plot(trial_ids, p_vdw_list, 'rs--', label='Van der Waals')

ax.set_xlabel('Trial ID')

ax.set_ylabel('Pressure (Pa)')

ax.set_title('Predicted vs Measured Pressure — CO₂')

ax.legend()

ax.grid(True)

plt.tight_layout()

plt.savefig('pressure_comparison.png', dpi=150)

plt.show()

print("Plot 1 saved.")

── Plot 2: Percent Error by Trial ───────────────────────────

fig, ax = plt.subplots(figsize=(10, 5))

ax.plot(trial_ids, pct_ideal_list, 'b^--', label='Ideal Gas Law % Error')

ax.plot(trial_ids, pct_vdw_list, 'rs--', label='Van der Waals % Error')

ax.axhline(y= SENSOR_ERROR_PCT, color='gray', linestyle=':', label='+1.5% sensor limit')

ax.axhline(y=-SENSOR_ERROR_PCT, color='gray', linestyle=':', label='−1.5% sensor limit')

ax.axhline(y=0, color='black', linewidth=0.8)

ax.set_xlabel('Trial ID')

ax.set_ylabel('Percent Error (%)')

ax.set_title('Model Percent Error vs Sensor Accuracy Band — CO₂')

ax.legend()

ax.grid(True)

plt.tight_layout()

plt.savefig('percent_error_comparison.png', dpi=150)

plt.show()

print("Plot 2 saved.")

============================================================

STEP 6 — Summary