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

Sensitivity #63

Draft
wants to merge 45 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
dd0f2f6
Add sketch for sensitivity class
jnnr Nov 8, 2021
5057edf
Elaborate sanity checks
jnnr Nov 9, 2021
13c5e8c
Pass assertion tests
jnnr Nov 9, 2021
c254777
Elaborate sanity checks
jnnr Nov 9, 2021
39022da
Clean up sanity checks
jnnr Nov 11, 2021
f533117
Sketch sampling
jnnr Nov 11, 2021
f9bb1e2
Add test files for sensitivity
jnnr Nov 11, 2021
0bcdf9d
Adapt sanity checks
jnnr Nov 11, 2021
9baa596
Decorate Sensitivity to allow passing EnergyDatapackage
jnnr Nov 13, 2021
f49980e
Subclass instead of decorate
jnnr Nov 13, 2021
ca5aa3a
Use EDPsensitivity in the previous tests
jnnr Nov 13, 2021
4dd0905
Add docstrings
jnnr Dec 17, 2021
1443886
Drop attribute n as it belongs to the sammpling methods
jnnr Dec 17, 2021
cd7c722
Define aux columns and var value centrally
jnnr Dec 17, 2021
70894ac
Rename variables assigned to EnergyDataPackage
jnnr Dec 17, 2021
ee13007
Run sanity check at init
jnnr Dec 17, 2021
cd77212
Fix index comparison
jnnr Dec 17, 2021
7b3cdbc
Add some tests
jnnr Dec 17, 2021
343f1dc
Fix test_init by setting smaller epsilon and fixing comparison
jnnr Dec 17, 2021
048376b
Save test EnergyDataPackages as unstacked
jnnr Dec 17, 2021
a0d3673
Adapt tests of unstacked and stacked
jnnr Dec 17, 2021
24100d0
Assert failure upon init
jnnr Dec 17, 2021
b7957d8
Rearrange
jnnr Dec 17, 2021
522b5f7
Check if EnergyDataPackage is stacked upon init
jnnr Dec 17, 2021
01f297f
Introduce attribute 'stacked'
jnnr Dec 17, 2021
df1030d
Improve code formatting
jnnr Dec 17, 2021
0fc760b
Sketch sampling oat
jnnr Jan 27, 2022
df74a21
Remove incorrect elements
jnnr Feb 21, 2022
01d924d
Copy datapackage before manipulation
jnnr Feb 21, 2022
6948b9b
Add sampling scheme linear slide
jnnr Feb 21, 2022
f90b0bd
Fix test
jnnr Apr 4, 2022
dc5256d
Drop unfinished bits
jnnr Apr 4, 2022
49541c7
Wrap sampling method
jnnr Apr 4, 2022
f8286a5
Merge branch 'dev' into features/sensitivity
jnnr May 20, 2022
d8f9d9e
Add Package metadata as attribute to EnergyDatapackage
jnnr May 20, 2022
ab13cc9
Sketch sampling with salib
jnnr Apr 4, 2022
6dd1050
Implement SAlib sampling
jnnr May 27, 2022
1cb6af3
Wrap sampling method
jnnr May 27, 2022
d9b6def
Clean up name
jnnr May 27, 2022
f4d9da9
Clean up test
jnnr May 27, 2022
b27567b
Add requirement
jnnr May 29, 2022
03cd8e1
Merge pull request #64 from rl-institut/feature/sensitivity-salib
jnnr May 29, 2022
9f4d481
Give non-int name to samples
jnnr May 29, 2022
932e142
Fix test after rename of samples
jnnr May 30, 2022
9e24131
Merge branch 'dev' into features/sensitivity
jnnr Oct 13, 2022
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
30 changes: 29 additions & 1 deletion oemoflex/model/datapackage.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ def __init__(self, *args, **kwargs):

self.components = kwargs.get("components")

self.metadata = kwargs.get("metadata", None)

self.stacked = False

@classmethod
def setup_default(
cls, name, basepath, datetimeindex, components, busses, regions, links, **kwargs
Expand Down Expand Up @@ -246,7 +250,11 @@ def from_metadata(cls, json_file_path):

data = cls._load_csv(cls, dir, rel_paths)

return cls(dir, data, rel_paths)
return cls(dir, data, rel_paths, metadata=dp)

def save_metadata(self, destination):
if self.metadata is not None:
self.metadata.to_json(destination)

def infer_metadata(self, foreign_keys_update=None):
r"""
Expand Down Expand Up @@ -289,6 +297,8 @@ def stack_components(self):
r"""
Stacks the component DataFrames into a single DataFrame.
"""
if self.stacked is True:
raise ValueError("EnergyDataPackage is already stacked.")

def is_element(rel_path):
directory = os.path.split(rel_path)[0]
Expand All @@ -308,16 +318,34 @@ def is_element(rel_path):
index_vars=["name", "var_name"],
)

self.stacked = True

def unstack_components(self):
r"""
Unstacks a single component DataFrame into separate DataFrames for each component.
"""
if self.stacked is False:
raise ValueError("EnergyDataPackage is already unstacked.")

self._separate_stacked_frame(
frame_name="component",
target_dir=os.path.join("data", "elements"),
group_by=["carrier", "tech"],
)

self.stacked = False

def to_csv_dir(self, destination, overwrite=False):
if self.stacked:
raise UserWarning("Saving stacked EnergyDatapackages is not supported yet.")

super().to_csv_dir(destination, overwrite)

if self.metadata is not None:
destination = os.path.join(self.basepath, "datapackage.json")
print(f"Saving metadata to {destination}")
self.save_metadata(destination)


class ResultsDataPackage(DataFramePackage):
def __init__(self, *args, **kwargs):
Expand Down
187 changes: 171 additions & 16 deletions oemoflex/model/variations.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,185 @@
import copy
import os
from oemoflex.model.datapackage import EnergyDataPackage


class VariationGenerator:
def __init__(self, datapackage):
def index_same(df_a, df_b):
return df_a.index.equals(df_b.index)

self.base_datapackage = datapackage

def create_variations(self, variations, destination):
def data_same(df_a, df_b):
jnnr marked this conversation as resolved.
Show resolved Hide resolved
return df_a.equals(df_b)

for id, changes in variations.iterrows():

dp = self.create_var(self.base_datapackage, changes)
def smaller_equal(df_a, df_b):
jnnr marked this conversation as resolved.
Show resolved Hide resolved
return (df_a < df_b).all()

variation_dir = os.path.join(destination, str(id))

dp.to_csv_dir(variation_dir)
def get_diff(df_a, df_b):
jnnr marked this conversation as resolved.
Show resolved Hide resolved
# # pandas>1.1.0
# diff = self.lb.compare(self.ub)
return ~((df_a == df_b) | ((df_a != df_a) & (df_b != df_b)))

def create_var(self, dp, changes):

_dp = copy.deepcopy(dp)
def diff_larger_eps(df_a, df_b, eps):
jnnr marked this conversation as resolved.
Show resolved Hide resolved
diff = get_diff(df_a, df_b)
df_diff = df_a.loc[diff] - df_b.loc[diff]
return abs(df_diff) > eps

changes = changes.to_dict()

for (resource, var_name), var_value in changes.items():
class Sensitivity(object):
r"""
Accepts two DataFrames that are the same in index and most columns, but have different entries
in the column 'var_value'. The DataFrames describe lower and upper bound of a variation. With
these intervals, different sampling methods can be invoked.
"""
AUX_COLUMNS = ["carrier", "region", "tech", "type"]
VAR_VALUE = "var_value"
jnnr marked this conversation as resolved.
Show resolved Hide resolved

_dp.data[resource].loc[:, var_name] = var_value
def __init__(self, lb, ub, eps=1e-6):
self.lb = lb
self.ub = ub
self.eps = eps
self.sanity_check()

return _dp
def sanity_check(self):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Decouple sample method and sanity check.

# assert index same
assert index_same(self.lb, self.ub), "Indexes of lb and ub are not the same."

# Assert that the data is different
assert not data_same(
self.lb, self.ub
), "There is no difference between lb and ub."

# Assert that the auxiliary columns are the same
assert data_same(
self.lb.loc[:, self.AUX_COLUMNS], self.ub.loc[:, self.AUX_COLUMNS]
)

# find the parameters that are different (comparing NaN)
diff = self.get_diff()

# assert lb <= ub
assert smaller_equal(
self.lb.loc[diff, self.VAR_VALUE], self.ub.loc[diff, self.VAR_VALUE]
)

# assert that the difference is larger than eps

assert diff_larger_eps(
self.lb.loc[diff, self.VAR_VALUE],
self.ub.loc[diff, self.VAR_VALUE],
self.eps,
).all(), f"The difference between lb and ub is lower than the defined minimum of {self.eps}"

def get_diff(self):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Revise naming

return get_diff(self.lb.loc[:, self.VAR_VALUE], self.ub.loc[:, self.VAR_VALUE])

def get_param(self):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Revise naming

# get the parameters that are varied
param = self.lb.index[self.get_diff()]
return param

def get_linear_slide(self, n):

params = self.get_param()

# for each param, create n samples
samples = {}

for i in range(n):
sample = self.lb.copy()
slider = i / (n - 1)
sample.loc[params, "var_value"] = (1 - slider) * self.lb.loc[
params, "var_value"
] + slider * self.ub.loc[params, "var_value"]
sample_name = "sample_" + str(i)
samples[sample_name] = sample

return samples

def get_salib_samples(self, salib_method, **kwargs):

params = self.get_param()

problem = {
"num_vars": len(params),
"names": list(params),
"bounds": list(
zip(self.lb.loc[params, "var_value"], self.ub.loc[params, "var_value"])
),
}

samples = salib_method(problem, **kwargs)

full_samples = {}
for i, sample in enumerate(samples):
full_sample = self.lb.copy()
full_sample.loc[params, "var_value"] = sample
sample_name = "sample_" + str(i)
full_samples[sample_name] = full_sample

return full_samples


class EDPSensitivity(Sensitivity):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This could be done more general. The above Sensitivity class does checking and sampling for a single dataframe. This class is supposed to do the same for DataFramePackages.

The implementation takes some time to grasp - could be more transparent?

r"""
Accepts two EnergyDataPackages that are have different values. The Packages describe lower and
upper bound of a variation. With these intervals, different sampling methods can be invoked.
"""

def __init__(self, lb_edp, ub_edp, eps=1e-6):
self.lb_edp = lb_edp
self.ub_edp = ub_edp
self.eps = eps
self.check_if_stacked()
self.sanity_check()

def check_if_stacked(self):
if not (self.lb_edp.stacked and self.ub_edp.stacked):
raise AssertionError("EnergyDataPackages have to be stacked")

def get_lb(self):
return self.lb_edp.data["component"]

def set_lb(self, value):
self.lb_edp.data["component"] = value

def del_lb(self):
del self.lb_edp

def get_ub(self):
return self.ub_edp.data["component"]

def set_ub(self, value):
self.ub_edp.data["component"] = value

def del_ub(self):
del self.ub_edp

lb = property(get_lb, set_lb, del_lb)
ub = property(get_ub, set_ub, del_ub)

def wrap_sampling(self, sampling):
def wrapped_sampling(*args, **kwargs):
samples = sampling(*args, **kwargs)
for name, df in samples.items():
edp = EnergyDataPackage(
name=name,
basepath=None,
rel_paths=self.lb_edp.rel_paths.copy(),
data=self.lb_edp.data.copy(),
components=None,
)
edp.data["component"] = df
edp.stacked = True
edp.metadata = self.lb_edp.metadata
samples[name] = edp

return samples

return wrapped_sampling

def get_linear_slide(self, n):
return self.wrap_sampling(super().get_linear_slide)(n)

def get_salib_samples(self, salib_method, **kwargs):
return self.wrap_sampling(super().get_salib_samples)(salib_method, **kwargs)
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def read(fname):
"oemof.tabular @ git+https://[email protected]/oemof/oemof-tabular@dev#egg=oemof.tabular",
"plotly",
"frictionless",
"SAlib",
"matplotlib",
],
# black version is specified so that each contributor uses the same one
Expand Down
3 changes: 3 additions & 0 deletions tests/_files/edp_lb/data/elements/biomass-gt.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name,capacity,capacity_cost,carrier,carrier_cost,efficiency,expandable,from_bus,marginal_cost,output_parameters,region,tech,to_bus,type
BB-biomass-st,400000,,biomass,0.040999999999999995,0.38,False,BB-biomass,0.0014,{},BB,gt,BB-electricity,conversion
BE-biomass-st,0,,biomass,0.040999999999999995,0.38,False,BE-biomass,0.0014,{},BE,gt,BE-electricity,conversion
11 changes: 11 additions & 0 deletions tests/_files/edp_lb/data/elements/bus.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
region,name,type,balanced
BE,BE-electricity,bus,True
BE,BE-ch4,bus,False
BE,BE-oil,bus,False
BE,BE-other,bus,False
BE,BE-biomass,bus,False
BB,BB-electricity,bus,True
BB,BB-ch4,bus,False
BB,BB-oil,bus,False
BB,BB-other,bus,False
BB,BB-biomass,bus,False
3 changes: 3 additions & 0 deletions tests/_files/edp_lb/data/elements/ch4-gt.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name,capacity,capacity_cost,carrier,carrier_cost,efficiency,expandable,from_bus,marginal_cost,output_parameters,region,tech,to_bus,type
BB-ch4-gt,600000,,ch4,0.021,0.619,False,BB-ch4,0.0045,{},BB,gt,BB-electricity,conversion
BE-ch4-gt,1500000,,ch4,0.021,0.619,False,BE-ch4,0.0045,{},BE,gt,BE-electricity,conversion
3 changes: 3 additions & 0 deletions tests/_files/edp_lb/data/elements/electricity-curtailment.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name,bus,capacity,carrier,marginal_cost,output_parameters,region,tech,type
BB-electricity-curtailment,BB-electricity,,electricity,0,{},BB,curtailment,excess
BE-electricity-curtailment,BE-electricity,,electricity,0,{},BE,curtailment,excess
3 changes: 3 additions & 0 deletions tests/_files/edp_lb/data/elements/electricity-demand.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name,amount,bus,carrier,profile,region,tech,type
BB-electricity-demand,20327320000,BB-electricity,electricity,BB-electricity-demand-profile,BB,demand,load
BE-electricity-demand,13400000000,BE-electricity,electricity,BE-electricity-demand-profile,BE,demand,load
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name,bus,capacity,capacity_cost,carrier,efficiency,expandable,input_parameters,loss_rate,marginal_cost,output_parameters,region,storage_capacity,storage_capacity_cost,tech,type
BB-electricity-liion_battery,BB-electricity,0,6.6,electricity,0.87,True,{},0,0,{},BB,0,36.99,liion_battery,storage
BE-electricity-liion_battery,BE-electricity,0,6.6,electricity,0.87,True,{},0,0,{},BE,0,36.99,liion_battery,storage
3 changes: 3 additions & 0 deletions tests/_files/edp_lb/data/elements/electricity-shortage.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name,bus,capacity,carrier,marginal_cost,output_parameters,region,tech,type
BB-electricity-shortage,BB-electricity,,electricity,100000,{},BB,shortage,shortage
BE-electricity-shortage,BE-electricity,,electricity,100000,{},BE,shortage,shortage
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
name,carrier,from_bus,from_to_capacity,loss,region,tech,to_bus,to_from_capacity,type
BE-BB-electricity-transmission,electricity,BE-electricity,3000000,0,BE_BB,transmission,BB-electricity,3000000,link
3 changes: 3 additions & 0 deletions tests/_files/edp_lb/data/elements/solar-pv.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name,bus,capacity,capacity_cost,carrier,marginal_cost,output_parameters,profile,region,tech,type
BB-solar-pv,BB-electricity,11300000,,solar,0,{},BB-solar-pv-profile,BB,pv,volatile
BE-solar-pv,BE-electricity,400000,,solar,0,{},BE-solar-pv-profile,BE,pv,volatile
3 changes: 3 additions & 0 deletions tests/_files/edp_lb/data/elements/wind-onshore.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name,bus,capacity,capacity_cost,carrier,marginal_cost,output_parameters,profile,region,tech,type
BB-wind-onshore,BB-electricity,9900000,,wind,0,{},BB-wind-onshore-profile,BB,onshore,volatile
BE-wind-onshore,BE-electricity,0,,wind,0,{},BE-wind-onshore-profile,BE,onshore,volatile
Loading