Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue604 kpis disaggregated #606

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7528147
Add method to get KPIs disaggregated.
javiarrobas Jan 11, 2024
050da8c
Interface to get_kpis_disaggregated at testcase.py
javiarrobas Jan 11, 2024
34eda99
Implement Rest API call.
javiarrobas Jan 11, 2024
8f7f31d
Add check for getting all core KPIs.
javiarrobas Jan 11, 2024
973c9da
Add check for getting all KPIs disaggregated.
javiarrobas Jan 11, 2024
c521868
Add reference results for new tests.
javiarrobas Jan 11, 2024
f032705
Calculate integral separately not to add up when computing by source.
javiarrobas Jan 11, 2024
08a4dfc
Normalize peak power only for peak_tot, not for peak_dict.
javiarrobas Jan 11, 2024
995d842
Update xxxx_dict references due to peak KPIs normalized only at xxxx_…
javiarrobas Jan 11, 2024
893ce5d
Describe new method in releasenotes.md.
javiarrobas Jan 11, 2024
622ae19
Add kpi_disaggregated to README.md.
javiarrobas Jan 11, 2024
f13818a
Update refs for numerical differences.
javiarrobas Jan 12, 2024
7df1a1c
Merge branch 'master' into issue604_kpisDisaggregated
javiarrobas Mar 22, 2024
3ac058d
Add release note.
javiarrobas Mar 22, 2024
af18410
Run pre-commit
javiarrobas Mar 22, 2024
0d0bfee
Add space before returns.
javiarrobas Mar 22, 2024
5a0a545
Add space before end of docstring.
javiarrobas Mar 22, 2024
c5fc249
Be more specific in README.md
javiarrobas Mar 22, 2024
2fb643e
Merge commit '71b5e01cca4febe336681fa2d7d987dbd5f4c269' into issue604…
javiarrobas Jun 7, 2024
ae96509
Move contribution to v0.6.0-dev.
javiarrobas Jun 7, 2024
8f29072
Add section in the design guide describing disaggregation.
javiarrobas Jun 7, 2024
c5615b6
Change reference for numerical difference in pgas_tot.
javiarrobas Jun 13, 2024
8706b2c
Change for numerical differences in submit references for bestest hyd…
javiarrobas Jun 13, 2024
4715f36
Change for numerical differences in submit references for multizone_r…
javiarrobas Jun 13, 2024
68f9c41
Use assertEqual to show numerical differences.
javiarrobas Jun 14, 2024
def70cc
Update refs for numerical diffs.
javiarrobas Jun 15, 2024
4c9be91
Allow unlimited diff output.
javiarrobas Jun 15, 2024
b5b9e9c
Update ref for numerical diff in multizon_residential_hydronic.
javiarrobas Jun 15, 2024
7dcec68
Update bestest_hydronic/submit.json for numerical differences.
javiarrobas Jul 26, 2024
c9d92d7
Merge branch 'master' into issue604_kpisDisaggregated
javiarrobas Aug 2, 2024
2b735b6
Update for numerical differences.
javiarrobas Aug 8, 2024
3d040cd
Merge commit '7deb826ce27b528a1bf0721fbb8a1424086ebc4d' into issue604…
javiarrobas Aug 8, 2024
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Example RESTful interaction:
| Receive control signal point names (u) and metadata. | GET ``inputs`` |
| Receive test result data for the given point names between the start and final time in seconds. | PUT ``results`` with required arguments ``point_names=<list of strings>``, ``start_time=<value>``, ``final_time=<value>``|
| Receive test KPIs. | GET ``kpi`` |
| Receive test KPIs disaggregated into contributing components (e.g. each equipment or zone) ...| GET ``kpi_disaggregated`` |
| Receive test case name. | GET ``name`` |
| Receive boundary condition forecast from current communication step for the given point names for the horizon and at the interval in seconds. | PUT ``forecast`` with required arguments ``point_names=<list of strings>``, ``horizon=<value>``, ``interval=<value>``|
| Receive boundary condition forecast available point names and metadata. | GET ``forecast_points`` |
Expand Down
21 changes: 21 additions & 0 deletions docs/design/source/core_kpi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,27 @@ Maximum allowed capital cost
relies on the BOPTEST user, who can use the objective quantification
of this KPI to take the decision.

Core KPIs disaggregation
---------------------------

The main purpose of the core KPIs is to enable benchmarking across different controllers.
That is the why these KPIs are provided in an aggregated format with all contributions
from each source added up to a unique value which is normlized by floor area or number of zones.
Returning a unique value facilitates comparisons between controllers.
However, there may be some cases where getting all contributions from each source separately
could be useful to gain insights and anlyze results.
We might be interested, for example, in what is the specific contribution of a pump and
a heat pump to the overal energy use of a building to decide whether we can
neglect the pump operation in our controller logic.

The GET ´´kpi_disaggregated´´ API call gives this information as it returns the value of the core
KPIs disaggregated by the contribution of each source element.
The returned results are in absolute values, that is, they are not normalized by floor area or
by number of zones.
In the case of peak power KPIs it should be noted that what is returned is the contribution of
each element to the total peak when it is reached (instead of providing the peak power of each
individual element separately).

Calculation Module
---------------------

Expand Down
69 changes: 50 additions & 19 deletions kpis/kpi_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,40 @@ def get_core_kpis(self, price_scenario='Constant'):

return ckpi

def get_kpis_disaggregated(self, price_scenario='Constant'):
'''Return the core KPIs of a test case disaggregated and
with absolute values (not normalized by area or zone)
to see the contributions of each element to each KPI.

Parameters
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add space before Parameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

----------
price_scenario : str, optional
Price scenario for cost kpi calculation.
'Constant' or 'Dynamic' or 'HighlyDynamic'.
Default is 'Constant'.

Returns
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add space before Returns.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-------
dkpi = dict
Dictionary with the core KPIs disaggregated and
with absolute values.

'''
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add space before '''.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


_ = self.get_core_kpis(price_scenario=price_scenario)

dkpi = OrderedDict()
dkpi['tdis'] = self.tdis_dict
dkpi['idis'] = self.idis_dict
dkpi['ener'] = self.ener_dict
dkpi['cost'] = self.cost_dict
dkpi['emis'] = self.emis_dict
dkpi['pele'] = self.pele_dict
dkpi['pgas'] = self.pgas_dict
dkpi['pdih'] = self.pdih_dict

return dkpi

def get_thermal_discomfort(self):
'''The thermal discomfort is the integral of the deviation
of the temperature with respect to the predefined comfort
Expand Down Expand Up @@ -333,11 +367,10 @@ def get_energy(self):
if 'Power' in source:
for signal in self.case.kpi_json[source]:
pow_data = np.array(self._get_data_from_last_index(signal,self.i_last_ener))
self.ener_dict[signal] += \
trapz(pow_data,
self._get_data_from_last_index('time',self.i_last_ener))*2.77778e-7 # Convert to kWh
self.ener_dict_by_source[source+'_'+signal] += \
self.ener_dict[signal]
integral = trapz(pow_data,
self._get_data_from_last_index('time',self.i_last_ener))*2.77778e-7 # Convert to kWh
self.ener_dict[signal] += integral
self.ener_dict_by_source[source+'_'+signal] += integral
self.ener_tot = self.ener_tot + self.ener_dict[signal]/self.case._get_area() # Normalize total by floor area

# Assign to case
Expand Down Expand Up @@ -382,10 +415,10 @@ def get_peak_electricity(self):
df_pow_data_all = pd.concat([df_pow_data_all, df_pow_data], axis=1)
df_pow_data_all.index = pd.TimedeltaIndex(df_pow_data_all.index, unit='s')
df_pow_data_all['total_demand'] = df_pow_data_all.sum(axis=1)
df_pow_data_all = df_pow_data_all.resample('15T').mean()/self.case._get_area()/1000.
df_pow_data_all = df_pow_data_all.resample('15T').mean()/1000.
i = df_pow_data_all['total_demand'].idxmax()
peak = df_pow_data_all.loc[i,'total_demand']
self.pele_tot = peak
self.pele_tot = peak/self.case._get_area()
# Find contributions to peak by each signal
for signal in self.case.kpi_json[source]:
self.pele_dict[signal] = df_pow_data_all.loc[i,signal]
Expand Down Expand Up @@ -429,10 +462,10 @@ def get_peak_gas(self):
df_pow_data_all = pd.concat([df_pow_data_all, df_pow_data], axis=1)
df_pow_data_all.index = pd.TimedeltaIndex(df_pow_data_all.index, unit='s')
df_pow_data_all['total_demand'] = df_pow_data_all.sum(axis=1)
df_pow_data_all = df_pow_data_all.resample('15T').mean()/self.case._get_area()/1000.
df_pow_data_all = df_pow_data_all.resample('15T').mean()/1000.
i = df_pow_data_all['total_demand'].idxmax()
peak = df_pow_data_all.loc[i,'total_demand']
self.pgas_tot = peak
self.pgas_tot = peak/self.case._get_area()
# Find contributions to peak by each signal
for signal in self.case.kpi_json[source]:
self.pgas_dict[signal] = df_pow_data_all.loc[i,signal]
Expand Down Expand Up @@ -476,10 +509,10 @@ def get_peak_district_heating(self):
df_pow_data_all = pd.concat([df_pow_data_all, df_pow_data], axis=1)
df_pow_data_all.index = pd.TimedeltaIndex(df_pow_data_all.index, unit='s')
df_pow_data_all['total_demand'] = df_pow_data_all.sum(axis=1)
df_pow_data_all = df_pow_data_all.resample('15T').mean()/self.case._get_area()/1000.
df_pow_data_all = df_pow_data_all.resample('15T').mean()/1000.
i = df_pow_data_all['total_demand'].idxmax()
peak = df_pow_data_all.loc[i,'total_demand']
self.pdih_tot = peak
self.pdih_tot = peak/self.case._get_area()
# Find contributions to peak by each signal
for signal in self.case.kpi_json[source]:
self.pdih_dict[signal] = df_pow_data_all.loc[i,signal]
Expand Down Expand Up @@ -541,11 +574,10 @@ def get_cost(self, scenario='Constant'):
# Calculate costs
for signal in self.case.kpi_json[source]:
pow_data = np.array(self._get_data_from_last_index(signal,self.i_last_cost))
self.cost_dict[signal] += \
trapz(np.multiply(source_price_data,pow_data),
integral = trapz(np.multiply(source_price_data,pow_data),
self._get_data_from_last_index('time',self.i_last_cost))*factor
self.cost_dict_by_source[source+'_'+signal] += \
self.cost_dict[signal]
self.cost_dict[signal] += integral
self.cost_dict_by_source[source+'_'+signal] += integral
self.cost_tot = self.cost_tot + self.cost_dict[signal]/self.case._get_area() # Normalize total by floor area

# Assign to case
Expand Down Expand Up @@ -585,11 +617,10 @@ def get_emissions(self):
['Emissions'+source])
for signal in self.case.kpi_json[source]:
pow_data = np.array(self._get_data_from_last_index(signal,self.i_last_emis))
self.emis_dict[signal] += \
trapz(np.multiply(source_emissions_data,pow_data),
integral = trapz(np.multiply(source_emissions_data,pow_data),
self._get_data_from_last_index('time',self.i_last_emis))*2.77778e-7 # Convert to kWh
self.emis_dict_by_source[source+'_'+signal] += \
self.emis_dict[signal]
self.emis_dict[signal] += integral
self.emis_dict_by_source[source+'_'+signal] += integral
self.emis_tot = self.emis_tot + self.emis_dict[signal]/self.case._get_area() # Normalize total by floor area

# Update last integration index
Expand Down
2 changes: 1 addition & 1 deletion releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Released on xx/xx/xxxx.
- Specify version of scipy to 1.13.0 in test case Dockerfile. This is for [#657](https://github.com/ibpsa/project1-boptest/issues/657).
- Remove javascript controller example. This is for [#664](https://github.com/ibpsa/project1-boptest/issues/664).
- Add a new directory ``/baselines``, containing baseline testing scripts and associated KPI results for the baseline controllers of all the testcases. This is for [#495](https://github.com/ibpsa/project1-boptest/issues/495).
- Implement method to get disaggregated KPIs with absolute values. This enables to make a more comprehensive analysis of which elements are contributing to each KPI. This is for [#604](https://github.com/ibpsa/project1-boptest/issues/604).

## BOPTEST v0.6.0

Expand All @@ -33,7 +34,6 @@ Released on 04/03/2024.
- Add ``activate`` control inputs to all test case documentation and update ``get_html_IO.py`` to print one file with all inputs, outputs, and forecasts. This is for [#555](https://github.com/ibpsa/project1-boptest/issues/555).
- Add storing of scenario result trajectories, kpis, and test information to simulation directory within test case docker container. This is for [#626](https://github.com/ibpsa/project1-boptest/issues/626).


## BOPTEST v0.5.0

Released on 10/04/2023.
Expand Down
8 changes: 8 additions & 0 deletions restapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ def get(self):
status, message, payload = case.get_kpis()
return construct(status, message, payload)

class KPI_Disaggregated(Resource):
'''Interface to test case KPIs disaggregated and with absolute values.'''

def get(self):
'''GET request to receive KPIs disaggregated and with absolute values.'''
status, message, payload = case.get_kpis_disaggregated()
return construct(status, message, payload)

class Forecast(Resource):
'''Interface to test case forecast data.'''
Expand Down Expand Up @@ -267,6 +274,7 @@ def post(self):
api.add_resource(Forecast_Points, '/forecast_points')
api.add_resource(Results, '/results')
api.add_resource(KPI, '/kpi')
api.add_resource(KPI_Disaggregated, '/kpi_disaggregated')
api.add_resource(Forecast, '/forecast')
api.add_resource(Scenario, '/scenario')
api.add_resource(Name, '/name')
Expand Down
44 changes: 44 additions & 0 deletions testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,50 @@ def get_kpis(self):

return status, message, payload

def get_kpis_disaggregated(self):
'''Returns KPIs disaggregated and with absolute values.
Requires standard sensor signals.

Parameters
----------
None

Returns
-------
status: int
Indicates whether a request for querying the KPIs has been completed.
If 200, the KPIs were successfully queried.
If 500, an internal error occured.
message: str
Includes detailed debugging information
payload : dict
Dictionary containing KPIs disaggregated and with absolute values.
{<kpi_ele_name>:<kpi_ele_value>}
Returns None if error during calculation.

'''

status = 200
message = "Queried disaggregated KPIs successfully."
try:
# Set correct price scenario for cost
if self.scenario['electricity_price'] == 'constant':
price_scenario = 'Constant'
elif self.scenario['electricity_price'] == 'dynamic':
price_scenario = 'Dynamic'
elif self.scenario['electricity_price'] == 'highly_dynamic':
price_scenario = 'HighlyDynamic'
# Calculate the disaggregated kpis
payload = self.cal.get_kpis_disaggregated(price_scenario=price_scenario)
except:
payload = None
status = 500
message = "Failed to query disaggregated KPIs: {}".format(traceback.format_exc())
logging.error(message)
logging.info(message)

return status, message, payload

def get_forecast_points(self):
'''Returns a dictionary of available forecast points and their meta-data.

Expand Down
2 changes: 1 addition & 1 deletion testing/references/bestest_hydronic/submit.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"dash_url": "https://dashboard.boptest.net//api/results", "payload": {"results": [{"account": {"apiKey": "valid_api_key"}, "boptestVersion": "0.6.0-dev\n", "buildingType": {"uid": "bestest_hydronic"}, "controlStep": "86400.0", "dateRun": "2020-05-17 00:00:00", "forecastParameters": {}, "isShared": true, "kpis": {"cost_tot": 0.4660775943925745, "emis_tot": 1.6291465071977262, "ener_tot": 9.00349952561408, "idis_tot": 0.0, "pdih_tot": null, "pele_tot": 0.00025517153990852024, "pgas_tot": 0.11798036181564837, "tdis_tot": 18.21783776691252, "time_rat": 0}, "scenario": {"electricityPrice": "dynamic", "timePeriod": "peak_heat_day", "weatherForecastUncertainty": "deterministic"}, "tags": ["baseline", "unit_test"], "uid": "1"}]}}
{"dash_url": "https://dashboard.boptest.net//api/results", "payload": {"results": [{"account": {"apiKey": "valid_api_key"}, "boptestVersion": "0.6.0-dev\n", "buildingType": {"uid": "bestest_hydronic"}, "controlStep": "86400.0", "dateRun": "2020-05-17 00:00:00", "forecastParameters": {}, "isShared": true, "kpis": {"cost_tot": 0.4660775943925745, "emis_tot": 1.6291465071977262, "ener_tot": 9.00349952561408, "idis_tot": 0.0, "pdih_tot": null, "pele_tot": 0.0002551715399085203, "pgas_tot": 0.11798036181564837, "tdis_tot": 18.21783776691252, "time_rat": 0}, "scenario": {"electricityPrice": "dynamic", "timePeriod": "peak_heat_day", "weatherForecastUncertainty": "deterministic"}, "tags": ["baseline", "unit_test"], "uid": "1"}]}}
1 change: 1 addition & 0 deletions testing/references/kpis/all_ckpis_MultiZone.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"tdis_tot": 10.866116056527034, "idis_tot": 515.1749482042578, "ener_tot": 2.1905882633882148, "cost_tot": 0.15334117843717504, "emis_tot": 0.438117652677643, "pele_tot": null, "pgas_tot": 0.10097014409038281, "pdih_tot": null, "time_rat": null}
1 change: 1 addition & 0 deletions testing/references/kpis/all_ckpis_SingleZone.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"tdis_tot": 6.044280949869132, "idis_tot": 365.6911873402533, "ener_tot": 3.06717186709752, "cost_tot": 0.613434373419504, "emis_tot": 1.53358593354876, "pele_tot": 0.11118336992336571, "pgas_tot": null, "pdih_tot": null, "time_rat": null}
1 change: 1 addition & 0 deletions testing/references/kpis/all_dkpis_MultiZone.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"tdis": {"TRooAirSou_dTlower_y": 6.861372185606556, "TRooAirSou_dTupper_y": 3.771997257241026, "TRooAirNor_dTlower_y": 8.2055849580535, "TRooAirNor_dTupper_y": 2.893277712152985}, "idis": {"CO2RooAirSou_dIupper_y": 1016.9440316099603, "CO2RooAirNor_dIupper_y": 13.40586479855533}, "ener": {"PHeaNor_y": 22.33656655950071, "PHeaSou_y": 21.475198708263587}, "cost": {"PHeaNor_y": 1.5635596591650494, "PHeaSou_y": 1.503263909578451}, "emis": {"PHeaNor_y": 4.467313311900141, "PHeaSou_y": 4.295039741652719}, "pele": null, "pgas": {"PHeaNor_y": 1.057801489003348, "PHeaSou_y": 0.9616013928043511}, "pdih": null}
1 change: 1 addition & 0 deletions testing/references/kpis/all_dkpis_SingleZone.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"tdis": {"TRooAir_dTlower_y": 5.174733104689715, "TRooAir_dTupper_y": 0.8695478451794166}, "idis": {"CO2RooAir_dIupper_y": 365.6911873402533}, "ener": {"PCoo_y": 2.5790120993548213, "PFan_y": 1.2243750151212227, "PHea_y": 143.38535826054658, "PPum_y": 0.035504245658337034}, "cost": {"PCoo_y": 0.5158024198709642, "PFan_y": 0.2448750030242446, "PHea_y": 28.677071652109316, "PPum_y": 0.007100849131667407}, "emis": {"PCoo_y": 1.2895060496774107, "PFan_y": 0.6121875075606114, "PHea_y": 71.69267913027329, "PPum_y": 0.017752122829168517}, "pele": {"PCoo_y": 0.0, "PFan_y": 0.005231953892668188, "PHea_y": 5.33156980242889, "PPum_y": 0.0}, "pgas": null, "pdih": null}
4 changes: 2 additions & 2 deletions testing/references/kpis/pele_dict_SingleZone.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
keys,value
PCoo_y,0.0
PFan_y,0.000108999039431
PHea_y,0.111074370884
PFan_y,0.00523195389267
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these changing because of a correction on accounting for area or not within the disaggregated dictionary values? Same with pgas_dict_MultiZone.csv.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly, this is due to 08a4dfc. For some reason for peak KPIs we were normalizing with the area at the contributions of each element already which is reflected at the pele_dict and pgas_dict dictionaries. This was not consistent with the rest of KPIs where we normalize only when calculating the total value (xxxx_tot).

PHea_y,5.33156980243
PPum_y,0.0
4 changes: 2 additions & 2 deletions testing/references/kpis/pgas_dict_MultiZone.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
keys,value
PHeaNor_y,0.0528900744502
PHeaSou_y,0.0480800696402
PHeaNor_y,1.057801489
PHeaSou_y,0.961601392804
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"dash_url": "https://dashboard.boptest.net//api/results", "payload": {"results": [{"account": {"apiKey": "valid_api_key"}, "boptestVersion": "0.6.0-dev\n", "buildingType": {"uid": "multizone_residential_hydronic"}, "controlStep": "86400.0", "dateRun": "2020-05-17 00:00:00", "forecastParameters": {}, "isShared": true, "kpis": {"cost_tot": 0.7911663939870216, "emis_tot": 1.4254044254077105, "ener_tot": 8.140200481262813, "idis_tot": 9114.558476792468, "pdih_tot": null, "pele_tot": 0.0017390231869758264, "pgas_tot": 0.09592720495532532, "tdis_tot": 22.00196096128404, "time_rat": 0}, "scenario": {"electricityPrice": "dynamic", "timePeriod": "peak_heat_day", "weatherForecastUncertainty": "deterministic"}, "tags": ["baseline", "unit_test"], "uid": "1"}]}}
{"dash_url": "https://dashboard.boptest.net//api/results", "payload": {"results": [{"account": {"apiKey": "valid_api_key"}, "boptestVersion": "0.6.0-dev\n", "buildingType": {"uid": "multizone_residential_hydronic"}, "controlStep": "86400.0", "dateRun": "2020-05-17 00:00:00", "forecastParameters": {}, "isShared": true, "kpis": {"cost_tot": 0.7911663939870216, "emis_tot": 1.4254044254077105, "ener_tot": 8.140200481262813, "idis_tot": 9114.558476792468, "pdih_tot": null, "pele_tot": 0.0017390231869758262, "pgas_tot": 0.09592720495532533, "tdis_tot": 22.00196096128404, "time_rat": 0}, "scenario": {"electricityPrice": "dynamic", "timePeriod": "peak_heat_day", "weatherForecastUncertainty": "deterministic"}, "tags": ["baseline", "unit_test"], "uid": "1"}]}}
Loading