source: TOOLS/ConsoGENCMIP6/bin/plot_bilan_jobs.py @ 2449

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

keep only one jobs plot per week

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