Skip to content

Commit 1489892

Browse files
Merge pull request #252 from TemoaProject/typing/utilities
adding types to utilities
2 parents 3ec2512 + d5d57ec commit 1489892

5 files changed

Lines changed: 102 additions & 71 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ python_version = "3.12"
144144
mypy_path = "stubs"
145145

146146
# Exclude specific directories from type checking will try to add them back gradually
147-
exclude = "(?x)(^temoa/utilities/|^stubs/)"
147+
exclude = "(?x)(^stubs/)"
148148

149149
# Strict typing for our own code
150150
disallow_untyped_defs = true

temoa/core/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def __init__(
7070
output_threshold_activity: float | None = None,
7171
output_threshold_emission: float | None = None,
7272
output_threshold_cost: float | None = None,
73+
sqlite: dict[str, object] | None = None,
7374
):
7475
if '-' in scenario:
7576
raise ValueError(
@@ -167,6 +168,8 @@ def __init__(
167168
self.cycle_count_limit = cycle_count_limit
168169
self.cycle_length_limit = cycle_length_limit
169170

171+
self.sqlite_settings = sqlite or {}
172+
170173
# warn if output db != input db
171174
if self.input_database.suffix == self.output_database.suffix: # they are both .db/.sqlite
172175
if self.input_database != self.output_database: # they are not the same db

temoa/utilities/capacity_analyzer.py

Lines changed: 73 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
is populated will influence the utility of using this method
55
"""
66

7+
import argparse
78
import itertools
8-
import os.path
99
import sqlite3
1010

11-
from definitions import PROJECT_ROOT
1211
from matplotlib import pyplot as plt
1312

1413
# Written by: J. F. Hyink
@@ -17,58 +16,75 @@
1716

1817
# Created on: 7/18/23
1918

20-
# filename of db to analyze...
21-
db = 'US_9R_8D_CT500.sqlite'
22-
23-
source_db_file = os.path.join(PROJECT_ROOT, 'data_files', 'untracked_data', db)
24-
print(source_db_file)
25-
res = []
26-
try:
27-
con = sqlite3.connect(source_db_file)
28-
cur = con.cursor()
29-
cur.execute('SELECT max_cap FROM max_capacity')
30-
for row in cur:
31-
res.append(row)
32-
33-
except sqlite3.Error as e:
34-
print(e)
35-
36-
finally:
37-
con.close()
38-
39-
# chain them together into a list
40-
caps = list(itertools.chain(*res))
41-
42-
cutoff = 1 # GW : An arbitrary cutoff between big and small capacity systems.
43-
small_cap_sources = [c for c in caps if c <= cutoff]
44-
large_cap_sources = [c for c in caps if c > cutoff]
45-
46-
aggregate_small_cap = sum(small_cap_sources)
47-
aggregate_large_cap = sum(large_cap_sources)
48-
49-
print(f'{len(small_cap_sources)} small cap sources account for: {aggregate_small_cap: 0.1f} GW')
50-
print(f'{len(large_cap_sources)} large cap sources account for: {aggregate_large_cap: 0.1f} GW')
51-
52-
plt.hist(caps, bins=100)
53-
plt.show()
54-
55-
56-
# make a cumulative contribution plot, and find a 5% cutoff
57-
cutoff_num_sources = 0
58-
caps.sort()
59-
total_cap = sum(caps)
60-
cumulative_caps = [
61-
caps[0] / total_cap,
62-
]
63-
for i, cap in enumerate(caps[1:]):
64-
cumulative_caps.append(cap / total_cap + cumulative_caps[i])
65-
if cumulative_caps[-1] < 0.05:
66-
cutoff_num_sources += 1
67-
68-
plt.plot(range(len(cumulative_caps)), cumulative_caps)
69-
plt.axvline(x=cutoff_num_sources, color='red', ls='--')
70-
plt.xlabel('Aggregated Sources')
71-
plt.ylabel('Proportion of Total Capacity')
72-
plt.title('Aggregate Capacity vs. Number of Sources')
73-
74-
plt.show()
19+
20+
def analyze_capacity(db_path: str) -> None:
21+
res = []
22+
con = None
23+
try:
24+
con = sqlite3.connect(db_path)
25+
cur = con.cursor()
26+
cur.execute('SELECT max_cap FROM max_capacity')
27+
for row in cur:
28+
res.append(row)
29+
30+
except sqlite3.Error as e:
31+
print(f'Error connecting to database: {e}')
32+
return
33+
34+
finally:
35+
if con:
36+
con.close()
37+
38+
if not res:
39+
print('No data found in max_capacity table.')
40+
return
41+
42+
# chain them together into a list
43+
caps = list(itertools.chain(*res))
44+
45+
cutoff = 1 # GW : An arbitrary cutoff between big and small capacity systems.
46+
small_cap_sources = [c for c in caps if c <= cutoff]
47+
large_cap_sources = [c for c in caps if c > cutoff]
48+
49+
aggregate_small_cap = sum(small_cap_sources)
50+
aggregate_large_cap = sum(large_cap_sources)
51+
52+
print(f'{len(small_cap_sources)} small cap sources account for: {aggregate_small_cap: 0.1f} GW')
53+
print(f'{len(large_cap_sources)} large cap sources account for: {aggregate_large_cap: 0.1f} GW')
54+
55+
plt.hist(caps, bins=100)
56+
plt.show()
57+
58+
# make a cumulative contribution plot, and find a 5% cutoff
59+
cutoff_num_sources = 0
60+
caps.sort()
61+
total_cap = sum(caps)
62+
cumulative_caps = [
63+
caps[0] / total_cap,
64+
]
65+
for i, cap in enumerate(caps[1:]):
66+
cumulative_caps.append(cap / total_cap + cumulative_caps[i])
67+
if cumulative_caps[-1] < 0.05:
68+
cutoff_num_sources += 1
69+
70+
plt.plot(range(len(cumulative_caps)), cumulative_caps)
71+
plt.axvline(x=cutoff_num_sources, color='red', ls='--')
72+
plt.xlabel('Aggregated Sources')
73+
plt.ylabel('Proportion of Total Capacity')
74+
plt.title('Aggregate Capacity vs. Number of Sources')
75+
76+
plt.show()
77+
78+
79+
def main() -> None:
80+
parser = argparse.ArgumentParser(
81+
description='Analyze capacity distribution in a Temoa database.'
82+
)
83+
parser.add_argument('db_path', help='Path to the SQLite database file.')
84+
args = parser.parse_args()
85+
86+
analyze_capacity(args.db_path)
87+
88+
89+
if __name__ == '__main__':
90+
main()

temoa/utilities/graph_utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@
4040
GraphType = TypeVar('GraphType', nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)
4141

4242

43-
def convert_graph_to_json[GraphType: (nx.Graph, nx.DiGraph, nx.MultiGraph, nx.MultiDiGraph)](
43+
def convert_graph_to_json[
44+
GraphType: (nx.Graph[Any], nx.DiGraph[Any], nx.MultiGraph[Any], nx.MultiDiGraph[Any])
45+
](
4446
nx_graph: GraphType,
4547
override_node_properties: dict[str, Any] | None,
4648
override_edge_properties: dict[str, Any] | None,

temoa/utilities/unit_cost_explorer.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
from temoa.components.costs import total_cost_rule
99
from temoa.components.storage import storage_energy_upper_bound_constraint
1010
from temoa.core.model import TemoaModel
11+
from temoa.types.core_types import (
12+
Period,
13+
Region,
14+
Season,
15+
Technology,
16+
TimeOfDay,
17+
Vintage,
18+
)
1119

1220
# Written by: J. F. Hyink
1321
# jeff@westernspark.us
@@ -29,8 +37,8 @@
2937

3038

3139
# indices
32-
rtv = ('A', 'battery', 2020) # rtv
33-
rptv = ('A', 2020, 'battery', 2020) # rptv
40+
rtv = (Region('A'), Technology('battery'), Vintage(2020)) # rtv
41+
rptv = (Region('A'), Period(2020), Technology('battery'), Vintage(2020)) # rptv
3442
model.time_future.construct([2020, 2025, 2030]) # needs to go 1 period beyond optimize horizon
3543
model.time_optimize.construct([2020, 2025])
3644
model.period_length.construct()
@@ -132,21 +140,23 @@
132140

133141
# More VARS
134142
model.v_storage_level.construct()
135-
model.segment_fraction_per_season.construct(
136-
data=seasonal_fractions
143+
model.segment_fraction_per_season.construct(data=seasonal_fractions)
144+
145+
model.is_seasonal_storage[Technology('battery')] = False
146+
upper_limit = storage_energy_upper_bound_constraint(
147+
model,
148+
Region('A'),
149+
Period(2020),
150+
Season('winter'),
151+
TimeOfDay('1'),
152+
Technology('battery'),
153+
Vintage(2020),
137154
)
138-
139-
model.is_seasonal_storage['battery'] = False
140-
upper_limit = storage_energy_upper_bound_constraint(model, 'A', 2020, 'winter', 1, 'battery', 2020)
141155
print('The storage level constraint for the single period in the "super day":\n', upper_limit)
142156

143157
# cross-check the multiplier...
144158
mulitplier = (
145-
storage_dur
146-
* model.segment_fraction_per_season['winter']
147-
* model.days_per_period
148-
* c2a
149-
* c
159+
storage_dur * model.segment_fraction_per_season['winter'] * model.days_per_period * c2a * c
150160
)
151161
print(f'The multiplier for the storage should be: {mulitplier}')
152162

0 commit comments

Comments
 (0)