Source code for microstructpy.seeding.seed

"""Seed

This module contains the class definition for the Seed class.
"""

# --------------------------------------------------------------------------- #
#                                                                             #
# Import Modules                                                              #
#                                                                             #
# --------------------------------------------------------------------------- #

from __future__ import division
from __future__ import print_function

import numpy as np
from matplotlib import collections
from matplotlib import patches
from matplotlib import pyplot as plt

from microstructpy import _misc
from microstructpy import geometry

__all__ = ['Seed']
__author__ = 'Kenneth (Kip) Hart'


# --------------------------------------------------------------------------- #
#                                                                             #
# Seed Class                                                                  #
#                                                                             #
# --------------------------------------------------------------------------- #
[docs]class Seed(object): """Seed particle The Seed class contains the information about a single seed in the mesh. These seeds have a geometry (from :mod:`microstructpy.geometry`), a phase number, a breakdown, and a position. Args: seed_geometry (from :mod:`microstructpy.geometry`) : The geometry of the seed. phase (int) : *(optional)* The phase number of the seed. Defaults to 0. breakdown (list or numpy.ndarray) : *(optional)* The circle/sphere approximation of this grain. The format for this input is:: # x y r breakdown_2D = [( 2, 3, 1), ( 0, 0, 4), (-2, 4, 8)] # x y z r breakdown_3D = [( 3, -1, 2, 1), ( 0, 2, -1, 1)] The default behavior is to call the ``approximate()`` function of the geometry. position (list or numpy.ndarray) : *(optional)* The coordinates of the seed. See :attr:`position` for more details. Defaults to the origin. """ # ----------------------------------------------------------------------- # # Initializer # # ----------------------------------------------------------------------- # def __init__(self, seed_geometry, phase=0, breakdown=None, position=None): self.geometry = seed_geometry self.phase = phase if position is None and self.geometry is not None: self.position = [0 for _ in range(self.geometry.n_dim)] else: self.position = position if breakdown is None: self.breakdown = seed_geometry.approximate() else: self.breakdown = breakdown # ----------------------------------------------------------------------- # # Factory Method # # ----------------------------------------------------------------------- #
[docs] @classmethod def factory(cls, seed_type, phase=0, breakdown=None, position=None, **kwargs): """Factory method for seeds This function returns a seed based on the seed type and keyword arguments associated with that type. The currently supported types are: * circle * ellipse * ellipsoid * rectangle * sphere * square If the seed_type is not on this list, an error is thrown. Args: seed_type (str): type of seed, from list above. phase (int): *(optional)* Material phase number of seed. Defaults to 0. breakdown (list or numpy.ndarray): *(optional)* List of circles or spheres that approximate the geometry. The list should be formatted as follows:: breakdown = [(x1, y1, z1, r1), (x2, y2, z2, r2), ...] The breakdown will be automatically generated if not provided. position (list or numpy.ndarray): *(optional)* The coordinates of the seed. Default is the origin. **kwargs: Keyword arguments that define the size, shape, etc of the seed geometry. Returns: Seed: An instance of the class. """ assert type(seed_type) is str seed_type = seed_type.strip().lower() if seed_type == 'nonetype': n_dim = 0 else: n_dim = geometry.factory(seed_type).n_dim if 'volume' in kwargs: if n_dim == 2: size = 2 * np.sqrt(kwargs['volume'] / np.pi) else: size = 2 * np.cbrt(3 * kwargs['volume'] / (4 * np.pi)) kwargs['size'] = size # Catch NoneType geometries if seed_type == 'nonetype': geom = None else: geom = geometry.factory(seed_type, **kwargs) if breakdown is None: if seed_type in ('circle', 'sphere'): breakdown = np.append(geom.center, geom.r).reshape(1, -1) else: breakdown = geom.approximate() if position is None: position = [0 for _ in range(geom.n_dim)] return cls(geom, phase, breakdown, position)
# ----------------------------------------------------------------------- # # Read from String # # ----------------------------------------------------------------------- #
[docs] @classmethod def from_str(cls, seed_str): """Create seed from a string. This method creates a seed particle from a string representation. This is used when reading in seeds from a file. Args: seed_str (str): String representation of the seed. Returns: Seed: An instance of a Seed derived class. """ # Convert to dictionary str_dict = {} for line in seed_str.strip().split('\n'): try: k_str, v_str = line.split(':') except ValueError: continue else: k = k_str.strip().lower().replace(' ', '_') v = _misc.from_str(v_str) str_dict[k] = v # Extract seed type, phase, and breakdown seed_type = str_dict['geometry'] del str_dict['geometry'] if 'phase' in str_dict: phase = str_dict['phase'] del str_dict['phase'] else: phase = 0 if 'breakdown' in str_dict: breakdown = str_dict['breakdown'] if not isinstance(breakdown[0], tuple): breakdown = (breakdown,) del str_dict['breakdown'] else: breakdown = None if 'position' in str_dict: position = str_dict['position'] del str_dict['position'] else: position = None return cls.factory(seed_type, phase, breakdown, position, **str_dict)
# ----------------------------------------------------------------------- # # String and Representation Functions # # ----------------------------------------------------------------------- # def __str__(self): geom_name = type(self.geometry).__name__.lower() str_str = 'Geometry: ' + geom_name + '\n' str_str += str(self.geometry) + '\n' str_str += 'Phase: ' + str(self.phase) + '\n' bkdwn_str = ', '.join([str(tuple(b)) for b in self.breakdown]) if len(self.breakdown) == 1: bkdwn_str += ',' # breakdowns will be a tuple of length 1 str_str += 'Breakdown: (' + bkdwn_str + ')\n' str_str += 'Position: (' + ', '.join([str(x) for x in self.position]) str_str += ')' return str_str def __repr__(self): repr_str = 'Seed(' repr_str += repr(self.geometry) + ', ' repr_str += 'phase=' + repr(self.phase) + ', ' bkdwn_str = ', '.join([repr(tuple(b)) for b in self.breakdown]) repr_str += 'breakdown=(' + bkdwn_str + '), ' repr_str += 'position=(' + ', '.join([repr(x) for x in self.position]) repr_str += ')' repr_str += ')' return repr_str # ----------------------------------------------------------------------- # # Comparison Functions # # ----------------------------------------------------------------------- # def __lt__(self, seed): if self.geometry is None: return True if seed.geometry is None: return False a_str = 'Seeds are not the same dimension.' assert self.geometry.n_dim == seed.geometry.n_dim, a_str if self.geometry.n_dim == 2: v_self = self.geometry.area v_seed = seed.geometry.area else: v_self = self.geometry.volume v_seed = seed.geometry.volume if v_self == v_seed: return self.phase < seed.phase else: return v_self < v_seed def __eq__(self, seed): if not isinstance(seed, Seed): return False if seed.phase != self.phase: return False if not np.all(np.isclose(seed.breakdown, self.breakdown)): return False if seed.geometry != self.geometry: return False if not np.all(np.isclose(seed.position, self.position)): return False return True # ----------------------------------------------------------------------- # # Position Getter/Setter # # ----------------------------------------------------------------------- # @property def position(self): """Position of the seed This is the location of the seed center. Note: If the breakdown of the seed has been populated, the setter function will update the position of the center and translate the breakdown circles/spheres. """ return self._position @position.setter def position(self, pos): try: old_pos = np.array(self.position) except AttributeError: old_pos = np.zeros(len(pos)) try: displace = np.array(pos) - old_pos for i, bkdwn in enumerate(self.breakdown): coords = bkdwn[:-1] rad = bkdwn[-1] new_coords = [x + d for x, d in zip(coords, displace)] new_bkdwn = new_coords + [rad] self.breakdown[i] = new_bkdwn except AttributeError: pass try: self.geometry.center = pos except AttributeError: pass self._position = pos # ----------------------------------------------------------------------- # # Volume # # ----------------------------------------------------------------------- # @property def volume(self): """float: The area (2D) or volume (3D) of the seed""" if self.geometry is None: return 0 if self.geometry.n_dim == 2: return self.geometry.area else: return self.geometry.volume # ----------------------------------------------------------------------- # # Limits # # ----------------------------------------------------------------------- # @property def limits(self): """list: The (lower, upper) bounds of the seed""" if self.geometry is None: return [] return self.geometry.limits # ----------------------------------------------------------------------- # # Plot # # ----------------------------------------------------------------------- #
[docs] def plot(self, **kwargs): """Plot the seed This function plots the geometry of the seed. The keyword arguments are passed through to matplotlib. See the plot methods in :mod:`microstructpy.geometry` for more details. Args: **kwargs: Plotting keyword arguments. """ if self.geometry is not None: self.geometry.plot(**kwargs)
# ----------------------------------------------------------------------- # # Plot Breakdown # # ----------------------------------------------------------------------- #
[docs] def plot_breakdown(self, **kwargs): """Plot breakdown of seed This function plots the circle/sphere breakdown of the seed. In 2D, this adds a :class:`matplotlib.collections.PatchCollection` to the current axes. Args: **kwargs: Matplotlib keyword arguments. """ n = len(self.breakdown[0]) - 1 if n == 2: pc = [patches.Circle([x, y], r) for x, y, r in self.breakdown] coll = collections.PatchCollection(pc, **kwargs) plt.gca().add_collection(coll) else: [geometry.Sphere(r=r, center=(x, y, z)).plot(**kwargs) for x, y, z, r in self.breakdown]