def _ystrings(ynames, ylabel, yunit, legends): """Generate a y-axis label and set of legend entries. """ if ynames: if ylabel is None: # Try to create a suitable axis label. descriptions = self.get_description(ynames) # If the descriptions are the same, label the y axis with # the 1st one. ylabel = descriptions[0] if len(set(descriptions)) <> 1: print("The y-axis variable descriptions are not all " "the same. The first has been used. Please " "provide the proper name via ylabel1 or ylabel2.") if legends == []: legends = ynames if incl_prefix: legends = [self.fbase + ': ' + leg for leg in legends] if suffix: legends = ([leg + ' (%s)' % suffix for leg in legends] if use_paren else [leg + suffix for leg in legends]) assert len(set(self.get_dimension(ynames))) == 1, \ "The variables on the y-axis do not have the same physical dimension." if yunit is None: # Use the unit of the 1st variable. yunit = self.get_unit(ynames[0]) if ylabel <> "": ylabel = label_number(ylabel, yunit) return ylabel, yunit, legends
def _ystrings(ynames, ylabel, yunit, legends): """Generate a y-axis label and set of legend entries. """ if ynames: if ylabel is None: # Try to create a suitable axis label. descriptions = self.get_description(ynames) # If the descriptions are the same, label the y axis with # the 1st one. ylabel = descriptions[0] if len(set(descriptions)) <> 1: print( "The y-axis variable descriptions are not all " "the same. The first has been used. Please " "provide the proper name via ylabel1 or ylabel2.") if legends == []: legends = ynames if incl_prefix: legends = [self.fbase + ': ' + leg for leg in legends] if suffix: legends = ([leg + ' (%s)' % suffix for leg in legends] if use_paren else [leg + suffix for leg in legends]) assert len(set(self.get_dimension(ynames))) == 1, \ "The variables on the y-axis do not have the same physical dimension." if yunit is None: # Use the unit of the 1st variable. yunit = self.get_unit(ynames[0]) if ylabel <> "": ylabel = label_number(ylabel, yunit) return ylabel, yunit, legends
def currdenfig(self, title=None, times=[0], z_index=0, leg_kwargs={}, **kwargs): """Plot current densities of the cell segments at times. **Arguments:** - *title*: Title for the figure - *times*: List of times at which the data should be sampled If multiple times are given, then subfigures will be generated. - *z_index*: z-index at which the current densities are taken - *leg_kwargs*: Dictionary of keyword arguments for :meth:`matplotlib.pyplot.legend` If *leg_kwargs* is *None*, then no legend will be shown. - *\*\*kwargs*: Additional arguments for :meth:`barfig` """ # Process the arguments. xlabel = kwargs.pop('xlabel', 'y-axis index (inlet to outlet)') name_template = self.cell + ('iprimeprime_seg[%i, ' + '%i]' % (z_index+1)) #description = self.get_description(name_template % 1) #unit = self.get_unit(name_template % 1) unit = self.get_unit(name_template % 1) #ylabel = kwargs.pop('ylabel', label_number(description, unit)) ylabel = kwargs.pop('ylabel', label_number("Current density", unit)) # Create the plot. ax = self.barfig(names=[name_template % (i_y+1) for i_y in range(1)], times=times, xlabel=xlabel, ylabel=ylabel, leg_kwargs=None, **kwargs) for a, time in zip(ax, times): a.axhline(self.get_values('cell.iprimeprime', time), linestyle='--', color='k', label='Entire cell') # Decorate. if title is None: if self.n_z == 1: plt.title("Current Distribution of Cell Segments") else: plt.title("Current Distribution of Cell Segments\n" "z-axis index %i (of %i)" % (z_index+1, self.n_z)) if leg_kwargs is not None: loc = leg_kwargs.pop('loc', 'best') if len(ax) == 1: ax[0].legend(loc=loc, **leg_kwargs) else: plt.figlegend(ax[0].lines, **leg_kwargs)
def plot(self, ynames1=[], ylabel1=None, yunit1=None, legends1=[], leg1_kwargs={'loc': 'best'}, ax1=None, ynames2=[], ylabel2=None, yunit2=None, legends2=[], leg2_kwargs={'loc': 'best'}, ax2=None, xname='Time', xlabel=None, xunit=None, title=None, label="xy", incl_prefix=False, suffix=None, use_paren=True, **kwargs): r"""Plot data as points and/or curves in 2D Cartesian coordinates. A new figure is created if necessary. **Arguments:** - *ynames1*: Names of variables for the primary y axis If any names are invalid, then they will be skipped. - *ylabel1*: Label for the primary y axis If *ylabel1* is *None* (default) and all of the variables have the same Modelica_ description string, then the common description will be used. Use '' for no label. - *yunit1*: String indicating the unit for the primary y-axis (see note for *xunit*) If *yunit1* is *None*, the Modelica_ *displayUnit* of the first entry of *ynames1* or the default unit (from *fcres.ini*) based on that variable's dimension will be used (in decreasing priority). .. Note:: Dimension checking is not currently performed, so it is important to ensure that a proper unit is chosen. - *legends1*: List of legend entries for variables assigned to the primary y axis If *legends1* is an empty list ([]), ynames1 will be used. If *legends1* is *None* and all of the variables on the primary axis have the same unit, then no legend will be shown. - *leg1_kwargs*: Dictionary of keyword arguments for the primary legend - *ax1*: Primary y axes If *ax1* is not provided, then axes will be created in a new figure. - *ynames2*, *ylabel2*, *yunit2*, *legends2*, *leg2_kwargs*, and *ax2*: Similar to *ynames1*, *ylabel1*, *yunit1*, *legends1*, *leg1_kwargs*, and *ax1* but for the secondary y axis - *xname*: Name of the x-axis data - *xlabel*: Label for the x axis If *xlabel* is *None* (default), the variable's Modelica_ description string will be applied. Use '' for no label. - *xunit*: String indicating the unit for the x axis (see note for *yunit1*) If *xunit* is *None*, the Modelica_ variable's *displayUnit* or the default unit (from *fcres.ini*) based on the variable's dimension will be used (in decreasing priority). - *title*: Title for the figure If *title* is *None* (default), then the title will be the base filename. Use '' for no title. - *label*: Label for the figure (ignored if ax is provided) This will be used as a base filename if the figure is saved. - *incl_prefix*: If *True*, prefix the legend strings with the base filename of the class. - *suffix*: String that will be added at the end of the legend entries - *use_paren*: Add parentheses around the suffix - *\*\*kwargs*: Additional arguments for :meth:`base.plot` (and thus to :meth:`matplotlib.pyplot.plot`) If both y axes are used (primary and secondary), then the *dashes* argument is ignored. The curves on the primary axis will be solid and the curves on the secondary axis will be dotted. **Returns:** 1. *ax1*: Primary y axes 2. *ax2*: Secondary y axes **Example:** .. testsetup:: >>> from fcres import closeall >>> closeall() .. code-block:: python >>> from fcres import SimRes, saveall >>> sim = SimRes('examples/SaturationPressure') >>> sim.plot(xname="subregion.gas.H2O.T", ... ynames1=["subregion.gas.H2O.p", "p_sat"], ... legends1=["FCSys (from Gibbs equilibrium)", ... "Modelica.Media (correlated function)"], ... ylabel1='Saturation pressure', ... title="Water Saturation Pressure", ... label='examples/SaturationPressure') # doctest: +ELLIPSIS (<matplotlib.axes._subplots.AxesSubplot object at 0x...>, None) >>> saveall() Saved examples/SaturationPressure.pdf Saved examples/SaturationPressure.png .. only:: html .. image:: ../examples/SaturationPressure.png :scale: 70 % :alt: plot of water saturation pressure .. only:: latex .. figure:: ../examples/SaturationPressure.pdf :scale: 70 % Plot of water saturation pressure """ # Note: ynames1 is the first argument (besides self) so that plot() # can be called with simply a variable name. def _ystrings(ynames, ylabel, yunit, legends): """Generate a y-axis label and set of legend entries. """ if ynames: if ylabel is None: # Try to create a suitable axis label. descriptions = self.get_description(ynames) # If the descriptions are the same, label the y axis with # the 1st one. ylabel = descriptions[0] if len(set(descriptions)) <> 1: print("The y-axis variable descriptions are not all " "the same. The first has been used. Please " "provide the proper name via ylabel1 or ylabel2.") if legends == []: legends = ynames if incl_prefix: legends = [self.fbase + ': ' + leg for leg in legends] if suffix: legends = ([leg + ' (%s)' % suffix for leg in legends] if use_paren else [leg + suffix for leg in legends]) assert len(set(self.get_dimension(ynames))) == 1, \ "The variables on the y-axis do not have the same physical dimension." if yunit is None: # Use the unit of the 1st variable. yunit = self.get_unit(ynames[0]) if ylabel <> "": ylabel = label_number(ylabel, yunit) return ylabel, yunit, legends # Process the inputs. ynames1 = base.flatten_list(ynames1) ynames2 = base.flatten_list(ynames2) assert ynames1 or ynames2, "No signals were provided." if title is None: title = self.fbase # Create primary and secondary axes if necessary. if not ax1: fig = base.figure(label) ax1 = fig.add_subplot(111) if ynames2 and not ax2: ax2 = ax1.twinx() # Generate the x-axis label. if xlabel is None: xlabel = 'Time' if xname == 'Time' else self.get_description(xname) # With Dymola 7.4, the description of the time variable will be # "Time in", which isn't good. if xunit is None: xunit = self.get_unit(xname) if xlabel<> "": xlabel = label_number(xlabel, xunit) # Generate the y-axis labels and sets of legend entries. ylabel1, yunit1, legends1 = _ystrings(ynames1, ylabel1, yunit1, legends1) ylabel2, yunit2, legends2 = _ystrings(ynames2, ylabel2, yunit2, legends2) # Read the data. if xname == 'Time': t_scale = lambda t: t*self._unitvalue('s')/self._unitvalue(xunit) y_1 = self.get_values(ynames1, f=self.to_unit(yunit1)) y_2 = self.get_values(ynames2, f=self.to_unit(yunit2)) else: x = self.get_values(xname, f=self.to_unit(xunit)) times = self.get_times(xname) y_1 = self.get_values_at_times(ynames1, times, f=self.to_unit(yunit1)) y_2 = self.get_values_at_times(ynames2, times, f=self.to_unit(yunit2)) # Plot the data. if ynames1: if ynames2: # Use solid lines for primary axis and dotted lines for # secondary. kwargs['dashes'] = [(None, None)] base.plot(y_1, self.get_times(ynames1, f=t_scale) if xname == 'Time' else x, ax1, label=legends1, **kwargs) kwargs['dashes'] = [(3, 3)] base.plot(y_2, self.get_times(ynames2, f=t_scale) if xname == 'Time' else x, ax2, label=legends2, **kwargs) else: base.plot(y_1, self.get_times(ynames1, f=t_scale) if xname == 'Time' else x, ax1, label=legends1, **kwargs) elif ynames2: base.plot(y_2, self.get_times(ynames2, f=t_scale) if xname == 'Time' else x, ax2, label=legends2, **kwargs) # Decorate the figure. ax1.set_title(title) ax1.set_xlabel(xlabel) if ylabel1: ax1.set_ylabel(ylabel1) if ylabel2: ax2.set_ylabel(ylabel2) if legends1: if legends2: # Put the primary legend in the upper left and secondary in # upper right. leg1_kwargs['loc'] = 2 leg2_kwargs['loc'] = 1 ax1.legend(**leg1_kwargs) ax2.legend(**leg2_kwargs) else: ax1.legend(**leg1_kwargs) elif legends2: ax2.legend(**leg2_kwargs) return ax1, ax2
def currdenfig(self, title=None, times=[0], z_index=0, leg_kwargs={}, **kwargs): """Plot current densities of the cell segments at times. **Arguments:** - *title*: Title for the figure - *times*: List of times at which the data should be sampled If multiple times are given, then subfigures will be generated. - *z_index*: z-index at which the current densities are taken - *leg_kwargs*: Dictionary of keyword arguments for :meth:`matplotlib.pyplot.legend` If *leg_kwargs* is *None*, then no legend will be shown. - *\*\*kwargs*: Additional arguments for :meth:`barfig` """ # Process the arguments. xlabel = kwargs.pop('xlabel', 'y-axis index (inlet to outlet)') name_template = self.cell + ('iprimeprime_seg[%i, ' + '%i]' % (z_index + 1)) #description = self.get_description(name_template % 1) #unit = self.get_unit(name_template % 1) unit = self.get_unit(name_template % 1) #ylabel = kwargs.pop('ylabel', label_number(description, unit)) ylabel = kwargs.pop('ylabel', label_number("Current density", unit)) # Create the plot. ax = self.barfig(names=[name_template % (i_y + 1) for i_y in range(1)], times=times, xlabel=xlabel, ylabel=ylabel, leg_kwargs=None, **kwargs) for a, time in zip(ax, times): a.axhline(self.get_values('cell.iprimeprime', time), linestyle='--', color='k', label='Entire cell') # Decorate. if title is None: if self.n_z == 1: plt.title("Current Distribution of Cell Segments") else: plt.title("Current Distribution of Cell Segments\n" "z-axis index %i (of %i)" % (z_index + 1, self.n_z)) if leg_kwargs is not None: loc = leg_kwargs.pop('loc', 'best') if len(ax) == 1: ax[0].legend(loc=loc, **leg_kwargs) else: plt.figlegend(ax[0].lines, **leg_kwargs)
def colorfig(self, suffix, times=[0], n_rows=1, title="", subtitles=[], label="color", slice_axis='z', slice_index=0, xlabel="", xticklabels=[], xticks=[], ylabel="", yticklabels=[], yticks=[], clabel="", cbar_orientation='vertical', margin_left=rcParams['figure.subplot.left'], margin_right=1 - rcParams['figure.subplot.right'], margin_bottom=rcParams['figure.subplot.bottom'], margin_top=1 - rcParams['figure.subplot.top'], margin_cbar=0.2, wspace=0.1, hspace=0.25, cbar_space=0.1, cbar_width=0.05, **kwargs): """Create a figure with 2D scalar data at given time(s) on a color axis in 2D Cartesian coordinates. **Arguments:** - *suffix*: Name of the variable to be plotted (relative to the names of the subregions) - *times*: List of times at which the data should be sampled If multiple times are given, then subfigures will be generated. - *n_rows*: Number of rows of (sub)plots - *title*: Title for the figure - *subtitles*: List of subtitles (i.e., titles for each subplot) If not provided, "t = xx s" will be used, where xx is the time of each entry. "(initial)" or "(final)" are appended if appropriate. - *label*: Label for the figure This will be used as a base filename if the figure is saved. - *slice_axis*: Axis normal to the screen or page ('x', 'y', or 'z') - *slice_index*: Position along slice_axis - *xlabel*: Label for the x-axes (only shown for the subplots on the bottom row) - *xticklabels*: Labels for the x-axis ticks (only shown for the subplots on the bottom row) - *xticks*: Positions of the x-axis ticks - *ylabel*: Label for the y axis (only shown for the subplots on the left column) - *yticklabels*: Labels for the y-axis ticks (only shown for the subplots on the left column) - *yticks*: Positions of the y-axis ticks - *clabel*: Label for the color- or c-bar axis - *cbar_orientation*: Orientation of the colorbar ("vertical" or "horizontal") - *margin_left*: Left margin - *margin_right*: Right margin (ignored if ``cbar_orientation == 'vertical'``) - *margin_bottom*: Bottom margin (ignored if ``cbar_orientation == 'horizontal'``) - *margin_top*: Top margin - *margin_cbar*: Margin reserved for the colorbar (right margin if ``cbar_orientation == 'vertical'`` and bottom margin if ``cbar_orientation == 'horizontal'``) - *wspace*: The amount of width reserved for blank space between subplots - *hspace*: The amount of height reserved for white space between subplots - *cbar_space*: Space between the subplot rectangles and the colorbar - *cbar_width*: Width of the colorbar if vertical (or height if horizontal) - *\*\*kwargs*: Additional arguments for :meth:`modelicares.base.color` """ # The procedure for coordinating the images with a single colorbar was # copied and modified from # http://matplotlib.sourceforge.net/examples/pylab_examples/multi_image.html, # accessed 11/8/10. # 5/26/11: TODO: This method needs to be updated. Use quiverfig() as a # reference. from res import color # Get the data. n_plots = len(times) if slice_axis == 'x': names = presuffix(self.subregions[slice_index], suffix=suffix) elif slice_axis == 'y': names = presuffix(self.subregions[:][slice_index], suffix=suffix) else: names = presuffix(self.subregions[:][:][slice_index], suffix=suffix) c = self.get_values_at_times(names, times) # Cast the data into a list of matrices (one at each sample time). if slice_axis == 'x': c = [ np.array([[ c[i_y + self.n_y * i_z][i] for i_y in range(self.n_y - 1, -1, -1) ] for i_z in range(self.n_z)]) for i in range(n_plots) ] elif slice_axis == 'y': c = [ np.array([[ c[i_z + self.n_z * i_x][i] for i_z in range(self.n_z - 1, -1, -1) ] for i_x in range(self.n_x)]) for i in range(n_plots) ] else: c = [ np.array([[ c[i_y + self.n_y * i_x][i] for i_z in range(self.n_y - 1, -1, -1) ] for i_x in range(self.n_x)]) for i in range(n_plots) ] [start_time, stop_time] = self.get_times('Time', [0, -1]) # Generate xlabel, xticks, and xticklabels. if not xlabel: if slice_axis == 'x': xlabel = 'z-axis index' elif slice_axis == 'y': xlabel = 'x-axis index' elif slice_axis == 'z': xlabel = 'y-axis index' if not xticklabels: if slice_axis == 'x': xticklabels = [str(i + 1) for i in range(self.n_z)] elif slice_axis == 'y': xticklabels = [str(i + 1) for i in range(self.n_x)] else: xticklabels = [str(i + 1) for i in range(self.n_x)] if not xticks: if slice_axis == 'x': xticks = range(self.n_z) elif slice_axis == 'y': xticks = range(self.n_x) else: xticks = range(self.n_x) # Generate ylabel, yticks, and yticklabels. if not ylabel: if slice_axis == 'x': ylabel = 'y-axis index' elif slice_axis == 'y': ylabel = 'z-axis index' else: ylabel = 'x-axis index' if not yticklabels: if slice_axis == 'x': yticklabels = [str(i + 1) for i in range(self.n_y)] elif slice_axis == 'y': yticklabels = [str(i + 1) for i in range(self.n_z)] else: yticklabels = [str(i + 1) for i in range(self.n_y)] if not yticks: if slice_axis == 'x': yticks = range(self.n_y) elif slice_axis == 'y': yticks = range(self.n_z) else: yticks = range(self.n_y) # Generate clabel. if not clabel: clabels = self.get_description(names) # If all of the descriptions are the same, use the first one. if len(set(clabels)) == 1: clabel = clabels[0] else: clabel = "Value" #units = self.get_unit(names) units = self.get_unit(names) if len(set(units)) == 1: clabel += label_number("", units[0]) else: raise UserWarning( "The variables have inconsistent units. The " "colorbar unit will not match the units of all of the " "variables.") # Set up the subplots. if not subtitles: #unit = unit2tex(self.get_unit('Time')) unit = unit2tex(self.get_unit('Time')) subtitles = [ "t = " + label_quantity(times[i], unit) for i in n_plots ] for i, time in enumerate(times): if time == start_time: subtitle += " (initial)" elif time == stop_time: subtitle += " (final)" ax, cax = setup_subplots(n_plots=n_plots, n_rows=n_rows, title=title, subtitles=subtitles, label=label, xlabel=xlabel, xticklabels=xticklabels, xticks=xticks, ylabel=ylabel, yticklabels=yticklabels, yticks=yticks, ctype=cbar_orientation, clabel=clabel, margin_left=margin_left, margin_right=margin_right, margin_bottom=margin_bottom, margin_top=margin_top, margin_cbar=margin_cbar, wspace=wspace, hspace=hspace, cbar_space=cbar_space, cbar_width=cbar_width) # Create the plots. profiles = [] c_min = np.inf c_max = -np.inf for i, time in enumerate(times): profiles.append(color(ax[i], c[i], **kwargs)) # Find the minimum and maximum of the color-axis data. c_min = min(c_min, np.amin(c[i])) c_max = max(c_max, np.amax(c[i])) # Set the first image as the master, with all the others observing it # for changes in norm. class ImageFollower: """Update image in response to changes in clim on another image. """ def __init__(self, follower): self.follower = follower def __call__(self, leader): self.follower.set_clim(leader.get_clim()) norm = Normalize(c_min=c_min, c_max=c_max) for i, profile in enumerate(profiles): profile.set_norm(norm) if i > 0: profiles[0].callbacksSM.connect('changed', ImageFollower(profile)) # Add the colorbar. (It is also based on the master image.) cbar = fig.colorbar(images[0], cax, orientation=cbar_orientation) # Scale the colorbar. if c_min == c_max: yticks = [c_min] for i in range(len(cbar.ax.get_yticks()) - 1): yticks.append(None) cbar.set_ticks(yticks)
def colorfig(self, suffix, times=[0], n_rows=1, title="", subtitles=[], label="color", slice_axis='z', slice_index=0, xlabel="", xticklabels=[], xticks=[], ylabel="", yticklabels=[], yticks=[], clabel="", cbar_orientation='vertical', margin_left=rcParams['figure.subplot.left'], margin_right=1-rcParams['figure.subplot.right'], margin_bottom=rcParams['figure.subplot.bottom'], margin_top=1-rcParams['figure.subplot.top'], margin_cbar=0.2, wspace=0.1, hspace=0.25, cbar_space=0.1, cbar_width=0.05, **kwargs): """Create a figure with 2D scalar data at given time(s) on a color axis in 2D Cartesian coordinates. **Arguments:** - *suffix*: Name of the variable to be plotted (relative to the names of the subregions) - *times*: List of times at which the data should be sampled If multiple times are given, then subfigures will be generated. - *n_rows*: Number of rows of (sub)plots - *title*: Title for the figure - *subtitles*: List of subtitles (i.e., titles for each subplot) If not provided, "t = xx s" will be used, where xx is the time of each entry. "(initial)" or "(final)" are appended if appropriate. - *label*: Label for the figure This will be used as a base filename if the figure is saved. - *slice_axis*: Axis normal to the screen or page ('x', 'y', or 'z') - *slice_index*: Position along slice_axis - *xlabel*: Label for the x-axes (only shown for the subplots on the bottom row) - *xticklabels*: Labels for the x-axis ticks (only shown for the subplots on the bottom row) - *xticks*: Positions of the x-axis ticks - *ylabel*: Label for the y axis (only shown for the subplots on the left column) - *yticklabels*: Labels for the y-axis ticks (only shown for the subplots on the left column) - *yticks*: Positions of the y-axis ticks - *clabel*: Label for the color- or c-bar axis - *cbar_orientation*: Orientation of the colorbar ("vertical" or "horizontal") - *margin_left*: Left margin - *margin_right*: Right margin (ignored if ``cbar_orientation == 'vertical'``) - *margin_bottom*: Bottom margin (ignored if ``cbar_orientation == 'horizontal'``) - *margin_top*: Top margin - *margin_cbar*: Margin reserved for the colorbar (right margin if ``cbar_orientation == 'vertical'`` and bottom margin if ``cbar_orientation == 'horizontal'``) - *wspace*: The amount of width reserved for blank space between subplots - *hspace*: The amount of height reserved for white space between subplots - *cbar_space*: Space between the subplot rectangles and the colorbar - *cbar_width*: Width of the colorbar if vertical (or height if horizontal) - *\*\*kwargs*: Additional arguments for :meth:`modelicares.base.color` """ # The procedure for coordinating the images with a single colorbar was # copied and modified from # http://matplotlib.sourceforge.net/examples/pylab_examples/multi_image.html, # accessed 11/8/10. # 5/26/11: TODO: This method needs to be updated. Use quiverfig() as a # reference. from res import color # Get the data. n_plots = len(times) if slice_axis == 'x': names = presuffix(self.subregions[slice_index], suffix=suffix) elif slice_axis == 'y': names = presuffix(self.subregions[:][slice_index], suffix=suffix) else: names = presuffix(self.subregions[:][:][slice_index], suffix=suffix) c = self.get_values_at_times(names, times) # Cast the data into a list of matrices (one at each sample time). if slice_axis == 'x': c = [np.array([[c[i_y + self.n_y*i_z][i] for i_y in range(self.n_y-1,-1,-1)] for i_z in range(self.n_z)]) for i in range(n_plots)] elif slice_axis == 'y': c = [np.array([[c[i_z + self.n_z*i_x][i] for i_z in range(self.n_z-1,-1,-1)] for i_x in range(self.n_x)]) for i in range(n_plots)] else: c = [np.array([[c[i_y + self.n_y*i_x][i] for i_z in range(self.n_y-1,-1,-1)] for i_x in range(self.n_x)]) for i in range(n_plots)] [start_time, stop_time] = self.get_times('Time', [0,-1]) # Generate xlabel, xticks, and xticklabels. if not xlabel: if slice_axis == 'x': xlabel = 'z-axis index' elif slice_axis == 'y': xlabel = 'x-axis index' elif slice_axis == 'z': xlabel = 'y-axis index' if not xticklabels: if slice_axis == 'x': xticklabels = [str(i+1) for i in range(self.n_z)] elif slice_axis == 'y': xticklabels = [str(i+1) for i in range(self.n_x)] else: xticklabels = [str(i+1) for i in range(self.n_x)] if not xticks: if slice_axis == 'x': xticks = range(self.n_z) elif slice_axis == 'y': xticks = range(self.n_x) else: xticks = range(self.n_x) # Generate ylabel, yticks, and yticklabels. if not ylabel: if slice_axis == 'x': ylabel = 'y-axis index' elif slice_axis == 'y': ylabel = 'z-axis index' else: ylabel = 'x-axis index' if not yticklabels: if slice_axis == 'x': yticklabels = [str(i+1) for i in range(self.n_y)] elif slice_axis == 'y': yticklabels = [str(i+1) for i in range(self.n_z)] else: yticklabels = [str(i+1) for i in range(self.n_y)] if not yticks: if slice_axis == 'x': yticks = range(self.n_y) elif slice_axis == 'y': yticks = range(self.n_z) else: yticks = range(self.n_y) # Generate clabel. if not clabel: clabels = self.get_description(names) # If all of the descriptions are the same, use the first one. if len(set(clabels)) == 1: clabel = clabels[0] else: clabel = "Value" #units = self.get_unit(names) units = self.get_unit(names) if len(set(units)) == 1: clabel += label_number("", units[0]) else: raise UserWarning("The variables have inconsistent units. The " "colorbar unit will not match the units of all of the " "variables.") # Set up the subplots. if not subtitles: #unit = unit2tex(self.get_unit('Time')) unit = unit2tex(self.get_unit('Time')) subtitles = ["t = " + label_quantity(times[i], unit) for i in n_plots] for i, time in enumerate(times): if time == start_time: subtitle += " (initial)" elif time == stop_time: subtitle += " (final)" ax, cax = setup_subplots(n_plots=n_plots, n_rows=n_rows, title=title, subtitles=subtitles, label=label, xlabel=xlabel, xticklabels=xticklabels, xticks=xticks, ylabel=ylabel, yticklabels=yticklabels, yticks=yticks, ctype=cbar_orientation, clabel=clabel, margin_left=margin_left, margin_right=margin_right, margin_bottom=margin_bottom, margin_top=margin_top, margin_cbar=margin_cbar, wspace=wspace, hspace=hspace, cbar_space=cbar_space, cbar_width=cbar_width) # Create the plots. profiles = [] c_min = np.inf c_max = -np.inf for i, time in enumerate(times): profiles.append(color(ax[i], c[i], **kwargs)) # Find the minimum and maximum of the color-axis data. c_min = min(c_min, np.amin(c[i])) c_max = max(c_max, np.amax(c[i])) # Set the first image as the master, with all the others observing it # for changes in norm. class ImageFollower: """Update image in response to changes in clim on another image. """ def __init__(self, follower): self.follower = follower def __call__(self, leader): self.follower.set_clim(leader.get_clim()) norm = Normalize(c_min=c_min, c_max=c_max) for i, profile in enumerate(profiles): profile.set_norm(norm) if i > 0: profiles[0].callbacksSM.connect('changed', ImageFollower(profile)) # Add the colorbar. (It is also based on the master image.) cbar = fig.colorbar(images[0], cax, orientation=cbar_orientation) # Scale the colorbar. if c_min == c_max: yticks = [c_min] for i in range(len(cbar.ax.get_yticks()) - 1): yticks.append(None) cbar.set_ticks(yticks)
def plot(self, ynames1=[], ylabel1=None, yunit1=None, legends1=[], leg1_kwargs={'loc': 'best'}, ax1=None, ynames2=[], ylabel2=None, yunit2=None, legends2=[], leg2_kwargs={'loc': 'best'}, ax2=None, xname='Time', xlabel=None, xunit=None, title=None, label="xy", incl_prefix=False, suffix=None, use_paren=True, **kwargs): r"""Plot data as points and/or curves in 2D Cartesian coordinates. A new figure is created if necessary. **Arguments:** - *ynames1*: Names of variables for the primary y axis If any names are invalid, then they will be skipped. - *ylabel1*: Label for the primary y axis If *ylabel1* is *None* (default) and all of the variables have the same Modelica_ description string, then the common description will be used. Use '' for no label. - *yunit1*: String indicating the unit for the primary y-axis (see note for *xunit*) If *yunit1* is *None*, the Modelica_ *displayUnit* of the first entry of *ynames1* or the default unit (from *fcres.ini*) based on that variable's dimension will be used (in decreasing priority). .. Note:: Dimension checking is not currently performed, so it is important to ensure that a proper unit is chosen. - *legends1*: List of legend entries for variables assigned to the primary y axis If *legends1* is an empty list ([]), ynames1 will be used. If *legends1* is *None* and all of the variables on the primary axis have the same unit, then no legend will be shown. - *leg1_kwargs*: Dictionary of keyword arguments for the primary legend - *ax1*: Primary y axes If *ax1* is not provided, then axes will be created in a new figure. - *ynames2*, *ylabel2*, *yunit2*, *legends2*, *leg2_kwargs*, and *ax2*: Similar to *ynames1*, *ylabel1*, *yunit1*, *legends1*, *leg1_kwargs*, and *ax1* but for the secondary y axis - *xname*: Name of the x-axis data - *xlabel*: Label for the x axis If *xlabel* is *None* (default), the variable's Modelica_ description string will be applied. Use '' for no label. - *xunit*: String indicating the unit for the x axis (see note for *yunit1*) If *xunit* is *None*, the Modelica_ variable's *displayUnit* or the default unit (from *fcres.ini*) based on the variable's dimension will be used (in decreasing priority). - *title*: Title for the figure If *title* is *None* (default), then the title will be the base filename. Use '' for no title. - *label*: Label for the figure (ignored if ax is provided) This will be used as a base filename if the figure is saved. - *incl_prefix*: If *True*, prefix the legend strings with the base filename of the class. - *suffix*: String that will be added at the end of the legend entries - *use_paren*: Add parentheses around the suffix - *\*\*kwargs*: Additional arguments for :meth:`base.plot` (and thus to :meth:`matplotlib.pyplot.plot`) If both y axes are used (primary and secondary), then the *dashes* argument is ignored. The curves on the primary axis will be solid and the curves on the secondary axis will be dotted. **Returns:** 1. *ax1*: Primary y axes 2. *ax2*: Secondary y axes **Example:** .. testsetup:: >>> from fcres import closeall >>> closeall() .. code-block:: python >>> from fcres import SimRes, saveall >>> sim = SimRes('examples/SaturationPressure') >>> sim.plot(xname="subregion.gas.H2O.T", ... ynames1=["subregion.gas.H2O.p", "p_sat"], ... legends1=["FCSys (from Gibbs equilibrium)", ... "Modelica.Media (correlated function)"], ... ylabel1='Saturation pressure', ... title="Water Saturation Pressure", ... label='examples/SaturationPressure') # doctest: +ELLIPSIS (<matplotlib.axes._subplots.AxesSubplot object at 0x...>, None) >>> saveall() Saved examples/SaturationPressure.pdf Saved examples/SaturationPressure.png .. only:: html .. image:: ../examples/SaturationPressure.png :scale: 70 % :alt: plot of water saturation pressure .. only:: latex .. figure:: ../examples/SaturationPressure.pdf :scale: 70 % Plot of water saturation pressure """ # Note: ynames1 is the first argument (besides self) so that plot() # can be called with simply a variable name. def _ystrings(ynames, ylabel, yunit, legends): """Generate a y-axis label and set of legend entries. """ if ynames: if ylabel is None: # Try to create a suitable axis label. descriptions = self.get_description(ynames) # If the descriptions are the same, label the y axis with # the 1st one. ylabel = descriptions[0] if len(set(descriptions)) <> 1: print( "The y-axis variable descriptions are not all " "the same. The first has been used. Please " "provide the proper name via ylabel1 or ylabel2.") if legends == []: legends = ynames if incl_prefix: legends = [self.fbase + ': ' + leg for leg in legends] if suffix: legends = ([leg + ' (%s)' % suffix for leg in legends] if use_paren else [leg + suffix for leg in legends]) assert len(set(self.get_dimension(ynames))) == 1, \ "The variables on the y-axis do not have the same physical dimension." if yunit is None: # Use the unit of the 1st variable. yunit = self.get_unit(ynames[0]) if ylabel <> "": ylabel = label_number(ylabel, yunit) return ylabel, yunit, legends # Process the inputs. ynames1 = base.flatten_list(ynames1) ynames2 = base.flatten_list(ynames2) assert ynames1 or ynames2, "No signals were provided." if title is None: title = self.fbase # Create primary and secondary axes if necessary. if not ax1: fig = base.figure(label) ax1 = fig.add_subplot(111) if ynames2 and not ax2: ax2 = ax1.twinx() # Generate the x-axis label. if xlabel is None: xlabel = 'Time' if xname == 'Time' else self.get_description(xname) # With Dymola 7.4, the description of the time variable will be # "Time in", which isn't good. if xunit is None: xunit = self.get_unit(xname) if xlabel <> "": xlabel = label_number(xlabel, xunit) # Generate the y-axis labels and sets of legend entries. ylabel1, yunit1, legends1 = _ystrings(ynames1, ylabel1, yunit1, legends1) ylabel2, yunit2, legends2 = _ystrings(ynames2, ylabel2, yunit2, legends2) # Read the data. if xname == 'Time': t_scale = lambda t: t * self._unitvalue('s') / self._unitvalue( xunit) y_1 = self.get_values(ynames1, f=self.to_unit(yunit1)) y_2 = self.get_values(ynames2, f=self.to_unit(yunit2)) else: x = self.get_values(xname, f=self.to_unit(xunit)) times = self.get_times(xname) y_1 = self.get_values_at_times(ynames1, times, f=self.to_unit(yunit1)) y_2 = self.get_values_at_times(ynames2, times, f=self.to_unit(yunit2)) # Plot the data. if ynames1: if ynames2: # Use solid lines for primary axis and dotted lines for # secondary. kwargs['dashes'] = [(None, None)] base.plot(y_1, self.get_times(ynames1, f=t_scale) if xname == 'Time' else x, ax1, label=legends1, **kwargs) kwargs['dashes'] = [(3, 3)] base.plot(y_2, self.get_times(ynames2, f=t_scale) if xname == 'Time' else x, ax2, label=legends2, **kwargs) else: base.plot(y_1, self.get_times(ynames1, f=t_scale) if xname == 'Time' else x, ax1, label=legends1, **kwargs) elif ynames2: base.plot( y_2, self.get_times(ynames2, f=t_scale) if xname == 'Time' else x, ax2, label=legends2, **kwargs) # Decorate the figure. ax1.set_title(title) ax1.set_xlabel(xlabel) if ylabel1: ax1.set_ylabel(ylabel1) if ylabel2: ax2.set_ylabel(ylabel2) if legends1: if legends2: # Put the primary legend in the upper left and secondary in # upper right. leg1_kwargs['loc'] = 2 leg2_kwargs['loc'] = 1 ax1.legend(**leg1_kwargs) ax2.legend(**leg2_kwargs) else: ax1.legend(**leg1_kwargs) elif legends2: ax2.legend(**leg2_kwargs) return ax1, ax2