The Ghost in My Stirling Engine: When Code Runs Perfectly But Physics Doesn't
Thereâs a particular flavor of debugging frustration that hits different: when your code runs without errors, produces reasonable-looking output, and yet something is fundamentally wrong. I spent several hours with Claude Code working on a Stirling engine simulation for a mechanical engineering class, and the journey taught me something valuable about the difference between code that works and code thatâs correct.
The Setup
The project involves simulating a beta-type Stirling engineâa heat engine that converts thermal energy into mechanical work. The core challenge is calculating how gas volumes change as the power piston and displacer move through their cycles, then using those volumes to determine pressure, torque, and ultimately flywheel sizing.
I had working MATLAB code. It ran without errors. It produced P-V diagrams that looked plausible. The numbers were in reasonable ranges. But the work output was about 30% higher than what the textbook example predicted for similar engine parameters. At first I chalked it up to different assumptions, but the nagging feeling wouldnât go away.
The Problem Nobody Asked About
The issue surfaced when I asked Claude a simple question about a plotting line:
plot(results.V_total*1000, results.P/1e6, 'b-', 'LineWidth', 2);
âWhere does results.V_total and results.P come from?â
This innocent question led us through a multi-hour investigation. Tracing the data flow revealed that while the values were being calculated correctly according to the codeâs logic, the logic itself had a conceptual problem: the displacer volume wasnât being accounted for anywhere.
Hereâs the thing about a beta-type Stirling engine: the displacer is a solid cylinder that shuttles back and forth inside the main cylinder, pushing gas between the hot end and cold end. It doesnât change the total gas volumeâthatâs the power pistonâs job. But the displacer itself takes up space. When it moves toward the hot end, it physically displaces gas (hence the name) into the cold region. The displacer in this simulation swept through about 15 cmÂł, which should reduce the available volume in whichever chamber it currently occupies.
My code calculated where the gas should go based on displacer position:
volumeSplitFactor = 0.5 * (1 - cos(pi * normalizedDisplacerPosition));
But it never subtracted the displacerâs physical volume from the total. The displacer was a ghostâinfluencing gas distribution without taking up any room.
The HTML Documentation Breakthrough
When I couldnât pinpoint exactly where the physics diverged from the implementation, I asked Claude to create an HTML file explaining every step of the volume calculation with full mathematical expressions. This turned out to be the breakthrough.
The documentation laid out the model clearly:
Mathematical Model (what should happen): \(V_{total}(θ) = V_{clearance} + V_{swept,piston}(θ) - V_{displacer}(θ)\)
Code Implementation (what actually happened):
V_total = V_clearance + V_swept_piston; % Where's V_displacer?
Seeing these side by sideâthe formal math saying âsubtract the displacer volumeâ and the code conspicuously not doing thatâmade the bug impossible to miss. I actually said âoh noâ out loud when I saw it. The comment in my code even said âdisplacer position affects gas distribution,â which is true, but the implementation only handled the distribution part, not the volume part.
That missing subtraction meant my simulation had more working gas than a real engine would, explaining the inflated work output. The fix took about ten minutes once I understood the problem. The debugging took most of an afternoon.
What I Learned
Tracing data flow reveals assumptions. When you ask âwhere does this value come from?â and keep asking until you hit first principles, you find the places where assumptions were made. Some of those assumptions are fine. Some arenât.
Running without errors isnât the same as being correct. Engineering simulations are tricky because wrong answers often look plausible. A P-V diagram with the wrong shape is still a closed curve. Work output thatâs 30% high just looks like an optimistic engine.
Documentation as debugging. Writing out the mathematical model in a separate documentânot just comments in codeâforces you to be explicit about what each equation represents. My intuition that something was off came from years of physics classes drilling in conservation laws. When the numbers felt too good, that physical intuition said âfree energy doesnât exist, so whereâs the leak in your model?â
AI assistants excel at tracing, less so at questioning. Claude Code helped me trace values through the codebase efficiently. But when I asked about the displacer volume and got an answer that technically addressed the code but not the physics, I felt a flicker of dissatisfactionâlike when someone answers your question grammatically but not substantively. Thatâs when I knew to push harder.
The Takeaway
Next time, Iâll write out the full mathematical model in LaTeX or on paper before coding, then compare equation-by-equation. A simple checklistââDoes my code include every term in equation 3?ââwould have caught this in minutes instead of hours.
The ratio of ten-minute fix to afternoon-long debug feels about right for conceptual bugs. The hard part is never the correctionâitâs seeing what youâve been looking past. Sometimes your code needs a ghost hunter more than a debugger.