Skip to content

Commit

Permalink
Merge branch '347_curtailment_with_TSA_xarray' into 'develop'
Browse files Browse the repository at this point in the history
allow calculation of curtailment when using timeseries agrregation and xarray

Closes #347

See merge request iek-3/shared-code/fine!373
  • Loading branch information
JohannesBehrens committed Nov 14, 2024
2 parents e6b183b + e600b8f commit f517ce1
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 2 deletions.
84 changes: 83 additions & 1 deletion fine/IOManagement/dictIO.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,27 @@

import fine as fn
from fine.IOManagement import utilsIO
from fine.utils import buildFullTimeSeries


def exportToDict(esM, useProcessedValues=False):
def reconstruct_full_timeseries(esM, timeseries, ip):
print("Reconstructing timeseries from TSA")

# switch first index level and column level
df = timeseries.copy()
df = df.stack().unstack(level=1)
df.index.names = [None] * len(df.index.names)
full_df = (
buildFullTimeSeries(df, esM.periodsOrder[ip], ip=ip, esM=esM, divide=False)
.reset_index(level=0, drop=True)
.T
)
full_df.columns = timeseries.columns

return full_df


def exportToDict(esM, useProcessedValues=False, useTSAvalues=False):
"""
Writes the input arguments of EnergySysteModel and its Components input to a dictionary.
Expand Down Expand Up @@ -88,6 +106,58 @@ def exportToDict(esM, useProcessedValues=False):
compDict[classname][componentname][prop] = getattr(
component, prop
)
# Add aggregatedRate timeseries from TSA
if esM.isTimeSeriesDataClustered:
prop_list_full_set = component.__dict__.keys()
for prop in prop_list_full_set:
if (prop != "self") and (prop != "esM"):
if ("aggregated" in prop) and ("Rate" in prop):
timeseries = getattr(component, prop)
# if only one time series was given by user, independent of the number of investment periods, we only save that
original_name = prop.replace("aggregated", "")
original_name = (
f"{original_name[:1].lower()}{original_name[1:]}"
)
inuptTimeSeries = getattr(component, original_name)

if isinstance(inuptTimeSeries, dict):
compDict[classname][componentname][prop] = {}
for ip in timeseries.keys():
ip_name = esM.investmentPeriodNames[ip]
# get years
if timeseries[ip] is not None:
compDict[classname][componentname][prop][
ip_name
] = reconstruct_full_timeseries(
esM, timeseries[ip], ip=ip
)
else:
compDict[classname][componentname][prop] = (
None
)
else:
ip = 0
if isinstance(timeseries, dict):
if (
timeseries[ip] is not None
): # we only save the first time series since they are all the same (becaue only one time series given by user)
compDict[classname][componentname][prop] = (
reconstruct_full_timeseries(
esM, timeseries[ip], ip=ip
)
)
else:
compDict[classname][componentname][prop] = (
None
)
elif timeseries is not None:
compDict[classname][componentname][prop] = (
reconstruct_full_timeseries(
esM, timeseries, ip=ip
)
)
else:
compDict[classname][componentname][prop] = None

return esmDict, compDict

Expand All @@ -110,8 +180,20 @@ def importFromDict(esmDict, compDict):
for classname in compDict:
# get class
class_ = getattr(fn, classname)
blacklist = [
"aggregated"
] # variable is only needed to save clusterd timeseries data

for comp in compDict[classname]:
# get all vars that start with entries in blacklist
compBlacklist = [
var
for var in compDict[classname][comp]
if any([var.startswith(bl) for bl in blacklist])
]
# remove all vars in blacklist
for var in compBlacklist:
compDict[classname][comp].pop(var)
esM.add(class_(esM, **compDict[classname][comp]))

return esM
29 changes: 28 additions & 1 deletion test/IOManagement/test_xarrayio.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ def compare_esm_outputs(esm_1: fn.EnergySystemModel, esm_2: fn.energySystemModel
model_results_1_sorted = model_results_1.sort_index()
model_results_2_sorted = model_results_2.sort_index()

assert_frame_equal(model_results_1_sorted, model_results_2_sorted, check_dtype=False)
assert_frame_equal(
model_results_1_sorted, model_results_2_sorted, check_dtype=False
)


def test_esm_input_to_dataset_and_back(minimal_test_esM):
Expand Down Expand Up @@ -280,3 +282,28 @@ def test_transmission_dims(minimal_test_esM):
assert operationRateMax.index.name == "time"

esM2.optimize()


def test_saving_clustered_timeseries_to_xarray(perfectForesight_test_esM):
"""Optimize an esM, write it to netCDF, then load the esM from this file.
Compare if both esMs are identical. Inputs are compared with exportToDict,
outputs are compared with optimizationSummary.
"""

esm_original_pf = deepcopy(perfectForesight_test_esM)
esm_original_pf.aggregateTemporally(
numberOfTypicalPeriods=1, numberOfTimeStepsPerPeriod=2
)
esm_original_pf.optimize()

xrIO.writeEnergySystemModelToNetCDF(
esm_original_pf, outputFilePath="test_esM_pf.nc"
)
esm_datasets = xrIO.writeEnergySystemModelToDatasets(esm_original_pf)
assert "ts_aggregatedOperationRateMax" in esm_datasets["Input"]["Source"]["PV"]

esm_pf_from_netcdf = xrIO.readNetCDFtoEnergySystemModel(filePath="test_esM_pf.nc")

compare_esm_inputs(esm_original_pf, esm_pf_from_netcdf)

Path("test_esM_pf.nc").unlink()

0 comments on commit f517ce1

Please sign in to comment.