Example #1
0
def plot_seeds(seeds, phases, domain, plot_files=[], plot_axes=True,
               color_by='material', colormap='viridis', **edge_kwargs):
    """Plot seeds

    This function creates formatted plots of a :class:`.SeedList`.

    Args:
        seeds (SeedList): Seed list to plot.
        phases (list): List of phase dictionaries. See :ref:`phase_dict_guide`
            for more details.
        domain (from :mod:`microstructpy.geometry`): Domain geometry.
        plot_files (list): *(optional)* List of files to save the output plot.
            Defaults to saving the plot to ``polymesh.png``.
        plot_axes (bool): *(optional)* Flag to turn the axes on or off.
            True shows the axes, False removes them. Defaults to True.
        color_by (str): *(optional)* {'material' | 'seed number' |
            'material number'} Option to choose how the polygons/polyhedra
            are colored. Defaults to 'material'.
        colormap (str): *(optional)* Name of the matplotlib colormap to color
            the seeds. Ignored if `color_by='material'`. Defaults to 'viridis',
            the standard matplotlib colormap.
            See `Choosing Colormaps in Matplotlib`_ for more details.
        **edge_kwargs: additional keyword arguments that will be passed to
            :meth:`.SeedList.plot`.

    """
    if not plot_files:
        return

    phase_names = []
    given_names = False
    for i, phase in enumerate(phases):
        if 'name' in phase:
            given_names = True
        name = phase.get('name', 'Material ' + str(i + 1))
        phase_names.append(name)

    seed_colors = _seed_colors(seeds, phases, color_by, colormap)
    n_dim = seeds[0].geometry.n_dim

    # Set up axes
    plt.clf()
    plt.close('all')
    fig = plt.figure()
    ax = fig.gca(projection={2: None, 3: Axes3D.name}[n_dim], label='seeds')

    if not plot_axes:
        if n_dim == 2:
            ax.set_axis_off()
            ax.get_xaxis().set_visible(False)
            ax.get_yaxis().set_visible(False)
        else:
            ax._axis3don = False

    # Plot seeds
    edge_kwargs.setdefault('edgecolors', {2: 'k', 3: 'none'}[n_dim])
    if given_names and color_by == 'material':
        seeds.plot(material=phase_names, facecolors=seed_colors, loc=4,
                   **edge_kwargs)
    else:
        seeds.plot(facecolors=seed_colors, **edge_kwargs)

    # Crop to Domain
    d_lims = domain.limits
    if n_dim == 2:
        plt.axis('square')
        plt.xlim(d_lims[0])
        plt.ylim(d_lims[1])
    elif n_dim == 3:
        plt.gca().set_xlim(d_lims[0])
        plt.gca().set_ylim(d_lims[1])
        plt.gca().set_zlim(d_lims[2])

        _misc.axisEqual3D(plt.gca())

    # Save plot
    for fname in plot_files:
        if n_dim == 3:
            fig.subplots_adjust(**_misc.plt_3d_adj)
            plt.savefig(fname, bbox_inches='tight', pad_inches=0.15)
        else:
            plt.savefig(fname, bbox_inches='tight', pad_inches=0)

    plt.close('all')
Example #2
0
    def plot(self, index_by='seed', material=[], loc=0, **kwargs):
        """Plot the seeds in the seed list.

        This function plots the seeds contained in the seed list.
        In 2D, the seeds are grouped into matplotlib collections to reduce
        the computational load. In 3D, matplotlib does not have patches, so
        each seed is rendered as its own surface.

        Additional keyword arguments can be specified and passed through to
        matplotlib. These arguments should be either single values
        (e.g. ``edgecolors='k'``), or lists of values that have the same
        length as the seed list.

        Args:
            index_by (str): *(optional)* {'material' | 'seed'}
                Flag for indexing into the other arrays passed into the
                function. For example,
                ``plot(index_by='material', color=['blue', 'red'])`` will plot
                the seeds with ``phase`` equal to 0 in blue, and seeds with
                ``phase`` equal to 1 in red. Defaults to 'seed'.
            material (list): *(optional)* Names of material phases. One entry
                per material phase (the ``index_by`` argument is ignored).
                If this argument is set, a legend is added to the plot with
                one entry per material.
            loc (int or str): *(optional)* The location of the legend,
                if 'material' is specified. This argument is passed directly
                through to :func:`matplotlib.pyplot.legend`. Defaults to 0,
                which is 'best' in matplotlib.
            **kwargs: Keyword arguments to pass to matplotlib

        """
        seed_args = [{} for seed in self]
        for seed_num, seed in enumerate(self):
            phase_num = seed.phase
            for key, val in kwargs.items():
                if type(val) in (list, np.array):
                    if index_by == 'seed' and len(val) > seed_num:
                        seed_args[seed_num][key] = val[seed_num]
                    elif index_by == 'material' and len(val) > phase_num:
                        seed_args[seed_num][key] = val[phase_num]
                else:
                    seed_args[seed_num][key] = val

        n = self[0].geometry.n_dim
        if n == 2:
            ax = plt.gca()
        else:
            ax = plt.gcf().gca(projection=Axes3D.name)
        n_obj = _misc.ax_objects(ax)
        if n_obj > 0:
            xlim = ax.get_xlim()
            ylim = ax.get_ylim()
        else:
            xlim = [float('inf'), -float('inf')]
            ylim = [float('inf'), -float('inf')]

        if n == 3:
            if n_obj > 0:
                zlim = ax.get_zlim()
            else:
                zlim = [float('inf'), -float('inf')]

            for seed, args in zip(self, seed_args):
                seed.plot(**args)

        elif n == 2:
            ellipse_data = {'w': [], 'h': [], 'a': [], 'xy': []}
            ec_kwargs = {}

            rect_data = {'xy': [], 'w': [], 'h': [], 'angle': []}
            rect_kwargs = {}

            pc_verts = []
            pc_kwargs = {}
            for seed, args in zip(self, seed_args):
                geom_name = type(seed.geometry).__name__.lower().strip()
                if geom_name == 'ellipse':
                    a, b = seed.geometry.axes
                    cen = np.array(seed.position)
                    t = seed.geometry.angle_deg

                    ellipse_data['w'].append(2 * a)
                    ellipse_data['h'].append(2 * b)
                    ellipse_data['a'].append(t)
                    ellipse_data['xy'].append(cen)

                    for key, val in args.items():
                        val_list = ec_kwargs.get(key, [])
                        val_list.append(val)
                        ec_kwargs[key] = val_list

                elif geom_name == 'circle':
                    diam = seed.geometry.diameter
                    cen = np.array(seed.position)

                    ellipse_data['w'].append(diam)
                    ellipse_data['h'].append(diam)
                    ellipse_data['a'].append(0)
                    ellipse_data['xy'].append(cen)

                    for key, val in args.items():
                        val_list = ec_kwargs.get(key, [])
                        val_list.append(val)
                        ec_kwargs[key] = val_list

                elif geom_name in ['rectangle', 'square']:
                    w, h = seed.geometry.side_lengths
                    corner = seed.geometry.corner
                    t = seed.geometry.angle_deg

                    rect_data['w'].append(w)
                    rect_data['h'].append(h)
                    rect_data['angle'].append(t)
                    rect_data['xy'].append(corner)

                    for key, val in args.items():
                        val_list = rect_kwargs.get(key, [])
                        val_list.append(val)
                        rect_kwargs[key] = val_list

                elif geom_name == 'curl':
                    xy = seed.geometry.plot_xy()
                    pc_verts.append(xy)
                    for key, val in args.items():
                        val_list = pc_kwargs.get(key, [])
                        val_list.append(val)
                        pc_kwargs[key] = val_list

                elif geom_name == 'nonetype':
                    pass

                else:
                    e_str = 'Cannot plot groups of ' + geom_name
                    e_str += ' yet.'
                    raise NotImplementedError(e_str)

            # abbreviate kwargs if all the same
            for key, val in ec_kwargs.items():
                v1 = val[0]
                same = True
                for v in val:
                    same &= v == v1
                if same:
                    ec_kwargs[key] = v1

            for key, val in pc_kwargs.items():
                v1 = val[0]
                same = True
                for v in val:
                    same &= v == v1
                if same:
                    pc_kwargs[key] = v1

            # Plot Circles and Ellipses
            ax = plt.gca()

            w = np.array(ellipse_data['w'])
            h = np.array(ellipse_data['h'])
            a = np.array(ellipse_data['a'])
            xy = np.array(ellipse_data['xy'])
            ec = collections.EllipseCollection(w,
                                               h,
                                               a,
                                               units='x',
                                               offsets=xy,
                                               transOffset=ax.transData,
                                               **ec_kwargs)
            ax.add_collection(ec)

            # Plot Rectangles
            rects = [
                Rectangle(xy=xyi, width=wi, height=hi, angle=ai)
                for xyi, wi, hi, ai in zip(rect_data['xy'], rect_data['w'],
                                           rect_data['h'], rect_data['angle'])
            ]
            rc = collections.PatchCollection(rects, False, **rect_kwargs)
            ax.add_collection(rc)

            # Plot Polygons
            pc = collections.PolyCollection(pc_verts, **pc_kwargs)
            ax.add_collection(pc)

            ax.autoscale_view()

        # Add legend
        if material:
            p_kwargs = [{'label': m} for m in material]
            if index_by == 'seed':
                for seed_kwargs, seed in zip(seed_args, self):
                    p = seed.phase
                    p_kwargs[p].update(seed_kwargs)
            else:
                for key, val in kwargs.items():
                    if type(val) in (list, np.array):
                        for i, elem in enumerate(val):
                            p_kwargs[i][key] = elem
                    else:
                        for i in range(len(p_kwargs)):
                            p_kwargs[i][key] = val

            # Replace plural keywords
            for p_kw in p_kwargs:
                for kw in _misc.mpl_plural_kwargs:
                    if kw in p_kw:
                        p_kw[kw[:-1]] = p_kw[kw]
                        del p_kw[kw]
            handles = [patches.Patch(**p_kw) for p_kw in p_kwargs]
            ax.legend(handles=handles, loc=loc)

        # Adjust Axes
        seed_lims = [np.array(s.geometry.limits).flatten() for s in self]
        mins = np.array(seed_lims)[:, 0::2].min(axis=0)
        maxs = np.array(seed_lims)[:, 1::2].max(axis=0)
        xlim = (min(xlim[0], mins[0]), max(xlim[1], maxs[0]))
        ylim = (min(ylim[0], mins[1]), max(ylim[1], maxs[1]))
        if n == 2:
            plt.axis('square')
            ax.set_xlim(xlim)
            ax.set_ylim(ylim)
        if n == 3:
            zlim = (min(zlim[0], mins[2]), max(zlim[1], maxs[2]))
            ax.set_xlim(xlim)
            ax.set_ylim(ylim)
            ax.set_zlim(zlim)
            _misc.axisEqual3D(ax)
Example #3
0
    def plot_breakdown(self, index_by='seed', material=[], loc=0, **kwargs):
        """Plot the breakdowns of the seeds in seed list.

        This function plots the breakdowns of seeds contained in the seed list.
        In 2D, the breakdowns are grouped into matplotlib collections to reduce
        the computational load. In 3D, matplotlib does not have patches, so
        each breakdown is rendered as its own surface.

        Additional keyword arguments can be specified and passed through to
        matplotlib. These arguments should be either single values
        (e.g. ``edgecolors='k'``), or lists of values that have the same
        length as the seed list.

        Args:
            index_by (str): *(optional)* {'material' | 'seed'}
                Flag for indexing into the other arrays passed into the
                function. For example,
                ``plot(index_by='material', color=['blue', 'red'])`` will plot
                the seeds with ``phase`` equal to 0 in blue, and seeds with
                ``phase`` equal to 1 in red. Defaults to 'seed'.
            material (list): *(optional)* Names of material phases. One entry
                per material phase (the ``index_by`` argument is ignored).
                If this argument is set, a legend is added to the plot with
                one entry per material.
            loc (int or str): *(optional)* The location of the legend,
                if 'material' is specified. This argument is passed directly
                through to :func:`matplotlib.pyplot.legend`. Defaults to 0,
                which is 'best' in matplotlib.
            **kwargs: Keyword arguments to pass to matplotlib

        """
        seed_args = [{} for seed in self]
        for seed_num, seed in enumerate(self):
            phase_num = seed.phase
            for key, val in kwargs.items():
                if type(val) in (list, np.array):
                    if index_by == 'seed' and len(val) > seed_num:
                        seed_args[seed_num][key] = val[seed_num]
                    elif index_by == 'material' and len(val) > phase_num:
                        seed_args[seed_num][key] = val[phase_num]
                else:
                    seed_args[seed_num][key] = val

        n = self[0].geometry.n_dim
        if n == 2:
            ax = plt.gca()
        else:
            ax = plt.gcf().gca(projection=Axes3D.name)
        n_obj = _misc.ax_objects(ax)
        if n_obj > 0:
            xlim = ax.get_xlim()
            ylim = ax.get_ylim()
        else:
            xlim = [float('inf'), -float('inf')]
            ylim = [float('inf'), -float('inf')]

        if n == 3:
            if n_obj > 0:
                zlim = ax.get_zlim()
            else:
                zlim = [float('inf'), -float('inf')]

            for seed, args in zip(self, seed_args):
                seed.plot_breakdown(**args)

        elif n == 2:
            breakdowns = np.zeros((0, 3))
            ec_kwargs = {}
            for seed, args in zip(self, seed_args):
                breakdowns = np.concatenate((breakdowns, seed.breakdown))
                n_c = len(seed.breakdown)
                for key, val in args.items():
                    val_list = ec_kwargs.get(key, [])
                    val_list.extend(n_c * [val])
                    ec_kwargs[key] = val_list
            d = 2 * breakdowns[:, -1]
            xy = breakdowns[:, :-1]
            a = np.full(len(breakdowns), 0)

            # abbreviate kwargs if all the same
            for key, val in ec_kwargs.items():
                v1 = val[0]
                same = True
                for v in val:
                    same &= v == v1
                if same:
                    ec_kwargs[key] = v1

            ax = plt.gca()
            ec = collections.EllipseCollection(d,
                                               d,
                                               a,
                                               units='x',
                                               offsets=xy,
                                               transOffset=ax.transData,
                                               **ec_kwargs)
            ax.add_collection(ec)
            ax.autoscale_view()

        # Add legend
        if material:
            p_kwargs = [{'label': m} for m in material]
            if index_by == 'seed':
                for seed_kwargs, seed in zip(seed_args, self):
                    p = seed.phase
                    p_kwargs[p].update(seed_kwargs)
            else:
                for key, val in kwargs.items():
                    if type(val) in (list, np.array):
                        for i, elem in enumerate(val):
                            p_kwargs[i][key] = elem
                    else:
                        for i in range(len(p_kwargs)):
                            p_kwargs[i][key] = val

            # Replace plural keywords
            for p_kw in p_kwargs:
                for kw in _misc.mpl_plural_kwargs:
                    if kw in p_kw:
                        p_kw[kw[:-1]] = p_kw[kw]
                        del p_kw[kw]
            handles = [patches.Patch(**p_kw) for p_kw in p_kwargs]
            if n == 2:
                ax.legend(handles=handles, loc=loc)
            else:
                plt.gca().legend(handles=handles, loc=loc)

        # Adjust Axes
        seed_lims = [np.array(s.geometry.limits).flatten() for s in self]
        mins = np.array(seed_lims)[:, 0::2].min(axis=0)
        maxs = np.array(seed_lims)[:, 1::2].max(axis=0)
        xlim = (min(xlim[0], mins[0]), max(xlim[1], maxs[0]))
        ylim = (min(ylim[0], mins[1]), max(ylim[1], maxs[1]))
        if n == 2:
            plt.axis('square')
            ax.set_xlim(xlim)
            ax.set_ylim(ylim)
        if n == 3:
            zlim = (min(zlim[0], mins[2]), max(zlim[1], maxs[2]))
            ax.set_xlim(xlim)
            ax.set_ylim(ylim)
            ax.set_zlim(zlim)
            _misc.axisEqual3D(ax)
Example #4
0
    def plot(self, index_by='element', material=[], loc=0, **kwargs):
        """Plot the mesh.

        This method plots the mesh using matplotlib.
        In 2D, this creates a :class:`matplotlib.collections.PolyCollection`
        and adds it to the current axes.
        In 3D, it creates a
        :class:`mpl_toolkits.mplot3d.art3d.Poly3DCollection` and
        adds it to the current axes.
        The keyword arguments are passed though to matplotlib.

        Args:
            index_by (str): *(optional)* {'element' | 'attribute'}
                Flag for indexing into the other arrays passed into the
                function. For example,
                ``plot(index_by='attribute', color=['blue', 'red'])`` will plot
                the elements with ``element_attribute`` equal to 0 in blue, and
                elements with ``element_attribute`` equal to 1 in red.
                Note that in 3D the facets are plotted instead of the elements,
                so kwarg lists must be based on ``facets`` and
                ``facet_attributes``. Defaults to 'element'.
            material (list): *(optional)* Names of material phases. One entry
                per material phase (the ``index_by`` argument is ignored).
                If this argument is set, a legend is added to the plot with
                one entry per material. Note that the ``element_attributes``
                in 2D or the ``facet_attributes`` in 3D must be the material
                numbers for the legend to be formatted properly.
            loc (int or str): *(optional)* The location of the legend,
                if 'material' is specified. This argument is passed directly
                through to :func:`matplotlib.pyplot.legend`. Defaults to 0,
                which is 'best' in matplotlib.
            **kwargs: Keyword arguments that are passed through to matplotlib.

        """
        n_dim = len(self.points[0])
        if n_dim == 2:
            ax = plt.gca()
        else:
            ax = plt.gcf().gca(projection=Axes3D.name)
        n_obj = _misc.ax_objects(ax)
        if n_obj > 0:
            xlim = ax.get_xlim()
            ylim = ax.get_ylim()
        else:
            xlim = [float('inf'), -float('inf')]
            ylim = [float('inf'), -float('inf')]
        if n_dim == 2:
            simps = np.array(self.elements)
            pts = np.array(self.points)
            xy = pts[simps, :]

            plt_kwargs = {}
            for key, value in kwargs.items():
                if type(value) in (list, np.array):
                    plt_value = []
                    for e_num, e_att in enumerate(self.element_attributes):
                        if index_by == 'element':
                            ind = e_num
                        elif index_by == 'attribute':
                            ind = int(e_att)
                        else:
                            e_str = 'Cannot index by {}.'.format(index_by)
                            raise ValueError(e_str)
                        v = value[ind]
                        plt_value.append(v)
                else:
                    plt_value = value
                plt_kwargs[key] = plt_value

            pc = collections.PolyCollection(xy, **plt_kwargs)
            ax.add_collection(pc)
            ax.autoscale_view()
        else:
            if n_obj > 0:
                zlim = ax.get_zlim()
            else:
                zlim = [float('inf'), -float('inf')]

            xy = [np.array([self.points[kp] for kp in f]) for f in self.facets]

            plt_kwargs = {}
            for key, value in kwargs.items():
                if type(value) in (list, np.array):
                    plt_value = []
                    for f_num, f_att in enumerate(self.facet_attributes):
                        if index_by == 'element':
                            ind = f_num
                        elif index_by == 'attribute':
                            ind = int(f_att)
                        else:
                            e_str = 'Cannot index by {}.'.format(index_by)
                            raise ValueError(e_str)
                        if ind < len(value):
                            v = value[ind]
                        else:
                            v = 'none'
                        plt_value.append(v)
                else:
                    plt_value = value
                plt_kwargs[key] = plt_value
            pc = Poly3DCollection(xy, **plt_kwargs)
            ax.add_collection(pc)

        # Add legend
        if material and index_by == 'attribute':
            p_kwargs = [{'label': m} for m in material]
            for key, value in kwargs.items():
                if type(value) not in (list, np.array):
                    for kws in p_kwargs:
                        kws[key] = value

                for i, m in enumerate(material):
                    if type(value) in (list, np.array):
                        p_kwargs[i][key] = value[i]
                    else:
                        p_kwargs[i][key] = value

            # Replace plural keywords
            for p_kw in p_kwargs:
                for kw in _misc.mpl_plural_kwargs:
                    if kw in p_kw:
                        p_kw[kw[:-1]] = p_kw[kw]
                        del p_kw[kw]
            handles = [patches.Patch(**p_kw) for p_kw in p_kwargs]
            ax.legend(handles=handles, loc=loc)

        # Adjust Axes
        mins = np.array(self.points).min(axis=0)
        maxs = np.array(self.points).max(axis=0)
        xlim = (min(xlim[0], mins[0]), max(xlim[1], maxs[0]))
        ylim = (min(ylim[0], mins[1]), max(ylim[1], maxs[1]))
        if n_dim == 2:
            plt.axis('square')
            plt.xlim(xlim)
            plt.ylim(ylim)
        elif n_dim == 3:
            zlim = (min(zlim[0], mins[2]), max(zlim[1], maxs[2]))
            ax.set_xlim(xlim)
            ax.set_ylim(ylim)
            ax.set_zlim(zlim)
            _misc.axisEqual3D(ax)