Units Checking#
The Temoa v4.0 database schema includes comprehensive units checking capabilities to ensure consistency and accuracy throughout the model. Unit checking validates that all units are properly formatted, dimensionally consistent, and align correctly across related tables.
Important
The units expressed and checked via pint do not “follow the values” through the mathematics
of the model. Unit checking is a pre-processing validation layer used to support documentation
and catch input errors. The units are not used in the model calculations themselves.
Unit Propagation to Outputs#
Starting with v4.0, units defined in input tables are automatically propagated to output tables when model results are written. This provides traceability of units from inputs through results.
How It Works:
output_flow_out/output_flow_in: Units fromcommodity.unitsfor the output/input commodityoutput_built_capacity/output_net_capacity/output_retired_capacity: Units fromexisting_capacity.unitsoutput_emission: Units fromcommodity.unitsfor the emission commodityoutput_cost: Common currency unit extracted from cost input tablesoutput_storage_level: Units from the stored commodity
Backward Compatibility:
Databases without unit data in input tables will write
NULLunits to outputsDatabases with older schemas (no
unitscolumn on output tables) continue to work normallyUnit propagation is automatic and requires no configuration
Overview#
The unit checking system uses the Python package pint to perform unit validation and dimensional
analysis. This leverages pint’s built-in unit registry to validate units with varying prefixes
(e.g., PJ, TJ, GJ) and enables future extensions.
The basis for most unit comparisons comes from:
commodity table: Defines native units for each commodity (energy network nodes)
efficiency table: Infers technology units via input/output ratios
capacity_to_activity table: Provides conversion factors for capacity-based measures
Enabling Unit Checking#
Via Configuration File#
Add to your config.toml:
check_units = true
Via CLI#
Standalone unit checking for any database:
# Basic usage
temoa check-units path/to/database.sqlite
# Custom output directory
temoa check-units database.sqlite --output ./reports
# Silent mode (for scripting)
temoa check-units database.sqlite --silent
How It Works#
The unit checker performs five sequential tests:
Database Version Check: Ensures database is v4.0+ (checks
metadatatable)Units Entry Validation: Checks for illegal characters, proper formatting, registry membership
Technology I/O Alignment: Validates
efficiencytable units match commoditiesRelated Tables: Checks tables referencing technologies for unit consistency
Cost Tables: Validates cost units and dimensional alignment
Expressing Units#
Format Requirements#
Warning
CRITICAL: The unit checker uses regex parsing with strict format requirements!
Units in efficiency and cost tables MUST use ratio format:
Numerator / (Denominator)
[V] CORRECT: PJ / (PJ)
[V] CORRECT: Mdollar / (PJ^2 / GW)
[X] WRONG: PJ/PJ (no parentheses)
[X] WRONG: Mdollar * GW / (PJ^2) (denominator incomplete)
The denominator MUST be fully enclosed in parentheses. The regex only captures content
within ( ) after the /.
Other tables should use plain entries:
[V] PJ
[V] petajoules
[V] GW
[V] Mt / (GW) (if ratio needed)
Custom Units Registry#
Temoa extends pint’s default registry with domain-specific units:
dollar(orUSD)euro(orEUR)passengerseat(for passenger-miles, seat-miles)ethos(dimensionless source commodity)
Common Footguns and Pitfalls#
1. Missing Parentheses in Ratios#
Problem: Forgetting to parenthesize the denominator
[X] cost_invest units: "Mdollar / PJ^2 / GW"
Error: RATIO_ELEMENT regex doesn't match, only "PJ^2" captured
Solution: Always use parentheses
[V] cost_invest units: "Mdollar / (PJ^2 / GW)"
2. Capacity vs Energy Units#
Problem: Using energy units (GW*year, kWh) in capacity tables
[X] existing_capacity units: "GW * year"
Error: Energy units (not capacity) in capacity table
Solution: Use power units (negative time dimension)
[V] existing_capacity units: "GW" ([time]^-3 = power)
[X] existing_capacity units: "GWh" ([time]^-2 = energy)
Physics:
- Capacity = Power (W, kW, MW, GW) -> [time]^-3
- Energy = Power × Time (Wh, kWh) -> [time]^-2
3. Cost Table Units and C2A#
Problem: Not accounting for capacity_to_activity conversion
For capacity-based costs, the expected denominator is:
Example:
- Output commodity: ELC = PJ
- C2A: PJ / (GW * year)
- Expected: PJ * [PJ/(GW*year)] * year = PJ^2/GW
[V] cost_invest: "Mdollar / (PJ^2 / GW)"
[V] cost_fixed: "Mdollar / (PJ^2 / GW / year)" (adds /year for period-based)
4. period_based Flag#
Problem: Misunderstanding period_based vs capacity_based
These flags are orthogonal:
capacity_based: Multiply expected units by C2Aperiod_based: Divide expected units by year
cost_fixed is BOTH capacity_based AND period_based!
Table |
capacity_based |
period_based |
Units Example |
|---|---|---|---|
cost_invest |
True |
False |
Mdollar / (PJ^2 / GW) |
cost_fixed |
True |
True |
Mdollar / (PJ^2 / GW / year) |
cost_variable |
False |
False |
Mdollar / (PJ) |
cost_emission |
False |
False |
Mdollar / (Mt) |
5. Schema Variations: tech vs tech_or_group#
Problem: v4.0 limit tables use tech_or_group not tech
The unit checker automatically detects which column exists, but be aware:
-- v3.1 schema
SELECT tech, units FROM limit_capacity...
-- v4.0 schema
SELECT tech_or_group, units FROM limit_capacity...
Tables affected: limit_activity, limit_capacity, limit_new_capacity
6. Technology Output Uniformity#
Problem: Same technology with multiple output commodities using different units
Warning
All instances of a technology MUST produce output in the same units, even if output commodities differ!
Why: Constraints span regions and output commodities, requiring a single unit of measure.
[X] WRONG:
tech E01, output ELC: PJ
tech E01, output HEAT: GJ (different units!)
[V] CORRECT:
tech E01, output ELC: PJ
tech E01, output HEAT: PJ (same units)
Testing and Troubleshooting#
Testing Units Manually#
Test unit validity outside the model:
from temoa.model_checking.unit_checking import ureg
# Check if units exist in registry
print('PJ' in ureg) # True
print('catfood' in ureg) # False
# Parse and check units
u = ureg('Mdollar / (PJ^2 / GW)')
print(u.dimensionality) # {[currency]: 1, [time]: 1, ...}
# Check currency dimension (for cost tables)
print('[currency]' in u.dimensionality) # True
Common Error Messages#
"Units lack currency dimension"
-> Cost table units don't contain currency (dollar/euro)
"Energy units (not capacity) in capacity table"
-> Using energy units (kWh, GWh, etc.) instead of capacity units (GW, MW)
"Non-matching measure unit"
-> Units don't match expected format after accounting for C2A/period
"failed to process query: no such column: tech"
-> Using old SQL queries on v4.0 schema (should auto-fix now)
Reading Unit Check Reports#
Reports are saved to output_path/unit_check_reports/units_check_TIMESTAMP.txt:
======== Units Check 1 (DB Version): Started ========
Units Check 1 (DB Version): Passed
======== Units Check 2 (Units Entries in Tables): Started ========
existing_capacity: Energy units (not capacity) in capacity table: GW*year at rows: 1, 2, 3
======== Units Check 3 (Tech I/O via Efficiency Table): Started ========
Efficiency units conflict with associated commodity for Technology E01 near row 1
======== Units Check 4 (Related Tables): Started ========
limit_capacity: Non-standard units for tech E01 (expected GW) got: MW at rows: 5, 6
======== Units Check 5 (Cost Tables): Started ========
cost_invest: Non-matching measure unit for tech/comm: E01
Table entry: Mdollar / (PJ)
Expected: petajoule ** 2 / gigawatt
Found: petajoule at rows: 1, 2, 3
Each check section shows: - Which table/tech has issues - What was expected vs what was found - Row numbers for easy correction
Tables Checked#
v4.0 Schema Coverage#
Table |
Has Units |
Check 2 |
Check 3 |
Check 4 |
Check 5 |
|---|---|---|---|---|---|
capacity_to_activity |
[V] |
[V] |
(used) |
(used) |
|
commodity |
[V] |
[V] |
[V] |
||
construction_input |
[V] |
[V] |
|||
cost_emission |
[V] |
[V] |
[V] |
||
cost_fixed |
[V] |
[V] |
[V] |
||
cost_invest |
[V] |
[V] |
[V] |
||
cost_variable |
[V] |
[V] |
[V] |
||
demand |
[V] |
[V] |
[V] |
||
efficiency |
[V] |
[V] |
[V] |
||
emission_activity |
[V] |
[V] |
|||
emission_embodied |
[V] |
[V] |
|||
emission_end_of_life |
[V] |
[V] |
|||
end_of_life_output |
[V] |
[V] |
|||
existing_capacity |
[V] |
[V] |
[V] |
||
lifetime_process |
[V] |
[V] |
|||
lifetime_tech |
[V] |
[V] |
|||
loan_lifetime_process |
[V] |
[V] |
|||
limit_activity |
[V] |
[V] |
[V] |
||
limit_capacity |
[V] |
[V] |
[V] |
||
limit_emission |
[V] |
[V] |
|||
limit_new_capacity |
[V] |
[V] |
[V] |
||
limit_resource |
[V] |
[V] |
Check Legend: - Check 2: Standard validation (format, characters, registry) - Check 3: Technology I/O alignment via Efficiency table - Check 4: Related tables consistency - Check 5: Cost tables validation
Best Practices#
Start with commodities: Define commodity units first, then build tech efficiency ratios
Use standard prefixes: Stick to k, M, G, T prefixes (kilo, mega, giga, tera)
Be consistent: Use the same unit style across your database (e.g., always “PJ” not mix of “PJ”/”petajoule”)
Test early: Run unit checker on partial databases during development
Document assumptions: Use notes fields to explain unusual unit choices
Reference implementation: See
temoa/tutorial_assets/utopia.sqlfor a fully compliant example
Quick Reference#
Common Unit Patterns#
# Energy commodities
commodity units: PJ, TJ, GJ, MWh
# Capacity
existing_capacity units: GW, MW, kW
# Efficiency (energy tech)
efficiency units: PJ / (PJ) (dimensionless)
efficiency units: PJ / (Mt) (energy from mass)
# C2A conversion
capacity_to_activity units: PJ / (GW * year)
# Costs (capacity-based tech with PJ output, PJ/(GW*year) C2A)
cost_invest units: Mdollar / (PJ^2 / GW)
cost_fixed units: Mdollar / (PJ^2 / GW / year)
cost_variable units: Mdollar / (PJ)
# Emissions
emission_activity units: Mt / (PJ)
emission_embodied units: Mt / (GW)
Dimension Reference#
Pint tracks seven base dimensions:
[length]: meter, km, mile[mass]: kg, tonne, Mt[time]: second, year[current]: ampere[temperature]: kelvin[substance]: mole[luminosity]: candela[currency]: dollar, euro (Temoa extension)
Derived dimensions:
Energy:
[length]^2 * [mass] / [time]^2(joule, kWh)Power:
[length]^2 * [mass] / [time]^3(watt, GW)Force:
[length] * [mass] / [time]^2(newton)
See Also#
database_schema - v4.0 schema reference
configuration - Configuration file format
temoa/model_checking/unit_checking/- Source codetests/test_unit_checking.py- Unit tests with examples