source: TOOLS/ConsoGENCMIP6/plot/plot_bilan.py @ 2411

Last change on this file since 2411 was 2411, checked in by labetoulle, 7 years ago

Add ConsoGENCMIP6 tools

  • Property svn:executable set to *
File size: 10.4 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
9import os
10import os.path
11import glob
12import datetime as dt
13import numpy as np
14import matplotlib.pyplot as plt
15from matplotlib.backends.backend_pdf import PdfPages
16
17from gencmip6 import *
18
19
20########################################
21class BilanDict(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, filein):
42    data = np.genfromtxt(
43      filein,
44      skip_header=1,
45      converters={0: string_to_date,
46                  1: string_to_float,
47                  2: string_to_percent,
48                  3: string_to_percent},
49      missing_values="nan",
50    )
51
52    for date, conso, real_use, theo_use in data:
53      if date in self:
54        self.add_item(date, conso, real_use, theo_use)
55        self[date].fill()
56
57  #---------------------------------------
58  def add_item(self, date, conso=np.nan,
59               real_use=np.nan, theo_use=np.nan):
60    """
61    """
62    self[date] = Conso(date, conso, real_use, theo_use)
63
64  #---------------------------------------
65  def theo_equation(self):
66    """
67    """
68    (dates, theo_uses) = \
69      zip(*((item.date, item.theo_use)
70            for item in self.get_items_in_full_range()))
71
72    (idx_min, idx_max) = \
73        (np.nanargmin(theo_uses), np.nanargmax(theo_uses))
74
75    x1 = dates[idx_min].timetuple().tm_yday
76    x2 = dates[idx_max].timetuple().tm_yday
77
78    y1 = theo_uses[idx_min]
79    y2 = theo_uses[idx_max]
80
81    m = np.array([
82      [x1, 1.],
83      [x2, 1.]
84    ], dtype="float")
85    n = np.array([
86      y1,
87      y2
88    ], dtype="float")
89
90    try:
91      (a, b) = np.linalg.solve(m, n)
92    except np.linalg.linalg.LinAlgError:
93      (a, b) = (None, None)
94
95    if a and b:
96      for date in dates:
97        self[date].theo_equ = date.timetuple().tm_yday*a + b
98
99  #---------------------------------------
100  def get_items_in_range(self, date_beg, date_end, inc=1):
101    """
102    """
103    items = (item for item in self.itervalues()
104                   if item.date >= date_beg and
105                      item.date <= date_end)
106    items = sorted(items, key=lambda item: item.date)
107
108    return items[::inc]
109
110  #---------------------------------------
111  def get_items_in_full_range(self, inc=1):
112    """
113    """
114    items = (item for item in self.itervalues())
115    items = sorted(items, key=lambda item: item.date)
116
117    return items[::inc]
118
119  #---------------------------------------
120  def get_items(self, inc=1):
121    """
122    """
123    items = (item for item in self.itervalues()
124                   if item.isfilled())
125    items = sorted(items, key=lambda item: item.date)
126
127    return items[::inc]
128
129
130class Conso(object):
131  #---------------------------------------
132  def __init__(self, date, conso=np.nan,
133               real_use=np.nan, theo_use=np.nan):
134    self.date     = date
135    self.conso    = conso
136    self.real_use = real_use
137    self.theo_use = theo_use
138    self.theo_equ = np.nan
139    self.filled   = False
140
141  #---------------------------------------
142  def __repr__(self):
143    return "{:.2f} ({:.2%})".format(self.conso, self.real_use)
144
145  #---------------------------------------
146  def isfilled(self):
147    return self.filled
148
149  #---------------------------------------
150  def fill(self):
151    self.filled = True
152
153
154########################################
155def plot_init():
156  paper_size  = np.array([29.7, 21.0])
157  fig, ax_conso = plt.subplots(figsize=(paper_size/2.54))
158  ax_theo = ax_conso.twinx()
159
160  return fig, ax_conso, ax_theo
161
162
163########################################
164def plot_data(ax_conso, ax_theo, xcoord, xlabels,
165              consos, theo_uses, real_uses, theo_equs):
166  """
167  """
168  if args.full:
169    line_style = "-"
170  else:
171    line_style = "+-"
172
173  ax_conso.bar(xcoord, consos, align="center", color="linen",
174               linewidth=0.2, label="conso (heures)")
175
176  ax_theo.plot(xcoord, theo_equs, "--",
177               color="firebrick", linewidth=0.5,
178               solid_capstyle="round", solid_joinstyle="round")
179  ax_theo.plot(xcoord, theo_uses, line_style, color="firebrick",
180               linewidth=1, markersize=8,
181               # solid_capstyle="round", solid_joinstyle="round",
182               label="conso théorique (%)")
183  ax_theo.plot(xcoord, real_uses, line_style, color="forestgreen",
184               linewidth=1, markersize=8,
185               # solid_capstyle="round", solid_joinstyle="round",
186               label="conso réelle (%)")
187
188
189########################################
190def plot_config(ax_conso, ax_theo, xcoord, xlabels, ymax, title):
191  """
192  """
193  # ... Config axes ...
194  # -------------------
195  # 1) Range
196  xmin, xmax = xcoord[0]-1, xcoord[-1]+1
197  ax_conso.set_xlim(xmin, xmax)
198  ax_conso.set_ylim(0., ymax)
199  ax_theo.set_ylim(0., 100)
200
201  # 2) Ticks labels
202  inc_label = 7 if nb_items > 37 else 1
203  ax_conso.ticklabel_format(axis="y", style="sci", scilimits=(0, 0))
204  ax_conso.set_xticks(xcoord, minor=True)
205  ax_conso.set_xticks(xcoord[::inc_label], minor=False)
206  ax_conso.set_xticklabels(
207    xlabels[::inc_label], rotation="45", size="x-small"
208  )
209
210  # 3) Define axes title
211  ax_conso.set_ylabel("heures", fontweight="bold")
212  ax_theo.set_ylabel("%", fontweight="bold")
213
214  # ... Main title and legend ...
215  # -----------------------------
216  ax_conso.set_title(title, fontweight="bold", size="large")
217  ax_theo.legend(loc="upper right", fontsize="x-small", frameon=False)
218  ax_conso.legend(loc="upper left", fontsize="x-small", frameon=False)
219
220
221########################################
222def plot_save(img_name):
223  """
224  """
225  dpi = 200.
226
227  with PdfPages(img_name) as pdf:
228    pdf.savefig(dpi=dpi)
229
230    # pdf file's metadata
231    d = pdf.infodict()
232    d["Title"]   = "Conso GENCMIP6"
233    d["Author"]  = "plot_bilan.py"
234    # d["Subject"] = "Time spent over specific commands during create_ts \
235    #                 jobs at IDRIS and four configurations at TGCC"
236    # d["Keywords"] = "bench create_ts TGCC IDRIS ncrcat"
237    # d["CreationDate"] = dt.datetime(2009, 11, 13)
238    # d["ModDate"] = dt.datetime.today()
239
240
241########################################
242def get_arguments():
243  parser = ArgumentParser()
244  parser.add_argument("-v", "--verbose", action="store_true",
245                      help="Verbose mode")
246  parser.add_argument("-f", "--full", action="store_true",
247                      help="plot the whole period")
248  parser.add_argument("-i", "--increment", action="store",
249                      type=int, default=1, dest="inc",
250                      help="sampling increment")
251  parser.add_argument("-r", "--range", action="store", nargs=2,
252                      type=string_to_date,
253                      help="date range: ssaa-mm-jj ssaa-mm-jj")
254  parser.add_argument("-m", "--max", action="store_true",
255                      help="plot with y_max = allocation")
256
257  return parser.parse_args()
258
259
260########################################
261if __name__ == '__main__':
262
263  # .. Initialization ..
264  # ====================
265  # ... Command line arguments ...
266  # ------------------------------
267  args = get_arguments()
268
269  # ... Files and directories ...
270  # -----------------------------
271  dir_data = os.path.join("..", "output")
272  file_pattern = "OUT_CONSO_"
273  file_param = get_last_file(dir_data, file_pattern+"PARAM")
274  file_utheo = get_last_file(dir_data, file_pattern+"UTHEO")
275  file_bilan = get_last_file(dir_data, file_pattern+"BILAN")
276  file_login = get_last_file(dir_data, file_pattern+"LOGIN")
277  file_store = get_last_file(dir_data, file_pattern+"STORE")
278
279  if args.verbose:
280    print(file_param)
281    print(file_utheo)
282    print(file_bilan)
283    print(file_login)
284    print(file_store)
285
286  # .. Get project info ..
287  # ======================
288  gencmip6 = Project()
289  gencmip6.fill_data(file_param)
290  gencmip6.get_date_init(file_utheo)
291
292  # .. Fill in conso data ..
293  # ========================
294  # ... Initialization ...
295  # ----------------------
296  bilan = BilanDict()
297  bilan.init_range(gencmip6.date_init, gencmip6.deadline)
298  # ... Extract data from file ...
299  # ------------------------------
300  bilan.fill_data(file_bilan)
301  # ... Compute theoratical use from known data  ...
302  # ------------------------------------------------
303  bilan.theo_equation()
304
305  # .. Extract data depending on C.L. arguments ..
306  # ==============================================
307  # args.range = [
308  #   string_to_date("2015-01-01"),
309  #   string_to_date("2015-01-31")
310  # ]
311  # args.full  = True
312  if args.full:
313    selected_items = bilan.get_items_in_full_range(args.inc)
314  elif args.range:
315    selected_items = bilan.get_items_in_range(
316      args.range[0], args.range[1], args.inc
317    )
318  else:
319    selected_items = bilan.get_items(args.inc)
320
321  # .. Compute data to be plotted ..
322  # ================================
323  nb_items = len(selected_items)
324
325  xcoord    = np.linspace(1, nb_items, num=nb_items)
326  xlabels   = ["{:%d-%m}".format(item.date)
327               for item in selected_items]
328  consos    = np.array([item.conso for item in selected_items],
329                        dtype=float)
330  theo_uses = np.array([100.*item.theo_use for item in selected_items],
331                       dtype=float)
332  real_uses = np.array([100.*item.real_use for item in selected_items],
333                       dtype=float)
334  theo_equs = np.array([100.*item.theo_equ for item in selected_items],
335                       dtype=float)
336
337  # .. Plot stuff ..
338  # ================
339  # ... Initialize figure ...
340  # -------------------------
341  (fig, ax_conso, ax_theo) = plot_init()
342
343  # ... Plot data ...
344  # -----------------
345  plot_data(ax_conso, ax_theo, xcoord, xlabels,
346            consos, theo_uses, real_uses, theo_equs)
347
348  # ... Tweak figure ...
349  # --------------------
350  if args.max:
351    ymax = gencmip6.alloc
352  else:
353    ymax = np.nanmax(consos) + np.nanmax(consos)*.1
354
355  title = "Consommation {}\n({:%d/%m/%Y} - {:%d/%m/%Y})".format(
356    gencmip6.project.upper(),
357    gencmip6.date_init,
358    gencmip6.deadline
359  )
360
361  plot_config(ax_conso, ax_theo, xcoord, xlabels, ymax, title)
362
363  # ... Save figure ...
364  # -------------------
365  dirout = "img"
366  img_name = "bilan.pdf"
367  plot_save(os.path.join(dirout, img_name))
368
369  plt.show()
Note: See TracBrowser for help on using the repository browser.