Repository navigation
Releases: loggger101/spacecost
Release list
spacecost 0.6.0
0.6.0 - 2026-10-06
Data contract 1.17.3 → 1.18.0: four values re-pinned to their sources.
Every one is a General_Research revision candidate whose source the registry
read and whose number the row's own method, applied correctly, moves. Each is
named.
- Atlas V 551 (rc-051,
launch_vehicles.csv): $153M is ULA's 2016
RocketBuilder base, and the row is labelled 2026 dollars. Carried by CPI-U
(Dec 2016 to Aug 2026, x1.3771) it is $211M, $11,215/kg to LEO where it
was $8,132. - Minotaur IV (rc-050): the high end carried ~$50M (2010) forward at x1.45,
an annual CPI chain that stopped at 2024. CPI-U to Aug 2026 is x1.5155, so
the high end is $76M and the band centre $61.6M where it was $60M. - Pegasus XL (rc-052): $40M (2017), unescalated, becomes $54M at CPI-U
x1.3484, $121,076/kg. The row isretired, so nothing searching operational
vehicles reads it. - Deep Space Network time (rc-030,
operational_costs.csv): the row
carried a superseded FY09 MOCS rate ($1,057) forward to $1,530/hr. JPL's DSN
Services Catalog 820-100 Rev H (Jun 2022) gives the hourly rate base as
$1,792; at CPI-U to Aug 2026 that is $2,082/hr for one 34-m antenna and
~$6,245 for a 70-m. The 1,000-4,000 band already held it and stays.
A column-by-column comparison against 0.5.3 differs in those four rows (prices,
the $/kg columns derived from them, and their notes), in 35
summary_sample.csv rows that fly Atlas V or Minotaur IV, and in
pipeline_version. Nothing else moved. The summary hash was re-recorded on the
reference platform (Windows AMD64, Python 3.13, numpy 2.2.6, pandas 2.3.3).
validate()'s flying-fleet launch band rises from $100-$100,000 to
$100-$200,000 per kg to LEO. Pegasus XL at its 2026 price, $121,076/kg, is a
real vehicle at a real price (NASA paid $127,088/kg for ICON in 2017 dollars),
and a band that fires on a true row stops being read. The concept band is
unchanged.
Not taken, and recorded as owner decisions in the registry: raising Falcon
Heavy (expendable)'s band to NASA's $178M Europa Clipper price (rc-022) and
anchoring Pegasus on NASA's ICON price (rc-078) would move a commercial list
price toward a government full-service one, which this table keeps apart
elsewhere (Atlas V's note). The lunar descent row (rc-016) is right to move and
moves every archived lunar price with it, so it waits for a release that
versions the delivery chain.
spacecost 0.5.3
0.5.3 - 2026-10-06
Data contract 1.17.2 → 1.17.3: four notes cells now say what their sources
actually hold. No value moved. Only notes cells, the CITATIONS table and
the pipeline_version stamp changed; every number in every table is the one
0.5.0 shipped. economicspace repins to this tag only to carry the corrected
notes. Each item names its General_Research revision candidate.
- 16 Psyche (rc-068,
environments.csv): 2.29e19 kg is the Kretlow (2020)
SiMDA perturber mass, as Kretlow 2022 (A&A 668:A141) Table 2 lists it, not a
Siltala & Granvik 2021 figure. Their GM of 1.482 ± 0.052 km3/s2 (2.22e19 kg,
3% lower) is now quoted beside it, and the ~111 km radius stays attributed to
them, from the 222 ± 4 km diameter they adopt. - 65803 Didymos (rc-067,
environments.csv): neither 5.28e11 kg nor 390 m
is in Daly et al. 2023, whose Table 1 gives (5.6 ± 0.5)e11 kg for the binary
system and a 761 ± 26 m volume-equivalent diameter. Both cells sit inside
that 1-sigma, so they stay, and the note now says their own source is not
recorded rather than naming one that does not hold them. - 1-sol Mars orbit → Earth (TEI) (rc-070,
delta_v_segments.csv): 900 m/s
is now labelled the minimum-energy Hohmann floor. NASA DRA 5.0's
all-propulsive TEI from the same orbit is 1.563 km/s in all eight 2031-2046
opportunities (Fig 4-2), because DRA flies a fast return; the floor is kept,
so a return from this orbit is priced at its cheapest. - Mars entry → surface (retroprop) (rc-072,
delta_v_segments.csv): DRA
5.0 Table 4-3 (595 m/s descent plus 15 m/s deorbit for a 110-t lander) is
now cited as the institutional figure. It sits inside the note's own 0.5-1.0
km/s band, so 800 stays as the conservative side of it.
The summary hash was re-recorded on the reference platform (Windows AMD64,
Python 3.13, numpy 2.2.6, pandas 2.3.3), which reproduced the 1.17.2 hash
first. A column-by-column comparison of all seven committed files against
0.5.2 differs in notes (4 cells) and pipeline_version only.
spacecost 0.5.2
Data contract 1.17.1 → 1.17.2: twenty-one notes cells now say what their
sources actually hold. No value moved. Only notes cells, one code comment
and the pipeline_version stamp changed; every number in every table is the
one 0.5.0 shipped. economicspace repins to this tag only to carry the
corrected notes.
All of it comes from the General_Research evidence registry, which read the
cited documents and found the attribution, not the number, wrong. Each item
names its revision candidate.
A figure attributed to a document that does not contain it
- NASA DRA 5.0, four times (
delta_v_segments.csv). The 2.65 km/s Mars
arrival v-infinity on the MOI row (rc-017) and the 2,100 m/s LMO
trans-Earth injection (rc-073) are two-body derivations this table already
shows its working for; DRA carries neither, and its only TEI is 1.563 km/s
from the 1-sol orbit. The 4,100 m/s Mars ascent (rc-074) appears nowhere in
DRA, which sizes its ascent vehicle by mass and engines, so that row is now
labelled an unsourced estimate. And the TMI note's "3.6-4.3 km/s" swing
(rc-071) is DRA's Fig 4-2 read wrongly: 3.545-4.103 km/s over 2028-2046,
departing a 407-km orbit rather than the row's 200 km. The 3,600 cell is the
low end and stays. - NASA SP-125 (rc-060) is an engine-design handbook with no ascent
delta-v in it; the 9,400 m/s surface-to-LEO note now cites Curtis alone. - arXiv:1406.5027 (rc-015) is a 13-object targeted survey with no
population statistic, so "median NEA Δv" pointed at nothing. The 6,500 m/s
row now cites the 6.65 km/s peak of the Benner compilation in Elvis et
al. 2011, and says the population median runs higher (8.88 km/s, ssoBFT). - ITU-R S.1003 (rc-079) is a disposal-orbit recommendation that never
says "eclipse". The GEO row's 72-minute maximum now cites ECSS-E-HB-31-01
Part 15A, and the ~45-day season is stated as geometry. - NASA ISS Facts and Figures (rc-069) says ~90 minutes and 16 sunrises a
day, not 92.7 min and ~36 min of shadow. Those are now attributed to orbital
mechanics for a 400-km circular orbit, which is what the cells encode. - The ESA Ariane 6 overview (rc-080) states payloads and no price. Both
Ariane 6 rows now say their prices are estimates with no institutional
source, since Arianespace publishes no list price.
A note whose own fact was off
- Launch insurance (rc-031): the 2023 rate reset followed the Viasat-3
and Inmarsat 6-F2 losses, not Intelsat 33e (Plane Talking Q4-2023). - RTG specific power (rc-041, rc-043): the MMRTG is 110 We at 44 kg,
2.5 W/kg (NASA OIG IG-23-010 Table 1), in bothoperational_costs.csvand
storage_systems.csv; and 1.5 kg/yr of Pu-238 is DOE's goal for 2026,
where actual output 2018-2021 was 0.77 kg against 1.5 kg planned. The
row's allocation-not-cost argument gets stronger. - Electric propulsion efficiency (rc-040): Hall thrusters run ~0.51-0.69,
the AEPS ETU-2 measurement across its throttle range. The 0.60 value and its
0.45-0.72 band already contain it. - Mission operations (rc-047): OSIRIS-REx operations obligated $210.0M
over FY2017-FY2023 ($30.0M/yr) per the Planetary Society budget dataset;
the $283M over 9 years the row was set from was the pre-return plan. 31.4 is
within 5% of the prime-mission rate and stays. - Nuclear thermal (rc-042): NERVA reactor tests through early 1968
demonstrated ~800 s; 825 s was the engine rating, and the XE engine tests had
not yet run. - ALICE (rc-044): 210 s is near the ideal value; static motors delivered
56-133 s, 27-64% of ideal. The row is gated out of runs either way. - Aerobraked LEO return (rc-053): Mars Global Surveyor flew it first, and
"~6 months" matched no campaign (Odyssey ~2.5, MRO ~5, MGS ~10). MARS_LANDED_MASS_FRACTION's comment (rc-039): on JPL's
best-estimated entry masses, MSL is 28.5% and Perseverance 30.5%, not 27.6%
and 29.8%. The constant, 0.30, is unaffected; README and CITATIONS.md quote
the new pair.
A citation that named no document
- Space Shuttle (rc-021): "Pielke & Byerly" now names its venue, Nature
472:38 (2011), with NASA OIG IG-24-001's ~$1.45B a launch as a cross-check. - Drilling / excavation energy (rc-026): "Zacny et al." now names
Metzger, Zacny & Morrison 2020 and Zeng et al. 2007. - In-space propellant transfer loss (rc-028): the 1% low end now cites a
ground LH2 transfer study, flagged as a preprint that assumes space-rated
pumps.
The summary hash was re-recorded on the reference platform (Windows AMD64,
Python 3.13, numpy 2.2.6, pandas 2.3.3, AVX512 off), which reproduced the
1.17.1 hash first. A column-by-column comparison of all seven committed files
against 0.5.1 differs in notes (21 cells) and pipeline_version only.
spacecost 0.5.1
Data contract 1.17.0 → 1.17.1: two citations that could not back their
numbers are withdrawn, and the numbers are now labelled unsourced. No value
moved. Only three notes cells and the pipeline_version stamp changed;
every number in every table is the one 0.5.0 shipped. economicspace repins to
this tag only to carry the corrected notes.
Two rows cited sources that do not hold their numbers
Both were found by the General_Research evidence registry (its revision
candidates rc-048 and rc-061).
Beneficiation / on-site processing energy(operational_costs.csv,
500 Wh/kg of refined product, range 100-2,000) cited "NASA
Money-Mass-ematics 2023". The only NASA document of that name is a 4-page
classroom worksheet for grades 7-8 about laser communications, and it never
mentions beneficiation, ISRU or processing energy. The row now says it is an
unsourced engineering estimate. No published per-kg beneficiation energy
figure has been found to replace it.HTP-98(component price ~$5/kg, which feeds theHTPand
HTP / RP-1rows ofpropellants.csv) cited "Evonik / Peroxide Propulsion
propellant-grade quotes 2024" on peroxidepropulsion.com. That domain now
serves unrelated content and the quote is gone. Three NASA peroxide-program
reports were read in its place and none carries a price. The component
comment and both rows' notes now say the price is unsourced and awaits
re-sourcing.
better has been found, not because anything confirmed them. Treat them as
judgement, the same as the other rows whose notes say so.
The summary hash was re-recorded on the reference platform (Windows AMD64,
Python 3.13, numpy 2.2.6, pandas 2.3.3, AVX512 off). That environment
reproduced the 1.17.0 hash before the change, and the new hash differs only
because of the pipeline_version column.
Also in this release, tests only: REEXPORTED_ON_CONSUMER_MAIN is retired from
tests/test_consumer_contract.py. It held ten names economicspace's Stage 3
adapter re-exported at import and nothing read, kept "until v1.36.0 merges
there". It has, and economicspace's main (master v1.37.0) references none of
the ten anywhere. CONSUMER_SURFACE was re-checked against that main by the
same grep and matches it name for name. The 0.5.0 entry below still describes
the tuple as it shipped.
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()...
spacecost 0.4.0
The launch table, re-audited and more than doubled: 36 rows to 76, every
figure given a stated range with the headline at its centre, and a new
delivered-price model built on it. Data contract 1.15.0 → 1.16.0, and
VALUES moved, so this is the direction of the rule that obliges the bump.
operational_costs gains one row (below). The other four tables are
value-for-value identical apart from their catalog_date and
pipeline_version stamps.
🚨 This release re-prices economicspace in BOTH Stage 2 and Stage 3.
Stage 2: every in-space price comes from delivered_cost_usd_per_kg, and that
model changed (next section). Stage 3: the launch CSV, the cross-join summary
(39,852 → 84,132 rows) and every $/kg a Module 4 ranking reads have moved. The
20-cell campaign and its frozen campaign/stage2/ prices were measured under
0.3.x, so nothing measured there compares across this release until it is
re-run. delivered_cost_usd_per_kg(dest, 4253.0, stage_hardware=False)
reproduces every 0.3.x delivered price bit for bit, which is what re-deriving
an old result needs. downleg_cost_usd_per_kg did not move.
Delivered prices: a new model, chosen for today's market
delivered_cost_usd_per_kg is the launch cost a kilogram already in space
avoids, and it is the miner's REVENUE. Two things were wrong with it. Both are
fixed against today's market, reading the same central figures as the
rest of the package: each launch row's headline and each cost row's value.
- The LEO price was Falcon 9 (reusable) at $4,253/kg, chosen as "the
cheapest operational figure", which it never was. It is now whatever a
stated rule selects: operational,open, priced by the launcher itself
(publishedorcontract), lowest headlineusd_per_kg_to_leo. That is
Falcon Heavy (expendable), $2,414/kg, SpaceX's $150-159M for 63.8 t. New
Glenn's $1,922 is excluded because the bottom of its range is a rival's
estimate; Starship because it does not fly. The rule is asserted at import, so a table edit
that changes the winner fails loudly instead of re-pricing every
destination. New exports:LEO_LAUNCH_VEHICLE,leo_anchor_candidates(). - Every stage a chain expends was charged for its launch and never for
being built.delivery_hardware_usd_per_kg()now charges tug dry mass at
the newExpendable upper stage recurring costrow, lander dry mass at
Surface lander recurring cost, what an entry discards at the TPS rate, and
the propellant at the hydrolox price, each at its row'svalue.
| destination | 0.3.x | 0.4.0 | of which hardware |
|---|---|---|---|
leo |
4,253 | 2,414 | 0 |
geo |
12,526 | 8,046 | 937 |
cislunar |
10,810 | 6,878 | 742 |
mars_orbit |
13,496 | 8,706 | 1,046 |
lunar_surface |
21,210 | 42,635 | 30,597 |
mars_surface |
45,105 | 184,811 | 159,209 |
Orbital destinations fall about a third; the two surfaces rise steeply,
because a lander ($200k/kg) and an aeroshell ($50k/kg) were being thrown away
for free. Two new tests hold the result
against the market: GEO may not exceed buying GTO directly and flying only the
apogee burn, and cislunar may not exceed the cheapest direct escape launch.
delivered_cost_usd_per_kg gains stage_hardware=True; with it False the
function is linear in leo_usd_per_kg again, as before.
and a hydrolox tug nobody sells as a product yet. Lunar surface at $42,635/kg
sits against real CLPS delivery at roughly $1M/kg.
New operational_costs row Expendable upper stage recurring cost,
$4,800/kg of stage dry mass: the geometric centre of a range from $1,750
(Falcon 9's mass-produced kerolox stage, $7-10M on ~4 t) to $13,400 (Centaur
III, the hydrolox stage the chains actually fly, built at a low rate).
Falcon Heavy (reusable)'s optimistic 57 t LEO is removed. It had no
source, and a headline centred on an unsourced end would carry the error into
every figure derived from it. Its band collapses onto 30 t.
Errors fixed in existing rows
These were wrong rather than stale. Each row's notes now says what it used
to carry.
- SLS Block 1B was
operational; it never flew, and NASA cancelled it
with the Exploration Upper Stage in Feb 2026. Nowconcept. Its $4.1B
was NASA OIG's per-Artemis-flight figure including Orion; it now carries
the launch-only $2.5-2.8B. Its 41 t GTO had been scaled, not published, and
is gone. SLS Block 1, which has flown twice, is added. - Zhuque-3 carried 21 t / 18.3 t LEO, which are the figures for the
enlarged ZQ-3E. The ZQ-3 that flies lifts 8 t reusable (11.8 t expendable).
Promoted tooperationalafter two successful orbital flights. - Terran R paired its 33.5 t expendable LEO with a reusable price; now
23.5 t. Nova carried 5 t as fully reusable; the figure is 3 t. - Vulcan VC6 was priced at $110M, which is ULA's starting price for the
smallest configuration. Its 7,200 kg "escape" was the GEO payload. And it
has never flown (every Vulcan to date was VC2S or VC4S), so it drops from
TRL 9 to 8 while stayingoperationalas a configuration of a flying
vehicle. - PSLV-XL carried 1,100 kg to escape. Chandrayaan-1 and the Mars Orbiter
Mission were both put into Earth orbit and raised themselves; now 0. - H3 (24L) cited the ¥5B target, which is for the H3-30.
- Soyuz-2.1b GTO was the Kourou figure; Soyuz has not flown from Kourou
since 2022. - Atlas V is still
operationalbut ULA stopped selling it in 2021; it is
now markedsold_out. - Prose: LVM3 was called human-rated (Gaganyaan has not flown crew); H-IIA was
"49 flights" (50); New Glenn's "3 successful flights" omitted that NG-3 left
its payload in the wrong orbit.
Every figure is a range, and the headline is its centre
Where a source gave a range, the row used whichever end was to hand, which for
New Glenn was the LOW price. Every row now states either one figure, where the
source gives one, or the low and high ends of a credible range, and a ranged
headline price and payload are DERIVED as the range's geometric centre,
sqrt(low x high) to three significant figures (vehicles.band_centre). A
narrow range barely moves; a wide one lands in the middle rather than at an
end. Geometric rather than arithmetic, because launch prices are uncertain by
factors: the Shuttle's $450M-$2.1B centres at $972M, not $1.28B.
The ranges themselves lean conservative, and that is where the conservatism
now lives: a target (anything not yet flying) is always the optimistic end,
never the headline. The largest LEO moves: Zhuque-3 $1,429 → $4,425/kg,
Starship $900 → $1,603 (a 35-100 t range, from what Block 2 delivered to the V3
target), Falcon Heavy reusable $1,702 → $3,333 (30 t, since SpaceX publishes no
LEO figure for that mode), Vega C $11,212 → $16,581, New Glenn $1,511 → $1,922.
Starship's tanker_flights_for_escape stays at 12, the centre of the 8-16
quoted. The full value-by-value list is recoverable with a git diff of
reference/launch_vehicles.csv.
40 new rows
Configurations people actually price: Falcon 9 and Falcon Heavy expendable,
Vulcan VC2 and VC4, H3-30. Flying vehicles that were missing: SLS Block 1,
Minotaur IV, Soyuz-2.1a, Angara A5, Proton-M, Spectrum, GSLV Mk II, SSLV, Nuri,
Long March 2C, 2D, 3B/E, 4C, 6A, 8A, 10B and 12, Kuaizhou-1A and -11, Ceres-1,
Gravity-1, Kinetica-1 and -2, Jielong-3, Zhuque-2E, Pallas-1. In development:
New Glenn 9x4, Soyuz-5, Epsilon S, Hyperbola-3. Retired, for the historical
figures a reader will meet: Pegasus XL, Ariane 5 ECA, Vega, Space Shuttle,
Saturn V.
Deliberately not added, for want of a price or payload worth citing:
Angara-1.2, Antares 330, Long March 11, 12A and 12B, Hyperbola-1, Ceres-2,
RFA One, Astra Rocket 4, and the Iranian and North Korean launchers, which are
not a market.
Schema: 20 new columns on launch_vehicles.csv
Appended after the existing 18, so no existing column moved position.
country,availability(open/restricted/government_only/
sold_out/unavailable),reusability,core_propellant,
first_flight_year,price_basis.list_price_usd_low/_highandpayload_{leo,gto,escape}_kg_low/
_high.usd_per_kg_to_{leo,gto,escape}_low/_high, derived.
usd_per_kg_to_* is now DERIVED at import, not typed. A row that
types one raises. So does a headline outside its own band, a vocabulary typo,
an unknown key, or a row that does not state availability.
payload_gto_kg and payload_escape_kg are now float columns. A
payload of NaN means "no figure published", which is not the same as 0, "does
not go there", and a column holding NaN cannot be int64. The CSV writes
5500.0 where it wrote 5500. first_flight_year is nullable Int64, so it
writes 2010, or nothing for a vehicle that has not flown.
API
cheapest_launch_to(..., purchasable_only=False)andspacecost launch --open-only: keep onlyavailability == "open". Off by default.validateholds the launch price and payload bands to the same
inside-its-own-range rule asoperational_costs.tests/test_delivery.pypinnedDATA_VERSION == "1.15.0"as a stand-in for
"delivery is not an output". It now checks that directly.
spacecost 0.3.2
A test fix, and it is a fix to the CONTRACT rather than to a number. No
package code changed, no value moved, the data contract stays 1.15.0.
🚨 0.3.1's tests claimed a cross-platform guarantee this package declines to make
tests/test_delivery.py asserted == on every platform. CI went red on the
first push, on lunar_surface and nothing else: Linux returns
21209.958393766807 where Windows returns 21209.9583937668. One ULP.
That was not a defect in delivery.py. It was the wrong contract, and this
package already had the right one written down. The output is a
contract says the six reference tables
are byte identical on every platform BECAUSE they pass values through, and the
composite summary is promised only "the same values, to a few ULP" BECAUSE it
runs through exp() -- the platform libm, which IEEE 754 does not require to
be correctly rounded. Every number in delivery.py is a mass ratio, so
every number in it is exp(). The tests were written to the consumer's
bit-identity standard and imported it into a package that says, in its own
README, that it cannot promise that here.
lunar_surface failing ALONE is the tell. It is the only chain with
two burns at different dry-mass fractions, so it compounds the most rounding;
the single-burn chains happened to land on the same float. A cross-platform
claim that holds for six of seven cases is a claim that has not been tested.
✅ Fixed with test_parity.py's own pattern rather than a new one: exact
on the platform reference/summary_meta.json was recorded on, and a 1e-12
relative tolerance elsewhere. Not a relaxation -- on the recording platform it
is the same exact comparison, and the tolerance only applies where an exact
one would assert something exp() cannot deliver.
could suffer, not by eye. Deriving TUG_ISP_S is the smallest at 2.96%;
1e-12 sits nine orders of magnitude below that and four above the observed
libm spread. Proved by feeding the checker the actual value CI returned (it
passes), the value the Isp swap would give (it fails), a 1e-9 drift (fails)
and a 1e-15 one (passes) -- and by confirming the exact comparison still bites
on the reference platform.
runs on the reference platform, its own bit-identity is unchanged and
independently verified, and it does not run this package's test suite. The
repin to 0.3.2 is so the tag it pins has a green suite, not because anything
it computes changed.
spacecost 0.3.1
Data contract unchanged at 1.15.0, no value moved, every delivery output
bit-identical to 0.3.0. An audit of what the module DERIVES against what it
merely states.
Four more lookups
The downleg departure burns were all typed at 0.3.0 on the argument that two
of six had no row. Four of them do, so four of them are lookups now:
cislunar off TLI -> NRHO insertion, mars_orbit off 1-sol Mars orbit -> Earth (TEI), mars_surface off the ascent-plus-TEI pair, and the 1,870 m/s
ascent component of lunar_surface off the descent row it is symmetric with.
Deriving what agrees and typing what does not is right; typing all six
because two could not be derived was not. That is the sharper version of the
0.3.0 note, and the correction is worth having: a blanket exception is how a
register stops being a decision.
🚨 TUG_ISP_S is 465 s and this package's hydrolox row says 452
The chains fly a 465 s cryogenic upper stage. PROPELLANTS_REFERENCE
carries hydrolox at 452 s, the RS-25 / RL-10 datasheet figure. 465 is the
top of the 450-465 s band an upper STAGE is quoted over, which is the right
figure for a tug and is what the consumer's published in-space prices were
computed with.
for 452 moves the delivered price +2.96% cislunar, +3.29% geo, +3.63%
mars_orbit, +3.65% mars_surface, +5.21% lunar_surface. Those are published
downstream. Left as it is; the discrepancy is asserted at import instead,
so the row cannot move under the comment that describes it.
The same treatment is now applied to the GEO deorbit burn, which is 1,490 m/s
against a row of 1,488: typed, and pinned to the row it disagrees with.
A register, because a derivation claim decays one literal at a time
tests/test_delivery.py reads the module's source and requires every
numeric literal to be either algebra (0 and 1) or a row on TYPED saying
which table cannot supply it. No test of the outputs can catch this: a
hardcoded 3,600 and a looked-up 3,600 give identical numbers and identical
hashes right up until the row moves and only one follows.
stopped being derived; a row with no literal is a permission still granted for
a number that has gone. Proved by planting one of each, and by planting a
literal that changes no value at all -- the case the import assertions cannot
reach.
spacecost 0.3.0
Data contract pipeline_version unchanged at 1.15.0, and that is the
headline rather than a footnote: build_catalog() writes the same seven CSVs
byte for byte, reference/ needed no regeneration, and economicspace does
not have to re-run Stage 3 for this. A repin is enough. The rule is
one-directional and this release is the other direction: new code, no new data.
A seventh module: delivery, the staged leg chains
Moved out of economicspace's modules/mineral_value.py (Stage 2), where it had
lived since that module's v1.2.0. It answers two questions:
delivered_cost_usd_per_kg(dest)— the launch cost a kilogram already in
space avoids, walked backwards through a chain of real stages.downleg_cost_usd_per_kg(dest)— capsule, TPS, a share of the recovery
campaign, and the departure burn that has to carry all three.
plus delivery_mass_ratio and stage_mass_ratio, and the constants behind
them: DELIVERY_CHAINS, DOWNLEG_DEPARTURE_DV_M_S, LEO_LAUNCH_USD_PER_KG,
MARS_LANDED_MASS_FRACTION.
Why here. The block it came from opened by admitting what it was: "Constants
below are cross-referenced to Module 3. They are duplicated rather than
imported because Module 2 runs BEFORE Module 3 in the pipeline order … If you
change one of these, change it in Module 3 too." Nine numbers retyped by hand
under a manual-sync instruction. That justification was concatenation order,
and it stopped applying the moment Stage 3 became this package: a pip-installed
package has no position in a pipeline. Nothing in the block knows what an
asteroid is.
Every chain delta-v is now a lookup, not a literal. DELTA_V_REFERENCE is
the single authority for all nine, and tests/test_delivery.py fails if a
literal creeps back in.
agree with a row exactly; a LEO deorbit burn has no row at all, and the GEO
figure disagrees with GEO -> Earth (deorbit to entry) by 2 m/s. Deriving all
six uniformly would have moved two published prices under a release that claims
to move none. They stay as literals with the mismatch tabulated beside them.
Reconciling those two is a real question and a separate release, because it
changes what a kilogram of platinum is worth at GEO and in LEO.
math.exp, not np.exp. rocket.py is the vectorised entry point and
this is the scalar one, and they are not interchangeable: the consumer argues
its releases from bit-identity and the two libraries may round the last bit
differently. Do not unify them.
Pinned bit-exact. tests/test_delivery.py holds all fourteen headline
values to full repr precision, captured by running the original
implementation before a line was edited — not a regression net taken
afterwards. An exact ==, because a tolerance would defeat the purpose.
Proved able to fail by perturbing the arithmetic, typing a literal into a chain,
and collapsing the None/[] distinction.
tests/test_consumer_contract.py, and they are imported by economicspace's
Stage 2, not its Stage 3 adapter. Stage 2 is the first stage to reach this
package, so a name dropped here now fails a pricing stage that runs before the
adapter does, and the traceback will not mention Stage 3.
requirements.txt, _MASTER_PIP_SPEC in build_master.py, _PIP_SPEC in
modules/transportation.py, the same in modules/mineral_value.py (new with
this release), and the README sentence naming it.
spacecost 0.2.0
Adds a sixth reference table and corrects a ranking that had always been wrong.
Data contract: pipeline_version 1.14.0 → 1.15.0. Every output CSV changed, because the stamp is a column on all of them. No pre-existing value moved: the five inherited tables are byte identical to the 1.14.0 build once the two provenance columns are stripped.
A sixth table: environments, 23 destinations
Solar flux, array mass factor, blackbody temperature, cycle period and eclipse fraction per destination — the power, thermal and comms penalty for being somewhere other than Earth orbit.
Behaviour change: propellantless rows are excluded by default
This moves the top of the ranking. A sail has no propellant, so its Isp is infinite, so the mass ratio is 1, so any Δv costs exactly $0.00 — and the three sail and tether rows swept the top three places of every ranking this package has ever returned, including the one spacecost propellant 6500 printed. cheapest_propellant_for now reads the two flags that exist to stop the arithmetic being believed. Pass include_propellantless=True to bring them back.
Three figures stopped being typed twice
RTG specific power, the eclipse fraction and the volatile containment mass each existed as a literal in both storage_systems and operational_costs, agreeing only because nobody had edited one yet. storage.py now reads all three from the ops rows, ops being what Stage 4 consumes and therefore the authority.
Also: validate can be heard and can fail, new schema and live-price tests, a reference row no longer reads a mutable config singleton, and dead imports removed.
Reference platform for the summary hash moved to Python 3.13 / numpy 2.2.6 / pandas 2.3.3, because that is the machine this release was cut on. It does not affect the six reference tables, which are byte identical on every platform.
Full detail in CHANGELOG.md.
pip install git+https://github.com/loggger101/spacecost@v0.2.0