55import logging
66import numpy as np
77import plotly .graph_objects as go
8+ import pandas as pd
89
910
1011from django .utils .translation import ugettext_lazy as _
@@ -151,6 +152,195 @@ class KPICostsMatrixResults(models.Model):
151152 simulation = models .ForeignKey (Simulation , on_delete = models .CASCADE )
152153
153154
155+ class OemofBusResults (pd .DataFrame ): # real results
156+ def __init__ (self , results ):
157+
158+ js = json .loads (results )
159+ mindex = pd .MultiIndex .from_tuples (
160+ js ["columns" ],
161+ names = [
162+ "bus" ,
163+ "energy_vector" ,
164+ "direction" ,
165+ "asset" ,
166+ "asset_type" ,
167+ "oemof_type" ,
168+ ],
169+ )
170+ df = pd .DataFrame (data = js ["data" ], columns = mindex )
171+
172+ ts_df = df .iloc [:- 1 ]
173+ ts_index = pd .to_datetime (js ["index" ][:- 1 ], unit = "ms" )
174+ investments = df .iloc [- 1 ]
175+ ts_df .index = ts_index
176+
177+ super ().__init__ (
178+ data = ts_df .T .to_dict (orient = "split" )["data" ],
179+ index = mindex ,
180+ columns = ts_df .index ,
181+ )
182+
183+ self ["investments" ] = investments
184+ self .sort_index (inplace = True )
185+
186+ def to_json (self , ** kwargs ):
187+ kwargs ["orient" ] = "split"
188+ return self .T .to_json (** kwargs )
189+
190+ def bus_flows (self ):
191+ return self .loc [:, self .columns != "investments" ]
192+
193+ def asset_optimized_capacities (self ):
194+ return self .loc [:, "investments" ]
195+
196+ def asset_optimized_capacity (self , asset_name ):
197+ optimized_capacity = self .loc [
198+ self .index .get_level_values ("asset" ) == asset_name , "investments"
199+ ].dropna ()
200+ if len (optimized_capacity ) == 1 :
201+ optimized_capacity = optimized_capacity [0 ]
202+ return optimized_capacity
203+
204+
205+ class FlowResults (models .Model ):
206+ flow_data = models .TextField () # to store the assets list
207+ simulation = models .ForeignKey (Simulation , on_delete = models .CASCADE )
208+ __df_flows = None
209+ __df_capacities = None
210+
211+ @property
212+ def df_flows (self ):
213+ if self .__df_flows is None :
214+ self .__df_flows = OemofBusResults (self .flow_data ).bus_flows ()
215+
216+ return self .__df_flows
217+
218+ def asset_optimized_capacity (self , asset_name ):
219+ return OemofBusResults (self .flow_data ).asset_optimized_capacity (asset_name )
220+
221+ @property
222+ def busses (self ):
223+ """returns a mapping of the bus to their energy_vectors"""
224+ return {
225+ k : v
226+ for k , v in zip (
227+ self .df_flows .index .get_level_values ("bus" ),
228+ self .df_flows .index .get_level_values ("energy_vector" ),
229+ )
230+ }
231+
232+ def single_bus_flows (self , bus_name ):
233+ df_bus = self .df_flows .loc [bus_name ]
234+ energy_vector = df_bus .index .get_level_values ("energy_vector" ).unique ()[0 ]
235+ df_bus .index = df_bus .index .droplevel (
236+ ["asset_type" , "energy_vector" , "oemof_type" ]
237+ )
238+ df = pd .concat (
239+ [
240+ df_bus .loc [df_bus .index .get_level_values ("direction" ) == "in" ].T ,
241+ df_bus .loc [df_bus .index .get_level_values ("direction" ) == "out" ].T * - 1 ,
242+ ],
243+ axis = 1 ,
244+ )
245+ df .name = bus_name
246+
247+ df .energy_vector = energy_vector
248+
249+ return df
250+
251+ def single_bus_flows_figure (self , bus_name ):
252+ df = self .single_bus_flows (bus_name )
253+ fig = go .Figure (
254+ data = [
255+ go .Scatter (
256+ x = df .index .tolist (),
257+ y = df .loc [:, col ].values .tolist (),
258+ name = col [1 ],
259+ stackgroup = col [0 ],
260+ )
261+ for col in df .columns
262+ ],
263+ layout = dict (
264+ title = f"{ bus_name } ({ df .energy_vector } )" , hovermode = "x unified"
265+ ),
266+ )
267+
268+ return fig .to_dict ()
269+
270+ # def all_bus_flows_figure(self, exclude=None):
271+ # if exclude is None:
272+ # exclude = []
273+ # df = self.single_bus_flows(bus_name)
274+ # fig = go.Figure(
275+ # data=[
276+ # go.Scatter(
277+ # x=df.index, y=df.loc[:, col].values, name=col[1], stackgroup=col[0]
278+ # )
279+ # for col in df.columns
280+ # ],
281+ # layout=dict(
282+ # title=f"{bus_name} ({df.energy_vector})", hovermode="x unified"
283+ # ),
284+ # )
285+ #
286+ # return fig.to_dict()
287+
288+ def load_duration_figure (self , energy_vector ):
289+ df_consumption = (
290+ self .df_flows .loc [
291+ (self .df_flows .index .get_level_values ("direction" ) == "out" )
292+ & (
293+ self .df_flows .index .get_level_values ("energy_vector" )
294+ == energy_vector
295+ )
296+ ]
297+ .groupby (level = "asset_type" )
298+ .sum ()
299+ .T
300+ )
301+
302+ # df_consumption["excess"] *= 0
303+ df_consumption = df_consumption .sum (axis = 1 )
304+ df_production = (
305+ self .df_flows .loc [
306+ (self .df_flows .index .get_level_values ("direction" ) == "in" )
307+ & (
308+ self .df_flows .index .get_level_values ("energy_vector" )
309+ == energy_vector
310+ )
311+ ]
312+ .groupby (level = "asset_type" )
313+ .sum ()
314+ .T
315+ )
316+ percentage = np .linspace (0 , 100 , df_production .index .size )
317+ fig = go .Figure (
318+ data = [
319+ go .Scatter (
320+ x = percentage .tolist (),
321+ y = df_production .loc [:, col ]
322+ .sort_values (ascending = False )
323+ .values .tolist (),
324+ name = col ,
325+ stackgroup = "production" ,
326+ )
327+ for col in df_production .columns
328+ ]
329+ + [
330+ go .Scatter (
331+ x = percentage .tolist (),
332+ y = df_consumption .sort_values (ascending = False ).values .tolist (),
333+ name = "demand" ,
334+ )
335+ ],
336+ layout = dict (
337+ title = f"Load duration curve for { energy_vector } " , hovermode = "x unified"
338+ ),
339+ )
340+
341+ return fig .to_dict ()
342+
343+
154344class AssetsResults (models .Model ):
155345 assets_list = models .TextField () # to store the assets list
156346 simulation = models .ForeignKey (Simulation , on_delete = models .CASCADE )
@@ -466,6 +656,8 @@ def graph_capacities(simulations, y_variables):
466656
467657 assets_results_obj = AssetsResults .objects .get (simulation = simulation )
468658
659+ # qs = FlowResults.objects.filter(simulation=simulation)
660+
469661 results_dict = json .loads (simulation .results )
470662
471663 kpi_scalar_matrix = results_dict ["kpi" ]["scalar_matrix" ]
@@ -484,25 +676,45 @@ def graph_capacities(simulations, y_variables):
484676 else _ ("Opt. Cap." ) + f"{ simulation .scenario .name } (kW)" ,
485677 }
486678 for y_var in y_variables :
487-
679+ do_not_add = False
488680 if "@" not in y_var :
489681 asset = assets_results_obj .single_asset_results (asset_name = y_var )
490- x_values . append ( y_var )
682+
491683 if asset is not None :
492684 installed_cap = asset ["installed_capacity" ]["value" ]
685+ print (asset ["asset_type" ])
686+ if "dso" in asset ["asset_type" ] or "demand" in asset ["asset_type" ]:
687+ do_not_add = True
493688 else :
494689 installed_cap = 0
495- installed_capacity_dict ["capacity" ].append (installed_cap )
690+ if do_not_add is False :
691+ x_values .append (y_var )
692+ installed_capacity_dict ["capacity" ].append (installed_cap )
693+ # TODO have all graphs made via this way
694+ # if qs.exists():
695+ # flow_results = qs.get()
696+ #
697+ # optimized_cap = flow_results.asset_optimized_capacity(y_var)
698+ #
699+ # if isinstance(optimized_cap, pd.Series):
700+ # if optimized_cap.empty is False:
701+ # optimized_cap = optimized_cap[
702+ # optimized_cap.index.get_level_values("direction")
703+ # == "out"
704+ # ].values[0]
705+ # else:
706+ # optimized_cap = None
707+ # else:
496708 if y_var in kpi_scalar_matrix :
497- optimized_capacity_dict ["capacity" ].append (
498- kpi_scalar_matrix [y_var ]["optimized_add_cap" ]
499- )
709+ optimized_cap = kpi_scalar_matrix [y_var ]["optimized_add_cap" ]
500710 else :
501711 optimized_cap = 0
502712 if asset is not None :
503713 if "optimized_add_cap" in asset :
504714 optimized_cap = asset ["optimized_add_cap" ]["value" ]
715+ if optimized_cap is not None and do_not_add is False :
505716 optimized_capacity_dict ["capacity" ].append (optimized_cap )
717+
506718 y_values .append (installed_capacity_dict )
507719 y_values .append (optimized_capacity_dict )
508720
@@ -800,6 +1012,26 @@ def fetch_parameters_values(self):
8001012 simulation = self .simulations .get (), energy_vector = energy_vector
8011013 )
8021014
1015+ if self .report_type == GRAPH_LOAD_DURATION :
1016+ energy_vector = parameters .get ("energy_vector" , None )
1017+
1018+ simulation = self .simulations .get ()
1019+ # if isinstance(energy_vector, list) is False:
1020+ # energy_vector = [energy_vector]
1021+ if energy_vector is not None :
1022+
1023+ sim = simulation
1024+ qs = FlowResults .objects .filter (simulation = sim )
1025+ if qs .exists ():
1026+ flow_results = qs .get ()
1027+ fig_dict = flow_results .load_duration_figure (energy_vector )
1028+ else :
1029+ fig_dict = {
1030+ "layout" : {"title" : "There is an error with this graph." }
1031+ }
1032+
1033+ return fig_dict
1034+
8031035
8041036def get_project_reportitems (project ):
8051037 """Given a project, return the ReportItem instances linked to that project"""
0 commit comments