-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathqueries.py
More file actions
298 lines (229 loc) · 10.8 KB
/
queries.py
File metadata and controls
298 lines (229 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
from lode.database.utilities import (
check_required_range, check_optional_range, multi_query, merge_meta)
import query_builders as qb
import warnings
def query_nodal_price(begin_date=None, end_date=None, dates=None,
begin_period=None, end_period=None, periods=None,
minimum_price=None, maximum_price=None, nodes="Major",
apply_meta=False, database=None):
"""
Query the Nodal Price in a simplified fashion with all of the information
held behind the scenes. This function is one of the primary interfaces
with the database although the queries can be run directly as needed using
other hidden functionality.
Parameters:
-----------
begin_date: What begin point of a date range to query, string or datetime
end_date: What end point of a date range to query, string or datetime
dates: What dates to specifically query, string, list
begin_period: Beginning point of a query range, int
end_period: End point of a query range, int
periods: What periods to query, defaults to all
nodes: What nodes to query, defaults to the Major nodes
minimum_price: Minimum price to query, useful for finding specific
instances of a priced situation over a wide number of time
instances
maximum_price: Maximum price to query, e.g. use in combination with
minimum_price
apply_meta: Whether to apply the associated meta information to the
returned dataframe
database: What database to connect to, leave None for the default setup
Returns:
--------
A DataFrame containing all of the information with the specific filters
applied
"""
# Set Database to the default nodal prices one
if database is None:
database = 'nodal_database'
# Core nodes are those of some importance to the market in some way
# E.g. major population centre, generation site, HVDC etc
if nodes == "Core":
nodes = ("OTA2201", "BEN2201", "HAY2201", "HLY2201",
"MAN2201", "BPE2201", "PEN2201", "ISL2201")
# Major nodes are the big three, Auckland, NI HVDC, SI HVDC
elif nodes == "Major":
nodes = ("OTA2201", "HAY2201", "BEN2201")
# Check the dates
check_required_range(dates, begin_date, end_date)
check_optional_range(periods, begin_period, end_period)
all_queries = qb.create_date_limited_sql("nodal_prices", dates=dates,
begin_date=begin_date,
end_date=end_date,
date_col="Trading_date")
completed_queries = []
for sql in all_queries:
if periods:
sql += qb.add_equality_constraint('Trading_period', periods)
if (begin_period and end_period):
sql += qb.add_range_constraint('Trading_period',
begin_period, end_period)
if nodes:
sql += qb.add_equality_constraint('Node', nodes)
if minimum_price:
sql += qb.add_minimum_constraint('Price', minimum_price)
if maximum_price:
sql += qb.add_maximum_constraint('Price', maximum_price)
# Finish modifying the SQL so add a semicolon to end it
sql += ';'
# Add to the completed queries
completed_queries.append(sql)
# Query using the query to dataframe method and a generator
prices = multi_query(database, completed_queries)
if apply_meta:
warnings.warn('Metadata may be incomplete')
prices = merge_meta(prices, 'price')
return prices
def query_nodal_demand(begin_date=None, end_date=None, dates=None,
begin_period=None, end_period=None, periods=None,
nodes=None, minimum_demand=None,
maximum_demand=None, apply_meta=False,
meta_group=None, meta_agg=None,
excl_nodes=None, database=None):
"""
Query the nodal demand from the database at the GXP level. This results
in very large DataFrames as there are approximately 450 nodes to query
at any given point in time. Some values may be negative (for generation)
and these can be excluded by setting a minimum demand or specifically via
the various nodes.
Meta information can also be applied but this may be incomplete at the
current point in time. Use at your own risk.
Parameters:
-----------
begin_date: What begin point of a date range to query, string or datetime
end_date: What end point of a date range to query, string or datetime
dates: What dates to specifically query, string, list
begin_period: Beginning point of a query range, int
end_period: End point of a query range, int
periods: What periods to query, defaults to all
nodes: What nodes to query, defaults to all
minimum_demand: Minimum GXP demand figure, may wish to set to 0.
maximum_demand: Maximum GXP demand figure
apply_meta: Whether to add the meta information such as richer locations
meta_group: Whether to group the nodes by any particular reference point
meta_agg: Apply a meta aggregation function
excl_nodes: What nodes to exclude, optionally set this to "Wind"
database: Optional, what Database to connect to, leave this Node for the
default, useful if you have changed the database names.
Returns:
--------
DataFrame: Pandas Dataframe containing the query information
"""
if database is None:
database = 'nodal_database'
# Helper function for the wind nodes as wind is a negative load at the
# moment on the GXPs
if excl_nodes == "Wind":
excl_nodes = ("TWC2201", "WDV1101", "WWD1101", "WWD1102",
"WWD1103", "TWH0331")
# Error checking on the dates and period range consistencies
check_required_range(dates, begin_date, end_date)
check_optional_range(periods, begin_period, end_period)
# Set up the initial SQL queries with dates loaded in
all_queries = qb.create_date_limited_sql("nodal_demand", dates=dates,
begin_date=begin_date,
end_date=end_date,
date_col="Trading_date")
completed_queries = []
for sql in all_queries:
if periods:
sql += qb.add_equality_constraint('Trading_period', periods)
if (begin_period and end_period):
sql += qb.add_range_constraint('Trading_period',
begin_period, end_period)
if nodes:
sql += qb.add_equality_constraint("Node", nodes)
if minimum_demand:
# Don't use zero values, instead use a very small float
if minimum_demand == 0:
minimum_demand = 0.0001
sql += qb.add_minimum_constraint("Demand", minimum_demand)
if maximum_demand:
sql += qb.add_maximum_constraint("Demand", maximum_demand)
# Exclude certain nodes
if excl_nodes:
sql += qb.add_exclusion_constraint("Node", excl_nodes)
sql += ';'
completed_queries.append(sql)
demand = multi_query(database, completed_queries)
if apply_meta:
warnings.warn('Metadata may be incomplete')
demand = merge_meta(demand, 'demand')
if meta_group and meta_agg:
grouped = demand.groupby(meta_group)
aggregate = grouped.aggregate(meta_agg)
return aggregate
return demand
def query_offer(offer_type, dates=None, begin_date=None, end_date=None,
periods=None, begin_period=None, end_period=None,
companies=None, stations=None, nodes=None,
as_offerframe=True, database=None):
"""
Master function to query the submitted Energy, Generator Reserve and IL
offers in the NZEM. Has some basic filters as well as the ability to return
the
Parameters:
-----------
offer_type: What type of offer to query, e.g. Energy, PLSR or IL
begin_date: What begin point of a date range to query, string or datetime
end_date: What end point of a date range to query, string or datetime
dates: What dates to specifically query, string, list
begin_period: Beginning point of a query range, int
end_period: End point of a query range, int
periods: What periods to query, defaults to all
nodes: What nodes to query, defaults to all
companies: What companies to query, e.g. MRPL, this is a four letter code
stations: What stations to query, three letter code
as_offerframe: Convert the raw offer to an OfferFrame, note requires
OfferFrame to be installed
Returns:
--------
OfferFrame or DataFrame of the offers queried
"""
if database is None:
database = 'offer_database'
# Create the tables
tables = {"Energy": 'energy_offers',
"PLSR": 'generatorreserves_offers',
"IL": 'ilreserves_offers'}
# Map grid locations based upon which query is being run.
grid_map = {"Energy": "grid_injection_point",
"PLSR": "grid_point",
"IL": "grid_exit_point"}
check_required_range(dates, begin_date, end_date)
check_optional_range(periods, begin_period, end_period)
# Create the queries
all_queries = qb.create_date_limited_sql(tables[offer_type], dates=dates,
begin_date=begin_date,
end_date=end_date)
# Add all of the other constraints:
completed_queries = []
for sql in all_queries:
if periods:
sql += qb.add_equality_constraint('trading_period', periods)
elif (begin_period and end_period):
sql += qb.add_range_constraint('trading_period',
begin_period, end_period)
if companies:
sql += qb.add_equality_constraint('company', companies)
if stations:
sql += qb.add_equality_constraint('station', stations)
if nodes:
sql += qb.add_equality_constraint(grid_map[offer_type], nodes)
# Once all constraints have been added end the SQL statement
sql += ';'
completed_queries.append(sql)
offers = multi_query(database, completed_queries)
# Optional to return it as an OfferFrame
if as_offerframe:
try:
from OfferPandas import Frame
except ImportError:
raise ImportError('OfferPandas has not been installed')
warnings.warn(
'Metadata may be incomplete or erroneous use at your own risk')
df = Frame(offers)
return df.modify_frame()
return offers
if __name__ == '__main__':
pass