Пример #1
0
 def test_from_copy(self):
     # test computing of grain geometrical quantities and copy of an
     # existing Microstructure dataset
     # create new microstructure copy of an already existing file
     filename = os.path.join(PYMICRO_EXAMPLES_DATA_DIR,
                             't5_dct_slice_data.h5')
     new_file = os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 'tmp_slice_dct')
     m = Microstructure.copy_sample(filename, new_file, autodelete=True,
                                    get_object=True)
     h5_file = m.h5_path
     xdmf_file = m.xdmf_path
     self.assertTrue(os.path.exists(h5_file))
     self.assertTrue(os.path.exists(xdmf_file))
     m.recompute_grain_bounding_boxes()
     m.recompute_grain_centers()
     # m.recompute_grain_volumes()
     m_ref = Microstructure(filename=filename)
     for i in range(m_ref.grains.nrows):
         print(' n°1 :',m.grains[i])
         print(' n°2 :',m_ref.grains[i])
         self.assertEqual(m.grains[i], m_ref.grains[i])
     volume = np.sum(m.get_mask(as_numpy=True))
     self.assertEqual(volume, 194025)
     del m
     self.assertTrue(not os.path.exists(h5_file))
     self.assertTrue(not os.path.exists(xdmf_file))
     del m_ref
Пример #2
0
 def setUp(self):
     print('testing the Microstructure class')
     self.test_eulers = [(45., 45, 0.), (10., 20, 30.), (191.9, 69.9, 138.9)]
     self.micro = Microstructure()
     self.micro.name = 'test'
     for i in range(len(self.test_eulers)):
         euler = self.test_eulers[i]
         self.micro.grains.append(Grain(i + 1, Orientation.from_euler(euler)))
Пример #3
0
 def test_find_neighbors(self):
     # read a test microstructure already created
     m = Microstructure(filename=os.path.join(PYMICRO_EXAMPLES_DATA_DIR,
                                              't5_dct_slice_data.h5'))
     neighbors = m.find_neighbors(grain_id=5, distance=3)
     self.assertEqual(len(neighbors), 9)
     for gid in [0, 1, 3, 14, 17, 18, 25, 51, 115]:
         self.assertTrue(gid in neighbors)
     del m
Пример #4
0
 def test_from_file(self):
     # read a test microstructure already created
     m = Microstructure(filename=os.path.join(PYMICRO_EXAMPLES_DATA_DIR,
                                              't5_dct_slice_data.h5'))
     self.assertEqual(m.grains.nrows, 21)
     self.assertEqual(m.get_voxel_size(), 0.0014)
     self.assertEqual(type(m.get_grain_map(as_numpy=True)), np.ndarray)
     self.assertEqual(type(m.get_mask(as_numpy=True)), np.ndarray)
     self.assertTrue(True)
     del m
Пример #5
0
 def test_base(self):
     micro = Microstructure(name='test', autodelete=True)
     micro.add_grains(self.test_eulers)
     self.assertTrue(micro.get_sample_name() == 'test')
     self.assertTrue(os.path.exists(micro.h5_file))
     self.assertTrue(os.path.exists(micro.xdmf_file))
     h5_file = micro.h5_file
     xdmf_file = micro.xdmf_file
     del micro
     self.assertTrue(not os.path.exists(h5_file))
     self.assertTrue(not os.path.exists(xdmf_file))
Пример #6
0
    def __init__(self,
                 microstructure=None,
                 lattice=None,
                 axis='Z',
                 hkl='111',
                 proj='stereo',
                 verbose=False):
        """
        Create an empty PoleFigure object associated with an empty Microstructure.

        :param microstructure: the :py:class:`~pymicro.crystal.microstructure.Microstructure` containing the collection of orientations to plot (None by default).
        :param lattice: the crystal :py:class:`~pymicro.crystal.lattice.Lattice`.
        :param str axis: the pole figure axis ('Z' by default), vertical axis in the direct pole figure and direction plotted on the inverse pole figure.

        .. warning::

           Any crystal structure is now supported (you have to set the proper
           crystal lattice) but it has only really be tested for cubic.

        :param str hkl: slip plane family ('111' by default)
        :param str proj: projection type, can be either 'stereo' (default) or 'flat'
        :param bool verbose: verbose mode (False by default)
        """
        self.proj = proj
        self.axis = axis
        self.map_field = None
        if microstructure:
            self.microstructure = microstructure
        else:
            self.microstructure = Microstructure()
        if lattice:
            self.lattice = lattice
        else:
            self.lattice = Lattice.cubic(1.0)
        self.family = None
        self.poles = []
        self.set_hkl_poles(hkl)
        self.verbose = verbose
        self.resize_markers = False
        self.mksize = 50
        self.pflegend = False
        self.x = np.array([1., 0., 0.])
        self.y = np.array([0., 1., 0.])
        self.z = np.array([0., 0., 1.])

        # list all crystal directions
        self.c001s = np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]],
                              dtype=np.float)
        self.c011s = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0], [0, -1, 1],
                               [-1, 0, 1], [-1, 1, 0]],
                              dtype=np.float) / np.sqrt(2)
        self.c111s = np.array([[1, 1, 1], [-1, -1, 1], [1, -1, 1], [-1, 1, 1]],
                              dtype=np.float) / np.sqrt(3)
Пример #7
0
 def test_to_h5(self):
     self.micro.to_h5()
     # read the file we have just written
     m = Microstructure.from_h5('%s.h5' % self.micro.name,
                                grain_centroid=None)
     self.assertEqual(len(m.grains), len(self.test_eulers))
     os.remove('%s.h5' % self.micro.name)
Пример #8
0
 def test_from_dct(self):
     # read a microstructure from a DCT index.mat file
     m = Microstructure.from_dct(data_dir=PYMICRO_EXAMPLES_DATA_DIR,
                                 grain_file='t5_dct_cen_index.mat',
                                 use_dct_path=False)
     m.autodelete = True
     self.assertEqual(m.grains.nrows, 146)
Пример #9
0
 def test_from_dct(self):
     # read a microstructure from a DCT index.mat file
     m = Microstructure.from_dct(data_dir=PYMICRO_EXAMPLES_DATA_DIR,
                                 grain_file='t5_dct_cen_index.mat',
                                 use_dct_path=False)
     self.assertEqual(len(m.grains), 146)
     self.assertEqual(m.voxel_size, 0.0014)
Пример #10
0
 def test_merge_microstructures(self):
     m1 = Microstructure(os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 'm1_data.h5'))
     m2 = Microstructure(os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 'm2_data.h5'))
     # merge the two microstructures
     m1m2 = Microstructure.merge_microstructures([m1, m2], overlap=16)
     m1m2.autodelete = True
     h5_file = m1m2.h5_file
     xdmf_file = m1m2.xdmf_file
     dims = (64, 64, 64)
     for i in range(3):
         self.assertEqual(m1m2.get_grain_map().shape[i], dims[i])
     self.assertEqual(m1m2.get_number_of_grains(), 27)
     del m1m2
     self.assertTrue(not os.path.exists(h5_file))
     self.assertTrue(not os.path.exists(xdmf_file))
     del m1, m2
Пример #11
0
 def test_find_neighbors(self):
     m = Microstructure.from_h5(
         os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 't5_dct_slice.h5'))
     neighbors = m.find_neighbors(grain_id=5, distance=3)
     self.assertEqual(len(neighbors), 9)
     for gid in [0, 1, 3, 14, 17, 18, 25, 51, 115]:
         self.assertTrue(gid in neighbors)
Пример #12
0
 def test_from_h5(self):
     # read a test microstructure
     m = Microstructure.from_h5(
         os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 't5_dct_slice.h5'))
     self.assertEqual(len(m.grains), 21)
     self.assertEqual(m.voxel_size, 0.0014)
     self.assertEqual(hasattr(m, 'grain_map'), True)
     self.assertEqual(hasattr(m, 'mask'), True)
Пример #13
0
class MicrostructureTests(unittest.TestCase):

    def setUp(self):
        print('testing the Microstructure class')
        self.test_eulers = [(45., 45, 0.), (10., 20, 30.), (191.9, 69.9, 138.9)]
        self.micro = Microstructure()
        self.micro.name = 'test'
        for i in range(len(self.test_eulers)):
            euler = self.test_eulers[i]
            self.micro.grains.append(Grain(i + 1, Orientation.from_euler(euler)))

    def test_to_h5(self):
        self.micro.to_h5()
        # read the file we have just written
        m = Microstructure.from_h5('%s.h5' % self.micro.name, grain_centroid=None)
        self.assertEqual(len(m.grains), len(self.test_eulers))
        os.remove('%s.h5' % self.micro.name)
Пример #14
0
 def set_microstructure(self, microstructure):
     if microstructure is None:
         # Create random name to avoid opening another microstructure with
         # same file name when initializing another sample
         randint = str(np.random.randint(1, 10000 + 1))
         microstructure = Microstructure(name='tmp_micro_' + randint,
                                         autodelete=True,
                                         verbose=True)
     self.microstructure = microstructure
Пример #15
0
class MicrostructureTests(unittest.TestCase):
    def setUp(self):
        print('testing the Microstructure class')
        self.test_eulers = [(45., 45, 0.), (10., 20, 30.),
                            (191.9, 69.9, 138.9)]
        self.micro = Microstructure()
        self.micro.name = 'test'
        for i in range(len(self.test_eulers)):
            euler = self.test_eulers[i]
            self.micro.grains.append(
                Grain(i + 1, Orientation.from_euler(euler)))

    def test_to_h5(self):
        self.micro.to_h5()
        # read the file we have just written
        m = Microstructure.from_h5('%s.h5' % self.micro.name)
        self.assertEqual(len(m.grains), len(self.test_eulers))
        os.remove('%s.h5' % self.micro.name)

    def test_from_dct(self):
        # read a microstructure from a DCT index.mat file
        m = Microstructure.from_dct(data_dir=PYMICRO_EXAMPLES_DATA_DIR,
                                    grain_file='t5_dct_cen_index.mat',
                                    use_dct_path=False)
        self.assertEqual(len(m.grains), 146)
        self.assertEqual(m.voxel_size, 0.0014)

    def test_from_h5(self):
        # read a test microstructure
        m = Microstructure.from_h5(
            os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 't5_dct_slice.h5'))
        self.assertEqual(len(m.grains), 21)
        self.assertEqual(m.voxel_size, 0.0014)
        self.assertEqual(hasattr(m, 'grain_map'), True)
        self.assertEqual(hasattr(m, 'mask'), True)

    def test_find_neighbors(self):
        m = Microstructure.from_h5(
            os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 't5_dct_slice.h5'))
        neighbors = m.find_neighbors(grain_id=5, distance=3)
        self.assertEqual(len(neighbors), 9)
        for gid in [0, 1, 3, 14, 17, 18, 25, 51, 115]:
            self.assertTrue(gid in neighbors)
Пример #16
0
 def test_crop(self):
     # read a test microstructure
     m = Microstructure(os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 'n27-id1_data.h5'))
     # crop the microstructure
     m1 = m.crop(x_start=20, x_end=40, y_start=10, y_end=40, z_start=15, z_end=55, autodelete=True)
     h5_file = m1.h5_file
     xdmf_file = m1.xdmf_file
     dims = (20, 30, 40)
     for i in range(3):
         self.assertEqual(m1.get_grain_map().shape[i], dims[i])
     self.assertEqual(m1.get_number_of_grains(), 16)
     gids_crop = [1, 2, 3, 4, 6, 7, 8, 13, 14, 15, 17, 20, 21, 22, 26, 27]
     for gid in m1.get_grain_ids():
         self.assertTrue(gid in gids_crop)
     self.assertEqual(np.sum(m1.get_grain_map(as_numpy=True) == 14), 396)
     del m1
     # verify that the mirostructure files have been deleted
     self.assertTrue(not os.path.exists(h5_file))
     self.assertTrue(not os.path.exists(xdmf_file))
     del m
Пример #17
0
 def test_renumber_grains(self):
     # read and copy a microstructure
     m1_path = os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 'm1_data.h5')
     copy_path = os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 'm1_copy_data.h5')
     m1 = Microstructure.copy_sample(m1_path, copy_path, autodelete=True,
                                     get_object=True)
     self.assertTrue(8 not in m1.get_grain_ids())
     m1.renumber_grains()
     self.assertTrue(8 in m1.get_grain_ids())
     m1.renumber_grains(sort_by_size=True)
     self.assertEqual(m1.get_grain_ids()[0], 18)
     del m1
Пример #18
0
    def get_color_from_field(self, grain):
        """Get the color of the given grain according to the chosen field.

        This function will return the color associated with the given grain.
        Depending on how the pole figure has been configured (see the
        `set_map_field` function), it will be obtained from:

         * the grain id, according to the `Microstructure.rand_cmap` function
         * ipf the colour will reflect the orientation according to the IPF
            coloring scheme
         * the field value mapped on a pyplot color map if the lut field of
            the PoleFigure instance is a string.
         * a color directly read from the lut field; in this case the field
            value must reflect the category of the given grain.

        :param grain: the `Grain` instance.
        :return: the color as a 3 element numpy array representing the rgb values.
        
        """
        if self.map_field:
            if self.map_field == 'grain_id':
                col = Microstructure.rand_cmap().colors[grain['idnumber']]
            elif self.map_field == 'ipf':
                if self.axis == 'X':
                    axis = np.array([1., 0., 0.])
                elif self.axis == 'Y':
                    axis = np.array([0., 1., 0.])
                else:
                    axis = np.array([0., 0., 1.])
                col = Orientation.from_rodrigues(
                    grain['orientation']).get_ipf_colour(axis=axis)
            else:
                # retrieve the position of the grain in the list
                rank = self.microstructure.get_grain_ids().tolist().index(
                    grain['idnumber'])
                if type(self.lut) is str:
                    # get the color map from pyplot
                    color_map = cm.get_cmap(self.lut, 256)
                    # use the field value for this grain and the field range bounds
                    color = int(255 * max(
                        min((self.field[rank] - self.field_min_level) /
                            float(self.field_max_level - self.field_min_level),
                            1.0), 0.0))
                    col = color_map(np.arange(256))[color]
                else:
                    col = self.lut[
                        self.field[rank]]  # directly access the color
            return col
        else:
            return np.array([0., 0., 0.])
Пример #19
0
    def plot(orientations, **kwargs):
        '''Plot a pole figure (both direct and inverse) for a list of orientations.

        :param orientations: the list of crystalline :py:class:`~pymicro.crystal.microstructure.Orientation` to plot.
        '''
        micro = Microstructure()
        if isinstance(orientations, list):
            for i in range(len(orientations)):
                micro.grains.append(Grain(i + 1, orientations[i]))
        elif isinstance(orientations, Orientation):
            micro.grains.append(Grain(1, orientations))
        else:
            print('Unrecognized argument: %s' % orientations.__repr__)
        pf = PoleFigure(microstructure=micro, **kwargs)
        pf.plot_pole_figures(display=True)
Пример #20
0
 def test_from_neper(self):
     # read a microstructure generated by neper
     m = Microstructure.from_neper(os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 'n100-id1.tesr'))
     m.autodelete = True
     self.assertEqual(m.grains.nrows, 100)
     # verify all orienations
     euler_neper = np.genfromtxt(os.path.join(PYMICRO_EXAMPLES_DATA_DIR, 'n100-id1.ori-plain'))
     for i in range(100):
         euler_pymicro = m.get_grain(i + 1).orientation.euler
         for j in range(3):
             self.assertAlmostEqual(euler_pymicro[j],
                                    euler_neper[i][j] % 360, 4)
     self.assertEqual(m.__contains__('grain_map'), True)
     self.assertAlmostEqual(m.get_voxel_size(), 0.018, 2)
     dims = (54, 65, 75)
     for i in range(3):
         self.assertEqual(m.get_grain_map(as_numpy=True).shape[i], dims[i])
     del m
Пример #21
0
    def plot(orientations, **kwargs):
        """Plot a pole figure (both direct and inverse) for a list of crystal
        orientations.

        :param orientations: the list of crystalline
            :py:class:`~pymicro.crystal.microstructure.Orientation` to
            plot.
        
        """
        micro = Microstructure(autodelete=True)
        if isinstance(orientations, list):
            for i in range(len(orientations)):
                micro.add_grains([o.euler for o in orientations])
        elif isinstance(orientations, Orientation):
            micro.add_grains([orientations.euler])
        else:
            print('Unrecognized argument: %s' % orientations.__repr__)
        pf = PoleFigure(microstructure=micro, **kwargs)
        pf.plot_pole_figures(display=True)
#!/usr/bin/env python
import os, numpy as np
from pymicro.crystal.microstructure import Microstructure, Grain, Orientation
from pymicro.crystal.texture import PoleFigure
from matplotlib import pyplot as plt, colors, cm

if __name__ == '__main__':
    '''
    Pole figure of a gold sample containing 6 grains with a strong <111> fiber texture.
    A Microstructure object is first created with the 6 grains of interest.
    The grain ids corerespond to the actual grain number (in an EBSD scan for instance).
    A PoleFigure object is then created using this microstructure and the pole figures
    (both direct and inverse) are drawn by calling the plot_pole_figures() method.
    '''
    micro = Microstructure(name='Au_6grains', overwrite_hdf5=True)
    micro.autodelete = True
    gid_list = [1158, 1349, 1585, 1805, 1833, 2268]
    euler_list = [(344.776, 52.2589, 53.9933), 
                  (344.899, 125.961, 217.330),
                  (228.039, 57.4791, 143.171),
                  (186.741, 60.333, 43.311),
                  (151.709, 55.0406, 44.1051),
                  (237.262, 125.149, 225.615),
                  ]
    micro.add_grains(euler_list, grain_ids=gid_list)

    # create pole figure (both direct and inverse)
    pf = PoleFigure(hkl='111', axis='Z', proj='stereo', microstructure=micro)
    pf.mksize = 100
    pf.set_map_field('grain_id')
    pf.pflegend = True  # this works well for a few grains
Пример #23
0
import os, numpy as np
from pymicro.crystal.texture import PoleFigure
from pymicro.crystal.microstructure import Microstructure

if __name__ == '__main__':
    """
    Example of a pole figure of a random microstructure with 200 grains. The 
    poles are colored according using IPF coloring and resized proportionally 
    to each grain volume.
    """
    # create a microstructure with a random texture and 200 grains
    micro = Microstructure.random_texture(n=200)
    micro.autodelete = True

    # set random values for the grain volumes
    np.random.seed(22)
    for g in micro.grains:
        g['volume'] = 100 * np.random.random()**3
        g.update()
    micro.grains.flush()

    # first pole figure
    pf = PoleFigure(microstructure=micro)
    pf.resize_markers = True
    pf.set_hkl_poles('001')
    pf.axis = 'Z'
    pf.set_map_field('ipf')
    pf.plot_pole_figures(plot_sst=True, display=False, save_as='png')
    del pf
    del micro
Пример #24
0
#!/usr/bin/env python
import os, numpy as np
from pymicro.crystal.microstructure import Microstructure, Grain, Orientation
from pymicro.crystal.texture import PoleFigure
from matplotlib import pyplot as plt, colors, cm

if __name__ == '__main__':
    '''
    111 Pole figure of a copper sample containing 10000 grains with a fibre
    texture.
    '''
    eulers = Orientation.read_euler_txt('../data/Cu_111.dat')
    micro = Microstructure(name='Cu_111')
    for index in eulers:
        micro.grains.append(Grain(index, eulers[index]))

    # create pole figure (both direct and inverse)
    pf = PoleFigure(hkl='111',
                    proj='stereo',
                    microstructure=micro,
                    verbose=False)
    pf.color_by_grain_id = False
    pf.mksize = 5
    pf.pflegend = False
    pf.plot_pole_figures(plot_sst=True, display=False, save_as='png')

    image_name = os.path.splitext(__file__)[0] + '.png'
    print('writting %s' % image_name)

    from matplotlib import image
Пример #25
0
def merge_dct_scans(scan_list,
                    samtz_list,
                    use_mask=False,
                    overlap=-1,
                    root_dir='.',
                    write_to_h5=True):
    """Merge two DCT scans.

    This function build a `Microstructure` instance for each DCT scan and calls merge_microstructures.
    The overlap can be deduced from the samtz values or specified directly.

    :param list scan_list: a list with the two DCT scan names.
    :param list samtz_list: a list with the two samtz value (the order should match the scan names).
    :param bool use_mask: a flag to also merge the absorption masks.
    :param int overlap: the value to use for the overlap if not computed automatically.
    :param str root_dir: the root data folder.
    :param bool write_to_h5: flag to write the result of the merging operation to an HDF5 file.
    :return: A new `Microstructure` instance of the 2 merged scans.
    """
    from pymicro.crystal.microstructure import Microstructure
    import numpy as np
    import os
    import h5py

    scan_shapes = []  # warning, shapes will be in (z, y, x) form
    micros = []

    for scan in scan_list:
        scan_path = os.path.join(root_dir, scan, '5_reconstruction',
                                 'phase_01_vol.mat')
        with h5py.File(scan_path) as f:
            scan_shapes.append(f['vol'].shape)
            print(f['vol'].shape)

    # figure out the maximum cross section
    max_shape = np.array(scan_shapes).max(axis=0)[[2, 1, 0]]

    for scan in scan_list:
        # read microstructure for this scan
        dct_analysis_dir = os.path.join(root_dir, scan)
        print('processing scan %s' % scan)
        micro = Microstructure.from_dct(data_dir=dct_analysis_dir)
        print('voxel_size is {}'.format(micro.voxel_size))

        # pad both grain map and mask
        print('max shape is {}'.format(max_shape))
        print('vol shape is {}'.format(micro.grain_map.shape))
        offset = max_shape - micro.grain_map.shape
        offset[2] = 0  # do not pad along Z
        padding = [(o // 2, max_shape[0] - micro.grain_map.shape[0] - o // 2)
                   for o in offset]
        print('padding is {}'.format(padding))
        micro.grain_map = np.pad(micro.grain_map, padding, mode='constant')
        print('has mask ? {}'.format(hasattr(micro, 'mask')))
        if use_mask:
            micro.mask = np.pad(micro.mask, padding, mode='constant')
        elif hasattr(micro, 'mask'):
            print('deleting mask attribute since we do not want to use it')
            delattr(micro, 'mask')
        micros.append(micro)

    # find out the overlap region (based on the difference in samtz)
    overlap_from_samtz = int(
        (samtz_list[1] + scan_shapes[1][0] // 2 * micros[1].voxel_size) /
        micros[1].voxel_size -
        (samtz_list[0] - scan_shapes[0][0] // 2 * micros[0].voxel_size) /
        micros[0].voxel_size)
    print('vertical overlap deduced from samtz positions is %d voxels' %
          overlap_from_samtz)
    if overlap < 0:
        overlap = overlap_from_samtz
    print('using an actual overlap of %d voxels' % overlap)

    # we have prepared the 2 microstructures, now merge them
    merged_micro = Microstructure.merge_microstructures(micros,
                                                        overlap,
                                                        plot=True)

    if write_to_h5:
        # write the result
        merged_micro.to_h5()

    return merged_micro
Пример #26
0
#!/usr/bin/env python
import os, numpy as np
from pymicro.crystal.microstructure import Microstructure
from pymicro.crystal.texture import PoleFigure

if __name__ == '__main__':
    """
    111 Pole figure of a copper sample containing 10000 grains with a fibre
    texture.
    """
    euler_list = np.genfromtxt('../data/Cu_111.dat', usecols=(0, 1, 2), max_rows=1000)
    micro = Microstructure(name='Cu_111', autodelete=True)
    micro.add_grains(euler_list)

    # create pole figure (both direct and inverse)
    pf = PoleFigure(hkl='111', proj='stereo', microstructure=micro)
    pf.color_by_grain_id = False
    pf.mksize = 5
    pf.pflegend = False
    pf.plot_pole_figures(plot_sst=True, display=False, save_as='png')
    del pf
    del micro

    image_name = os.path.splitext(__file__)[0] + '.png'
    print('writing %s' % image_name)

    from matplotlib import image

    image.thumbnail(image_name, 'thumb_' + image_name, 0.2)
Пример #27
0
class PoleFigure:
    """A class to handle pole figures.

    A pole figure is a popular tool to plot multiple crystal orientations,
    either in the sample coordinate system (direct pole figure) or
    alternatively plotting a particular direction in the crystal
    coordinate system (inverse pole figure).
    """
    def __init__(self,
                 microstructure=None,
                 lattice=None,
                 axis='Z',
                 hkl='111',
                 proj='stereo',
                 verbose=False):
        """
        Create an empty PoleFigure object associated with an empty Microstructure.

        :param microstructure: the :py:class:`~pymicro.crystal.microstructure.Microstructure` containing the collection of orientations to plot (None by default).
        :param lattice: the crystal :py:class:`~pymicro.crystal.lattice.Lattice`.
        :param str axis: the pole figure axis ('Z' by default), vertical axis in the direct pole figure and direction plotted on the inverse pole figure.

        .. warning::

           Any crystal structure is now supported (you have to set the proper
           crystal lattice) but it has only really be tested for cubic.

        :param str hkl: slip plane family ('111' by default)
        :param str proj: projection type, can be either 'stereo' (default) or 'flat'
        :param bool verbose: verbose mode (False by default)
        """
        self.proj = proj
        self.axis = axis
        self.map_field = None
        if microstructure:
            self.microstructure = microstructure
        else:
            self.microstructure = Microstructure()
        if lattice:
            self.lattice = lattice
        else:
            self.lattice = Lattice.cubic(1.0)
        self.family = None
        self.poles = []
        self.set_hkl_poles(hkl)
        self.verbose = verbose
        self.resize_markers = False
        self.mksize = 50
        self.pflegend = False
        self.x = np.array([1., 0., 0.])
        self.y = np.array([0., 1., 0.])
        self.z = np.array([0., 0., 1.])

        # list all crystal directions
        self.c001s = np.array([[0, 0, 1], [0, 1, 0], [1, 0, 0]],
                              dtype=np.float)
        self.c011s = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0], [0, -1, 1],
                               [-1, 0, 1], [-1, 1, 0]],
                              dtype=np.float) / np.sqrt(2)
        self.c111s = np.array([[1, 1, 1], [-1, -1, 1], [1, -1, 1], [-1, 1, 1]],
                              dtype=np.float) / np.sqrt(3)

    def get_orientations(self):
        """Get the list of orientations in the PoleFigure.
        
        :return: a list of `Orientation` instances.
        """
        return [grain.orientation for grain in self.microstructure.grains]

    def set_hkl_poles(self, hkl='111'):
        """Set the pole (aka hkl planes) list to to use in the `PoleFigure`.

        The list of poles can be given by the family type or directly by a list of `HklPlanes` objects.

        :params str/list hkl: slip plane family ('111' by default)
        """
        if type(hkl) is str:
            self.family = hkl  # keep a record of this
            hkl_planes = self.lattice.get_hkl_family(self.family)
        elif type(hkl) is list:
            self.family = None
            hkl_planes = hkl
        self.poles = hkl_planes  #[p.normal() for p in hkl_planes]

    def set_map_field(self,
                      field_name,
                      field=None,
                      field_min_level=None,
                      field_max_level=None,
                      lut='hot'):
        '''Set the PoleFigure to color poles with the given field.

        This method activates a mode where each symbol in the pole figure
        is color coded with respect to a field, which can be either the
        grain id, or a given field given in form of a list. If the grain
        volume or strain. For the grain id, the color is set according the
        each grain id in the :py:class:`~pymicro.crystal.microstructure.Microstructure`
        and the :py:meth:`~pymicro.crystal.microstructure.rand_cmap` function.
        For a given field, the color is set from the lookup table and
        according to the value in the given list. The list must contain a
        record for each grain. Minimum and maximum value to map the field
        values and the colors can be specify, if not they are directly taken
        as the min() and max() of the field.

        :param str field_name: The field name, could be 'grain_id', or any other name describing the field.
        :param list field: A list containing a record for each grain.
        :param float field_min_level: The minimum value to use for this field.
        :param float field_max_level: The maximum value to use for this field.
        :param str lut: A string describing the colormap to use (among matplotlib ones available).
        :raise ValueError: If the given field does not contain enough values.
        '''
        self.map_field = field_name
        self.lut = lut
        if field_name == 'grain_id':
            self.field = [g.id for g in self.microstructure.grains]
        elif field_name == 'ipf':
            self.field = [g.id for g in self.microstructure.grains]
        else:
            if len(field) < len(self.microstructure.grains):
                raise ValueError(
                    'The field must contain a record for each grain in the microstructure'
                )
            self.field = field
            if not field_min_level:
                self.field_min_level = field.min()
            else:
                self.field_min_level = field_min_level
            if not field_max_level:
                self.field_max_level = field.max()
            else:
                self.field_max_level = field_max_level

    def plot_pole_figures(self, plot_sst=True, display=True, save_as='pdf'):
        '''Plot and save a picture with both direct and inverse pole figures.

        :param bool plot_sst: controls wether to plot the full inverse pole \
        figure or only the standard stereographic triangle (True by default).
        :param bool display: display the plot if True, else save a picture \
        of the pole figures (True by default)
        :param str save_as: File format used to save the image such as pdf \
        or png ('pdf' by default)

        ::

          micro = Microstructure(name = 'AlLi_sam8')
          micro.grains.append(Grain(11, Orientation.from_euler(np.array([262.364, 16.836, 104.691]))))
          Al_fcc = Lattice.face_centered_cubic(0.405) # not really necessary since default lattice is cubic
          pf = PoleFigure(microstructure=micro, proj='stereo', lattice=Al_fcc, hkl='111')
          pf.mksize = 12
          pf.set_map_field('grain_id')
          pf.pflegend = True # this works well for a few grains
          pf.plot_pole_figures()

        .. figure:: _static/AlLi_sam8_pole_figure.png
            :width: 750 px
            :height: 375 px
            :alt: AlLi_sam8_pole_figure
            :align: center

            A 111 pole figure plotted for a single crystal orientation.
        '''
        fig = plt.figure(figsize=(10, 5))
        # direct PF
        ax1 = fig.add_subplot(121, aspect='equal')
        self.plot_pf(ax=ax1, mk='o', ann=False)
        # inverse PF
        ax2 = fig.add_subplot(122, aspect='equal')
        if plot_sst:
            self.plot_sst(ax=ax2)
        else:
            self.plot_ipf(ax=ax2)
        if display:
            plt.show()
        else:
            plt.savefig('%s_pole_figure.%s' %
                        (self.microstructure.name, save_as),
                        format=save_as)

    def plot_crystal_dir(self, c_dir, **kwargs):
        '''Function to plot a crystal direction on a pole figure.

        :param c_dir: A vector describing the crystal direction.
        :param dict kwargs: a dictionnary of keyword/values to control the plot, it should at least contain a reference
        to a pyplot axes to draw the pole using keywors 'ax'.
        :raise ValueError: if the projection type is not supported
        '''
        if c_dir[2] < 0:
            c_dir *= -1  # make unit vector have z>0
        if self.proj == 'flat':
            cp = c_dir
        elif self.proj == 'stereo':
            c = c_dir + self.z
            c /= c[2]  # SP'/SP = r/z with r=1
            cp = c
            # cp = np.cross(c, self.z)
        else:
            raise ValueError('Error, unsupported projection type', self.proj)
        ax = kwargs.get('ax')
        mk = kwargs.get('mk', 'o')
        edge_col = kwargs.get('markeredgecolor', 'k')
        ann = kwargs.get('ann', None)
        lab = kwargs.get('lab', '')
        col = kwargs.get('col', 'k')
        col = col.reshape(1, -1)
        #ax.plot(cp[0], cp[1], linewidth=0, markerfacecolor=col, marker=mk,
        #        markeredgecolor=edge_col, markersize=self.mksize, label=lab)
        mksize = kwargs.get('mksize', self.mksize)
        ax.scatter(cp[0],
                   cp[1],
                   linewidth=0,
                   c=col,
                   marker=mk,
                   edgecolors=edge_col,
                   s=mksize,
                   label=lab)
        # Next 3 lines are necessary in case c_dir[2]=0, as for Euler angles [45, 45, 0]
        if c_dir[2] < 0.000001:
            ax.scatter(-cp[0],
                       -cp[1],
                       linewidth=0,
                       c=col,
                       marker=mk,
                       s=mksize,
                       label=lab)
            #ax.plot(-cp[0], -cp[1], linewidth=0, markerfacecolor=col, marker=mk,
            #        markersize=self.mksize, label=lab)
        if ann:
            ax.annotate(c_dir.view(), (cp[0], cp[1] - 0.1),
                        xycoords='data',
                        fontsize=8,
                        horizontalalignment='center',
                        verticalalignment='center')

    def plot_line_between_crystal_dir(self,
                                      c1,
                                      c2,
                                      ax=None,
                                      steps=11,
                                      col='k'):
        '''Plot a curve between two crystal directions.

        The curve is actually composed of several straight lines segments to
        draw from direction 1 to direction 2.

        :param c1: vector describing crystal direction 1
        :param c2: vector describing crystal direction 2
        :param ax: a reference to a pyplot ax to draw the line
        :param int steps: number of straight lines composing the curve (11 by default)
        :param col: line color (black by default)
        '''
        path = np.zeros((steps, 2), dtype=float)
        for j, i in enumerate(np.linspace(0., 1., steps)):
            ci = i * c1 + (1 - i) * c2
            ci /= np.linalg.norm(ci)
            if self.proj == 'stereo':
                ci += self.z
                ci /= ci[2]
            path[j, 0] = ci[0]
            path[j, 1] = ci[1]
        ax.plot(path[:, 0],
                path[:, 1],
                color=col,
                markersize=self.mksize,
                linewidth=2)

    def plot_pf_background(self, ax, labels=True):
        '''Function to plot the background of the pole figure.

        :param ax: a reference to a pyplot ax to draw the backgroud.
        :param bool labels: add lables to axes (True by default).
        '''
        an = np.linspace(0, 2 * np.pi, 100)
        #plt.hold('on')
        ax.plot(np.cos(an), np.sin(an), 'k-')
        ax.plot([-1, 1], [0, 0], 'k-')
        ax.plot([0, 0], [-1, 1], 'k-')
        axe_labels = ['X', 'Y', 'Z']
        if self.axis == 'Z':
            (h, v, u) = (0, 1, 2)
        elif self.axis == 'Y':
            (h, v, u) = (0, 2, 1)
        else:
            (h, v, u) = (1, 2, 0)
        if labels:
            ax.annotate(axe_labels[h], (1.01, 0.0),
                        xycoords='data',
                        fontsize=16,
                        horizontalalignment='left',
                        verticalalignment='center')
            ax.annotate(axe_labels[v], (0.0, 1.01),
                        xycoords='data',
                        fontsize=16,
                        horizontalalignment='center',
                        verticalalignment='bottom')

    def plot_pf_dir(self, c_dir, **kwargs):
        '''Plot a crystal direction in a direct pole figure.'''
        if self.axis == 'Z':
            (h, v, u) = (0, 1, 2)
        elif self.axis == 'Y':
            (h, v, u) = (0, 2, 1)
        else:
            (h, v, u) = (1, 2, 0)
        # the direction to plot is given by c_dir[h,v,u]
        if self.verbose:
            print('corrected for pf axis:', c_dir[[h, v, u]])
        self.plot_crystal_dir(c_dir[[h, v, u]], **kwargs)

    def plot_pf(self, ax=None, mk='o', ann=False):
        '''Create the direct pole figure.

        :param ax: a reference to a pyplot ax to draw the poles.
        :param mk: marker used to plot the poles (disc by default).
        :param bool ann: Annotate the pole with the coordinates of the vector if True (False by default).
        '''
        self.plot_pf_background(ax)
        kwargs = {'ax': ax, 'mk': mk, 'ann': ann}
        if self.resize_markers:
            # compute a list of the grain volume fractions
            fracs = self.microstructure.get_grain_volume_fractions()
            frac_max = max(fracs)
            print('frac max', frac_max)
        for ii, grain in enumerate(self.microstructure.grains):
            g = grain.orientation_matrix()
            gt = g.transpose()
            if self.resize_markers:
                kwargs['mksize'] = 0.15 * np.sqrt(fracs[ii] / frac_max) * 1000
            label = ''
            if self.map_field == 'grain_id':
                label = 'grain ' + str(grain.id)
            kwargs['lab'] = label

            for i, hkl_plane in enumerate(self.poles):
                if i > 0:
                    kwargs['lab'] = ''
                c = hkl_plane.normal()
                c_rot = gt.dot(c)
                if self.verbose:
                    h, k, l = hkl_plane.miller_indices()
                    print(
                        'plotting (%d%d%d) with normal %s in sample CS (corrected for pf axis): %s'
                        % (h, k, l, c, c_rot))
                col = self.get_color_from_field(grain)
                kwargs['col'] = col
                self.plot_pf_dir(c_rot, **kwargs)
        ax.axis([-1.1, 1.1, -1.1, 1.1])
        if self.pflegend and self.map_field == 'grain_id':
            ax.legend(bbox_to_anchor=(0.05, 1),
                      loc=1,
                      numpoints=1,
                      prop={'size': 10})
        ax.axis('off')
        ax.set_title('{%s} direct %s projection' % (self.family, self.proj))

    def create_pf_contour(self, ax=None, ang_step=10):
        '''Compute the distribution of orientation and plot it using contouring.

        This plot the distribution of orientation in the microstructure
        associated with this PoleFigure instance, as a continuous
        distribution using angular bining with the specified step.
        the distribution is constructed at runtime by discretizing the
        angular space and counting the number of poles in each bin.
        Then the plot_pf_contour method is called to actually plot the data.

        :param ax: a reference to a pyplot ax to draw the contours.
        :param int ang_step: angular step in degrees to use for constructing the orientation distribution data (10 degrees by default)
        '''
        # discretise the angular space (azimuth and altitude)
        ang_step *= np.pi / 180  # change to radians
        n_phi = int(1 + 2 * np.pi / ang_step)
        n_psi = int(1 + 0.5 * np.pi / ang_step)
        phis = np.linspace(0, 2 * np.pi, n_phi)
        psis = np.linspace(0, np.pi / 2, n_psi)
        xv, yv = np.meshgrid(phis, psis)
        values = np.zeros((n_psi, n_phi), dtype=int)
        for grain in self.microstructure.grains:
            g = grain.orientation_matrix()
            gt = g.transpose()
            for hkl_plane in self.poles:
                c = hkl_plane.normal()
                c_rot = gt.dot(c)
                # handle poles pointing down
                if c_rot[2] < 0:
                    c_rot *= -1  # make unit vector have z>0
                if c_rot[1] >= 0:
                    phi = np.arccos(c_rot[0] /
                                    np.sqrt(c_rot[0]**2 + c_rot[1]**2))
                else:
                    phi = 2 * np.pi - np.arccos(
                        c_rot[0] / np.sqrt(c_rot[0]**2 + c_rot[1]**2))
                psi = np.arccos(c_rot[2])  # since c_rot is normed
                i_phi = int((phi + 0.5 * ang_step) / ang_step) % n_phi
                j_psi = int((psi + 0.5 * ang_step) / ang_step) % n_psi
                values[j_psi, i_phi] += 1
        if self.proj == 'stereo':  # double check which one is flat/stereo
            x = (2 * yv / np.pi) * np.cos(xv)
            y = (2 * yv / np.pi) * np.sin(xv)
        else:
            x = np.sin(yv) * np.cos(xv)
            y = np.sin(yv) * np.sin(xv)
        # close the pole figure by duplicating azimuth=0
        values[:, -1] = values[:, 0]
        self.plot_pf_contour(ax, x, y, values)

    def plot_pf_contour(self, ax, x, y, values):
        '''Plot the direct pole figure using contours. '''
        self.plot_pf_background(ax)
        ax.contourf(x, y, values)
        # ax.plot(x, y, 'ko')
        ax.axis([-1.1, 1.1, -1.1, 1.1])
        ax.axis('off')
        ax.set_title('{%s} direct %s projection' % (self.family, self.proj))

    def sst_symmetry(self, v):
        """Transform a given vector according to the lattice symmetry associated with the pole figure.

        This function transform a vector so that it lies in the smallest symmetry equivalent zone.

        :param v: the vector to transform.
        :return: the transformed vector.
        """
        # get the symmetry from the lattice associated with the pole figure
        symmetry = self.lattice._symmetry
        if symmetry is Symmetry.cubic:
            return PoleFigure.sst_symmetry_cubic(v)
        elif symmetry is Symmetry.hexagonal:
            syms = symmetry.symmetry_operators()
            for i in range(syms.shape[0]):
                sym = syms[i]
                v_sym = np.dot(sym, v)
                # look at vectors pointing up
                if v_sym[2] < 0:
                    v_sym *= -1
                # now evaluate if projection is in the sst
                if v_sym[1] < 0 or v_sym[0] < 0:
                    continue
                elif v_sym[1] / v_sym[0] > np.tan(np.pi / 6):
                    continue
                else:
                    break
            return v_sym
        else:
            print('unsupported symmetry for the moment: %s' % symmetry)
            return None

    @staticmethod
    def sst_symmetry_cubic(z_rot):
        '''Transform a given vector according to the cubic symmetry.

        This function transform a vector so that it lies in the unit SST triangle.

        :param z_rot: vector to transform.
        :return: the transformed vector.
        '''
        if z_rot[0] < 0: z_rot[0] = -z_rot[0]
        if z_rot[1] < 0: z_rot[1] = -z_rot[1]
        if z_rot[2] < 0: z_rot[2] = -z_rot[2]

        if (z_rot[2] > z_rot[1]):
            z_rot[1], z_rot[2] = z_rot[2], z_rot[1]

        if (z_rot[1] > z_rot[0]):
            z_rot[0], z_rot[1] = z_rot[1], z_rot[0]

        if (z_rot[2] > z_rot[1]):
            z_rot[1], z_rot[2] = z_rot[2], z_rot[1]

        return np.array([z_rot[1], z_rot[2], z_rot[0]])

    def get_color_from_field(self, grain):
        """Get the color of the given grain according to the chosen field.
        
        This function will return the color associated with the given grain. Depending on how the pole figure has been 
        configured (see the `set_map_field` function), it will be obtained from:
         
         * the grain id, according to the `Microstructure.rand_cmap` function
         * ipf the colour will reflect the orientation according to the IPF scheme
         * the field value mapped on a pyplot color map if the lut field of the PoleFigure instance is a string.
         * a color directly read from the lut field; in this case the field value must reflect the category of the given grain. 
        
        :param grain: the `Grain` instance.
        :return: the color as a 3 element numpy array representing the rgb values. 
        """
        if self.map_field:
            if self.map_field == 'grain_id':
                col = Microstructure.rand_cmap().colors[grain.id]
            elif self.map_field == 'ipf':
                if self.axis == 'X':
                    axis = np.array([1., 0., 0.])
                elif self.axis == 'Y':
                    axis = np.array([0., 1., 0.])
                else:
                    axis = np.array([0., 0., 1.])
                col = grain.orientation.get_ipf_colour(axis=axis)
            else:
                # retreive the position of the grain in the list
                rank = self.microstructure.grains.index(grain)
                if type(self.lut) is str:
                    # get the color map from pyplot
                    color_map = cm.get_cmap(self.lut, 256)
                    # use the field value for this grain and the field range bounds
                    color = int(255 * max(
                        min((self.field[rank] - self.field_min_level) /
                            float(self.field_max_level - self.field_min_level),
                            1.0), 0.0))
                    col = color_map(np.arange(256))[color]
                else:
                    col = self.lut[
                        self.field[rank]]  # directly access the color
            return col
        else:
            return np.array([0., 0., 0.])

    def plot_sst(self, **kwargs):
        """ Create the inverse pole figure in the unit standard triangle.

        :param ax: a reference to a pyplot ax to draw the poles.
        :param mk: marker used to plot the poles (square by default).
        :param bool ann: Annotate the pole with the coordinates of the vector if True (False by default).
        """
        # first draw the boundary of the symmetry domain limited by 3 hkl plane normals, called here A, B and C
        symmetry = self.lattice.get_symmetry()
        ax = kwargs.get('ax')
        if symmetry is Symmetry.cubic:
            sst_poles = [(0, 0, 1), (1, 0, 1), (1, 1, 1)]
            ax.axis([-0.05, 0.45, -0.05, 0.40])
        elif symmetry is Symmetry.hexagonal:
            sst_poles = [(0, 0, 1), (2, -1, 0), (1, 0, 0)]
            ax.axis([-0.05, 1.05, -0.05, 0.6])
        else:
            print('unssuported symmetry: %s' % symmetry)
        A = HklPlane(*sst_poles[0], lattice=self.lattice)
        B = HklPlane(*sst_poles[1], lattice=self.lattice)
        C = HklPlane(*sst_poles[2], lattice=self.lattice)
        self.plot_line_between_crystal_dir(A.normal(),
                                           B.normal(),
                                           ax=ax,
                                           col='k')
        self.plot_line_between_crystal_dir(B.normal(),
                                           C.normal(),
                                           ax=ax,
                                           col='k')
        self.plot_line_between_crystal_dir(C.normal(),
                                           A.normal(),
                                           ax=ax,
                                           col='k')
        # display the 3 crystal axes
        poles = [A, B, C]
        v_align = ['top', 'top', 'bottom']
        for i in range(3):
            hkl = poles[i]
            c_dir = hkl.normal()
            c = c_dir + self.z
            c /= c[2]  # SP'/SP = r/z with r=1
            pole_str = '%d%d%d' % hkl.miller_indices()
            if symmetry is Symmetry.hexagonal:
                pole_str = '%d%d%d%d' % HklPlane.three_to_four_indices(
                    *hkl.miller_indices())
            ax.annotate(pole_str, (c[0], c[1] - (2 * (i < 2) - 1) * 0.01),
                        xycoords='data',
                        fontsize=12,
                        horizontalalignment='center',
                        verticalalignment=v_align[i])

        # now plot the sample axis
        if self.resize_markers:
            # compute a list of the grain volume fractions
            fracs = self.microstructure.get_grain_volume_fractions()
            frac_max = max(fracs)
        for ii, grain in enumerate(self.microstructure.grains):
            g = grain.orientation_matrix()
            if self.resize_markers:
                kwargs['mksize'] = 0.15 * np.sqrt(fracs[ii] / frac_max) * 1000
            # compute axis and apply SST symmetry
            if self.axis == 'Z':
                axis = self.z
            elif self.axis == 'Y':
                axis = self.y
            else:
                axis = self.x
            axis_rot = self.sst_symmetry(g.dot(axis))
            label = ''
            if self.map_field == 'grain_id':
                label = 'grain ' + str(grain.id)
            kwargs['lab'] = label
            kwargs['col'] = self.get_color_from_field(grain)
            self.plot_crystal_dir(axis_rot, **kwargs)
            if self.verbose:
                print('plotting %s in crystal CS: %s' % (self.axis, axis_rot))
        ax.axis('off')
        ax.set_title('%s-axis SST inverse %s projection' %
                     (self.axis, self.proj))

    def plot_ipf_symmetry(self, ax):
        """ Plot the inverse pole figure elements of symmetry.
        
        This assume the symmetry from the `Lattice` associated with the first pole.
        :param ax: a reference to a pyplot ax to draw the poles.
        """
        kwargs = {'ax': ax, 'col': 'k', 'ann': False}
        symmetry = self.lattice.get_symmetry()
        if symmetry is Symmetry.cubic:
            for c in self.c111s:
                for i in range(3):
                    d = c.copy()
                    d[i] = 0
                    e = np.zeros_like(c)
                    e[i] = c[i]
                    self.plot_line_between_crystal_dir(c, d, ax=ax)
                    self.plot_line_between_crystal_dir(c, e, ax=ax)
            markers = ['s', 'o', '^']
            for i, dirs in enumerate([self.c001s, self.c011s, self.c111s]):
                kwargs['mk'] = markers[i]
                [self.plot_crystal_dir(c, **kwargs) for c in dirs]
                # also plot the negative direction of those lying in the plane z==0
                for c in dirs:
                    if np.dot(c, self.z) == 0.0:
                        self.plot_crystal_dir(-c, **kwargs)
        elif symmetry is Symmetry.hexagonal:
            from math import sin, cos, pi
            for angle in np.linspace(0, 2 * pi, 12, endpoint=False):
                ax.plot([0, cos(angle)], [0, sin(angle)],
                        color='k',
                        markersize=self.mksize,
                        linewidth=2)
        else:
            print('skipping unsupported symmetry for now')

    def plot_ipf(self, plot_symmetry=False, **kwargs):
        """ Create the inverse pole figure for direction Z.

        :param bool plot_symmetry: if True plot the lines representing the symmetry of the lattice.
        :param ax: a reference to a pyplot ax to draw the poles.
        :param mk: marker used to plot the poles (square by default).
        :param bool ann: Annotate the pole with the coordinates of the vector if True (False by default).
        """
        ax = kwargs.get('ax')
        self.plot_pf_background(ax, labels=False)
        if plot_symmetry:
            self.plot_ipf_symmetry(ax)
        # now plot the sample axis
        for grain in self.microstructure.grains:
            g = grain.orientation_matrix()
            if self.axis == 'Z':
                axis = self.z
            elif self.axis == 'Y':
                axis = self.y
            else:
                axis = self.x
            axis_rot = g.dot(axis)
            kwargs['col'] = self.get_color_from_field(grain)
            self.plot_crystal_dir(axis_rot, **kwargs)
            if self.verbose:
                print('plotting ', self.axis, ' in crystal CS:', axis_rot)
        ax.axis([-1.1, 1.1, -1.1, 1.1])
        ax.axis('off')
        ax.set_title('%s-axis inverse %s projection' % (self.axis, self.proj))

    @staticmethod
    def plot(orientations, **kwargs):
        '''Plot a pole figure (both direct and inverse) for a list of orientations.

        :param orientations: the list of crystalline :py:class:`~pymicro.crystal.microstructure.Orientation` to plot.
        '''
        micro = Microstructure()
        if isinstance(orientations, list):
            for i in range(len(orientations)):
                micro.grains.append(Grain(i + 1, orientations[i]))
        elif isinstance(orientations, Orientation):
            micro.grains.append(Grain(1, orientations))
        else:
            print('Unrecognized argument: %s' % orientations.__repr__)
        pf = PoleFigure(microstructure=micro, **kwargs)
        pf.plot_pole_figures(display=True)

    @staticmethod
    def plot_euler(phi1, Phi, phi2, **kwargs):
        '''Directly plot a pole figure for a single orientation given its
        three Euler angles.

        ::

          PoleFigure.plot_euler(10, 20, 30)

        :param float phi1: first Euler angle (in degree).
        :param float Phi: second Euler angle (in degree).
        :param float phi2: third Euler angle (in degree).
        '''
        PoleFigure.plot(Orientation.from_euler(np.array([phi1, Phi, phi2])),
                        **kwargs)
Пример #28
0
 def set_microstructure(self, microstructure):
     if microstructure is None:
         microstructure = Microstructure()
     self.microstructure = microstructure
Пример #29
0
 def load(file_path='experiment.txt'):
     with open(file_path, 'r') as f:
         dict_exp = json.load(f)
     sample = Sample()
     sample.set_name(dict_exp['Sample']['Name'])
     sample.set_position(dict_exp['Sample']['Position'])
     if 'Geometry' in dict_exp['Sample']:
         sample_geo = ObjectGeometry()
         sample_geo.set_type(dict_exp['Sample']['Geometry']['Type'])
         sample.set_geometry(sample_geo)
     if 'Material' in dict_exp['Sample']:
         a, b, c = dict_exp['Sample']['Material']['Lengths']
         alpha, beta, gamma = dict_exp['Sample']['Material']['Angles']
         centering = dict_exp['Sample']['Material']['Centering']
         symmetry = Symmetry.from_string(
             dict_exp['Sample']['Material']['Symmetry'])
         material = Lattice.from_parameters(a,
                                            b,
                                            c,
                                            alpha,
                                            beta,
                                            gamma,
                                            centering=centering,
                                            symmetry=symmetry)
         sample.set_material(material)
     if 'Microstructure' in dict_exp['Sample']:
         micro = Microstructure(
             dict_exp['Sample']['Microstructure']['Name'])
         for i in range(len(
                 dict_exp['Sample']['Microstructure']['Grains'])):
             dict_grain = dict_exp['Sample']['Microstructure']['Grains'][i]
             grain = Grain(
                 dict_grain['Id'],
                 Orientation.from_euler(
                     dict_grain['Orientation']['Euler Angles (degrees)']))
             grain.position = np.array(dict_grain['Position'])
             grain.volume = dict_grain['Volume']
             micro.grains.append(grain)
         sample.set_microstructure(micro)
     exp = Experiment()
     exp.set_sample(sample)
     source = XraySource()
     source.set_position(dict_exp['Source']['Position'])
     if 'Min Energy (keV)' in dict_exp['Source']:
         source.set_min_energy(dict_exp['Source']['Min Energy (keV)'])
     if 'Max Energy (keV)' in dict_exp['Source']:
         source.set_max_energy(dict_exp['Source']['Max Energy (keV)'])
     exp.set_source(source)
     for i in range(len(dict_exp['Detectors'])):
         dict_det = dict_exp['Detectors'][i]
         if dict_det['Class'] == 'Detector2d':
             det = Detector2d(size=dict_det['Size (pixels)'])
             det.ref_pos = dict_det['Reference Position (mm)']
         if dict_det['Class'] == 'RegArrayDetector2d':
             det = RegArrayDetector2d(size=dict_det['Size (pixels)'])
             det.pixel_size = dict_det['Pixel Size (mm)']
             det.ref_pos = dict_det['Reference Position (mm)']
             if 'Binning' in dict_det:
                 det.set_binning(dict_det['Binning'])
             det.u_dir = np.array(dict_det['u_dir'])
             det.v_dir = np.array(dict_det['v_dir'])
             det.w_dir = np.array(dict_det['w_dir'])
         exp.add_detector(det)
     return exp
Пример #30
0
#!/usr/bin/env python
import os, numpy as np
from pymicro.crystal.microstructure import Microstructure, Grain, Orientation
from pymicro.crystal.texture import PoleFigure
from matplotlib import pyplot as plt, colors, cm

if __name__ == '__main__':
    '''
    Pole figure of a gold sample containing 6 grains with a strong <111> fiber texture.
    A Microstructure object is first created with the 6 grains of interest.
    The grain ids corerespond to the actual grain number (in an EBSD scan for instance).
    A PoleFigure object is then created using this microstructure and the pole figures
    (both direct and inverse) are drawn by calling the plot_pole_figures() method.
    '''
    micro = Microstructure(name='Au_6grains')
    micro.grains.append(Grain(1158, Orientation.from_euler(np.array([344.776, 52.2589, 53.9933]))))
    micro.grains.append(Grain(1349, Orientation.from_euler(np.array([344.899, 125.961, 217.330]))))
    micro.grains.append(Grain(1585, Orientation.from_euler(np.array([228.039, 57.4791, 143.171]))))
    micro.grains.append(Grain(1805, Orientation.from_euler(np.array([186.741, 60.333, 43.311]))))
    micro.grains.append(Grain(1833, Orientation.from_euler(np.array([151.709, 55.0406, 44.1051]))))
    micro.grains.append(Grain(2268, Orientation.from_euler(np.array([237.262, 125.149, 225.615]))))

    # create pole figure (both direct and inverse)
    pf = PoleFigure(hkl='111', axis='Z', proj='stereo', microstructure=micro)
    pf.mksize = 100
    pf.set_map_field('grain_id')
    pf.pflegend = True  # this works well for a few grains
    pf.plot_pole_figures(plot_sst=True, display=False, save_as='png')

    image_name = os.path.splitext(__file__)[0] + '.png'
    print('writting %s' % image_name)