Skip to content

Commit 0d1534a

Browse files
authored
Merge pull request #127 from pnnl/develop
Develop
2 parents cc4847e + 30c9c48 commit 0d1534a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1319
-17123
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ Change log:
2727
- v1.2.1 Fixed python shell files, fixed complex python to use helics complex.
2828
- v1.2.2 Fixed the installation for Ubuntu 22.04.
2929
- v1.3.0 Refactor the TESP PyPI api. Upgrade all models(GridLAB-D, EnergyPlus, NS3) to work with HELICS 3.4. Add modifier.py for GridLAB-D models
30+
- v1.3.2 Updated model and modifier for GridLAB-D models, added readme for GLM modifier and Store examples

doc/Demonstrations_and_Examples.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ TESP Capability Demonstrations
3030
./demonstrations/houses.rst
3131
./demonstrations/gld_player_recorder.rst
3232
./demonstrations/gld_modifier.rst
33+
./demonstrations/datastore.rst
34+
3335

3436
TESP uses TCP/IP port 5570 for communication and requires Python 3. Simulations can start many processes, and take minutes or hours to complete. At this time, instructions are given only for the Linux package or Docker version of TESP, which is installable. See below for advice on running TESP in native Mac OS X or Windows.
3537

doc/demonstrations/gld_modifier.rst

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,29 +41,32 @@ With the modifier, we can read the GridLAB-D model into the GLMModifier data str
4141
GridLAB-D split their functionality into various modules and for this example, we're going to be adding houses to the model which means we need to make sure the "residential" module gets added to the model file.::
4242

4343
glmMod.add_module('residential', [])
44+
45+
The GLMModifier has an attribute that holds the entire GridLAB-D model, "glm". Purely to make our live a few characters easier when using the GLMModifier, we can assign this to another variable of our choice and strip out the need constantly pre-pend many of our commands with "GLMModifier".::
4446

45-
GLMModifier makes it easy to get the names of all of the objects of a given class and in this case, to add houses, we need to look for GridLAB-D's "triplex_meters" to attach the houses to.::
46-
47-
tp_meter_names = glmMod.get_object_names('triplex_meter')
47+
glm = GLMMod.glm
4848

49-
If we wanted the objects themselves we would instead call::
49+
GLMModifier makes it easy to get the names of all of the objects of a given class and in this case, to add houses, we need to look for GridLAB-D's "triplex_meters" to attach the houses to.::
5050

51-
tp_meter_objs = glmMod.get_object('triplex_meter').instance
51+
tp_meter_objs = glm.triplex_meter
52+
tp_meter_names = list(tp_meter_objs.keys())
5253

53-
``tp_meter_objs`` is then a Python dictionary with the keys being the object names and the value being an Python dictionary of the object parameters and values.
54+
``tp_meter_objs`` is a Python dictionary with the keys being the object names and the value being an Python dictionary of the object parameters and values. To make a list of the names of the meters, we just need to ask for the keys of the dictionary as a list.
5455

5556
Adding Objects
5657
--------------
5758

5859
``tp_meter_names`` is a list of the names of the GridLAB-D ``triplex_meter`` objects as strings. Using those names we can build up a Python dictionary that defines the parameters of another ``triplex_meter`` object we're going to add to the model. The dictionary is called "meter_params" and has three members all defined by the data from an existing specific ``triplex_meter`` in the model.::
5960

61+
new_name = tp_meter_names[house_num]
62+
billing_meter_name = f"{new_name}_billing"
6063
meter_params = {
61-
'parent': tp_meter_names[house_num],
62-
'phases': glmMod.get_object('triplex_meter').instance[f'{tp_meter_names[house_num]}']['phases'],
63-
'nominal_voltage': glmMod.get_object('triplex_meter').instance[tp_meter_names[house_num]]['nominal_voltage']
64+
"parent": new_name,
65+
"phases": glm.triplex_meter[f"{new_name}"]["phases"],
66+
"nominal_voltage": glm.triplex_meter[f"{new_name}"]["nominal_voltage"],
6467
}
6568

66-
The ``phases`` and ``nominal_voltage`` show a common practice with the GLMModifier: ask GLMModifier for all objects of a certain type in the model and then ask for a specific one from that list by name. This shows up as a ``.get_objects("name of GridLAB-D class").instance("object name")``. ``.instance()`` returns a Python dictionary with all the GridLAB-D parameters as members. Alternatively, there is a specific API that does the same thing: ``glmMod.get_object_named_instance("name of GridLAB-D class", "object name")``
69+
The ``phases`` and ``nominal_voltage`` are easily defined using GLMModifier as they are just members of a dictionary that defines a specific triplex meter.
6770

6871
Once the Python dictionary with the GridLAB-D object parameters are defined, it can simply be added to the model.::
6972

@@ -73,23 +76,23 @@ Once the Python dictionary with the GridLAB-D object parameters are defined, it
7376

7477
Adding and Modifying Existing Object Parameter Values
7578
-----------------------------------------------------
76-
Further down in the example, there's an example of how to modify an existing object. In this case, the ``.add_object()`` method returns the the GridLAB-D object (effectively a Python dictionary). Its also possible to get the same object using the ``.get_objects().instance()``. Using either method, once you have the GridLAB-D object, its easy to modify any of its properties such as::
79+
Further down in the example, there's a portion of code showing of how to modify an existing object. In this case, we use the fact that ``.add_object()`` method returns the the GridLAB-D object (effectively a Python dictionary) once it is added to the model. Once you have the GridLAB-D object, its easy to modify any of its properties such as::
7780

7881
house_obj['floor_area'] = 2469
7982

8083
This exact syntax is also valid for adding a parameter that is undefined to an existing GridLAB-D object.
8184

8285
Deleting Existing Object Parameter Values
8386
-----------------------------------------
84-
To delete a GridLAB-D object parameter value there is a dedicated API call::
87+
To delete a GridLAB-D object parameter value, you can just set to to `None`::
8588

86-
glmMod.del_object_attr('house', house_name, 'Rroof')
89+
house_to_edit["Rroof"] = None
8790

88-
Note that GridLAB-D requires some parameters to be defined to run its simulations. Removing the parameter will remove it from the GridLAB-D model file that gets created (.glm) but may effectively force GridLAB-D to use its internal default value.
91+
Note that GridLAB-D requires some parameters to be defined to run its simulations. Removing the parameter will remove it from the GridLAB-D model file that gets created (.glm) but may effectively force GridLAB-D to use its internal default value. That is, clearing the parameter value in this way is not the same as setting it to an undefined value.
8992

9093
Deleting Existing Objects
9194
-------------------------
92-
Its possible to delete an object and all its parameter values::
95+
Its possible to delete an object and all its parameter values from the GridLAB-D model::
9396

9497
glmMod.del_object('house', house_to_delete)
9598

examples/capabilities/datastore/te30_usestore.py

Lines changed: 97 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,111 @@
66
"""
77

88

9-
import tesp_support.api.store as fle
9+
import tesp_support.api.store as store
10+
import os
11+
import pprint
12+
import matplotlib.pyplot as plt
13+
import pandas as pd
14+
15+
# Setting up pretty printing, mostly for debugging.
16+
pp = pprint.PrettyPrinter(indent=4)
1017

1118

1219
def process_results(case_name):
1320
"""
1421
Opens up datastore (.zip) and metadata (.json) to process results
15-
22+
1623
Assumes te30_store.zip and te30_store.json have been copied from ../te30 folder to
1724
the same folder as this script (examples/capabilities/datastore).
1825
"""
19-
# Load in metadata to see what contents are in the TE30 store
20-
# fle.unzip(case_name)
21-
22-
# example
23-
my_store = fle.Store(case_name)
24-
# this is a cvs file
25-
my_file = my_store.get_schema('weather')
26-
data = my_file.get_series_data('weather', '2013-07-01 00:00', '2013-07-02 00:00')
27-
tseries = [data]
28-
print(tseries)
29-
30-
# List all the files in the store for inspection
31-
for item in my_store.get_schema():
32-
print(item)
33-
34-
# Arbitrarily, let's look at the real power load at a specific billing meter
35-
# This data is coming from GridLAB-D (based on the TE30 documentation)
36-
37-
# metadata table has the name of each parameter and "value" is the unit.
38-
# Same order as what's in the index tables
26+
start_date_1 = "2013-07-01 00:00"
27+
end_date_1 = "2013-07-02 00:00"
28+
29+
# Depending on how your unzip tools work, unzipping the data store may
30+
# create another folder and put all the zipped files into it. If so,
31+
# we need to change our working directory to that folder.
32+
if os.path.exists(case_name):
33+
os.chdir(case_name)
34+
35+
# Define the data store
36+
te30_store = store.Store(case_name)
37+
38+
# List all the files in the store for inspection; particularly useful if
39+
# you're new to the dataset
40+
print(f"Schemas in data store:")
41+
for item in te30_store.get_schema():
42+
print(f"\t{item}")
43+
44+
# Get weather data (CSV)
45+
# "weather" is the name of the data store schema for "weather.csv
46+
weather_schema = te30_store.get_schema("weather")
47+
48+
# Inspect the available tables and data
49+
print(f"Weather tables {pp.pformat(weather_schema.tables)}")
50+
print(f"Weather columns {pp.pformat(weather_schema.columns)}")
51+
# The "solar_flux" column is what we're looking for.
52+
53+
# For better or worse, the single table of data inside weather.csv is
54+
# also named "weather".
55+
weather_data = weather_schema.get_series_data("weather", start_date_1, end_date_1)
56+
57+
# Checking data type for timestamp and convert if necessary
58+
weather_time = weather_data["timestamp"]
59+
if isinstance(weather_time.iloc[0], str):
60+
weather_time = pd.to_datetime(weather_time, format="%Y-%m-%d %H:%M:%S PDT")
61+
# And convert the data as strings to numeric values
62+
if isinstance(weather_data["solar_flux"].iloc[0], str):
63+
solar_data = pd.to_numeric(weather_data["solar_flux"])
64+
65+
# As a convenience, make a new dataframe with only the data I need
66+
weather_data = pd.concat([weather_time.T, solar_data], axis=1)
67+
weather_data = weather_data.set_index("timestamp")
68+
69+
# Get rooftop solar production data (HDF5)
70+
inverter_schema = te30_store.get_schema("inverter_TE_ChallengeH_metrics")
71+
72+
print(f"Inverter tables list {pp.pformat(inverter_schema.tables)}")
73+
print(f"Inverter columns dictionary {pp.pformat(inverter_schema.columns)}")
74+
# For silly reasons, GridLAB-D stores each day of data in its own table
75+
# called "index1", "index2", etc.
76+
77+
# The schema is just a dictionary so if we want to look at columns for just a
78+
# table it's pretty easy. (In this case the columns are identical for each day so
79+
# it's not that exciting.)
80+
print(f"What is this? {pp.pformat(inverter_schema.tables)}")
81+
print(
82+
f"Inverter columns list for table of data for first simulated day ('index1') "
83+
f"{pp.pformat(inverter_schema.columns['index1'])}"
84+
)
85+
86+
inverter_data = inverter_schema.get_series_data("index1", start_date_1, end_date_1)
87+
# Just going to be looking at data from a single house
88+
houseA11_inv = inverter_data.loc[(inverter_data["name"] == b"inv_F1_house_A11")]
89+
inverter_time = houseA11_inv["date"]
90+
# If the date is typed as a string instead of a datetime object, we need to
91+
# convert it to a datetime object to allow for indexing.
92+
if isinstance(inverter_time.iloc[0], str):
93+
inverter_time = inverter_time.str[:-4]
94+
inverter_time = pd.to_datetime(inverter_time, format="%Y-%m-%d %H:%M:%S")
95+
# Making a new DataFrame for convenience
96+
inverter_data = pd.concat([inverter_time.T, houseA11_inv["real_power_avg"]], axis=1)
97+
inverter_data = inverter_data.set_index("date")
98+
99+
# Plot the resulting data
100+
fig = plt.figure()
101+
ax1 = fig.add_subplot()
102+
ax1.set_xlabel("Time")
103+
ax1.set_ylabel("Inverter Power (W)")
104+
ax1.plot(inverter_data["real_power_avg"], label="Inverter Power", color="blue")
105+
ax2 = ax1.twinx()
106+
ax2.set_ylabel("Solar Flux (W/ft^2)")
107+
ax2.plot(weather_data["solar_flux"], label="Solar Flux", color="orange")
108+
h1, l1 = ax1.get_legend_handles_labels()
109+
h2, l2 = ax2.get_legend_handles_labels()
110+
ax1.legend(h1 + h2, l1 + l2, loc=2)
111+
fig.autofmt_xdate(rotation=45)
112+
plt.show()
39113

40114

41115
if __name__ == "__main__":
42-
process_results("te30_store")
116+
process_results("te30_store")

0 commit comments

Comments
 (0)