def __init__(self, nrows=4, ncols=1, figsize=(14, 11)): self.fig, self.axes = mplutils.make_fig(nrows=nrows, ncols=ncols, figsize=figsize) # self.axs = self.axes.ravel() self.col = ['r', 'k'] self.alf = [1.0, 0.7] self.i = 0
def plot_extremes_blade_radial(self, df_ext, fpath): nrows = 2 ncols = 2 figsize = (11, 7.15) fig, axes = mplutils.make_fig(nrows=nrows, ncols=ncols, figsize=figsize) # self.col = ['r', 'k'] # self.alf = [1.0, 0.7] # self.i = 0 mx_max = df_ext['max'][df_ext.comp == 'x'].dropna() mx_min = df_ext['min'][df_ext.comp == 'x'].dropna() my_max = df_ext['max'][df_ext.comp == 'y'].dropna() my_min = df_ext['min'][df_ext.comp == 'y'].dropna() # nodes = df_ext.node.ix[mx_max.index] axes[0, 0].plot(mx_max, 'r^', label='$M_{x_{max}}$') axes[0, 1].plot(mx_min, 'bv', label='$M_{x_{min}}$') axes[1, 0].plot(my_max, 'r^', label='$M_{y_{max}}$') axes[1, 1].plot(my_min, 'bv', label='$M_{y_{min}}$') axs = axes.ravel() for ax in axs: ax.grid() ax.legend(loc='best') # axs[0].set_xticklabels([]) # axs[1].set_xticklabels([]) # axs[2].set_xticklabels([]) # axs[-1].set_xlabel('time [s]') fig.tight_layout() fig.subplots_adjust(hspace=0.06) fig.subplots_adjust(top=0.98) fdir = os.path.dirname(fpath) # fname = os.path.basename(fpath) if not os.path.exists(fdir): os.makedirs(fdir) print('saving: %s ...' % fpath, end='') fig.savefig(fpath) #.encode('latin-1') print('done') fig.clear() # save as tables df_ext.ix[mx_max.index].to_excel(fpath.replace('.png', '_mx_max.xls')) df_ext.ix[mx_min.index].to_excel(fpath.replace('.png', '_mx_min.xls')) df_ext.ix[my_max.index].to_excel(fpath.replace('.png', '_my_max.xls')) df_ext.ix[my_min.index].to_excel(fpath.replace('.png', '_my_min.xls'))
def plot_staircase(sim_ids, post_dirs, run_dirs, fig_dir_base=None, cname='dlc00_stair_wsp04_25_noturb.htc'): """ Default stair and ramp names: dlc00_stair_wsp04_25_noturb dlc00_ramp_wsp04_25_04_noturb """ stairs = [] col = ['r', 'k'] alf = [1.0, 0.7] # if sim_id is a list, combine the two dataframes into one if type(sim_ids).__name__ == 'list': for ii, sim_id in enumerate(sim_ids): if isinstance(post_dirs, list): post_dir = post_dirs[ii] else: post_dir = post_dirs stairs.append(sim.Cases(post_dir, sim_id, rem_failed=True)) else: sim_id = sim_ids sim_ids = [sim_id] post_dir = post_dirs stairs.append(sim.Cases(post_dir, sim_id, rem_failed=True)) fig, axes = mplutils.make_fig(nrows=3, ncols=1, figsize=(14, 10)) ax = axes.ravel() for i, cc in enumerate(stairs): if cname in cc.cases_fail: print('no result for %s' % cc.sim_id) continue cc.change_results_dir(run_dirs[i]) res = cc.load_result_file(cc.cases[cname]) respath = cc.cases[cname]['[run_dir]'] fname = os.path.join(respath, cname) df_respost = pd.read_hdf(fname + '_postres.h5', 'table') sim_id = cc.sim_id time = res.sig[:, 0] t0, t1 = time[0], time[-1] # find the wind speed for channame, chan in res.ch_dict.items(): if channame.startswith('windspeed-global-Vy-0.00-0.00'): break wind = res.sig[:, chan['chi']] chi = res.ch_dict['bearing-pitch1-angle-deg']['chi'] pitch = res.sig[:, chi] chi = res.ch_dict['bearing-shaft_rot-angle_speed-rpm']['chi'] rpm = res.sig[:, chi] chi = res.ch_dict['bearing-pitch1-angle-deg']['chi'] pitch = res.sig[:, chi] chi = res.ch_dict['tower-tower-node-001-momentvec-x']['chi'] tx = res.sig[:, chi] chi = res.ch_dict['tower-tower-node-001-momentvec-y']['chi'] ty = res.sig[:, chi] chi = res.ch_dict['DLL-2-inpvec-2']['chi'] power = res.sig[:, chi] chi = res.ch_dict['DLL-2-inpvec-2']['chi'] power_mech = df_respost['stats-shaft-power'] ax[0].plot(time, wind, col[i] + '--', label='%s wind speed' % sim_id, alpha=alf[i]) ax[0].plot(time, pitch, col[i] + '-.', label='%s pitch' % sim_id, alpha=alf[i]) ax[0].plot(time, rpm, col[i] + '-', label='%s RPM' % sim_id, alpha=alf[i]) ax[1].plot(time, tx, col[i] + '--', label='%s Tower FA' % sim_id, alpha=alf[i]) ax[1].plot(time, ty, col[i] + '-', label='%s Tower SS' % sim_id, alpha=alf[i]) ax[2].plot(time, power / 1e6, col[i] + '-', label='%s El Power' % sim_id, alpha=alf[i]) ax[2].plot(time, power_mech / 1e3, col[i] + '-', alpha=alf[i], label='%s Mech Power' % sim_id) ax[0].set_xlim([t0, t1]) ax[0].grid() ax[0].legend(loc='best') ax[0].set_xticklabels([]) # ax[0].set_xlabel('time [s]') ax[1].set_xlim([t0, t1]) ax[1].grid() ax[1].legend(loc='best') ax[1].set_xticklabels([]) # ax[1].set_xlabel('time [s]') ax[2].set_xlim([t0, t1]) ax[2].grid() ax[2].legend(loc='best') ax[2].set_xlabel('time [s]') fig.tight_layout() fig.subplots_adjust(hspace=0.06) fig.subplots_adjust(top=0.92) if not os.path.exists(fig_dir_base): os.makedirs(fig_dir_base) fig_path = os.path.join(fig_dir_base, '-'.join(sim_ids) + '_stair.png') print('saving: %s ...' % fig_path, end='') fig.savefig(fig_path) #.encode('latin-1') print('done') fig.clear()
def plot_dlc_stats(df_stats, plot_chans, fig_dir_base, labels=None, figsize=(8, 6), dlc_ignore=['00'], run_dirs=None, sim_ids=[], eps=False, ylabels=None, title=True, chans_ms_1hz={}): """Create for each DLC an overview plot of the statistics. df_stats required columns: * [DLC] * [run_dir] * [wdir] * [Windspeed] * channel * stat parameters Parameters ---------- df_stats : pandas.DataFrame plot_chans : dict Dictionary of channels to be plotted. Key is used for the plot title, value is a list of unique channel names that will be included for the statistic values. For example, plot_chans['$B123 M_x$'] = ['blade1-blade1-node-003-momentvec-x', 'blade2-blade2-node-003-momentvec-x'] fig_dir_base : str Base directory of where to store all the figures. A new sub-directory will be created for each DLC. labels : list, default=None Labels used in the legend when comparing various DLB's figsize : tuple, default=(8,6) dlc_ignore : list, default=['dlc00'] By default all but dlc00 (stair case, wind ramp) are plotted. Add more dlc numbers here if necessary. run_dirs : list, default=None If run_dirs is not defined it will be taken from the unique values in the DataFrame. The order of the elements in this list needs to be consistent with labels, sim_ids (if defined). sim_ids : list, default=[] Only used when creating the file name of the figures: appended at the end of the file name (which starts with the unique channel name). chans_ms_1hz : dict, default={} Key/value pairs of channel and list of to be plotten m values. Channel refers to plot title/label as used as the key value in plot_chans. """ def fig_epilogue(fig, ax, fname_base): ax.grid() ax.set_xlim(xlims) leg = ax.legend(bbox_to_anchor=(1, 1), loc='lower right', ncol=3) leg.get_frame().set_alpha(0.7) title_space = 0.0 if title: fig_title = '%s %s' % (dlc_name, ch_dscr) # FIXME: dlc_name is assumed to be not in math mode ($$), so # escape underscores to avoid latex going bananas if mpl.rcParams['text.usetex']: fig_title = '%s %s' % (dlc_name.replace('_', '\\_'), ch_dscr) fig.suptitle(fig_title) title_space = 0.02 ax.set_xlabel(xlabel) if ylabels is not None: ax.set_ylabel(ylabels[ch_name]) fig.tight_layout() spacing = 0.94 - title_space - (0.065 * (ii + 1)) fig.subplots_adjust(top=spacing) fig_path = os.path.join(fig_dir, dlc_name) if len(sim_ids) == 1: fname = fname_base + '.png' else: fname = '%s_%s.png' % (fname_base, '_'.join(sim_ids)) if not os.path.exists(fig_path): os.makedirs(fig_path) fig_path = os.path.join(fig_path, fname) fig.savefig(fig_path) #.encode('latin-1') if eps: fig.savefig(fig_path.replace('.png', '.eps')) fig.clear() print('saved: %s' % fig_path) mfcs1 = ['k', 'w'] mfcs2 = ['b', 'w'] mfcs3 = ['r', 'w'] mfcs4 = ['k', 'b'] mark4 = ['s', 'o', '<', '>'] mfls4 = ['-', '--'] required = [ '[DLC]', '[run_dir]', '[wdir]', '[Windspeed]', '[res_dir]', '[Case folder]' ] cols = df_stats.columns for col in required: if col not in cols: print('plot_dlc_stats requires DataFrame with following columns:') print(required) print('following column is missing in stats DataFrame:', col) return if run_dirs is None: run_dirs = df_stats['[run_dir]'].unique() if not sim_ids: sim_ids = [] for run_dir in run_dirs: # in case this is a windows path: tmp = run_dir.replace('\\', '/').replace(':', '') sim_ids.append(tmp.split('/')[-2]) # first, take each DLC appart for gr_name, gr_dlc in df_stats.groupby(df_stats['[Case folder]']): dlc_name = gr_name if dlc_name[:3].lower() == 'dlc': # FIXME: this is messy since this places a hard coded dependency # between [Case folder] and [Case id.] when the tag [DLC] is # defined in dlcdefs.py dlc_name = gr_name.split('_')[0] # do not plot the stats for dlc00 if dlc_name.lower() in dlc_ignore: continue # cycle through all the target plot channels for ch_dscr, ch_names in plot_chans.items(): # second, group per channel. Note that when the channel names are not # identical, we need to manually pick them. # figure file name will be the first channel if isinstance(ch_names, list): ch_name = ch_names[0] fname_base = ch_names[0].replace('/', '_') df_chan = gr_dlc[gr_dlc.channel.isin(ch_names)] else: ch_name = ch_names ch_names = [ch_names] df_chan = gr_dlc[gr_dlc.channel == ch_names] fname_base = ch_names.replace('/', '_') # if not, than we are missing a channel description, or the channel # is simply not available in the given result set # if not len(df_chan.channel.unique()) == len(ch_names): # continue lens = [] # instead of groupby, select the run_dir in the same order as # occuring in the labels and post_dirs lists for run_dir in run_dirs: lens.append(len(df_chan[df_chan['[run_dir]'] == run_dir])) # for key, gr_ch_dlc_sid in df_chan.groupby(df_chan['[run_dir]']): # lens.append(len(gr_ch_dlc_sid)) # when the channel is simply not present if len(lens) == 0: continue # when only one of the channels was present, but the set is still # complete. # FIXME: what if both channels are present? if len(ch_names) > 1 and (lens[0] < 1): continue elif len(ch_names) > 1 and len(lens) == 2 and lens[1] < 1: continue print('start plotting: %s %s' % (dlc_name.ljust(10), ch_dscr)) fig, axes = mplutils.make_fig(nrows=1, ncols=1, figsize=figsize, dpi=120) ax = axes[0, 0] # seperate figure for the mean of the 1Hz equivalent loads fig2, axes2 = mplutils.make_fig(nrows=1, ncols=1, figsize=figsize, dpi=120) ax2 = axes2[0, 0] if fig_dir_base is None and len(sim_ids) < 2: res_dir = df_chan['[res_dir]'][:1].values[0] fig_dir = os.path.join(fig_dir_base, res_dir) elif fig_dir_base is None and len(sim_ids) > 0: fig_dir = os.path.join(fig_dir_base, '-'.join(sim_ids)) # elif fig_dir_base and len(sim_ids) < 2: # res_dir = df_chan['[res_dir]'][:1].values[0] # fig_dir = os.path.join(fig_dir_base, res_dir) elif fig_dir_base is not None: # create the compare directory if not defined fig_dir = fig_dir_base # if we have a list of different cases, we also need to group those # because the sim_id wasn't saved before in the data frame, # we need to derive that from the run dir # if there is only one run dir nothing changes # sid_names = [] # for clarity, set off-set on wind speed when comparing two DLB's if len(lens) == 2: windoffset = [-0.2, 0.2] dirroffset = [-5, 5] else: windoffset = [0] dirroffset = [0] # in case of a fully empty plot xlims will remain None and there # is no need to save the plot xlims = None # instead of groupby, select the run_dir in the same order as # occuring in the labels, post_dirs lists for ii, run_dir in enumerate(run_dirs): gr_ch_dlc_sid = df_chan[df_chan['[run_dir]'] == run_dir] if len(gr_ch_dlc_sid) < 1: print('no data for run_dir:', run_dir) continue # for run_dir, gr_ch_dlc_sid in df_chan.groupby(df_chan['[run_dir]']): if labels is None: sid_name = sim_ids[ii] else: sid_name = labels[ii] # sid_names.append(sid_name) print(' sim_id/label:', sid_name) # FIXME: will this go wrong in PY3? if dlc_name.lower() in ['dlc61', 'dlc62']: key = '[wdir]' xdata = gr_ch_dlc_sid[key].values + dirroffset[ii] xlabel = 'wind direction [deg]' xlims = [0, 360] else: key = '[Windspeed]' xdata = gr_ch_dlc_sid[key].values + windoffset[ii] xlabel = 'Wind speed [m/s]' xlims = [3, 27] dmin = gr_ch_dlc_sid['min'].values dmean = gr_ch_dlc_sid['mean'].values dmax = gr_ch_dlc_sid['max'].values dstd = gr_ch_dlc_sid['std'].values if len(sim_ids) == 1: lab1 = 'mean' lab2 = 'min' lab3 = 'max' lab4 = '1Hz EqL' else: lab1 = 'mean %s' % sid_name lab2 = 'min %s' % sid_name lab3 = 'max %s' % sid_name lab4 = '1Hz EqL %s' % sid_name mfc1 = mfcs1[ii] mfc2 = mfcs2[ii] mfc3 = mfcs3[ii] ax.errorbar(xdata, dmean, mec='k', marker='o', mfc=mfc1, ls='', label=lab1, alpha=0.7, yerr=dstd, ecolor='k') ax.plot(xdata, dmin, mec='b', marker='^', mfc=mfc2, ls='', label=lab2, alpha=0.7) ax.plot(xdata, dmax, mec='r', marker='v', mfc=mfc3, ls='', label=lab3, alpha=0.7) # mean of 1Hz equivalent loads ms = [] if ch_dscr in chans_ms_1hz: ms = chans_ms_1hz[ch_dscr] for im, m in enumerate(ms): # average over seed and possibly yaw angles # wind speed or yaw inflow according to dlc case gr_key = gr_ch_dlc_sid[key] d1hz = gr_ch_dlc_sid[m].groupby(gr_key).mean() ax2.plot(d1hz.index, d1hz.values, mec=mfcs4[ii], alpha=0.7, marker=mark4[im], ls=mfls4[ii], mfc=mfc1, label=lab4, color=mfcs4[ii]) # for wind, gr_wind in gr_ch_dlc.groupby(df_stats['[Windspeed]']): # wind = gr_wind['[Windspeed]'].values # dmin = gr_wind['min'].values#.mean() # dmean = gr_wind['mean'].values#.mean() # dmax = gr_wind['max'].values#.mean() ## dstd = gr_wind['std'].mean() # ax.plot(wind, dmean, 'ko', label='mean', alpha=0.7) # ax.plot(wind, dmin, 'b^', label='min', alpha=0.7) # ax.plot(wind, dmax, 'rv', label='max', alpha=0.7) ## ax.errorbar(wind, dmean, c='k', ls='', marker='s', mfc='w', ## label='mean and std', yerr=dstd) # if str(dlc_name) not in ['61', '62']: # ax.set_xticks(gr_ch_dlc_sid['[Windspeed]'].values) # don't save empyt plots if xlims is None: continue fig_epilogue(fig, ax, fname_base) # don't save empty plots if len(ms) < 1: continue fig_epilogue(fig2, ax2, fname_base + '_1hz_eql')