Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
a0ea75b
data: add manual_input_usa.csv
finozzifa Oct 8, 2025
658a937
code: create parser file
finozzifa Oct 8, 2025
5d787d0
code: improve manual_input_usa.py
finozzifa Oct 9, 2025
423b730
Merge branch 'prototype-2' of https://github.com/open-energy-transiti…
finozzifa Oct 20, 2025
2db7119
Merge branch 'prototype-2' of https://github.com/open-energy-transiti…
finozzifa Oct 20, 2025
57c13da
code: update manual_input_usa.py
finozzifa Oct 20, 2025
514bc8b
code: update manual_input_usa.py
finozzifa Oct 20, 2025
404cb07
code: update carrier and unit
finozzifa Oct 22, 2025
f43a2d6
code: pre-commit
finozzifa Oct 22, 2025
367c745
code: new update
finozzifa Oct 22, 2025
fa869ca
pre-commit
finozzifa Oct 22, 2025
f35276b
code: modify manual_input_usa.py
finozzifa Oct 23, 2025
2bfb02f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 23, 2025
a5cbf34
code: include pre-commit hooks
finozzifa Oct 23, 2025
f1b876d
code: solve merge conflicts
finozzifa Oct 23, 2025
85d2197
code: re-set parameter dictionary
finozzifa Oct 23, 2025
bd2f6d5
Merge branch 'prototype-2' of https://github.com/open-energy-transiti…
finozzifa Oct 28, 2025
fa24584
Merge branch 'prototype-2' of https://github.com/open-energy-transiti…
finozzifa Nov 3, 2025
b7cb057
code: solve merge conflicts
finozzifa Nov 19, 2025
2bc2903
code: move parse_input_arguments to Commons
finozzifa Nov 20, 2025
37326c0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 20, 2025
843cc85
code: move parse_input_arguments to Commons
finozzifa Nov 20, 2025
5a6bbdd
Merge branch 'issue_59_prototest_manual_input' of https://github.com/…
finozzifa Nov 20, 2025
a8a06d5
code: add unit tests
finozzifa Nov 20, 2025
9a3f7e1
Merge branch 'prototype-2' of https://github.com/open-energy-transiti…
finozzifa Dec 8, 2025
7297df9
Update src/technologydata/utils/carriers.txt
finozzifa Jan 16, 2026
1ac28ec
Update src/technologydata/package_data/manual_input_usa/manual_input_…
finozzifa Jan 16, 2026
5a48edb
Update src/technologydata/package_data/manual_input_usa/manual_input_…
finozzifa Jan 16, 2026
ef205e4
Update src/technologydata/package_data/manual_input_usa/manual_input_…
finozzifa Jan 16, 2026
bbd3cc1
merge from prototype-2
finozzifa Jan 16, 2026
191f891
merge from origin
finozzifa Jan 16, 2026
f4aa965
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
386ed7d
Update src/technologydata/parsers/manual_input_usa/manual_input_usa.py
finozzifa Jan 16, 2026
3623d17
Update src/technologydata/parsers/manual_input_usa/manual_input_usa.py
finozzifa Jan 16, 2026
7e39994
further changes
finozzifa Jan 16, 2026
4c277ad
Merge remote-tracking branch 'origin/issue_59_prototest_manual_input'…
finozzifa Jan 16, 2026
cdb0e31
code: update manual_input_usa technologies.json
finozzifa Jan 16, 2026
544fac6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
5bfc391
code: include changes to parser
finozzifa Jan 16, 2026
ecf0393
include pre-commit
finozzifa Jan 16, 2026
0d2c03d
Merge branch 'issue_59_prototest_manual_input' of https://github.com/…
finozzifa Jan 16, 2026
3e1a503
doc: update documentation'
finozzifa Jan 16, 2026
744e6fc
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 16, 2026
f91f666
include pre-commit
finozzifa Jan 16, 2026
724e3f1
Merge branch 'issue_59_prototest_manual_input' of https://github.com/…
finozzifa Jan 16, 2026
5de286d
modify MANIFEST.in
finozzifa Jan 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
include src/technologydata/utils/*.txt
include src/technologydata/package_data/dea_energy_storage/*.json
include src/technologydata/package_data/raw/*
include src/technologydata/parsers/dea_energy_storage/*.json
include src/technologydata/parsers/manual_input_usa/*.json
include src/technologydata/parsers/raw/*
include test/test_data/currency_conversion/WB_CNY_2020/*
include test/test_data/currency_conversion/WB_EUR_2020/*
include test/test_data/currency_conversion/WB_USD_2020/*
Expand Down
7 changes: 3 additions & 4 deletions REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,21 @@ path = ["uv.lock", "*.yaml", "docs/**", "*.md", "*.in"]
SPDX-FileCopyrightText = "technologydata contributors"
SPDX-License-Identifier = "MIT"


[[annotations]]
path = ["test/test_data/**",]
path = ["test/test_data/**"]
SPDX-FileCopyrightText = "technologydata contributors"
SPDX-License-Identifier = "CC-BY-4.0"

[[annotations]]
path = [
"src/technologydata/package_data/dea_energy_storage/*.json", "src/technologydata/package_data/schemas/*.json"
"src/technologydata/parsers/dea_energy_storage/*.json", "src/technologydata/parsers/schemas/*.json", "src/technologydata/parsers/raw/manual_input_usa.csv", "src/technologydata/parsers/manual_input_usa/*.json",
]
SPDX-FileCopyrightText = "technologydata contributors"
SPDX-License-Identifier = "CC-BY-4.0"

[[annotations]]
path = [
"src/technologydata/package_data/raw/Technology_datasheet_for_energy_storage.xlsx"
"src/technologydata/parsers/raw/Technology_datasheet_for_energy_storage.xlsx"
]
SPDX-FileCopyrightText = "The Danish Energy Agency"
SPDX-License-Identifier = "CC-BY-4.0"
16 changes: 8 additions & 8 deletions docs/examples/dea_storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

## Overview

The Danish Energy Agency (DEA) data parser `dea_energy_storage.py` demonstrates a full data-cleaning and transformation pipeline for converting raw tabular data into the `technologydata` schema files `technologies.json` and `sources.json`. The parser is implemented in `src/technologydata/package_data/dea_energy_storage/dea_energy_storage.py`.
The Danish Energy Agency (DEA) data parser `dea_energy_storage.py` demonstrates a full data-cleaning and transformation pipeline for converting raw tabular data into the `technologydata` schema files `technologies.json` and `sources.json`. The parser is implemented in `src/technologydata/parsers/dea_energy_storage/dea_energy_storage.py`.

## Dataset Description

The original dataset is available at this [link](https://ens.dk/media/6589/download). A full description of the dataset is available at this [link](https://ens.dk/media/6588/download). The raw source file is included in the repository at `src/technologydata/package_data/raw/Technology_datasheet_for_energy_storage.xlsx`.
The original dataset is available at this [link](https://ens.dk/media/6589/download). A full description of the dataset is available at this [link](https://ens.dk/media/6588/download). The raw source file is included in the repository at `src/technologydata/parsers/raw/Technology_datasheet_for_energy_storage.xlsx`.

The dataset is in Excel format, and it includes, under the data sheet `alldata_flat`, a flat table of technology parameters for a range of energy storage technologies. Columns include `Technology`, `ws`, `par` (parameter name), `val` (value), `unit`, `year`, `est` (case/estimate), `priceyear`, plus metadata columns such as `cat`, `ref`, `note`. Rows are individual parameter records (parameter value + unit + context) for technologies and estimation cases.

Expand All @@ -25,7 +25,7 @@ Function `parse_input_arguments()` defines and parses the command-line arguments

### Read the raw data

The script reads the raw data available at `src/technologydata/package_data/raw/Technology_datasheet_for_energy_storage.xlsx`, under sheet `alldata_flat`, in a `pandas` dataframe. It uses `pandas.read_excel(..., engine=calamine, dtype=str)`. All entries are handled as strings initially.
The script reads the raw data available at `src/technologydata/parsers/raw/Technology_datasheet_for_energy_storage.xlsx`, under sheet `alldata_flat`, in a `pandas` dataframe. It uses `pandas.read_excel(..., engine=calamine, dtype=str)`. All entries are handled as strings initially.

### Data cleaning, validation and dealing with missing/null values

Expand Down Expand Up @@ -64,21 +64,21 @@ Function `build_technology_collection()`:
- for each group, builds a dictionary of `Parameter` objects (each with `magnitude`, `units`, `sources`, `provenance`).
- creates a `Technology` object for each group, with `name` = `ws`, `detailed_technology` = `Technology`, `year`=`year`, `region` = `EU`, `case` = `est` and collects them into a `TechnologyCollection` object.
- writes the `TechnologyCollection` object to a `technologies.json`.
- if `--export_schema` is used, schema files produced during export are moved to the sub-folder `src/technologydata/package_data/schemas`.
- if `--export_schema` is used, schema files produced during export are moved to the sub-folder `src/technologydata/parsers/schemas`.

## Running the parser

### Execution instructions

From repository root:

- Basic run: `python src/technologydata/package_data/dea_energy_storage/dea_energy_storage.py`
- Basic run: `python src/technologydata/parsers/dea_energy_storage/dea_energy_storage.py`
- Example with options: `--num_digits 3 --store_source --filter_params --export_schema`

### Outputs

The parser generates the following outputs:

- `src/technologydata/package_data/dea_energy_storage/technologies.json`.
- `src/technologydata/package_data/dea_energy_storage/sources.json`.
- Optional schema files moved to `src/technologydata/package_data/schemas` when `--export_schema` is used.
- `src/technologydata/parsers/dea_energy_storage/technologies.json`.
- `src/technologydata/parsers/dea_energy_storage/sources.json`.
- Optional schema files moved to `src/technologydata/parsers/schemas` when `--export_schema` is used.
78 changes: 78 additions & 0 deletions docs/examples/manual_input_usa.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Manual Input USA Parser Documentation

## Overview

The Manual Input USA data parser `manual_input_usa.py` demonstrates a data-cleaning and transformation pipeline for converting manually curated, USA-specific tabular data into the `technologydata` schema files `technologies.json` and `sources.json`. The parser is implemented in `src/technologydata/parsers/manual_input_usa/manual_input_usa.py`.

## Dataset Description

The original dataset is a manually curated CSV file containing USA-specific technology parameters available at this [link](https://github.com/PyPSA/technology-data/blob/v0.13.4/inputs/US/manual_input_usa.csv). The raw source file is included in the repository at `src/technologydata/parsers/raw/manual_input_usa.csv`.

The dataset is in CSV format and includes a flat table of technology parameters for various energy technologies relevant to the USA context. Columns include `technology`, `parameter`, `year`, `value`, `unit`, `currency_year`, `source`, `further_description`, `financial_case`, and `scenario`. Rows are individual parameter records (parameter value + unit + context) for technologies with different scenarios and financial cases.

## Parser description

The parser is articulated in the following steps.

### Command line argument parsing

Function `CommonsParser.parse_input_arguments()` defines and parses the command-line arguments:

- `--num_digits` (int, default 4) — number of decimals used when rounding numeric values. The default value is 4.
- `--store_source` (boolean flag) — whether to store the source on the Wayback Machine. The default value is `false`.

### Read the raw data

The script reads the raw data available at `src/technologydata/parsers/raw/manual_input_usa.csv` in a `pandas` dataframe. It uses `pandas.read_csv(..., dtype=str, na_values="None")`. All entries are handled as strings initially except for the `value` column which is converted to float.

### Data cleaning, validation and dealing with missing/null values

The data cleaning and validation happens with the following steps.

Function `extract_units_carriers_heating_value()` extracts standardized units, carriers, and heating values from input unit strings. This function maps complex unit representations to simplified unit, carrier, and heating value combinations using a predefined dictionary of special patterns. Examples include:

- `USD_2022/MW_FT` → unit: `USD_2022/MW`, carrier: `1/FT`, heating_value: `1/LHV`
- `MWh_H2/MWh_FT` → unit: `MWh/MWh`, carrier: `H2/FT`, heating_value: `LHV`
- `MWh_el/MWh_FT` → unit: `MWh/MWh`, carrier: `el/FT`, heating_value: `LHV`
- `t_CO2/MWh_FT` → unit: `t/MWh`, carrier: `CO2/FT`, heating_value: `LHV`
- `USD_2022/kWh_H2` → unit: `USD_2022/kWh`, carrier: `1/H2`, heating_value: `LHV`
- `USD_2023/t_CO2/h` → unit: `USD_2023/t/h`, carrier: `1/CO2`, heating_value: `None`
- `MWh_el/t_CO2` → unit: `MWh/t`, carrier: `el/CO2`, heating_value: `LHV`
- `MWh_th/t_CO2` → unit: `MWh/t`, carrier: `thermal/CO2`, heating_value: `LHV`

The parser also fills missing values in the `scenario` column with `"not_available"`.

The parser applies the following unit conversions:

- Convert `per unit` to `%` and multiply the corresponding `value` by 100.0, rounding to `num_digits` decimals.

Function `Commons.update_unit_with_currency_year(unit, currency_year)` appends `currency_year` information to currency units when present. This is because `technologydata` follows the currency pattern `\b(?P<cu_iso3>[A-Z]{3})_(?P<year>\d{4})\b`, as for example `USD_2022`.

### Populate and export the source and technology collections

Function `build_technology_collection()`:

- if `store_source` is set, constructs a `Source` object for the manual input USA dataset, calls `ensure_in_wayback()` and writes `sources.json`; otherwise reads an existing `sources.json`.
- groups the cleaned DataFrame by `scenario`, `year`, `technology`.
- for each group, builds a dictionary of `Parameter` objects (each with `magnitude`, `sources`, and optionally `carrier`, `heating_value`, `units`, `note`).
- captures the `financial_case` value from rows within each group to combine with `scenario`.
- creates a `case` value by combining `scenario` and `financial_case` in the format `"{scenario} - {financial_case}"` when `financial_case` is present; otherwise uses `scenario` alone.
- creates a `Technology` object for each group, with `name` = `technology`, `detailed_technology` = `technology`, `year` = `year`, `region` = `USA`, `case` = combined case value, and collects them into a `TechnologyCollection` object.
- writes the `TechnologyCollection` object to a `technologies.json`.

## Running the parser

### Execution instructions

From repository root:

- Basic run: `python src/technologydata/parsers/manual_input_usa/manual_input_usa.py`
- Example with options: `--num_digits 3 --store_source`

### Outputs

The parser generates the following outputs:

- `src/technologydata/parsers/manual_input_usa/technologies.json`.
- `src/technologydata/parsers/manual_input_usa/sources.json`.
- Optional schema files moved to `src/technologydata/parsers/schemas` when `--export_schema` is used.
1 change: 1 addition & 0 deletions mkdocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ nav:

- Examples:
- Danish Energy Agency Parser: examples/dea_storage.md
- Manual Input USA Parser: examples/manual_input_usa.md

- API Reference:
- Data Package: api/datapackage.md
Expand Down
136 changes: 136 additions & 0 deletions src/technologydata/parsers/commons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# SPDX-FileCopyrightText: technologydata contributors
#
# SPDX-License-Identifier: MIT

"""Classes for Commons methods for the data parsers."""

import argparse
from typing import Annotated, Any

import pydantic
from pydantic import BaseModel, ConfigDict


class ArgumentConfig(BaseModel):
"""
Pydantic model for defining argument configurations.

Allows flexible configuration of command-line arguments with type checking
and validation.
"""

name: Annotated[str, pydantic.Field(description="Name of the argument config")]
arg_type: Annotated[
type | None,
pydantic.Field(
description="The type to which the command-line argument should be converted."
),
] = None
default: Annotated[
Any | None, pydantic.Field(description="Default value of the argument config")
] = None
help: Annotated[
str | None,
pydantic.Field(description="A brief description of what the argument does."),
] = None
action: Annotated[
str | None,
pydantic.Field(
description="Specification of how the command-line arguments should be handled"
),
] = None
required: Annotated[
bool, pydantic.Field(description="Flag to check whether field is mondatory")
] = False

# Allow extra fields for maximum flexibility
model_config = ConfigDict(extra="allow")


class CommonsParser:
"""Commons methods for the data parsers."""

@staticmethod
@pydantic.validate_call
def parse_input_arguments(
additional_arguments: list[ArgumentConfig] | None = None,
description: str = "Flexible command line argument parser",
) -> argparse.Namespace:
"""
Parse command line arguments with robust configuration.

Parameters
----------
additional_arguments : Optional[List[ArgumentConfig]]
A list of ArgumentConfig objects defining extra arguments.
description : str
Description for the argument parser. Defaults to a generic message.

Returns
-------
argparse.Namespace
Parsed command line arguments

Examples
--------
>>> extra_args = [
... ArgumentConfig(
... name="--input_file",
... arg_type=str,
... required=True,
... help="Path to input CSV file"
... ),
... ArgumentConfig(
... name="--verbose",
... action="store_true",
... help="Enable verbose output"
... )
... ]
>>> args = CommonsParser.parse_input_arguments(additional_arguments=extra_args)

"""
# Create parser with provided or default description
parser = argparse.ArgumentParser(
description=description,
formatter_class=argparse.RawTextHelpFormatter,
)

# Default arguments
default_args = [
ArgumentConfig(
name="--num_digits",
arg_type=int,
default=4,
help="Number of significant digits to round the values.",
),
ArgumentConfig(
name="--store_source",
action="store_true",
help="Store_source, store the source object on the wayback machine. Default: false",
),
]

# Combine default and additional arguments
all_arguments = default_args + (additional_arguments or [])

# Add arguments to parser (Option 1)
for arg_config in all_arguments:
# Convert Pydantic model to argparse-compatible dictionary
arg_dict = {
k: v
for k, v in arg_config.model_dump().items()
if v is not None and k != "name"
}

if arg_dict.get("arg_type") is not None:
arg_dict["type"] = arg_dict.pop("arg_type")

print("arg_dict", arg_dict)

# Add argument to parser
parser.add_argument(arg_config.name, **arg_dict)

# Parse arguments
args = parser.parse_args()

return args
Loading