Skip to content

Latest commit

 

History

History
474 lines (365 loc) · 13.5 KB

File metadata and controls

474 lines (365 loc) · 13.5 KB

Regional Analysis Tutorial

Learn how to analyze OTEC potential for specific geographic regions using real oceanographic data from CMEMS.

Prerequisites

  • OTEX installed (pip install otex)
  • Internet connection for data download
  • HYCOM: No credentials needed (recommended for getting started)
  • CMEMS: Requires free Copernicus Marine account (see Installation Guide)

Overview

Regional analysis in OTEX:

  1. Downloads temperature profiles from CMEMS or HYCOM for your region
  2. Identifies feasible OTEC sites (adequate water depth)
  3. Sizes plants for each site based on local conditions
  4. Calculates LCOE considering distance to shore
  5. Generates time-resolved power profiles

Available Regions

OTEX includes a bundled database of pre-defined regions covering tropical areas worldwide:

from otex.data import load_regions

regions = load_regions()
print(regions.head(20))

Popular regions include:

  • Caribbean: Jamaica, Cuba, Dominican Republic, Puerto Rico, Bahamas
  • Pacific: Hawaii, Philippines, Fiji, Guam, Samoa
  • Indian Ocean: Mauritius, Maldives, Seychelles, Reunion
  • Southeast Asia: Indonesia, Malaysia, Vietnam
  • Africa: Kenya, Tanzania, Mozambique

Basic Usage

Command Line

After installing OTEX via pip, the otex-regional command is available:

# Analyze Jamaica with default settings (136 MW, low_cost, 2020, CMEMS)
otex-regional Jamaica

# Using HYCOM data (no credentials needed)
otex-regional Jamaica --data-source HYCOM

# Specify plant size and year
otex-regional Jamaica --power -50000 --year 2021

# Use different cycle and cost assumptions
otex-regional Philippines --cycle kalina --cost high_cost

Python API

from otex.regional import run_regional_analysis

# Run analysis with HYCOM (no credentials needed)
otec_plants, sites_df = run_regional_analysis(
    studied_region='Jamaica',
    p_gross=-50000,          # 50 MW
    cost_level='low_cost',
    year_start=2020,
    year_end=2020,
    cycle_type='rankine_closed',
    fluid_type='ammonia',
    use_coolprop=True,
    data_source='HYCOM',     # or 'CMEMS' (default)
)

Step-by-Step Guide

Step 1: Choose Your Region

First, verify your region exists in the database:

from otex.data import load_regions

regions = load_regions()
print(regions[regions['region'].str.contains('Jam', case=False)])

Output:

         region   north    east  south    west    demand
Jamaica  19.358 -74.009 14.083 -80.833  3.092992

Step 2: Check Available Sites

View potential OTEC sites in your region:

from otex.data import load_sites

sites = load_sites()
jamaica_sites = sites[sites['region'] == 'Jamaica']

print(f"Total sites in Jamaica: {len(jamaica_sites)}")
print(f"Water depth range: {jamaica_sites['water_depth'].min():.0f} to {jamaica_sites['water_depth'].max():.0f} m")
print(f"Distance to shore: {jamaica_sites.iloc[:, 4].min():.1f} to {jamaica_sites.iloc[:, 4].max():.1f} km")

Step 3: Run the Analysis

from otex.regional import run_regional_analysis

otec_plants, sites_df = run_regional_analysis(
    studied_region='Jamaica',
    p_gross=-50000,
    year_start=2020,
    year_end=2020,
)

This will:

  1. Download temperature data from CMEMS or HYCOM (~5-15 minutes first time)
  2. Process and cache data locally
  3. Run OTEC sizing for all valid sites
  4. Calculate LCOE for each site
  5. Save results to Data_Results/Jamaica/

Step 4: Examine Results

import pandas as pd
import matplotlib.pyplot as plt

# Load results
results = pd.read_csv(
    'Data_Results/Jamaica/Jamaica_2020_50.0_MW_low_cost/OTEC_sites_Jamaica_2020_50.0_MW_low_cost.csv',
    sep=';',
    index_col='id'
)

print(results.head())
print(f"\nNumber of feasible sites: {len(results)}")
print(f"LCOE range: {results['LCOE'].min():.2f} - {results['LCOE'].max():.2f} ct/kWh")
print(f"Best site LCOE: {results['LCOE'].min():.2f} ct/kWh")

Step 5: Visualize Results

import matplotlib.pyplot as plt

fig, axes = plt.subplots(2, 2, figsize=(14, 10))

# LCOE map
ax1 = axes[0, 0]
scatter = ax1.scatter(
    results['longitude'],
    results['latitude'],
    c=results['LCOE'],
    cmap='RdYlGn_r',
    s=50
)
plt.colorbar(scatter, ax=ax1, label='LCOE (ct/kWh)')
ax1.set_xlabel('Longitude')
ax1.set_ylabel('Latitude')
ax1.set_title('LCOE by Location')

# LCOE histogram
ax2 = axes[0, 1]
ax2.hist(results['LCOE'], bins=20, edgecolor='white')
ax2.axvline(results['LCOE'].median(), color='red', linestyle='--', label='Median')
ax2.set_xlabel('LCOE (ct/kWh)')
ax2.set_ylabel('Number of sites')
ax2.set_title('LCOE Distribution')
ax2.legend()

# Net power vs LCOE
ax3 = axes[1, 0]
ax3.scatter(results['p_net_nom'], results['LCOE'], alpha=0.6)
ax3.set_xlabel('Net Power (MW)')
ax3.set_ylabel('LCOE (ct/kWh)')
ax3.set_title('Net Power vs LCOE')

# Temperature difference
ax4 = axes[1, 1]
delta_T = results['T_WW_med'] - results['T_CW_med']
ax4.scatter(delta_T, results['LCOE'], alpha=0.6)
ax4.set_xlabel('Temperature Difference (°C)')
ax4.set_ylabel('LCOE (ct/kWh)')
ax4.set_title('ΔT vs LCOE')

plt.tight_layout()
plt.savefig('jamaica_analysis.png', dpi=150)
plt.show()

Understanding the Output Files

OTEC Sites CSV

Column Description Unit Single-yr Multi-yr
id Site identifier -
longitude Site longitude degrees
latitude Site latitude degrees
p_net_nom Nominal net power MW
AEP Lifetime-average annual energy MWh
CAPEX Capital expenditure $M
LCOE Levelized cost of energy ct/kWh
LCOE_legacy Legacy single-rate CRF LCOE for comparison ct/kWh
AEP_min Minimum yearly AEP across the run window MWh
AEP_p50 Median yearly AEP MWh
AEP_max Maximum yearly AEP MWh
AEP_std Standard deviation of yearly AEP MWh
Configuration Optimal ΔT configuration -
T_WW_min/med/max Warm water temperature stats °C
T_CW_min/med/max Cold water temperature stats °C

For multi-year runs, a companion CSV OTEC_sites_yearly_*.csv is also emitted with one row per (site, year) and columns id, year, p_net_mean_kW, AEP_MWh. Useful for boxplots of inter-annual variability or for fitting trend lines.

Power Profiles CSV

Daily average net power output over the year:

profiles = pd.read_csv(
    'Data_Results/Jamaica/.../net_power_profiles_per_day_Jamaica_2020_50.0_MW_low_cost.csv',
    sep=';',
    index_col=0,
    parse_dates=True
)

# Plot annual profile
profiles.plot(figsize=(12, 4))
plt.ylabel('Net Power (kW)')
plt.title('Average Daily Net Power Output')
plt.show()

Choosing a Data Source

OTEX supports two oceanographic data sources. Choose based on your needs:

Feature CMEMS HYCOM
Authentication Required (free account) Not required
Temporal coverage 1993–present 1994–2015, 2019–2024
Spatial resolution 0.083° (~9 km) 0.08° (~9 km)
Depth levels 50 40
Data gap None 2016–2018

Recommendation: Use HYCOM for quick analyses and getting started. Use CMEMS for years outside HYCOM coverage or when continuous multi-year time series are needed.

# Compare results from both sources
otec_hycom, sites_hycom = run_regional_analysis(
    studied_region='Jamaica', year_start=2020, year_end=2020,
    data_source='HYCOM'
)

otec_cmems, sites_cmems = run_regional_analysis(
    studied_region='Jamaica', year_start=2020, year_end=2020,
    data_source='CMEMS'
)

Advanced Options

Custom Plant Size

from otex.regional import run_regional_analysis

# Analyze different plant sizes
for size_mw in [20, 50, 100, 200]:
    run_regional_analysis(
        studied_region='Jamaica',
        p_gross=-size_mw * 1000,
        year_start=2020,
        year_end=2020
    )

Different Thermodynamic Cycles

from otex.regional import run_regional_analysis

# Compare cycles
cycles = ['rankine_closed', 'kalina', 'uehara']
for cycle in cycles:
    run_regional_analysis(
        studied_region='Jamaica',
        p_gross=-50000,
        cycle_type=cycle
    )

Multi-Year Analysis

Since 0.2.0, multi-year simulations are supported natively. Pass an inclusive year range and the pipeline reads N NetCDFs (one per year), concatenates them along the time axis, and recomputes LCOE using a discounted-cashflow NPV formulation that accounts for leap years, configurable degradation, and OPEX escalation.

# Single CLI invocation covering 2018-2021
otex-regional Jamaica --year-start 2018 --year-end 2021
from otex import run_regional_analysis

run_regional_analysis(
    studied_region='Jamaica',
    year_start=2018,
    year_end=2021,
)

For a multi-year run the output OTEC_sites_*.csv adds inter-annual variability columns (AEP_min, AEP_p50, AEP_max, AEP_std) and a LCOE_legacy column for comparison with the single-year formulation. A second CSV OTEC_sites_yearly_*.csv reports per-(site, year) energy.

To run analyses with independent yearly snapshots (the legacy workflow, no NPV), keep the loop:

for year in 2018 2019 2020 2021; do
    otex-regional Jamaica --year $year
done

NPV LCOE — degradation and OPEX escalation

When n_years > 1, LCOE is computed as the per-year discounted cashflow:

                     CAPEX + Σ_t  OPEX_t / (1+r)^t
LCOE  =  100 ¢ ×  ─────────────────────────────────
                       Σ_t  E_t / (1+r)^t

where the sum runs over the full project lifetime (Economics.lifetime_years, 30 years by default). Years outside the simulated window are filled by cyclically replicating the simulated pattern. Two configurable multipliers are applied to each year:

  • Power degradation (lowers E_t). Three models, configurable on Economics.degradation:

    Model Formula Defaults
    constant (1 - rate)^t rate = 0.005 (0.5 %/yr)
    logistic 1 - L / (1 + exp(-k(t - t0))) L=0.30, k=0.30, t0=15
    step discrete drops at scheduled years years=[10, 20], drops=[0.05, 0.05]
  • OPEX escalation (raises OPEX_t). Three models, configurable on Economics.opex_escalation:

    Model Formula Defaults
    flat constant
    fixed_rate (1 + rate)^t rate = 0.0
    indexed user-supplied vector of length lifetime_years

Example with custom degradation and 2 % OPEX escalation:

from otex.config import OTEXConfig, Economics
from otex.economics import DegradationConfig, OpexEscalationConfig

config = OTEXConfig()
config.economics = Economics(
    lifetime_years=30,
    discount_rate=0.08,
    degradation=DegradationConfig(model='logistic',
                                   logistic_L=0.20,
                                   logistic_k=0.25,
                                   logistic_t0=12),
    opex_escalation=OpexEscalationConfig(model='fixed_rate', rate=0.02),
)
inputs = config.to_legacy_dict()
# pass `inputs` to run_regional_analysis via parameters_and_constants(...)

Single-year runs (n_years == 1) preserve the legacy single-rate CRF LCOE for backward compatibility — they ignore the degradation and escalation config.

Combining with Uncertainty Analysis

After regional analysis, run uncertainty analysis on the best site:

import pandas as pd
from otex.analysis import MonteCarloAnalysis, UncertaintyConfig

# Load regional results
results = pd.read_csv('...OTEC_sites_Jamaica_2020_50.0_MW_low_cost.csv', sep=';')

# Find best site
best_site = results.loc[results['LCOE'].idxmin()]
print(f"Best site: ({best_site['longitude']}, {best_site['latitude']})")
print(f"T_WW: {best_site['T_WW_med']:.1f}°C, T_CW: {best_site['T_CW_med']:.1f}°C")

# Run uncertainty analysis for this site
config = UncertaintyConfig(n_samples=500, seed=42)
mc = MonteCarloAnalysis(
    T_WW=best_site['T_WW_med'],
    T_CW=best_site['T_CW_med'],
    config=config,
    p_gross=-50000
)
ua_results = mc.run()

stats = ua_results.compute_statistics()
print(f"\nLCOE with uncertainty:")
print(f"Mean: {stats['lcoe']['lcoe_mean']:.2f} ct/kWh")
print(f"90% CI: [{stats['lcoe']['lcoe_p5']:.2f}, {stats['lcoe']['lcoe_p95']:.2f}]")

Performance Tips

  1. First run is slower: Data download and processing takes 5-15 minutes
  2. Subsequent runs are faster: Processed data is cached in HDF5 files
  3. Reduce memory usage: Use smaller regions or reduce spatial resolution
  4. Parallel processing: Enabled by default for Monte Carlo

Troubleshooting

"No valid sites found"

Check that your region has sufficient water depth:

from otex.data import load_sites

sites = load_sites()
region_sites = sites[sites['region'] == 'YourRegion']
print(f"Depths: {region_sites['water_depth'].describe()}")

Sites need water depth of at least 600-1000m.

Download failures

CMEMS:

  1. Verify credentials: copernicusmarine login --check
  2. Check internet connection
  3. Try again later (CMEMS servers may be busy)
  4. Try HYCOM as an alternative: data_source='HYCOM'

HYCOM:

  1. Verify the year is within coverage (1994–2015 or 2019–2024)
  2. HYCOM OPeNDAP servers may be temporarily unavailable — retry later
  3. Check internet connection

Memory errors

Reduce plant size or use a smaller region.

Next Steps