Metadata-Version: 2.4
Name: costingfe
Version: 0.1.0a1
Summary: JAX-native fusion power plant costing framework
Project-URL: Homepage, https://github.com/1cFE/1costingfe
Project-URL: Issues, https://github.com/1cFE/1costingfe/issues
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.10
Requires-Dist: jax>=0.4.0
Requires-Dist: jaxlib>=0.4.0
Requires-Dist: pydantic>=2.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: dev
Requires-Dist: matplotlib>=3.7; extra == 'dev'
Requires-Dist: pre-commit>=3.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff==0.12.0; extra == 'dev'
Description-Content-Type: text/markdown

# 1costingfe

JAX-native fusion power plant costing framework. Replaces the PyFECONS adapter in the fusion-tea SysML pipeline with a differentiable, 5-layer customer-first model.

## Install

```bash
pip install -e .
# or with dev dependencies:
pip install -e ".[dev]"
```

Requires Python 3.10+.

## Quick Start

```python
from costingfe import CostModel, ConfinementConcept, Fuel

# Create a model for a DT tokamak
model = CostModel(concept=ConfinementConcept.TOKAMAK, fuel=Fuel.DT)

# Forward costing: customer requirements -> LCOE
result = model.forward(
    net_electric_mw=1000.0,
    availability=0.85,
    lifetime_yr=30,
)

print(f"LCOE: {result.costs.lcoe:.1f} $/MWh")
print(f"Overnight cost: {result.costs.overnight_cost:.0f} $/kW")
print(f"Fusion power: {result.power_table.p_fus:.0f} MW")
```

## Sensitivity Analysis (JAX autodiff)

```python
sens = model.sensitivity(result.params)

# Engineering levers (sorted by |elasticity|)
for k, v in sorted(sens["engineering"].items(), key=lambda x: abs(x[1]), reverse=True):
    print(f"  {k:25s} {v:+.4f}")

# Financial parameters
for k, v in sens["financial"].items():
    print(f"  {k:25s} {v:+.4f}")
```

Elasticity = (dLCOE/dp) * (p/LCOE) -- dimensionless, comparable across parameters.

## Batch Parameter Sweeps (JAX vmap)

```python
# Sweep blanket thickness from 0.5m to 1.0m
lcoes = model.batch_lcoe(
    {"blanket_t": [0.5, 0.6, 0.7, 0.8, 0.9, 1.0]},
    result.params,
)
```

## Cross-Concept Comparison

```python
from costingfe import compare_all

results = compare_all(net_electric_mw=1000.0, availability=0.85, lifetime_yr=30)
for r in results[:5]:
    print(f"  {r.concept.value:15s} {r.fuel.value:5s} {r.lcoe:6.1f} $/MWh")
```

## Backcasting

```python
from costingfe.analysis.backcast import backcast_single

# What availability achieves 60 $/MWh?
avail = backcast_single(
    model, target_lcoe=60.0, param_name="availability",
    param_range=(0.70, 0.98), base_params=result.params,
)
```

## Cost Overrides

Override any CAS account or CAS22 sub-account with a known value (M$). Downstream totals (CAS20, total capital, LCOE) recompute automatically.

```python
# Override an entire CAS account
result = model.forward(
    net_electric_mw=1000.0,
    availability=0.85,
    lifetime_yr=30,
    cost_overrides={"CAS21": 50.0},  # "I know my building cost"
)
assert result.costs.cas21 == 50.0
print(result.overridden)  # ["CAS21"]

# Override a CAS22 sub-account (coils)
result = model.forward(
    net_electric_mw=1000.0,
    availability=0.85,
    lifetime_yr=30,
    cost_overrides={"C220103": 300.0},  # "Use this coil cost"
)
# CAS22 total is recomputed from patched sub-accounts
print(result.cas22_detail["C220103"])  # 300.0
print(result.cas22_detail["C220000"])  # Recomputed total
```

Available CAS-level keys: `CAS10`, `CAS21`-`CAS28`.

Available CAS22 sub-account keys: `C220101` (blanket), `C220102` (shield), `C220103` (coils), `C220104` (heating), `C220105` (structure), `C220106` (vacuum), `C220107` (power supplies), `C220108` (divertor), `C220109` (DEC), `C220111` (installation), `C220112` (isotope sep), `C220200` (coolant), `C220300` (aux cooling), `C220400` (rad waste), `C220500` (fuel handling), `C220600` (other equipment), `C220700` (I&C).

Sub-line convention: an account that bundles components with distinct cost bases also emits informational `<CODE>_<component>` keys alongside the canonical total — currently `C220106_vessel` and `C220106_pump` (vessel shell vs gas-load pumping). These sub-lines are excluded from all aggregation and are for visibility/sensitivity only; the canonical `C220106` carries the sum.

CAS70 sub-accounts: `CAS71` (annual O&M), `CAS72` (annualized scheduled replacement — blanket/FW + divertor, PV-discounted at interest rate).

Sensitivity analysis works with overrides -- overridden accounts become constants with zero gradient:

```python
sens = model.sensitivity(result.params, cost_overrides={"CAS21": 50.0})
```

## Fusion-Tea Adapter

```python
from costingfe.adapter import FusionTeaInput, run_costing

inp = FusionTeaInput(
    concept="tokamak",
    fuel="dt",
    net_electric_mw=1000.0,
    availability=0.85,
    lifetime_yr=30,
)
out = run_costing(inp)
# out.lcoe, out.costs (CAS-keyed dict), out.power_table, out.sensitivity
```

The adapter supports two additional override mechanisms for the fusion-tea pipeline:

```python
inp = FusionTeaInput(
    concept="tokamak",
    fuel="dt",
    net_electric_mw=1000.0,
    availability=0.85,
    lifetime_yr=30,
    # Inject known CAS account values (M$)
    cost_overrides={"CAS21": 50.0, "C220103": 300.0},
    # Override costing constants (unit costs, fractions, etc.)
    costing_overrides={"blanket_unit_cost_dt": 1.0},
)
out = run_costing(inp)
print(out.overridden)           # ["CAS21", "C220103"]
print(out.costs["C220103"])     # 300.0 (CAS22 sub-accounts included in costs dict)
```

- `cost_overrides` -- replace computed CAS account values with known costs. Passed through to `CostModel.forward()`.
- `costing_overrides` -- override fields on `CostingConstants` (unit costs, scaling coefficients). Applied via `cc.replace()` before model construction.
- `out.overridden` -- list of keys that were injected rather than computed.

## Supported Concepts

| Family | Concept | Key features |
|--------|---------|-------------|
| MFE | `tokamak` | Toroidal confinement, TF/CS/PF coils |
| MFE | `stellarator` | Steady-state, complex 3D coils |
| MFE | `mirror` | Cylindrical, end-loss DEC opportunity |
| IFE | `laser_ife` | Split laser drivers, target factory |
| IFE | `zpinch` | Pulsed power driver |
| IFE | `heavy_ion` | Heavy ion accelerator |
| MIF | `mag_target` | Magnetized target, liner factory |
| MIF | `plasma_jet` | Plasma jet driver |

## Fuels

- `dt` -- Deuterium-Tritium (breeding blanket, heavy shielding)
- `dd` -- Deuterium-Deuterium (no breeding, moderate shielding)
- `dhe3` -- Deuterium-Helium-3 (mostly aneutronic)
- `pb11` -- Proton-Boron-11 (fully aneutronic, minimal shielding)

## Engineering Overrides

Pass any engineering parameter as a keyword argument:

```python
result = model.forward(
    net_electric_mw=1000.0,
    availability=0.85,
    lifetime_yr=30,
    eta_th=0.50,        # Override thermal efficiency
    blanket_t=0.90,     # Thicker blanket
    axis_t=7.0,         # Larger major radius
)
```

See `src/costingfe/data/defaults/` YAML files for all available parameters.

## Power Cycle Selection

Select the thermal cycle used for power conversion. This sets `eta_th` and BOP cost
coefficients (CAS23, CAS26) appropriate for the chosen cycle.

```python
from costingfe import CostModel, ConfinementConcept, Fuel, PowerCycle

# sCO2 Brayton cycle
model = CostModel(
    concept=ConfinementConcept.TOKAMAK,
    fuel=Fuel.DT,
    power_cycle=PowerCycle.BRAYTON_SCO2,
)
result = model.forward(net_electric_mw=1000.0, availability=0.85, lifetime_yr=30)
```

Available cycles:

| Cycle | `eta_th` | Description |
|-------|----------|-------------|
| `RANKINE` (default) | 0.40 | Steam Rankine (sub/supercritical) |
| `BRAYTON_SCO2` | 0.47 | Supercritical CO2 recompression Brayton |
| `COMBINED` | 0.53 | Gas topping + steam bottoming |

You can override `eta_th` or BOP coefficients independently:

```python
# sCO2 Brayton but with custom efficiency
result = model.forward(
    net_electric_mw=1000.0, availability=0.85, lifetime_yr=30,
    eta_th=0.50,  # Override preset's 0.47
)
```

## Tests

```bash
pytest tests/ -v
```

## Architecture

```
Customer Requirements (net_electric_mw, availability, lifetime_yr)
    |
    v
Layer 2: Physics (power balance, inverse for target p_net)
    |
    v
Layer 3: Engineering (radial build -> geometry -> volumes)
    |
    v
Layer 4: Costs (CAS 10-60 accounts, volume-based + power-scaled)
    |
    v
Layer 5: Economics (CAS 70-90, LCOE)
```
