Repository navigation
spacecost 0.5.0
Data contract 1.16.0 → 1.17.0: every fairing volume is now derived from a
cited drawing, or left blank with a reason, and values moved. A consumer
reading fairing_volume_m3 should take the release that carries this and
re-run. economicspace repins to this tag. The tests, CI, docs and
build-floor changes below it moved no name, table or number.
Fairing volumes are read off a drawing, or left blank
Until now, 37 of the 76 launch rows had no fairing_volume_m3, and the other
39 typed one with no source. A consumer that filled the blanks with a default
was pricing those rows on a number nobody had chosen. economicspace's Stage 4
used 100 m³, and SLS Block 1 won 332 rows of a cislunar sample on it. The
column is the usable payload envelope, and the new spacecost/fairings.py
derives it at import. A row that types it raises.
guide: the envelope a user's guide draws with its dimensions printed,
revolved about the axis. Arcs with a printed radius are followed, cones take
their top diameter from a printed half-angle, and an unprinted top closes to
a point.published: the maker states the usable volume (LVM3, Gravity-1,
Saturn V).estimate: only the outer diameter and length are published. The
volume is that cylinder timesFAIRING_FILL_RATIO, the median envelope fill
of the guide rows that print both (0.482, range 0.27-0.63). It is derived,
never typed.none: NaN, with the reason innotes. Most Chinese commercial
vehicles publish a diameter only.
The method reproduces the two totals a guide prints, and import asserts both:
New Glenn's 458 m³ and SLS Block 1B's 621 m³. New column fairing_basis, and
every launch row's notes gains a closing Fairing volume: sentence naming the
document and figure. Sources are in CITATIONS.md.
Every row whose volume moved:
| row | status | was | now | basis |
|---|---|---|---|---|
| Falcon 9 (reusable) | operational | 145 | 157 | guide |
| Falcon 9 (expendable) | operational | 145 | 157 | guide |
| Falcon Heavy (reusable side cores) | operational | 145 | 157 | guide |
| Falcon Heavy (expendable) | operational | 145 | 157 | guide |
| SLS Block 1 | operational | blank | 216 | guide |
| Atlas V 551 | operational | 233 | 127 | guide |
| Vulcan Centaur VC2 | operational | 233 | 171 | guide |
| Vulcan Centaur VC4 | operational | 233 | 171 | guide |
| Vulcan Centaur VC6 | operational | 233 | 171 | guide |
| New Glenn | operational | 480 | 458 | guide |
| Electron | operational | 1.85 | 1.19 | guide |
| Alpha | operational | 22 | 12 | guide |
| Minotaur IV | operational | blank | 13.4 | guide |
| Soyuz-2.1a | operational | 80 | 75.1 | guide |
| Soyuz-2.1b | operational | 80 | 75.1 | guide |
| Angara A5 | operational | blank | 127 | estimate |
| Proton-M | operational | blank | 91.1 | guide |
| Ariane 6 (A62) | operational | 124 | 140 | guide |
| Ariane 6 (A64) | operational | 124 | 245 | guide |
| Vega C | operational | 47 | 38.8 | guide |
| H3 (24L) | operational | 184 | 168 | estimate |
| H3 (30) | operational | blank | 107 | estimate |
| PSLV-XL | operational | 34 | blank | none |
| Long March 2C | operational | blank | 35.7 | guide |
| Long March 2D | operational | blank | 29.7 | estimate |
| Long March 3B/E | operational | blank | 63.7 | guide |
| Long March 5 | operational | 157 | 126 | estimate |
| Long March 7 | operational | 111 | blank | none |
| Long March 10B | operational | blank | 128 | estimate |
| Ceres-1 | operational | blank | 3.41 | guide |
| Gravity-1 | operational | blank | 100 | published |
| Zhuque-3 | operational | 190 | blank | none |
| Pallas-1 | operational | blank | 75.4 | guide |
| Delta IV Heavy | retired | 310 | 216 | guide |
| H-IIA 204 | retired | 122 | 82.5 | guide |
| Pegasus XL | retired | blank | 2.04 | guide |
| Ariane 5 ECA | retired | blank | 209 | guide |
| Vega | retired | blank | 20 | guide |
| Saturn V | retired | blank | 91.5 | published |
| Starship (projected) | development | 1000 | 684 | guide |
| Neutron | development | 113 | 183 | guide |
| Terran R | development | 340 | blank | none |
| Nova | development | 80 | blank | none |
| Eclipse (MLV) | development | 160 | blank | none |
| Tianlong-3 | development | 150 | blank | none |
| Long March 10 | development | 310 | blank | none |
| Long March 9 | development | 1000 | blank | none |
| SLS Block 1B (Cargo) | concept | 340 | 621 | guide |
| SpinLaunch Orbital | concept | 0.6 | blank | none |
| Light-gas gun (orbital) | concept | 0.05 | blank | none |
| StarTram (maglev) | concept | 200 | blank | none |
| Skylon / SABRE | concept | 140 | 212 | guide |
| Sea Dragon | concept | 6000 | blank | none |
IV Heavy's 310 m³ and Starship's 1,000 m³ read as outer volumes; the drawn
envelopes are 216 and 684. Atlas V 551 is quoted with the 5-m Short fairing,
not the 5-m Long that 233 m³ implies. SLS Block 1 takes the 5-m Delta IV
fairing NASA documents for a Block 1 cargo flight (ESD 30000 s6.2.1). That
configuration has never flown, and the row's note says so.
7, Zhuque-3 and several development and concept rows) had no source for it. A
consumer that defaults a blank now defaults these too.
The consumer contract follows what economicspace reads
tests/test_consumer_contract.py now lists what economicspace actually
reads, found by grepping it for spacecost.<name> on its main (master
v1.35.0) and on its pending master v1.36.0. It had mirrored that repo's Stage
3 adapter, which re-exports most of __all__, and so had missed what Stage 2
reads directly:
- added:
LEO_LAUNCH_VEHICLE,delivery_mass_ratioand
delivery_hardware_usd_per_kg, which Stage 2 reads, and
ENVIRONMENTS_REFERENCE, which the Stage 3 adapter re-exports and its
banner counts. - new,
SUBMODULE_SURFACE: four names reached through a submodule, which
__all__does not cover (delivery's three hardware rates, read by the
worked calculation, andprices.merge_propellant_prices, read by
verify_stage3.py). - new,
REEXPORTED_ON_CONSUMER_MAIN: ten names the adapter on
economicspace's main re-exports at import and nothing there reads:
LITRES_PER_GAL,LITRES_PER_BBL,COMMODITY_DENSITY_KG_PER_L,
load_storage,load_environments,propellant_mass_for_dv,
cost_per_dv_usd_per_kg,build_transportation_summary,
cheapest_launch_toandmission_cost_breakdown.
master v1.36.0 stops re-exporting them. v1.36.0 is on an unmerged branch.
economicspace's main still assigns every one at import, so a contract taken
from the branch would have let this package delete a name the released
consumer cannot import without. They stay until v1.36.0 merges there. The
tenth, load_environments, had never been listed.
CI now asks the contract of the built wheel, from outside the tree, as
well as of the source. The wheel step walked __all__ alone, so it could see
neither a name deleted together with its __all__ entry nor anything reached
through a submodule. Checked both ways: the step passes with the tree broken
and the wheel intact, and fails naming spacecost.delivery.STAGE_HARDWARE_USD_PER_KG
when that one name is removed from the installed wheel.
An audit of the prose against the code
Four README passages still described an earlier release, each beside a
correct one:
- The delivery example printed
26813.64forlunar_surface. The model
has said 42,635 since v0.4.0, and the table two screens further down
says so.tests/test_docs.pynow runs every Python block in the README, as
a doctest with ELLIPSIS where it shows>>>, so the example is held to the
code rather than to memory. It stops its digits short because every figure
in it is anexp(). - "The launch price ... at the low end of its band." Since v0.4.0 the
delivery chains read the headline, the band's centre, and the section below
that table says so. Now it says centre. - "The downleg departure delta-v: typed." That was v0.3.0. Since v0.3.1
four of the six areDELTA_V_REFERENCElookups. Three figures are still
typed: the LEO deorbit, the GEO deorbit and the lunar TEI component. - "All 64 bracketed rows". v0.4.0's
Expendable upper stage recurring costmade it 65. A test now holds the figure to the tables.
Smaller: LH2 carries about twenty times kerolox's tank per kilogram
burnt, not fourteen. Fourteen is the volume ratio, before the deep-cryogen
tank multiplier. The package docstring promised all seven CSVs "byte for
byte", which the README declined to promise for the summary; it now states
both contracts. And it and the README now say Stage 2 reads this package too.
The strictest test had never run in CI
test_summary_hash_on_the_reference_platform holds the summary's BYTES, and
it skips on any host that is not the recorded platform: Windows, AMD64,
Python 3.13, numpy 2.2.6, pandas 2.3.3. The matrix runs 3.9, 3.12 and
3.14 on the newest numpy and pandas, so no leg ever matched. The test was
skipped on every run, which is the rot the README's "Changing a row" warns
about, reached a different way: not a stale block, but a matrix that moved
around a correct one.
A new CI job, reference-platform, reads that block out of
summary_meta.json, installs exactly those versions, and runs
tests/test_parity.py with SPACECOST_REQUIRE_REFERENCE_PLATFORM=1, under
which a platform mismatch FAILS instead of skipping. Because the job reads the
block, re-recording the platform moves the job with it. Its first run passed
the hash test outright: 17 passed, none skipped.
exp() kernel by CPU at run time; an AVX512 host takes numpy's own AVX512F
implementation. The platform was recorded on an AVX2-only machine and the
first run drew an AVX2-only runner. Hosted runners vary, so the job sets
NPY_DISABLE_CPU_FEATURES to switch AVX512 off, and prints numpy's dispatch
so a mismatch can be read from the log.
Nothing in reference/ moved. The recorded hash still reproduces off the
reference platform too: Windows, Python 3.14, numpy 2.5.2 and pandas 3.0.5 on
an AVX2 machine give the same bytes.
The build floor, and two things CI would have tripped on
setuptools>=68could not build this package. The PEP 639
license = "MIT"string needs 77: 76.1.0 rejectspyproject.tomland 77.0.3
builds it, both tried. The floor is 77 now. pip's isolated build always
fetched a newer one, which is why it never showed.- The Python 3.9 leg moves to
ubuntu-24.04.ubuntu-latestbecomes
26.04 from 2026-10-19, and there is no 3.9 build for it, so that leg would
have gone red with nothing here changed. The other Linux legs still track
latest. actions/checkoutv5 andactions/setup-pythonv6, the first majors on
Node 24. Every run was carrying a warning that the v4 / v5 pair was being
forced off the deprecated Node 20.
spacecost launch prints the whole band
It printed the headline and the LOW end of its range, which since v0.4.0 is
half a band around a centre, and all three payload columns whichever
destination was asked about. It now prints the destination's payload, then
_low, headline and _high, and price_basis, which is what says whether a
price is the launcher's own or somebody's estimate. New Glenn, cheapest on the
open market at $1,922/kg, reads reported there, and that is the whole reason
the delivery chains do not anchor on it. Output only; the ranking is
unchanged.
cheapest_launch_to with an unknown destination still raises KeyError, as
it did, but the message names the three it accepts instead of echoing the bad
key back.