source: TOOLS/ConsoGENCI/bin/plot_bilan.py @ 2713

Last change on this file since 2713 was 2713, checked in by labetoulle, 8 years ago

Initial import

File size: 18.2 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# this must come first
5from __future__ import print_function, unicode_literals, division
6
7# standard library imports
8from argparse import ArgumentParser
9# import os
10# import pprint
11import datetime as dt
12from dateutil.relativedelta import relativedelta
13import numpy as np
14
15# Application library imports
16from libconso import *
17import libconsodb as cdb
18
19
20########################################
21class DataDict(dict):
22  #---------------------------------------
23  def __init__(self):
24    self = {}
25
26  #---------------------------------------
27  def init_range(self, date_beg, date_end, inc=1):
28    """
29    """
30    delta = date_end - date_beg
31
32    (deb, fin) = (0, delta.days+1)
33
34    dates = (date_beg + dt.timedelta(days=i)
35             for i in xrange(deb, fin, inc))
36
37    for date in dates:
38      self.add_item(date)
39
40  #---------------------------------------
41  def fill_data(self, projet):
42  # def fill_data(self, filein):
43    """
44    """
45    table_name = "conso.tbl_consumption"
46    request = (
47      "SELECT date, total_hrs "
48      "FROM " + table_name + " "
49      "WHERE allocation_id = '" + str(projet.id) + "'"
50      "  AND login IS NULL "
51      "ORDER BY date"
52      ";"
53    )
54    cdb.select_db(cursor, request)
55
56    for date, conso in cursor:
57      if date.date() in self:
58        # print(date.date(), conso)
59        # Days since start_date
60        real_use = conso / projet.alloc
61        self.add_item(
62          date=date,
63          conso=conso,
64          real_use=real_use,
65        )
66        self[date.date()].fill()
67
68  #   try:
69  #     data = np.genfromtxt(
70  #       filein,
71  #       skip_header=1,
72  #       converters={
73  #         0: string_to_date,
74  #         1: string_to_float,
75  #         2: string_to_percent,
76  #         3: string_to_percent,
77  #         4: string_to_float,
78  #         5: string_to_float,
79  #         6: string_to_float,
80  #         7: string_to_float,
81  #       },
82  #       missing_values="nan",
83  #     )
84  #   except Exception as rc:
85  #     print("Empty file {}:\n{}".format(filein, rc))
86  #     exit(1)
87
88  #   for date, conso, real_use, theo_use, \
89  #       run_mean, pen_mean, run_std, pen_std in data:
90  #     if date in self:
91  #       self.add_item(
92  #         date,
93  #         conso,
94  #         real_use,
95  #         theo_use,
96  #         run_mean,
97  #         pen_mean,
98  #         run_std,
99  #         pen_std,
100  #       )
101  #       self[date].fill()
102
103  #---------------------------------------
104  def add_item(self, date, conso=np.nan,
105               real_use=np.nan, theo_use=np.nan,
106               run_mean=np.nan, pen_mean=np.nan,
107               run_std=np.nan, pen_std=np.nan):
108    """
109    """
110    self[date.date()] = Conso(
111      date, conso,
112      real_use, theo_use,
113      run_mean, pen_mean,
114      run_std, pen_std
115    )
116
117  #---------------------------------------
118  def theo_equation(self, projet):
119    """
120    """
121    (dates, theo_uses) = \
122      zip(*((item.date, item.theo_use)
123            for item in self.get_items_in_full_range()))
124
125    # (idx_min, idx_max) = \
126    #     (np.nanargmin(theo_uses), np.nanargmax(theo_uses))
127
128    # x1 = dates[idx_min].timetuple().tm_yday
129    # x2 = dates[idx_max].timetuple().tm_yday
130    x1 = projet.start_date.timetuple().tm_yday
131    x2 = projet.end_date.timetuple().tm_yday
132
133    # y1 = theo_uses[idx_min]
134    # y2 = theo_uses[idx_max]
135    y1 = 0
136    y2 = 1
137
138    m = np.array([[x1, 1.], [x2, 1.]], dtype="float")
139    n = np.array([y1, y2], dtype="float")
140
141    poly_ok = True
142    try:
143      poly_theo = np.poly1d(np.linalg.solve(m, n))
144    except np.linalg.linalg.LinAlgError:
145      poly_ok = False
146
147    if poly_ok:
148      delta = (dates[0] + relativedelta(months=2) - dates[0]).days
149
150      poly_delay = np.poly1d(
151        [poly_theo[1], poly_theo[0] - poly_theo[1] * delta]
152      )
153
154      self.poly_theo = poly_theo
155      self.poly_delay = poly_delay
156
157  #---------------------------------------
158  def get_items_in_range(self, date_beg, date_end, inc=1):
159    """
160    """
161    items = (item for item in self.itervalues()
162                   if item.date >= date_beg and
163                      item.date <= date_end)
164    items = sorted(items, key=lambda item: item.date)
165
166    return items[::inc]
167
168  #---------------------------------------
169  def get_items_in_full_range(self, inc=1):
170    """
171    """
172    items = (item for item in self.itervalues())
173    items = sorted(items, key=lambda item: item.date)
174
175    return items[::inc]
176
177  #---------------------------------------
178  def get_items(self, inc=1):
179    """
180    """
181    items = (item for item in self.itervalues()
182                   if item.isfilled())
183    items = sorted(items, key=lambda item: item.date)
184
185    return items[::inc]
186
187
188class Conso(object):
189  #---------------------------------------
190  def __init__(self, date, conso=np.nan,
191               real_use=np.nan, theo_use=np.nan,
192               run_mean=np.nan, pen_mean=np.nan,
193               run_std=np.nan, pen_std=np.nan):
194    self.date     = date
195    self.conso    = conso
196    self.real_use = real_use
197    self.theo_use = theo_use
198    self.poly_theo = np.poly1d([])
199    self.poly_delay = np.poly1d([])
200    self.run_mean = run_mean
201    self.pen_mean = pen_mean
202    self.run_std  = run_std
203    self.pen_std  = pen_std
204    self.filled   = False
205
206  #---------------------------------------
207  def __repr__(self):
208    return "{:.2f} ({:.2%})".format(self.conso, self.real_use)
209
210  #---------------------------------------
211  def isfilled(self):
212    return self.filled
213
214  #---------------------------------------
215  def fill(self):
216    self.filled = True
217
218
219########################################
220def plot_init():
221  paper_size  = np.array([29.7, 21.0])
222  fig, ax_conso = plt.subplots(figsize=(paper_size/2.54))
223  ax_theo = ax_conso.twinx()
224
225  return fig, ax_conso, ax_theo
226
227
228########################################
229def plot_data(ax_conso, ax_theo, xcoord, dates,
230              consos, theo_uses, real_uses, theo_equs, theo_delay,
231              run_mean, pen_mean, run_std, pen_std):
232  """
233  """
234  line_style = "-"
235  if args.full:
236    line_width = 0.05
237  else:
238    line_width = 0.1
239
240  ax_conso.bar(
241    xcoord, consos, width=1, align="center", color="linen",
242    linewidth=line_width, label="conso (heures)"
243  )
244
245  ax_theo.plot(
246    xcoord, real_uses, line_style,
247    color="forestgreen", linewidth=1, markersize=8,
248    solid_capstyle="round", solid_joinstyle="round",
249    label="conso\nréelle (%)"
250  )
251  ax_theo.plot(
252    xcoord, theo_equs, "--",
253    color="firebrick", linewidth=0.5,
254    solid_capstyle="round", solid_joinstyle="round"
255  )
256  ax_theo.plot(
257    xcoord, theo_uses, line_style,
258    color="firebrick", linewidth=1, markersize=8,
259    solid_capstyle="round", solid_joinstyle="round",
260    label="conso\nthéorique (%)"
261  )
262  ax_theo.plot(
263    xcoord, theo_delay, ":",
264    color="firebrick", linewidth=0.5,
265    solid_capstyle="round", solid_joinstyle="round",
266    label="retard de\ndeux mois (%)"
267  )
268
269
270########################################
271def plot_config(fig, ax_conso, ax_theo, xcoord, dates, title,
272                conso_per_day, conso_per_day_2):
273  """
274  """
275  from matplotlib.ticker import AutoMinorLocator
276
277  # ... Config axes ...
278  # -------------------
279  # 1) Range
280  conso_max = np.nanmax(consos)
281  if args.max:
282    ymax = conso_max  # + conso_max*.1
283  else:
284    ymax = 3. * max(conso_per_day, conso_per_day_2)
285
286  if conso_max > ymax:
287    ax_conso.annotate(
288      "{:.2e} heures".format(conso_max),
289      ha="left",
290      va="top",
291      fontsize="xx-small",
292      bbox=dict(boxstyle="round", fc="w", ec="0.5", color="gray",),
293      xy=(np.nanargmax(consos)+1.2, ymax),
294      textcoords="axes fraction",
295      xytext=(0.01, 0.9),
296      arrowprops=dict(
297        arrowstyle="->",
298        shrinkA=0,
299        shrinkB=0,
300        color="gray",
301      ),
302    )
303
304  xmin, xmax = xcoord[0]-1, xcoord[-1]+1
305  ax_conso.set_xlim(xmin, xmax)
306  ax_conso.set_ylim(0., ymax)
307  ax_theo.set_ylim(0., 100)
308
309  # 2) Ticks labels
310  (date_beg, date_end) = (dates[0], dates[-1])
311  date_fmt = "{:%d-%m}"
312
313  if date_end - date_beg > dt.timedelta(weeks=9):
314    maj_xticks = [x for x, d in zip(xcoord, dates)
315                     if d.weekday() == 0]
316    maj_xlabs  = [date_fmt.format(d) for d in dates
317                     if d.weekday() == 0]
318  else:
319    maj_xticks = [x for x, d in zip(xcoord, dates)]
320    maj_xlabs  = [date_fmt.format(d) for d in dates]
321
322  ax_conso.ticklabel_format(axis="y", style="sci", scilimits=(0, 0))
323
324  ax_conso.set_xticks(xcoord, minor=True)
325  ax_conso.set_xticks(maj_xticks, minor=False)
326  ax_conso.set_xticklabels(
327    maj_xlabs, rotation="vertical", size="x-small"
328  )
329
330  # minor_locator = AutoMinorLocator()
331  # ax.yaxis.set_minor_locator(minor_locator)
332
333  yticks = list(ax_conso.get_yticks())
334  yticks.append(conso_per_day)
335  if conso_per_day_2:
336    yticks.append(conso_per_day_2)
337  ax_conso.set_yticks(yticks)
338
339  ax_theo.spines["right"].set_color("firebrick")
340  ax_theo.tick_params(colors="firebrick")
341  ax_theo.yaxis.label.set_color("firebrick")
342
343  ax_conso.axhline(y=conso_per_day, color="blue", alpha=0.5,
344                   label="conso journaliÚre\nidéale (heures)")
345
346  if conso_per_day_2:
347    ax_conso.axhline(y=conso_per_day_2, color="blue", alpha=0.5)
348
349  for x, d in zip(xcoord, dates):
350    # if d.weekday() == 0 and d.hour == 0:
351    if d.weekday() == 0:
352      ax_conso.axvline(x=x, color="black", alpha=0.5,
353                       linewidth=0.5, linestyle=":")
354
355  # 3) Define axes title
356  for ax, label in (
357    (ax_conso, "heures"),
358    (ax_theo, "%"),
359  ):
360    ax.set_ylabel(label, fontweight="bold")
361    ax.tick_params(axis="y", labelsize="small")
362
363  # 4) Define plot size
364  fig.subplots_adjust(
365    left=0.08,
366    bottom=0.09,
367    right=0.93,
368    top=0.93,
369  )
370
371  # ... Main title and legend ...
372  # -----------------------------
373  fig.suptitle(title, fontweight="bold", size="large")
374  for ax, loc in (
375    (ax_conso, "upper left"),
376    (ax_theo, "upper right"),
377  ):
378    ax.legend(loc=loc, fontsize="x-small", frameon=False)
379
380
381########################################
382def get_arguments():
383  parser = ArgumentParser()
384  parser.add_argument("project", action="store",
385                      help="Project name")
386  parser.add_argument("center", action="store",
387                      help="Center name (idris/tgcc)")
388
389  parser.add_argument("-v", "--verbose", action="store_true",
390                      help="verbose mode")
391  parser.add_argument("-f", "--full", action="store_true",
392                      help="plot the whole period")
393  parser.add_argument("-i", "--increment", action="store",
394                      type=int, default=1, dest="inc",
395                      help="sampling increment")
396  parser.add_argument("-r", "--range", action="store", nargs=2,
397                      type=string_to_date,
398                      help="date range: ssaa-mm-jj ssaa-mm-jj")
399  parser.add_argument("-m", "--max", action="store_true",
400                      help="plot with y_max = allocation")
401  parser.add_argument("-s", "--show", action="store_true",
402                      help="interactive mode")
403  parser.add_argument("-d", "--dods", action="store_true",
404                      help="copy output on dods")
405
406  return parser.parse_args()
407
408
409########################################
410if __name__ == '__main__':
411
412  # .. Initialization ..
413  # ====================
414  # ... Command line arguments ...
415  # ------------------------------
416  args = get_arguments()
417
418  # ... Turn interactive mode off ...
419  # ---------------------------------
420  if not args.show:
421    import matplotlib
422    matplotlib.use('Agg')
423
424  import matplotlib.pyplot as plt
425  # from matplotlib.backends.backend_pdf import PdfPages
426
427  if not args.show:
428    plt.ioff()
429
430  # # ... Files and directories ...
431  # # -----------------------------
432  # project_name, DIR, OUT = parse_config("bin/config.ini")
433
434  # (file_param, file_utheo, file_data) = \
435  #     get_input_files(DIR["SAVEDATA"],
436  #                     [OUT["PARAM"], OUT["UTHEO"], OUT["BILAN"]])
437
438  img_name = os.path.splitext(
439               os.path.basename(__file__)
440             )[0].replace("plot_", "")
441
442  # project_name = "rgzi"
443
444  # Connection info
445  db_host = "134.157.170.104"
446  # db_port = "5432"
447  db_name = "prodiguer"
448  db_user = "prodiguer_db_user"
449  # db_pwd = "secret"
450
451  conn, cursor = cdb.connect_db(db_host, db_name, db_user)
452
453  # today = os.path.basename(file_param).strip(OUT["PARAM"])
454  today = dt.date.strftime(dt.date.today(), "%Y%m%d")
455
456  # if args.verbose:
457  #   fmt_str = "{:10s} : {}"
458  #   print(fmt_str.format("args", args))
459  #   print(fmt_str.format("today", today))
460  #   print(fmt_str.format("file_param", file_param))
461  #   print(fmt_str.format("file_utheo", file_utheo))
462  #   print(fmt_str.format("file_data", file_data))
463  #   print(fmt_str.format("img_name", img_name))
464
465  # .. Get project info ..
466  # ======================
467  # projet = Project(project_name)
468  # projet.fill_data(file_param)
469  # projet.get_date_init(file_utheo)
470
471  table_name = "conso.tbl_allocation"
472  request = (
473    "SELECT * "
474    "FROM " + table_name + " "
475    "WHERE project = '" + args.project + "'"
476    ";"
477  )
478
479  cdb.select_db(cursor, request)
480  # print(cursor.rowcount)
481  # print(cursor.fetchall())
482
483  print(cursor.description)
484  for row in cursor:
485    projet = Project(args.project)
486    projet.fill_data(row)
487    # print(row["project"], type(row["project"]))
488    # print(row["centre"], type(row["centre"]))
489    # print(row["machine"], type(row["machine"]))
490    # print(row["node_type"], type(row["node_type"]))
491    # print(row["start_date"], type(row["start_date"]))
492    # print(row["end_date"], type(row["end_date"]))
493    # print(row["total_hrs"], type(row["total_hrs"]))
494
495  # .. Fill in data ..
496  # ==================
497  # ... Initialization ...
498  # ----------------------
499  bilan = DataDict()
500  bilan.init_range(projet.start_date, projet.end_date)
501
502  # ... Extract data from file ...
503  # ------------------------------
504  # table_name = "conso.tbl_consumption"
505  # request = (
506  #   "SELECT date, total_hrs "
507  #   "FROM " + table_name + " "
508  #   "WHERE allocation_id = '" + str(projet.id) + "'"
509  #   "  AND login IS NULL "
510  #   "ORDER BY date"
511  #   ";"
512  # )
513  # cdb.select_db(cursor, request)
514
515  bilan.fill_data(projet)
516  # for item in bilan.iteritems():
517  #   print(item)
518  # exit()
519
520  # ... Compute theoratical use from known data  ...
521  # ------------------------------------------------
522  bilan.theo_equation(projet)
523
524  # print(cursor.description)
525  # # for row in cursor:
526  # #   print(row)
527
528  # consos = np.array([conso for date, conso in cursor])
529  # dates = [date for date, conso in cursor]
530
531  # people = ['Tom', 'Dick', 'Harry', 'Slim', 'Jim']
532  # x_pos = np.arange(len(people))
533  # performance = 3 + 10 * np.random.rand(len(people))
534
535  # # x_pos = np.arange(len(consos))
536  # print (len(x_pos), type(x_pos))
537  # # print (len(people), type(people))
538  # print (len(performance), type(performance))
539  # print (len(consos), type(consos))
540
541  # # plt.barh(x_pos, performance, align='center', alpha=0.4)
542
543  # x_pos = np.arange(len(consos))
544  # print (len(x_pos), type(x_pos))
545  # plt.bar(x_pos, consos, align='center', alpha=0.4)
546  # # plt.yticks(x_pos, dates)
547
548  # # plt.barh(x_pos, conso)
549  # # plt.yticks(x_pos, people)
550  # # plt.xlabel('Performance')
551  # # plt.title('How fast do you want to go today?')
552
553  # plt.show()
554
555
556
557
558
559
560  # .. Extract data depending on C.L. arguments ..
561  # ==============================================
562  if args.full:
563    selected_items = bilan.get_items_in_full_range(args.inc)
564  elif args.range:
565    selected_items = bilan.get_items_in_range(
566      args.range[0], args.range[1], args.inc
567    )
568  else:
569    selected_items = bilan.get_items(args.inc)
570
571  print(selected_items)
572
573  # .. Compute data to be plotted ..
574  # ================================
575  nb_items = len(selected_items)
576
577  xcoord = np.linspace(1, nb_items, num=nb_items)
578  dates = [item.date for item in selected_items]
579
580  cumul = np.array([item.conso for item in selected_items],
581                        dtype=float)
582  consos = []
583  consos.append(cumul[0])
584  consos[1:nb_items] = cumul[1:nb_items] - cumul[0:nb_items-1]
585  consos = np.array(consos, dtype=float)
586
587  if projet.project == "gencmip6":
588    alloc1 = 5.e6
589    alloc2 = projet.alloc - alloc1
590    conso_per_day   = 2 * alloc1 / projet.days
591    conso_per_day_2 = 2 * alloc2 / projet.days
592  else:
593    conso_per_day = projet.alloc / projet.days
594    conso_per_day_2 = None
595
596  theo_uses = np.array(
597    [100.*item.theo_use for item in selected_items],
598    dtype=float
599  )
600  real_uses = np.array(
601    [100.*item.real_use for item in selected_items],
602    dtype=float
603  )
604  theo_equs = np.array(
605    [100. * bilan.poly_theo(date.timetuple().tm_yday)
606      for date in dates],
607    dtype=float
608  )
609  theo_delay = np.array(
610    [100. * bilan.poly_delay(date.timetuple().tm_yday)
611      for date in dates],
612    dtype=float
613  )
614
615  run_mean = np.array([item.run_mean for item in selected_items],
616                       dtype=float)
617  pen_mean = np.array([item.pen_mean for item in selected_items],
618                       dtype=float)
619  run_std  = np.array([item.run_std for item in selected_items],
620                       dtype=float)
621  pen_std  = np.array([item.pen_std for item in selected_items],
622                       dtype=float)
623
624  # .. Plot stuff ..
625  # ================
626  # ... Initialize figure ...
627  # -------------------------
628  (fig, ax_conso, ax_theo) = plot_init()
629
630  # ... Plot data ...
631  # -----------------
632  plot_data(ax_conso, ax_theo, xcoord, dates,
633            consos, theo_uses, real_uses, theo_equs, theo_delay,
634            run_mean, pen_mean, run_std, pen_std)
635
636  # ... Tweak figure ...
637  # --------------------
638  title = "Consommation {}\n({:%d/%m/%Y} - {:%d/%m/%Y})".format(
639    projet.project.upper(),
640    projet.start_date,
641    projet.end_date
642  )
643
644  plot_config(
645    fig, ax_conso, ax_theo, xcoord, dates, title,
646    conso_per_day, conso_per_day_2
647  )
648
649  # ... Save figure ...
650  # -------------------
651  # img_in  = os.path.join(DIR["PLOT"], "{}.pdf".format(img_name))
652  # img_out = os.path.join(DIR["SAVEPLOT"],
653  #                        "{}_{}.pdf".format(img_name, today))
654  img_in  = os.path.join(".", "{}.pdf".format(img_name))
655  img_out = os.path.join(".",
656                         "{}_{}.pdf".format(img_name, today))
657
658  plot_save(img_in, img_out, title)
659
660  # # ... Publish figure on dods ...
661  # # ------------------------------
662  # if args.dods:
663  #   if args.verbose:
664  #     print("Publish figure on dods")
665  #   dods_cp(img_in, DIR)
666
667  # if args.show:
668  #   plt.show()
669
670  # exit(0)
671
Note: See TracBrowser for help on using the repository browser.