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

Last change on this file since 2633 was 2633, checked in by labetoulle, 9 years ago

typo

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