Skip to content

Commit a9f2cdb

Browse files
author
flma0001
committed
Clean up and improve models
1 parent 6924044 commit a9f2cdb

24 files changed

+639
-1663
lines changed

README.md

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ draf is a Python library for analyzing price-based demand response. It is develo
1010

1111
# Quick start
1212

13-
```bash
13+
```sh
1414
git clone https://github.com/DrafProject/draf
1515
cd draf
1616

@@ -32,7 +32,7 @@ pytest
3232

3333
A `CaseStudy` object can contain several `Scenario` instances e.g.:
3434

35-
``` None
35+
```none
3636
CaseStudy
3737
⤷ Year, Country, timely resolution (freq)
3838
⤷ Plots (scenario comparision, pareto analysis)
@@ -45,6 +45,8 @@ CaseStudy
4545
# Some features
4646

4747
- Intuitive handling of complex data structures.
48+
- Fast model formulation with gurobipy.
49+
- Open-source model formulation with pyomo.
4850
- Uses the power of gurobi, the fastest MILP solver available and its community for model formulation and solving.
4951
- Easy and automatic scenario generation and sensitivity analyses.
5052
- Naming conventions for parameters and variables.
@@ -55,7 +57,7 @@ CaseStudy
5557
- Modules for peak-load analysis.
5658
- Economic assessment uses historic day-ahead market prices.
5759
- Convenient plotting and presentation functions.
58-
- Automatic unit conversion, great descriptions and documentation.
60+
- Automatic unit conversion, descriptions and documentation.
5961
- Uses Python's modern type annotations.
6062
- Whole case studies and individual scenarios can be saved including all results.
6163

@@ -73,44 +75,40 @@ Raw-data parquet-files pd.Series
7375

7476
You can make your own model...
7577

76-
``` Python
78+
```py
79+
from gurobipy import GRB, Model, quicksum
80+
7781
import draf
78-
import gurobipy as gp
7982

8083
cs = draf.CaseStudy(name="foo", year=2019, freq="60min", country="DE")
84+
cs.set_time_horizon(start="Jan-02 00", steps=24 * 2)
8185

8286
sc = cs.add_REF_scen()
83-
sc.add_dim("T", infer=True)
84-
sc.prep.add_c_GRID_RTP_T()
85-
sc.prep.add_E_dem_T(profile="G3", annual_energy=5e5)
86-
sc.add_var("C_", unit="€/a", lb=-gp.GRB.INFINITY)
87+
sc.dim("T", infer=True)
88+
sc.var("C_", unit="€/a", lb=-GRB.INFINITY)
89+
sc.prep.c_GRID_RTP_T()
90+
sc.prep.E_dem_T(profile="G3", annual_energy=5e5)
8791

88-
def model_func(d, p, v, m): # (d)imensions, (p)arameters, (v)ariables, (m)odel
89-
m.setObjective(v.C_, gp.GRB.MINIMIZE)
90-
m.addConstr(v.C_ == gp.quicksum(p.E_dem_T[t] * p.c_GRID_RTP_T[t] for t in d.T))
92+
def model_func(m: Model, d: draf.Dimensions, p: draf.Params, v: draf.Vars):
93+
m.setObjective(v.C_, GRB.MINIMIZE)
94+
m.addConstr(v.C_ == quicksum(p.E_dem_T[t] * p.c_GRID_RTP_T[t] for t in d.T))
9195

92-
cs.set_model(model_func).optimize().save()
96+
cs.set_model(mod.model_func).optimize().save()
9397
```
9498

9599
... or use an existing one.
96100

97-
``` Python
101+
```py
98102
import draf
99-
import draf.models.PV_BES as mod
103+
from draf.models.gp import pv_bes as mod
100104

101105
cs = draf.CaseStudy(name="ShowCase", year=2017, freq="60min", country="DE")
102-
103-
cs.add_REF_scen(doc="no BES").set_params(mod.params_func).update_params(
104-
P_PV_CAPx_=100, c_GRID_peak_=50
105-
)
106-
107-
cs.add_scens(
108-
scen_vars=[("c_GRID_T", "t", ["c_GRID_RTP_T", "c_GRID_TOU_T"]), ("E_BES_CAPx_", "b", [1000])],
109-
nParetoPoints=4,
110-
)
111-
112-
cs.improve_pareto_and_set_model(mod.model_func).optimize(mod.postprocess_func).save()
113-
106+
sc = cs.add_REF_scen(doc="no BES").set_params(mod.params_func)
107+
sc.update_params(P_PV_CAPx_=100, c_GRID_peak_=50)
108+
cs.add_scens(scen_vars=[("c_GRID_T", "t", ["c_GRID_RTP_T", "c_GRID_TOU_T"]),
109+
("E_BES_CAPx_", "b", [1000])],
110+
nParetoPoints=3)
111+
cs.set_model(mod.model_func).optimize(postprocess_func=mod.postprocess_func).save()
114112
```
115113

116114
# Common Abbreviations
@@ -139,6 +137,15 @@ cs.improve_pareto_and_set_model(mod.model_func).optimize(mod.postprocess_func).s
139137

140138
This piece of software is in a very early stage. Use at your own risk.
141139

140+
# Dokumentation
141+
142+
## Conventions
143+
144+
### Naming conventions
145+
146+
All parameters and variable names
147+
148+
'E_GRID_buy_T'
142149

143150
# License
144151

draf/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44

55
__author__ = "Markus Fleschutz | [email protected]"
66
__copyright__ = "Copyright (C) 2021 Markus Fleschutz"
7-
from draf import helper as hp
8-
from draf import models, prep, tools
7+
from draf import helper, models, prep, tools
98
from draf.core.case_study import CaseStudy, open_casestudy, open_latest_casestudy
109
from draf.core.entity_stores import Dimensions, Params, Results, Vars
1110
from draf.core.scenario import Scenario
12-
from draf.paths import BASE
1311

1412
from ._version import __version__

draf/core/params_prepping.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,21 @@ def __getstate__(self):
2525
"""For serialization with pickle."""
2626
return None
2727

28-
def add_n_comp_(self, name="n_comp_"):
28+
def n_comp_(self, name="n_comp_"):
2929
"""Add cost weighting factor to compensate part year analysis."""
3030
sc = self.sc
31-
return self.sc.add_par(
31+
return self.sc.param(
3232
name=name,
3333
unit="",
3434
doc="Weighting factor to compensate part year analysis",
3535
data=len(sc.dtindex) / len(sc.dtindex_custom),
3636
)
3737

3838
@hp.copy_doc(get_emissions, start="Args:")
39-
def add_ce_GRID_T(self, name="ce_GRID_T", method="XEF_PP", **kwargs):
39+
def ce_GRID_T(self, name="ce_GRID_T", method="XEF_PP", **kwargs):
4040
"""Add dynamic carbon emission factors."""
4141
sc = self.sc
42-
return self.sc.add_par(
42+
return self.sc.param(
4343
name=name,
4444
unit="kgCO2eq/kWh_el",
4545
doc=f"{method} for {sc.year}, {sc.freq}, {sc.country}",
@@ -51,10 +51,10 @@ def add_ce_GRID_T(self, name="ce_GRID_T", method="XEF_PP", **kwargs):
5151
),
5252
)
5353

54-
def add_c_GRID_RTP_T(self, name="c_GRID_RTP_T", method="hist_EP", **kwargs):
54+
def c_GRID_RTP_T(self, name="c_GRID_RTP_T", method="hist_EP", **kwargs):
5555
"""Add Real-time-prices-tariffs."""
5656
sc = self.sc
57-
return self.sc.add_par(
57+
return self.sc.param(
5858
name=name,
5959
unit="€/kWh_el",
6060
doc=f"Day-ahead-market-prices {sc.year}, {sc.freq}, {sc.country}",
@@ -64,10 +64,10 @@ def add_c_GRID_RTP_T(self, name="c_GRID_RTP_T", method="hist_EP", **kwargs):
6464
),
6565
)
6666

67-
def add_c_GRID_PP_T(self, name="c_GRID_PP_T", method="PP"):
67+
def c_GRID_PP_T(self, name="c_GRID_PP_T", method="PP"):
6868
"""Add marginal costs from PP-method. Only for Germany."""
6969
sc = self.sc
70-
return self.sc.add_par(
70+
return self.sc.param(
7171
name=name,
7272
unit="€/kWh_el",
7373
doc=f"Marginal Costs {sc.year}, {sc.freq}, {sc.country}",
@@ -76,10 +76,10 @@ def add_c_GRID_PP_T(self, name="c_GRID_PP_T", method="PP"):
7676
],
7777
)
7878

79-
def add_c_GRID_PWL_T(self, name="c_GRID_PWL_T", method="PWL", **kwargs):
79+
def c_GRID_PWL_T(self, name="c_GRID_PWL_T", method="PWL", **kwargs):
8080
"""Add marginal costs from PWL-method."""
8181
sc = self.sc
82-
return self.sc.add_par(
82+
return self.sc.param(
8383
name=name,
8484
unit="€/kWh_el",
8585
doc=f"Marginal Costs {sc.year}, {sc.freq}, {sc.country}",
@@ -88,7 +88,7 @@ def add_c_GRID_PWL_T(self, name="c_GRID_PWL_T", method="PWL", **kwargs):
8888
),
8989
)
9090

91-
def add_c_GRID_TOU_T(
91+
def c_GRID_TOU_T(
9292
self, name: str = "c_GRID_TOU_T", prices: Optional[List[float]] = None, prov: str = "BW"
9393
):
9494
"""A Time-of-Use tariff with two prices.
@@ -124,14 +124,14 @@ def add_c_GRID_TOU_T(
124124
_lt = min(prices)
125125
_ht = max(prices)
126126

127-
return self.sc.add_par(
127+
return self.sc.param(
128128
name=name,
129129
unit="€/kWh_el",
130130
doc=f"Time-Of-Use-tariff with the prices {_lt:.3f}€ and {_ht:.3f}€",
131131
data=_lt * _y_lt + _ht * _y_ht,
132132
)
133133

134-
def add_c_GRID_FLAT_T(
134+
def c_GRID_FLAT_T(
135135
self, price: Optional[float] = None, name="c_GRID_FLAT_T", doc_addon: str = ""
136136
):
137137
if price is None:
@@ -144,15 +144,15 @@ def add_c_GRID_FLAT_T(
144144
)
145145

146146
unit = "€/kWh_el"
147-
return self.sc.add_par(
147+
return self.sc.param(
148148
name=name,
149149
unit=unit,
150150
doc=f"Flat-electricity tariff ({price:.4f} {unit}). {doc_addon}",
151151
fill=price,
152152
)
153153

154154
@hp.copy_doc(prep.get_el_SLP)
155-
def add_E_dem_T(
155+
def E_dem_T(
156156
self,
157157
name="E_dem_T",
158158
profile="G1",
@@ -164,7 +164,7 @@ def add_E_dem_T(
164164
"""Add an electricity demand"""
165165
sc = self.sc
166166

167-
return self.sc.add_par(
167+
return self.sc.param(
168168
name=name,
169169
unit="kWh_el",
170170
doc=f"Electricity demand from standard load profile {profile}",
@@ -183,7 +183,7 @@ def add_E_dem_T(
183183
)
184184

185185
@hp.copy_doc(prep.get_thermal_demand)
186-
def add_H_dem_T(
186+
def H_dem_T(
187187
self,
188188
name="H_dem_T",
189189
annual_energy: float = 1.0,
@@ -196,7 +196,7 @@ def add_H_dem_T(
196196

197197
ser_amb_temp = prep.get_ambient_temp(year=sc.year, freq=sc.freq, location=location)
198198

199-
return self.sc.add_par(
199+
return self.sc.param(
200200
name=name,
201201
unit="kWh_th",
202202
doc=f"Heat demand derived from ambient temperatur in {location}",
@@ -210,17 +210,17 @@ def add_H_dem_T(
210210
),
211211
)
212212

213-
def add_E_PV_profile_T(self, name="E_PV_profile_T"):
213+
def E_PV_profile_T(self, name="E_PV_profile_T"):
214214
"""Add a photovoltaic profile."""
215215
sc = self.sc
216-
return self.sc.add_par(
216+
return self.sc.param(
217217
name=name,
218218
unit="kW_el/kW_peak",
219219
doc=f"Produced PV-power for 1 kW_peak",
220220
data=sc.trim_to_datetimeindex(prep.get_PV_profile()),
221221
)
222222

223-
def add_c_GRID_addon_T(
223+
def c_GRID_addon_T(
224224
self,
225225
name="c_GRID_addon_T",
226226
AbLa_surcharge=0.00006,
@@ -251,7 +251,7 @@ def add_c_GRID_addon_T(
251251
Sales,
252252
]
253253

254-
return self.sc.add_par(
254+
return self.sc.param(
255255
name=name,
256256
unit="€/kWh_el",
257257
doc="Add-on electricity price component",

draf/core/scenario.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def set_params(self, params_builder_func: Callable) -> Scenario:
194194
except RuntimeError as e:
195195
logger.error(e)
196196

197-
self.add_par(
197+
self.param(
198198
"timelog_params_",
199199
self._cs._get_time_diff(),
200200
doc="Time for building the params",
@@ -223,7 +223,7 @@ def set_model(
223223

224224
self._cs._set_time_trace()
225225
self._activate_vars()
226-
self.add_par(
226+
self.param(
227227
"timelog_vars_",
228228
self._cs._get_time_diff(),
229229
doc="Time for building the variables",
@@ -241,7 +241,7 @@ def set_model(
241241

242242
model_builder_func(m=self.mdl, d=self.dims, p=params, v=self.vars)
243243

244-
self.add_par(
244+
self.param(
245245
"timelog_model_",
246246
self._cs._get_time_diff(),
247247
doc="Time for building the model",
@@ -367,7 +367,7 @@ def _optimize_gurobipy(
367367
self.mdl.setParam("LogToConsole", int(logToConsole), verbose=False)
368368
self.mdl.setParam("OutputFlag", int(outputFlag), verbose=False)
369369
self.mdl.optimize()
370-
self.add_par(
370+
self.param(
371371
"timelog_solve_",
372372
self._cs._get_time_diff(),
373373
doc="Time for solving the model",
@@ -416,7 +416,7 @@ def _optimize_pyomo(
416416

417417
results = solver.solve(self.mdl, tee=logToConsole, load_solutions=False, logfile=logfile)
418418

419-
self.add_par(
419+
self.param(
420420
"timelog_solve_",
421421
self._cs._get_time_diff(),
422422
doc="Time for solving the model",
@@ -530,7 +530,7 @@ def get_coords(self, dims: str) -> List:
530530
"""Returns a list of coordinates for the given dimensions."""
531531
return [getattr(self.dims, dim) for dim in list(dims)]
532532

533-
def add_var(
533+
def var(
534534
self,
535535
name: str,
536536
doc: str = "",
@@ -597,7 +597,7 @@ def get_meta(self, ent_name: str, meta_type: str) -> str:
597597

598598
def update_params(self, **kwargs) -> Scenario:
599599
"""Update multiple existing parameters.
600-
e.g. sc.update_params(E_GRID_dem_T=2000, c_GRID_addon_T=0, c_el_peak_=0)
600+
e.g. sc.update_params(E_GRID_dem_T=2000, c_GRID_addon_T=0, c_GRID_peak_=0)
601601
"""
602602
for ent_name, data in kwargs.items():
603603

@@ -611,9 +611,9 @@ def update_params(self, **kwargs) -> Scenario:
611611
raise RuntimeError(f"The parameter {ent_name} you want to update does not exist.")
612612

613613
if self.fits_convention(ent_name, data):
614-
self.add_par(ent_name, data=data, update=True)
614+
self.param(ent_name, data=data, update=True)
615615
else:
616-
self.add_par(ent_name, fill=data, update=True)
616+
self.param(ent_name, fill=data, update=True)
617617

618618
return self
619619

@@ -626,7 +626,7 @@ def fits_convention(self, ent_name: str, data: Union[int, float, pd.Series]) ->
626626
elif isinstance(data, pd.Series):
627627
return data.index.nlevels == len(dims)
628628

629-
def add_par(
629+
def param(
630630
self,
631631
name: Optional[str] = None,
632632
data: Optional[Union[int, float, list, np.ndarray, pd.Series]] = None,
@@ -657,7 +657,7 @@ def add_par(
657657
if from_db is not None:
658658
if name is not None:
659659
from_db.name = name
660-
return self.add_par(**from_db.__dict__)
660+
return self.param(**from_db.__dict__)
661661

662662
dims = self._get_dims(name)
663663

@@ -692,7 +692,7 @@ def add_par(
692692
self.params._changed_since_last_dic_export = True
693693
return data
694694

695-
def add_dim(
695+
def dim(
696696
self,
697697
name: str,
698698
data: Union[List, np.ndarray] = None,

0 commit comments

Comments
 (0)