source: TOOLS/ConsoGENCMIP6/bin/plot_jobs_daily.py @ 3940

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