Reveal.js
Copyright (C) 2011-2024 Hakim El Hattab, http://hakim.se, and reveal.js contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
KaTeX
The MIT License (MIT)
Copyright (c) 2013-2020 Khan Academy and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
COMP 521 · Software Engineering for Scientists
Software Engineering for Scientists
COMP 521
Anna Rosen · San Diego State University
Why This Matters
Most scientists learn to code by trial and error.
This works for small scripts, but fails for:
Code that must be correct (not just “seems to work”)
Code that others must read and trust
Code you must debug at 2am before a deadline
The Core Insight
Think → Plan → Code → Check
Plan the check before coding; run it after implementing.
Principles for scientific software
The 11 Commandments
Read these every week until they become instinct
Commandment 01 / 11
Think Before You Type
1. Think Before You Type
“Let me just start coding and figure it out as I go.”
Write one expected result before implementing.
Predict
What should mean([2, 4]) return?
Explain
3: add the two values and divide by two.
Commandment 02 / 11
Write Down the Contract
2. Write Down the Contract
Before coding, answer in writing:
Question
Example
What are the inputs?
mass: float, [0.1, 100] M⊙
What are the outputs?
luminosity: float, L⊙
What could go wrong?
Negative mass, wrong units
How will I validate?
L(1.0) ≈ 0.698
Predict
Which contract clause rejects luminosity(0)?
Explain
The input range: mass must be at least 0.1.
Commandment 03 / 11
Assume Your Code is Wrong
3. Assume Your Code is Wrong
Support correctness claims with tests and validation evidence.
Not “it runs without errors.”
Not “the output looks reasonable.”
Show me the evidence.
Three Kinds of Evidence
Name the claim a check probes. The command that runs it does not determine its evidence type.
Bad Repo vs Good Repo
Students usually copy structure before they copy principles.
Reproducibility means another person can regenerate the reported results from the saved code, inputs, environment, and documented steps. Check that workflow from a clean clone.
Commandment 04 / 11
Fail Fast
4. Fail Fast
Predict
For mass = float("nan"), what do mass < 0.1 and mass > 100 return?
Both return False. Require the valid range instead:
Reject invalid mass
def luminosity(mass, Z=0.02):
# Check inputs FIRST
if not 0.1 <= mass <= 100:
raise ValueError("Mass must be in [0.1, 100] M_sun")
# ... then compute
A clear error at the input beats garbage output that “looks reasonable.”
Commandment 05 / 11
Plot First, Not Last
5. Plot First, Not Last
In software engineering: working software is progress.
In scientific computing: plots are currency.
Diagnose — Where does it go wrong?
Validate — Does the pattern match known trends?
Communicate — Papers, talks, reports
Predict
Before plotting y=x2: where is the minimum, and what should x=−2 and x=2 give?
Explain
The minimum is (0,0); both inputs give y=4. Check these predictions on the plot.
The Anti-Pattern
Most students:
Write all the code
Debug until it runs
Make plots at the end
Discover something is fundamentally wrong
Start over 😱
The Right Way
Plot as soon as you have plottable output:
Workflow
luminosity() -> Plot L vs M -> Look right?
v YES
radius() -> Plot R vs M -> Look right?
v YES
T_eff() -> Plot HR diagram -> Look right?
v YES
Continue...
Each plot is a checkpoint.
Commandment 06 / 11
Test Requirements, Not Code
6. Test Requirements, Not Code
Bad test:
Python example
def test_luminosity():
L = luminosity(1.0)
assert L == luminosity(1.0) # Tautology!
Good test:
Python example
def test_luminosity_solar_mass():
L = luminosity(1.0)
assert L == pytest.approx(0.698, rel=0.02)
Commandment 07 / 11
Debug with Hypotheses
7. Debug with Hypotheses
“I think X is wrong because Y”
Expected: f(x) = x**2. Observed: f(2) returns 2.
Hypothesis: the implementation is missing the square.
Changing that operation predicts f(2) == 4. Check this input before changing anything else.
The Binary Search Method
If you don’t know where the bug is:
Check output at the end — wrong?
Check output at the middle — wrong?
Yes → Bug in first half
No → Bug in second half
Repeat until found
With ordered checkpoints and a failure you can localize to one half, the number of checkpoint decisions grows as O(logn). Arbitrary bugs do not automatically satisfy that assumption.
Common Bug Symptoms
Symptom
Likely cause
Off by factor of ~2.3
log vs log10
Off by powers of 10
Unit conversion
Wrong sign
Subtraction order
NaN or Inf
Division by zero, invalid domains, overflow, or earlier nonfinite input
Shape mismatch
Scalar vs array
Commandment 08 / 11
Delete Bad Code
8. Delete Bad Code
You’ve spent 3 hours on a function. It’s ugly and buggy.
“I’ve already put so much time into this...”
This is the sunk cost fallacy.
Those 3 hours are gone. What’s fastest from here?
Permission to Delete
You have permission to:
Delete functions that aren’t working
Rewrite modules that got tangled
Throw away your first approach entirely
Start fresh with what you learned
Before deleting, preserve the failing input and its test. Git protects content saved in a commit; untracked and ignored files need separate protection.
Commandment 09 / 11
One Source of Truth
9. One Source of Truth
Bad:
Python example
# file1.py
SOLAR_MASS = 1.989e33
# file2.py
M_SUN = 1.989e33 # Same value, different name!
If you change SOLAR_MASS in file1.py, will file2.py follow?
Good:
Python example
# constants.py (the ONLY place)
MSUN = 1.989e33
# In each caller
from constants import MSUN
Commandment 10 / 11
Read More Than You Write
10. Read More Than You Write
Professional developers spend more time reading than writing.
Before changing code:
Understand what it does now
Understand why it was written that way
Then modify
Commandment 11 / 11
Commit Before You Experiment
11. Commit Before You Experiment
Git commands
git status
git diff
git add path/to/changed_file.py
git commit -m "Save working baseline before experiment"
Committed: model.py. Untracked: notes.txt. Which file can you recover?
Only model.py. A commit protects the content staged in it.
Committed content can be recovered. Uncommitted or ignored files are not protected by a commit; restoring a file replaces its current uncommitted contents.
Recover a useful next step
Getting Unstuck
The Walk Away Rule
If you’ve been stuck for 30+ minutes:
Stop typing
Go do something else — walk, shower, eat
Come back with fresh eyes
This isn’t procrastination. Your brain works on problems in the background.
Rubber Duck Debugging
Before asking for help, explain the problem out loud.
To a rubber duck. A stuffed animal. An empty chair.
The act of articulating often reveals the solution.
How to Ask for Help
Bad:
“My luminosity function gives wrong values. Help?”
Good:
“My luminosity(1.0) returns 0.45, but should be ~0.698.
Here’s my code: [10 lines].
I checked: coefficients, log10 vs ln, units.
What am I missing?”
Put the principles into practice
The Professional Workflow
The 5-Step Process
Understand — Read specs, identify requirements
Plan — Write contracts, identify validation
Implement — One function at a time, validate immediately
Test — Encode requirements as tests
Debug — Systematically, with hypotheses
The Professional Loop
Spec → Contract and planned checks → Implement → Run checks and diagnostics → Interpret → Iterate.
The Key Insight
Debug your understanding, not your code.
Understanding the problem gives you a contract and predictions to test. Implementation still requires careful work.
When repeated patches fail, revisit the assumptions and the smallest case you can explain by hand.
Worked example: specify the result first
We want the arithmetic mean of a nonempty sequence of finite real values.
Input: a sequence of finite numbers; the sequence is not modified.
Output: the arithmetic mean in the same units as the input.
Empty or nonfinite input: raise ValueError.
Independent checks: [1, 3] gives 2; one value returns itself.
This small example lets us practice the workflow before applying it to a numerical solver.
Predict the result for a one-element input before running it.
The result is finite and the program runs. Neither observation establishes correctness.
Worked example: test a causal hypothesis
Hypothesis: the denominator uses n - 1 instead of n.
For [1, 3], the sum is 4. The correct denominator is 2, so the mean is 2.
Changing only that denominator predicts that this test will pass. It does not yet address empty or nonfinite input.
Test the prediction first, then add the missing contract checks.
Worked example: implement the contract
A checked arithmetic mean
import math
def finite_mean(values):
values = tuple(values)
if not values:
raise ValueError("At least one value is required")
if not all(math.isfinite(x) for x in values):
raise ValueError("All values must be finite")
return math.fsum(values) / len(values)
The numerical range must also be representable in the chosen floating-point type. This is a teaching example, not an overflow-proof reduction.
Worked example: independent checks
Check requirements, not implementation
assert finite_mean([1.0, 3.0]) == 2.0
assert finite_mean([7.0]) == 7.0
assert finite_mean([4.0, 6.0]) == 5.0
import pytest
for values in ([], [float("nan")],
[float("inf")], [-float("inf")]):
with pytest.raises(ValueError):
finite_mean(values)
The translated inputs [4, 6] shift the mean by 3. Each check has an expected result independent of calling the implementation twice.
Transfer the workflow to your project
Write the input, output, domain and failure contract.
Pick one reference case you can calculate independently.
Run the smallest implementation that can fail that check.
State a hypothesis, predict the outcome, and change one cause.
Check limits, convergence and diagnostics relevant to the method.
Save the configuration, commands and evidence needed to reproduce it.
Keep the original stellar examples as a second context: the same workflow applies, but the reference values and validity domain come from that model.
The Takeaway
Think → Plan → Code → Check
Plan the check before coding; run it after implementing.