Exporter I/O¶
Module: esfex.io.exporter
ResultsExporter¶
Read-only exporter that converts existing HDF5 results files to other formats.
Parameters:
| Parameter | Type | Description |
|---|---|---|
results_path |
str or Path |
Path to an existing HDF5 results file. |
Raises: FileNotFoundError if the results file does not exist.
Methods¶
to_csv¶
Export all datasets from the HDF5 file as individual CSV files. Preserves group hierarchy as directory structure.
Output structure:
output_dir/
summary/
total_cost.csv
gen_investment_power.csv
...
year_2025_threshold_0.5/
generation/
Solar_PV.csv
Diesel.csv
...
battery_charge/
Li_Ion.csv
...
battery_discharge/
Li_Ion.csv
...
curtailment.csv
electricity_prices.csv
CO2_emissions.csv
power_flow.csv
year_2026_threshold_0.5/
...
demand/
...
to_excel¶
Export results to a single Excel workbook with multiple sheets: "Summary" (summary_results group) and "Generation" (first scenario's generation data, summed across nodes).
Uses openpyxl engine.
to_json¶
Export metadata and summary data to a JSON file with nested structure matching the HDF5 hierarchy. Scenario names and attributes are included; large hourly arrays are NOT exported to JSON (use CSV or HDF5 for full time-series data).
JSON structure:
{
"metadata": {
"creation_date": "2026-01-15T10:30:00",
"hours": "8760",
"num_nodes": "4",
"num_generators": "7",
"num_batteries": "1"
},
"summary": {
"total_cost": [1234567.0, ...],
"gen_investment_power": [...]
},
"scenarios": {
"year_2025_threshold_0.5": {
"year": "2025",
"threshold": "0.5",
"objective": "5432100.0"
}
}
}
Example¶
from esfex.io.exporter import ResultsExporter
exporter = ResultsExporter("results/isla_juventud.h5")
# Export to CSV directory
exporter.to_csv("results/csv/")
# Export to Excel workbook
exporter.to_excel("results/report.xlsx")
# Export to JSON
exporter.to_json("results/data.json")
export_system_results¶
def export_system_results(
results_dict: Dict[str, Any],
generators: List[dict],
batteries: List[dict],
hours: int,
num_nodes: int,
output_filename: Optional[str] = None,
output_dir: Union[str, Path] = "results",
temporal_resolution_hours: int = 1,
) -> Path
Write optimization results to an HDF5 file. Called by the runner after each simulation.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
results_dict |
dict |
required | Dictionary with optimization results keyed by scenario. Keys can be (year, threshold) tuples or string identifiers. |
generators |
list[dict] |
required | List of generator configurations (used for names and metadata). |
batteries |
list[dict] |
required | List of battery configurations. |
hours |
int |
required | Number of hours in the simulation. |
num_nodes |
int |
required | Number of nodes. |
output_filename |
str or None |
None |
Output filename. If None, auto-generates as results_YYYYMMDD_HHMMSS.h5. |
output_dir |
str or Path |
"results" |
Output directory (created if it does not exist). |
temporal_resolution_hours |
int |
1 |
Temporal resolution of the data (for metadata). |
Returns: Path to the created HDF5 file.
HDF5 File Structure¶
results_20260215_143000.h5
[attrs]
creation_date: "2026-02-15T14:30:00"
hours: 8760
num_nodes: 4
temporal_resolution_hours: 1
num_generators: 7
num_batteries: 1
generator_names: ["Solar_PV", "Wind", "Diesel", ...]
battery_names: ["Li_Ion"]
detailed_results/
year_2025_threshold_0.5/
[attrs]
year: 2025
threshold: 0.5
objective: 5432100.0
total_generation: 1234000.0
renewable_generation: 987000.0
renewable_penetration: 0.80
co2_emissions: 12345.0
hourly_data/
generation/
Solar_PV [nodes x hours] (units: MW)
Wind [nodes x hours] (units: MW)
Diesel [nodes x hours] (units: MW)
...
battery_charge/
Li_Ion [nodes x hours] (units: MW)
battery_discharge/
Li_Ion [nodes x hours] (units: MW)
curtailment [nodes x hours] (units: MW)
electricity_prices [hours] (units: $/MWh)
nodal_electricity_prices [nodes x hours] (units: $/MWh)
CO2_emissions [hours] (units: tonnes)
power_flow [from x to x hours] (units: MW)
EV_charging [nodes x hours] (units: MW)
EV_V2G [nodes x hours] (units: MW)
loss_of_inertia [hours] (units: GW*s)
capacity_factor/
Solar_PV [1] (dimensionless)
Wind [1] (dimensionless)
...
lcoe/
Solar_PV [1] (USD/MWh)
Diesel [1] (USD/MWh)
...
vallcoe/
Solar_PV [1] (USD/MWh)
...
technology_selling_prices/
Solar_PV/
[attrs] total_generation, total_revenue,
average_selling_price, technology_type
prices_weights [N x 3] (price, generation, timestep)
...
All time-series datasets use gzip compression and chunked storage for efficient access.
Derived Metrics¶
Derived metrics computed by the runner's _compute_derived_metrics() method:
| Metric | Description | Formula |
|---|---|---|
| Capacity Factor | Fraction of maximum possible output | total_generation / (rated_power * hours) |
| LCOE | Levelized cost of energy | (fixed_cost + fuel_cost + maintenance) / total_generation |
| VALLCOE | Value-adjusted LCOE | LCOE - (revenue_from_selling_price / total_generation) |
| Technology Selling Price | Revenue-weighted average price | sum(price * generation) / sum(generation) |
Stored as per-generator datasets in the capacity_factor/, lcoe/, vallcoe/, and technology_selling_prices/ groups.
read_results¶
Read results from an HDF5 file back into Python.
Parameters:
| Parameter | Type | Description |
|---|---|---|
results_path |
str or Path |
Path to HDF5 results file. |
Returns: Dictionary with structure:
{
"metadata": {
"creation_date": "2026-02-15T14:30:00",
"hours": 8760,
"num_nodes": 4,
...
},
"scenarios": {
"year_2025_threshold_0.5": {
"attrs": {"year": 2025, "threshold": 0.5, "objective": 5432100.0},
"hourly_data": {
"generation": {"Solar_PV": np.ndarray, "Diesel": np.ndarray, ...},
"battery_charge": {"Li_Ion": np.ndarray},
"curtailment": np.ndarray,
"electricity_prices": np.ndarray,
...
}
}
}
}
Handles both HDF5 formats:
- Incremental format: data directly in scenario group.
- One-shot format: data nested in hourly_data/ subgroup.
Example:
from esfex.io.exporter import read_results
results = read_results("results/isla_juventud.h5")
gen_data = results["scenarios"]["year_2030_threshold_0.5"]["hourly_data"]["generation"]
solar_output = gen_data["Solar_PV"] # np.ndarray shape (nodes, hours)
print(f"Peak solar: {solar_output.max():.1f} MW")