Releases: Net-Zero-Horizon/OTEX
Release list
0.4.1 — Fix Rankine Hybrid
[0.4.1] - 2026-07-08
Fixed
- Rankine Hybrid cycle produced identical results to Rankine Closed.
core.cycles.RankineHybridCyclecomputed a full 8-state hybrid
(closed primary + open flash secondary) via
calculate_cycle_states, butplant.utils.enthalpies_entropies
routed the hybrid through the sameh_1..h_4-only branch as a plain
closed Rankine (states 5-8 of the flash-steam secondary were silently
dropped). Every downstream sizing/LCOE call therefore reduced to the
closed-cycle result, and the two cycles emitted byte-different h5
files with numerically identicalLCOE_nomandp_net_nom.
Added
Efficiencies.hybrid_secondary_boost(default0.10) — the
design-time power uplift attributed to the flash-steam secondary
turbine (midpoint of the 8-15 % range documented in
RankineHybridCycle). Exposed in the parameters dict as
inputs['hybrid_secondary_boost']so uncertainty and sensitivity
studies can sweep it.plant.utils.enthalpies_entropiesnow has an explicithybrid
branch that tags the returned enthalpies with
cycle_type='rankine_hybrid'.plant.sizing.otec_sizingapplies_hybrid_boostonly to
p_grossinside thep_netformula — the primary evaporator,
condenser and pump sizing use the un-boosted rating, so CAPEX stays
identical to the corresponding closed Rankine. The hybrid advantage
therefore materialises purely as ~10-15 % more electrical output at
the same capital cost, and LCOE drops by roughly the boost factor.scripts/regenerate_paper_figure_2.pyand
scripts/regenerate_paper_figure_3.py— reproducible scripts that
rebuild the case-study figures from
Data_Results/Cuba/Time_series_data_Cuba_*.h5.
v0.3.1 — fix hardcoded cycle/fluid in uncertainty analysis
[0.3.1] - 2026-05-14
Fixed
- Uncertainty / sensitivity analysis silently hard-coded the
thermodynamic configuration torankine_closed+ammonia+
offshore. Calling :class:MonteCarloAnalysis,
:class:TornadoAnalysisor :class:SobolAnalysisagainst any
other cycle (Kalina, Uehara, Open / Hybrid Rankine), working
fluid, or onshore installation produced LCOE uncertainty bounds
for the wrong plant — the per-simulation worker rebuilt
inputsviaparameters_and_constants(p_gross=..., cost_level=...)
with no other arguments, so the default cycle / fluid / install
came back regardless of what the user requested.
cycle_type,fluid_type,installation_typeand
use_coolpropare now first-class parameters on every
uncertainty / sensitivity class and are forwarded into the
per-simulation arg tuple consumed by_run_single_simulation.
v0.3.0 — climate scenarios and Mode Inverso optimisation
[0.3.0] - 2026-05-14
Added
- Mode Inverso — per-site formal design optimisation. A second
operation mode parallel to the existing forward pipeline. Instead
of asking "what's the LCOE if I install P_gross MW?", the new
otex-regional-optimalcommand answers "what plant design
minimises LCOE at each site?". The optimiser solves a 4-D
continuous NLP per site overx = (p_gross, dT_WW, dT_CW, depth_CW)via L-BFGS-B on a normalised [0,1] cube, with a
quadratic penalty method on both physical constraints (pinch
points, pipe diameter, parasitic ratio, bathymetric fit, ΔT
margin) and user-supplied exogenous caps (max AEP, CAPEX,
p_net, p_gross, or parasitic ratio) — at least one user cap is
required for an interior optimum because OTEX's modelled cost
function is monotone in p_gross. Categorical choices (cycle,
fluid, installation type) stay exogenous. The optimiser uses a
warm-start that respects the active user cap to converge 3-10×
faster than from the box centre. New moduleotex.optimization
(DesignVector,SiteContext,UserConstraints,
evaluate,optimize_site,run_regional_optimization).
Tutorial:docs/tutorials/optimal_sizing.md. End-to-end smoke
on Jamaica 2020-2023 with--max-p-gross-MW 120 --no-coolprop:
242/242 sites feasible in 87 s (0.36 s/site), median LCOE 22.63
¢/kWh vs 24.97 in forward mode @ 100 MW fixed (−8.6 %). - CMIP6 climate-scenario support (delta-method downscaling).
run_regional_analysisaccepts a non-historical scenario
(ssp126/ssp245/ssp370/ssp585) plus a target year;
OTEX pulls thetao deltas from a 3-model GCM ensemble
(MPI-ESM1-2-LR,EC-Earth3,CanESM5) via the public
Pangeo CMIP6 Zarr archive on Google Cloud, interpolates them onto
each site's coordinates, and adds them to both the warm and cold
CMEMS time series before the off-design analysis runs. Output
files include a_<scenario>_<target_year>suffix; default
historicalis a no-op equivalent to the 0.2.0 pipeline.- New module :mod:
otex.data.climate(fetch_thetao_mean,
compute_delta_field,ensemble_delta,delta_at_points). - New :class:
otex.config.ClimateConfigand three CLI flags:
--climate-scenario,--climate-year,--climate-models. - Per-(model, scenario, period, depth, bbox) Pangeo slices are
cached as Parquet under~/.otex/cache/cmip6/so subsequent
runs are sub-millisecond. - Smoke test on Jamaica 2020-2023 under SSP2-4.5 / 2050: median
LCOE drops 5.6% vs the historical baseline because surface
warming (+1.0 °C) outpaces 1062 m warming (+0.3 °C), widening
ΔT and raising AEP by ~1.8%.
- New module :mod:
Performance
otex.data.cmems._extract_year_datarewritten to drop the
np.hstack-per-match pattern that was O(M²) in memory ops.
Matches are accumulated into Python lists and concatenated once
at the end; T_water is now sliced with a single fancy-index
operation instead of one slice per cell. 6× faster on a
200×200 grid with ~2000 matched sites (1.76 s → 0.30 s),
scales linearly with grid size.
Added
- End-to-end regression test
TestRegionalPipelineE2E::test_run_regional_analysis_full_pipeline
exercises the fullrun_regional_analysispath with synthetic
CMEMS NetCDFs. Runs in ~5 seconds, asserts CSV schema, multi-year
columns, output_dir routing, and per-(site, year) row counts.
Changed
scripts/regional_batch.py::run_regionis now a thin wrapper
aroundotex.regional.run_regional_analysis. Removed ~90 LOC
of duplicated pipeline code, plus two pre-existing bugs that
would have surfaced in non-default usage:parent_dir = os.getcwd() + 'Data_Results/'(missing path
separator).pd.read_csv('CMEMS_points_with_properties.csv', ...)
referencing the bundled CSV that was deleted in 0.2.0.
scripts/regional_analysis.pydeleted entirely — was a stale
duplicate ofotex.regional.run_regional_analysiswith a
docstring still referencing the removed bundled CSV. Use
from otex.regional import run_regional_analysisdirectly.
Fixed
otex.data.cmems.download_dataignored itsnew_pathargument
and always wrote NetCDFs toData_Results/<region>/relative to
the current working directory. Bothcopernicusmarine.subset
call sites now forwardnew_pathdirectly, and the directory is
created up front. Worked by accident in the default
regional.pyflow because that path matched; broke any caller
using a custom output directory (HPC scratch dirs, batch scripts
with--output-dir, tests). Regression test added.
v0.2.0 — multi-year simulations and on-demand catalogs
[0.2.0] - Multi-year simulations + on-demand site catalog
Added
On-demand region & site catalog (replaces bundled CSVs)
- New :mod:
otex.data.regions— resolve country/territory by name or ISO
3166-1 alpha-2/alpha-3 against Natural Earth admin-0 boundaries.
Multi-part regions that cross the antimeridian (e.g. Fiji) yield
multiple :class:BBoxparts automatically. - New :mod:
otex.data.bathymetry— fetch ETOPO 2022 1-arcmin
bathymetry subsets via NOAA NCEI THREDDS OPeNDAP, cached locally
per-bbox in~/.otex/cache/bathymetry/. - New :mod:
otex.data.coastline— distance-to-shore via Natural Earth
1:50m coastlines, densified to ~2 km point cloud, KDTree on the
unit-sphere. ±1 km accurate, sub-millisecond per query after warmup. - New :func:
otex.data.build_sites— builds the OTEC site DataFrame
on demand for a region / bbox / polygon, with caching at
~/.otex/cache/sites/<key>.parquet. - New :mod:
otex.data.demand— multi-source electricity demand
lookup. Tries Our World in Data first (bulk CSV, ~9 MB cached
once locally), falls back to the World Bank Open Data API
(EG.USE.ELEC.KH.PC × SP.POP.TOTL). Both sources are free and
require no authentication. Used byregional_batch.pyfor
demand-driven plant sizing.
Multi-year simulations
- Multi-year simulations.
DataConfignow acceptsyear_startand
year_end(inclusive). The thermal/oceanographic pipeline reads N years
of NetCDFs, concatenates them along the time axis, and propagates a
continuousDatetimeIndexthrough the rest of the analysis.- CLI:
otex-regional --year-start 2020 --year-end 2022. - Loader validates that the SAME sites appear in every year of the
configured range; mismatches raise a clear error pointing at the bad
year so users know which NetCDF to re-download.
- CLI:
- NPV-based LCOE (
otex.economics.lcoe_npv). Replaces the legacy CRF
annualisation whenn_years > 1with explicit per-year discounted
cashflows. Inputs from the simulated window are extrapolated cyclically
to the project lifetime. - Configurable degradation models (
DegradationConfig):
constant,logistic, andstep. - Configurable OPEX escalation (
OpexEscalationConfig):
flat,fixed_rate, andindexed. - New helper module
otex.economics.timeserieswith
aggregate_p_net_by_year()andannual_energy_kwh()(leap-year aware). - Multi-year aware regional outputs:
OTEC_sites_*.csvgainsLCOE_legacy,AEP_min,AEP_p50,
AEP_max, andAEP_stdcolumns whenn_years > 1.- New per-(site, year) CSV
OTEC_sites_yearly_*.csvfor diagnostic
plotting.
- HYCOM backend gains the same multi-year loop; runs that span multiple
experiment epochs (e.g. reanalysis ↔ analysis) resolve the right
experiment per year.
Removed
- All bundled CSV catalogs. The wheel ships no static data
catalog any more:CMEMS_points_with_properties.csv(9.7 MB site grid) → replaced
by :func:otex.data.build_sites(ETOPO 2022 + Natural Earth).download_ranges_per_region.csv(region bboxes) → replaced by
:func:otex.data.regions.resolve_region(Natural Earth admin-0).country_demand.csv(electricity demand by country) → replaced
by :func:otex.data.demand.fetch_demand_TWh(OWID + World Bank).
Changed
- :func:
otex.data.load_sitesnow requires aregionargument
(the legacy zero-arg call returned a 218 k-row global DataFrame
built from the now-deleted CSV). Pass a country name / ISO code
and optionallymin_depth/max_depth. run_regional_analysiscallsload_sites(region, ...)with
the depth filter fromDepthLimitsinstead of slicing a bundled
global catalog.cmems.download_dataandhycom.download_dataresolve region
bboxes via Natural Earth at runtime; they no longer read the
deleted bbox CSV.- Computed
water_depthanddist_shorewill differ slightly
from the legacy CSV values (~5-10 % typical) because the
underlying bathymetry source changed (ETOPO 2022 instead of the
unspecified source used to build the original CSV). Downstream
LCOE values for the same site may shift on the order of 1-2 %
for that reason alone. time_resolutiondefault inDataConfigis now'24h'(lowercase) to
match pandas ≥ 2.2 frequency aliases. Previously this raised
ValueError: Invalid frequency: Hon modern pandas installs.- Cache filenames produced by
data_processing()and the off-design
time-series writer use the multi-year label (2020-2022) when
applicable. Single-year filenames are unchanged. otex/analysis/visualization.pyaxis labels for OPEX changed from
'OPEX ($/year)'to'OPEX ($/yr, lifetime-avg)'to reflect that
multi-year OPEX is the lifetime-discounted average.
Deprecated
DataConfig.yearand theyear=parameter on
parameters_and_constants()/get_default_config()/
run_regional_analysis(). They still work but emit a
DeprecationWarning. Useyear_start/year_endinstead.lcoe_time_series()without atimestampargument (legacy CRF path).
Notes
- Single-year LCOE is unchanged. The NPV path is only triggered when
n_years > 1, so existing single-year case studies produce the same
numbers as 0.1.x.
v0.1.4 — Cycle thermodynamics fixes
Fixes severe thermodynamic violations in the closed mixture cycles and faithfully re-implements them against the published literature.
Fixed
- Kalina and Uehara turbines no longer violate the Carnot limit (the previous 50%-drop approximation included latent heat of condensation in the turbine work, inflating efficiency by 5-6×).
- Antoine coefficients for the NH3-H2O mixture properties were off by ~5000× at OTEC temperatures. Replaced with NIST
log10(P_bar)form. - Rankine Open
Q_evapwas inflated 194× by the inherited base-class formula. Added a proper override. - Rankine Hybrid had
T_ww_post_evapcolder than the NH3 boil temperature (heat-flow violation). Corrected.
Changed
- Uehara rewritten as a faithful single-loop topology (separator + 2-stage turbine + regenerator + absorber), matching Uehara & Ikegami (1990).
- Kalina rewritten as a faithful KCS-11 with exact mass balance at the separator and energy balance at the recuperator.
- Mixture cycles now use the basic-solution bubble point for
p_evap/p_condinstead of the pure-NH3 saturation pressure.
Installation
pip install otex==0.1.4
pip install otex[siting]==0.1.4v0.1.3 — Site-screening layers
Highlights
Adds site-screening (siting) layers for OTEC candidate-site analysis:
-
Hard exclusions
- WDPA (April 2026) protected areas, IUCN Ia/Ib/II/III/IV with configurable buffer (default 5 km)
- World Bank Global Vessel Density above the 95th percentile
-
Soft cost multipliers
- Shipping intensity (proportional to AIS percentile) — applied to both CAPEX and OPEX
- GEM Global Seismic Hazard Map PGA at 475-yr return — applied to CAPEX
- NOAA IBTrACS cyclone-track frequency within 100 km — applied to OPEX
All flags are off by default; existing scripts and notebooks behave unchanged. Enable per run via OTEXConfig.siting.
Installation
pip install otex==0.1.3
# With geospatial dependencies for site screening
pip install otex[siting]==0.1.3Usage
from otex.config import OTEXConfig
cfg = OTEXConfig()
cfg.siting.enable_mpa_filter = True
cfg.siting.enable_ais_filter = True
cfg.siting.enable_hazard_costs = TrueLayers download on demand into ~/.otex/siting_cache/ and are cached across runs. See docs/tutorials/siting.md for the full design and configuration reference.
Data sources and licences
| Layer | Source | Licence |
|---|---|---|
| WDPA | Zenodo 19873142 | CC-BY 4.0 |
| Vessel density | World Bank Global Shipping Traffic Density | CC-BY 4.0 |
| Seismic hazard | GEM v2023.1 | CC-BY-NC-SA 4.0 |
| Cyclones | NOAA IBTrACS v04r01 | Public domain |
Tests
217 tests passing (8 new regression tests for the int32 / NaN / DBL_MAX raster sampling edge cases found during the Jamaica end-to-end smoke test).
Changelog
See CHANGELOG.md for the full list of changes.
v0.1.2.1
HYCOM Data Source Support
OTEX now supports HYCOM (Hybrid Coordinate Ocean Model) as an alternative oceanographic data source alongside CMEMS. HYCOM requires no authentication, making it easier to get started.
New Features
otex.data.hycommodule — Download ocean temperature data via OPeNDAP- Reanalysis (GLBv0.08/expt_53.X): 1994–2015
- Analysis (GLBy0.08/expt_93.0): 2019–2024
- 40 standard depth levels, 0.08° spatial resolution
data_sourceparameter inrun_regional_analysis()and CLI (--data-source)- Automatic experiment selection based on year
- Automatic depth mapping to nearest HYCOM standard level
- CMEMS-compatible NetCDF output — existing processing pipeline works unchanged
Usage
from otex.regional import run_regional_analysis
# No credentials needed
otec_plants, sites = run_regional_analysis(
studied_region='Jamaica',
data_source='HYCOM',
year=2020,
)otex-regional Jamaica --data-source HYCOM --year 2020Data Source Comparison
| Feature | CMEMS | HYCOM |
|---|---|---|
| Authentication | Required (free account) | Not required |
| Temporal coverage | 1993–present | 1994–2015, 2019–2024 |
| Spatial resolution | 0.083° | 0.08° |
| Depth levels | 50 | 40 |
v0.1.2.0
OTEX now allows creating custom cost schemes