source: TOOLS/ConsoGENCMIP6/bin/plot_jobs.py @ 2849

Last change on this file since 2849 was 2849, checked in by labetoulle, 6 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: 13.8 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 libconso import *
16
17
18########################################
19class DataDict(dict):
20  #---------------------------------------
21  def __init__(self):
22    self = {}
23
24  #---------------------------------------
25  def init_range(self, date_beg, date_end, inc=1):
26    """
27    """
28    delta = date_end - date_beg + dt.timedelta(days=1)
29
30    (deb, fin) = (0, int(delta.total_seconds() / 3600))
31
32    dates = (date_beg + dt.timedelta(hours=i)
33             for i in xrange(deb, fin, inc))
34
35    for date in dates:
36      self.add_item(date)
37
38  #---------------------------------------
39  def fill_data(self, file_list):
40    """
41    """
42    for filein in sorted(file_list):
43      try:
44        data = np.genfromtxt(
45          filein,
46          skip_header=1,
47          converters={
48            0: string_to_datetime,
49            1: float,
50            2: float,
51          },
52          missing_values="nan",
53        )
54      except Exception as rc:
55        print("Problem with file {} :\n{}".format(filein, rc))
56        exit(1)
57
58      if len(data) == 24:
59        run_mean = np.nanmean(
60            np.array([run for _, run, _ in data])
61        )
62        pen_mean = np.nanmean(
63            np.array([pen for _, _, pen in data])
64        )
65        run_std = np.nanstd(
66            np.array([run for _, run, _ in data])
67        )
68        pen_std = np.nanstd(
69            np.array([pen for _, _, pen in data])
70        )
71      else:
72        run_mean = np.nan
73        pen_mean = np.nan
74        run_std  = np.nan
75        pen_std  = np.nan
76
77      for date, run, pen in data:
78        if date.hour == 0:
79          self.add_item(
80            date,
81            run,
82            pen,
83            run_mean,
84            pen_mean,
85            run_std,
86            pen_std
87          )
88        else:
89          self.add_item(date, run, pen)
90        self[date].fill()
91
92  #---------------------------------------
93  def add_item(self, date, run=np.nan, pen=np.nan,
94               run_mean=np.nan, pen_mean=np.nan,
95               run_std=np.nan, pen_std=np.nan):
96    """
97    """
98    self[date] = Conso(
99      date,
100      run,
101      pen,
102      run_mean,
103      pen_mean,
104      run_std,
105      pen_std
106    )
107
108  #---------------------------------------
109  def get_items_in_range(self, date_beg, date_end, inc=1):
110    """
111    """
112    items = (item for item in self.itervalues()
113                   if item.date >= date_beg and
114                      item.date <= date_end)
115    items = sorted(items, key=lambda item: item.date)
116
117    return items[::inc]
118
119  #---------------------------------------
120  def get_items_in_full_range(self, inc=1):
121    """
122    """
123    items = (item for item in self.itervalues()
124                   if item.date >= projet.date_init and
125                      item.date <= projet.deadline  and
126                      item.date.hour == 0)
127    items = sorted(items, key=lambda item: item.date)
128
129    return items[::inc]
130
131  #---------------------------------------
132  def get_items(self, inc=1):
133    """
134    """
135    items = (item for item in self.itervalues()
136                   if item.isfilled())
137    items = sorted(items, key=lambda item: item.date)
138
139    return items[::inc]
140
141
142class Conso(object):
143  #---------------------------------------
144  def __init__(self, date, run=np.nan, pen=np.nan,
145               run_mean=np.nan, pen_mean=np.nan,
146               run_std=np.nan, pen_std=np.nan):
147
148    self.date     = date
149    self.run      = run
150    self.pen      = pen
151    self.run_mean = run_mean
152    self.pen_mean = pen_mean
153    self.run_std  = run_std
154    self.pen_std  = pen_std
155    self.filled   = False
156
157  #---------------------------------------
158  def __repr__(self):
159    return "R{:.0f} ({:.0f}/{:.0f}) P{:.0f} ({:.0f}/{:.0f})".format(
160      self.run,
161      self.run_mean,
162      self.run_std,
163      self.pen,
164      self.pen_mean,
165      self.pen_std,
166    )
167
168  #---------------------------------------
169  def isfilled(self):
170    return self.filled
171
172  #---------------------------------------
173  def fill(self):
174    self.filled = True
175
176
177########################################
178def plot_init():
179  paper_size  = np.array([29.7, 21.0])
180  fig, ax = plt.subplots(figsize=(paper_size/2.54))
181
182  return fig, ax
183
184
185########################################
186def plot_data(ax, xcoord, dates, run_jobs, pen_jobs, run_std, pen_std):
187  """
188  """
189  line_width = 0.
190  width = 1.05
191
192  ax.bar(
193    xcoord, run_jobs, width=width, yerr=run_std/2.,
194    linewidth=line_width, align="center",
195    color="lightgreen", ecolor="green", antialiased=True,
196    label="jobs running"
197  )
198  if args.mode == "machine":
199    label = "jobs pending\n(Ressources & Priority)"
200  else:
201    label = "jobs pending"
202  ax.bar(
203    xcoord, pen_jobs, bottom=run_jobs, width=width,
204    linewidth=line_width, align="center",
205    color="firebrick", antialiased=True,
206    label=label
207  )
208
209
210########################################
211def plot_config(
212  fig, ax, xcoord, dates, title, conso_per_day, conso_per_day_2
213):
214  """
215  """
216  from matplotlib.ticker import AutoMinorLocator
217
218  # ... Compute useful stuff ...
219  # ----------------------------
220  multialloc = False
221
222  if args.mode == "machine":
223    xi = 0
224    xn = xi
225    xf = len(dates)
226    yi = 80000.
227    yf = yi
228  else:
229    if conso_per_day_2:
230      date_inter = projet.date_init + dt.timedelta(days=projet.days//2)
231      if projet.date_init in dates:
232        xi = dates.index(projet.date_init)
233      else:
234        xi = 0
235
236      if projet.deadline in dates:
237        xf = dates.index(projet.deadline)
238      else:
239        xf = len(dates)
240
241      if date_inter in dates:
242        xn = dates.index(date_inter)
243        yi = conso_per_day
244        yf = conso_per_day_2
245        multialloc = True
246      else:
247        if dates[-1] < date_inter:
248          xn = xf
249          yi = conso_per_day
250          yf = conso_per_day
251        elif dates[0] > date_inter:
252          xn = xi
253          yi = conso_per_day_2
254          yf = conso_per_day_2
255
256  # ... Config axes ...
257  # -------------------
258  # 1) Range
259  xmin, xmax = xcoord[0]-1, xcoord[-1]+1
260  if multialloc:
261    ymax = 4. * max(yi, yf)
262  else:
263    ymax = 4. * yi
264  ax.set_xlim(xmin, xmax)
265  ax.set_ylim(0, ymax)
266
267  # 2) Plot ideal daily consumption
268  line_color = "blue"
269  line_alpha = 0.5
270  line_label = "conso journaliÚre idéale"
271  ax.plot(
272    [xi, xn, xn, xf], [yi, yi, yf, yf],
273    color=line_color, alpha=line_alpha, label=line_label,
274  )
275
276  # 3) Ticks labels
277  (date_beg, date_end) = (dates[0], dates[-1])
278
279  if date_end - date_beg > dt.timedelta(weeks=9):
280    date_fmt = "{:%d-%m}"
281    maj_xticks = [x for x, d in zip(xcoord, dates)
282                     if d.weekday() == 0 and d.hour == 0]
283    maj_xlabs  = [date_fmt.format(d) for d in dates
284                     if d.weekday() == 0 and d.hour == 0]
285  else:
286    date_fmt = "{:%d-%m %Hh}"
287    maj_xticks = [x for x, d in zip(xcoord, dates)
288                     if d.hour == 0 or d.hour == 12]
289    maj_xlabs  = [date_fmt.format(d) for d in dates
290                     if d.hour == 0 or d.hour == 12]
291
292  ax.set_xticks(xcoord, minor=True)
293  ax.set_xticks(maj_xticks, minor=False)
294  ax.set_xticklabels(maj_xlabs, rotation="vertical", size="x-small")
295
296  minor_locator = AutoMinorLocator()
297  ax.yaxis.set_minor_locator(minor_locator)
298
299  yticks = list(ax.get_yticks())
300  if args.mode == "machine":
301    yticks.append(yi)
302  else:
303    yticks.append(conso_per_day)
304    if multialloc:
305      yticks.append(conso_per_day_2)
306  ax.set_yticks(yticks)
307
308  for x, d in zip(xcoord, dates):
309    if d.weekday() == 0 and d.hour == 0:
310      ax.axvline(x=x, color="black", linewidth=1., linestyle=":")
311
312  # 4) Define axes title
313  ax.set_ylabel("cœurs", fontweight="bold")
314  ax.tick_params(axis="y", labelsize="small")
315
316  # 5) Define plot size
317  fig.subplots_adjust(
318    left=0.08,
319    bottom=0.09,
320    right=0.93,
321    top=0.93,
322  )
323
324  # ... Main title and legend ...
325  # -----------------------------
326  fig.suptitle(title, fontweight="bold", size="large")
327  ax.legend(loc="upper right", fontsize="x-small", frameon=False)
328
329
330########################################
331def get_arguments():
332  parser = ArgumentParser()
333  parser.add_argument("-v", "--verbose", action="store_true",
334                      help="verbose mode")
335  parser.add_argument("-f", "--full", action="store_true",
336                      help="plot the whole period")
337  parser.add_argument("-i", "--increment", action="store",
338                      type=int, default=1, dest="inc",
339                      help="sampling increment")
340  parser.add_argument("-r", "--range", action="store", nargs=2,
341                      type=string_to_date,
342                      help="date range: ssaa-mm-jj ssaa-mm-jj")
343  parser.add_argument("-s", "--show", action="store_true",
344                      help="interactive mode")
345  parser.add_argument("-d", "--dods", action="store_true",
346                      help="copy output on dods")
347  parser.add_argument("-m", "--mode", action="store",
348                      choices=["project", "machine"],
349                      default="project",
350                      help="copy output on dods")
351
352  return parser.parse_args()
353
354
355########################################
356if __name__ == '__main__':
357
358  # .. Initialization ..
359  # ====================
360  # ... Command line arguments ...
361  # ------------------------------
362  args = get_arguments()
363
364  # ... Constants ...
365  # -----------------
366  WEEK_NB = 3
367
368  # ... Turn interactive mode off ...
369  # ---------------------------------
370  if not args.show:
371    import matplotlib
372    matplotlib.use('Agg')
373
374  import matplotlib.pyplot as plt
375  # from matplotlib.backends.backend_pdf import PdfPages
376
377  if not args.show:
378    plt.ioff()
379
380  # ... Files and directories ...
381  # -----------------------------
382  project_name, DIR, OUT = parse_config("bin/config.ini")
383
384  (file_param, file_utheo) = \
385      get_input_files(DIR["SAVEDATA"], [OUT["PARAM"], OUT["UTHEO"]])
386
387  if args.mode == "project":
388    pattern = "OUT_JOBS_PENDING_"
389  else:
390    pattern = "OUT_JOBS_PEN_FULL_"
391  file_list = glob.glob(
392    os.path.join(DIR["SAVEDATA"], pattern + "*")
393  )
394
395  img_name = "jobs_{}".format(args.mode)
396
397  today   = dt.datetime.today()
398  weeknum = today.isocalendar()[1]
399
400  if args.verbose:
401    fmt_str = "{:10s} : {}"
402    print(fmt_str.format("args", args))
403    print(fmt_str.format("today", today))
404    print(fmt_str.format("file_param", file_param))
405    print(fmt_str.format("file_utheo", file_utheo))
406    print(fmt_str.format("file_list", file_list))
407    print(fmt_str.format("img_name", img_name))
408
409  # .. Get project info ..
410  # ======================
411  projet = Project(project_name)
412  projet.fill_data(file_param)
413  projet.get_date_init(file_utheo)
414
415  # .. Fill in data ..
416  # ==================
417  # ... Initialization ...
418  # ----------------------
419  bilan = DataDict()
420  bilan.init_range(projet.date_init, projet.deadline)
421  # ... Extract data from file ...
422  # ------------------------------
423  bilan.fill_data(file_list)
424
425  # .. Extract data depending on C.L. arguments ..
426  # ==============================================
427  if args.full:
428    selected_items = bilan.get_items_in_full_range(args.inc)
429  elif args.range:
430    selected_items = bilan.get_items_in_range(
431      args.range[0], args.range[1], args.inc
432    )
433  else:
434    range_end   = today
435    range_start = (
436      range_end + dt.timedelta(weeks=-WEEK_NB) - dt.timedelta(days=1)
437    )
438    selected_items = bilan.get_items_in_range(
439      range_start, range_end, args.inc
440    )
441
442  # .. Compute data to be plotted ..
443  # ================================
444  nb_items = len(selected_items)
445
446  xcoord   = np.linspace(1, nb_items, num=nb_items)
447  dates  = [item.date for item in selected_items]
448
449  if args.full:
450    run_jobs = np.array([item.run_mean for item in selected_items],
451                         dtype=float)
452    pen_jobs = np.array([item.pen_mean for item in selected_items],
453                         dtype=float)
454    run_std  = np.array([item.run_std for item in selected_items],
455                         dtype=float)
456    pen_std  = np.array([item.pen_std for item in selected_items],
457                         dtype=float)
458  else:
459    run_jobs = np.array([item.run for item in selected_items],
460                         dtype=float)
461    pen_jobs = np.array([item.pen for item in selected_items],
462                         dtype=float)
463    run_std  = np.nan
464    pen_std  = np.nan
465
466  if args.verbose:
467    for i in selected_items:
468      if not np.isnan(i.run_mean):
469        print(
470          "{} {:13.2f} {:13.2f} {:13.2f} {:13.2f}".format(
471           i.date,
472           i.run_mean, i.pen_mean,
473           i.run_std, i.pen_std
474          )
475        )
476
477  if projet.project == "gencmip6":
478    alloc1 = (1 * projet.alloc) / 3
479    alloc2 = (2 * projet.alloc) / 3
480    conso_per_day   = 2 * alloc1 / (projet.days * 24.)
481    conso_per_day_2 = 2 * alloc2 / (projet.days * 24.)
482  else:
483    conso_per_day = projet.alloc / (projet.days * 24.)
484    conso_per_day_2 = None
485
486  # .. Plot stuff ..
487  # ================
488  # ... Initialize figure ...
489  # -------------------------
490  (fig, ax) = plot_init()
491
492  # ... Plot data ...
493  # -----------------
494  plot_data(ax, xcoord, dates, run_jobs, pen_jobs, run_std, pen_std)
495
496  # ... Tweak figure ...
497  # --------------------
498  title = "Suivi des jobs {}\n({:%d/%m/%Y} - {:%d/%m/%Y})".format(
499    projet.project.upper(),
500    projet.date_init,
501    projet.deadline
502  )
503
504  plot_config(
505    fig, ax, xcoord, dates, title, conso_per_day, conso_per_day_2
506  )
507
508  # ... Save figure ...
509  # -------------------
510  img_in  = os.path.join(DIR["PLOT"], "{}.pdf".format(img_name))
511  img_out = os.path.join(DIR["SAVEPLOT"],
512                         "{}_w{:02d}.pdf".format(img_name, weeknum))
513
514  plot_save(img_in, img_out, title, DIR)
515
516  # ... Publish figure on dods ...
517  # ------------------------------
518  if args.dods:
519    dods_cp(img_in, DIR)
520
521  if args.show:
522    plt.show()
523
524  exit(0)
Note: See TracBrowser for help on using the repository browser.