source: TOOLS/ConsoGENCMIP6/bin/plot_bilan.py @ 4035

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

[gencmip6] running/pending jobs:

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