source: TOOLS/ConsoGENCI/trunk/bin/plot_bilan.py @ 2776

Last change on this file since 2776 was 2776, checked in by labetoulle, 6 years ago

typos and cleaning

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