def get_DIC_image(dic_map, scaling_factor):

    # Construct an array of Euler angles
    grain_quats = np.empty((len(dic_map), 4))

    # Transformation orientations from EBSD orientation reference frame
    # to EBSD spatial reference frame
    frame_transform = Quat.fromAxisAngle(np.array((1, 0, 0)), np.pi)

    if dic_map.ebsdMap.crystalSym == 'hexagonal':
        # Convert hex convention from y // a2 of EBSD map to x // a1 for DAMASK
        hex_transform = Quat.fromAxisAngle(np.array([0, 0, 1]), -np.pi / 6)
        for i, grain in enumerate(dic_map):
            grain_quats[i] = (hex_transform * grain.ebsdGrain.refOri *
                              frame_transform).quatCoef

    else:
        for i, grain in enumerate(dic_map):
            grain_quats[i] = (grain.ebsdGrain.refOri *
                              frame_transform).quatCoef

    # Filter out -1 (grain boundary points) and -2 (too small grains)
    # values in the grain image
    grain_image = dic_map.grains
    remove_boundary_points(grain_image)
    remove_small_grain_points(grain_image)
    remove_boundary_points(grain_image)
    remove_boundary_points(grain_image, force_remove=True)

    # scale down image if needed
    if scaling_factor != 1:
        grain_image = zoom(grain_image,
                           scaling_factor,
                           order=0,
                           prefilter=False,
                           mode='nearest')

    # downstream expects grain numbering to start at 0 not 1
    grain_image -= 1

    DIC_image = {
        'orientations': {
            'type': 'quat',
            'unit_cell_alignment': {
                'x': 'a'
            },
            'quaternions': grain_quats,
            'P': 1,  # DefDAP uses P=+1 (e.g see `defdap.quat.Quat.__mul__`)
            'quat_component_ordering': 'scalar-vector',
        },
        'grains': grain_image,
    }
    try:
        DIC_image['scale'] = dic_map.scale
    except ValueError:
        pass

    return DIC_image
def construct_beta_quat_array(
    ebsd_map: ebsd.Map,
    alpha_phase_id: int = 0,
    variant_map: np.ndarray = None,
) -> np.ndarray:
    """Construct

    Parameters
    ----------
    ebsd_map:
        EBSD map to assign the beta variants for.
    alpha_phase_id
        Index of the alpha phase in the EBSD map.

    """
    if variant_map is None:
        variant_map = construct_variant_map(ebsd_map, alpha_phase_id)

    transformations = []
    for sym in unq_hex_syms:
        transformations.append(burg_trans * sym.conjugate)
    trans_comps = Quat.extract_quat_comps(transformations)
    trans_comps = trans_comps[:, variant_map[variant_map >= 0]]

    quat_comps = Quat.extract_quat_comps(ebsd_map.quatArray[variant_map >= 0])
    quat_comps_beta = np.empty_like(quat_comps)

    # transformations[variant] * quat
    quat_comps_beta[0, :] = (trans_comps[0, :] * quat_comps[0, :] -
                             trans_comps[1, :] * quat_comps[1, :] -
                             trans_comps[2, :] * quat_comps[2, :] -
                             trans_comps[3, :] * quat_comps[3, :])
    quat_comps_beta[1, :] = (trans_comps[1, :] * quat_comps[0, :] +
                             trans_comps[0, :] * quat_comps[1, :] -
                             trans_comps[3, :] * quat_comps[2, :] +
                             trans_comps[2, :] * quat_comps[3, :])
    quat_comps_beta[2, :] = (trans_comps[2, :] * quat_comps[0, :] +
                             trans_comps[0, :] * quat_comps[2, :] -
                             trans_comps[1, :] * quat_comps[3, :] +
                             trans_comps[3, :] * quat_comps[1, :])
    quat_comps_beta[3, :] = (trans_comps[3, :] * quat_comps[0, :] +
                             trans_comps[0, :] * quat_comps[3, :] -
                             trans_comps[2, :] * quat_comps[1, :] +
                             trans_comps[1, :] * quat_comps[2, :])
    # swap into positive hemisphere if required
    quat_comps_beta[:, quat_comps_beta[0, :] < 0] *= -1

    beta_quat_array = np.empty_like(ebsd_map.quatArray)
    beta_quat_array[variant_map < 0] = Quat(1, 0, 0, 0)
    for i, idx in enumerate(zip(*np.where(variant_map >= 0))):
        beta_quat_array[idx] = Quat(quat_comps_beta[:, i])

    return beta_quat_array
def get_EBSD_image(ebsd_map, scaling_factor):

    # Construct an array of Euler angles
    grain_quats = np.empty((len(ebsd_map), 4))

    # Transformation orientations from EBSD orientation reference frame
    # to EBSD spatial reference frame
    frame_transform = Quat.fromAxisAngle(np.array((1, 0, 0)), np.pi)

    if ebsd_map.crystalSym == 'hexagonal':
        # Convert hex convention from y // a2 of EBSD map to x // a1 for DAMASK
        hex_transform = Quat.fromAxisAngle(np.array([0, 0, 1]), -np.pi / 6)
        for i, grain in enumerate(ebsd_map):
            grain_quats[i] = (hex_transform * grain.refOri *
                              frame_transform).quatCoef

    else:
        for i, grain in enumerate(ebsd_map):
            grain_quats[i] = (grain.refOri * frame_transform).quatCoef

    # Filter out -2 (too small grains) values in the grain image
    grain_image = ebsd_map.grains
    remove_small_grain_points(grain_image)

    # scale down image if needed
    if scaling_factor != 1:
        grain_image = zoom(grain_image,
                           scaling_factor,
                           order=0,
                           prefilter=False,
                           mode='nearest')

    # downstream expects grain numbering to start at 0 not 1
    grain_image -= 1

    EBSD_image = {
        'orientations': {
            'type': 'quat',
            'unit_cell_alignment': {
                'x': 'a'
            },
            'quaternions': grain_quats,
            'P': 1,  # DefDAP uses P=+1 (e.g see `defdap.quat.Quat.__mul__`)
            'quat_component_ordering': 'scalar-vector',
        },
        'grains': grain_image,
        'scale': ebsd_map.scale,
        'phase_labels': [phase.name for phase in ebsd_map.phases],
        'grain_phases': [grain.phaseID for grain in ebsd_map],
    }

    return EBSD_image
Ejemplo n.º 4
0
    def plotGrainDataIPF(self,
                         direction,
                         mapData=None,
                         grainData=None,
                         grainIds=-1,
                         **kwargs):
        """
        Plot IPF of grain reference (average) orientations with
        points coloured by grain average values from map data.

        Parameters
        ----------
        mapData : numpy.ndarray
            Array of map data to grain average. This must be cropped!
        direction : numpy.ndarray
            Vector of reference direction for the IPF.
        plotColourBar : bool, optional
            Set to False to exclude the colour bar from the plot.
        vmin : float, optional
            Minimum value of colour scale.
        vmax : float, optional
            Maximum value for colour scale.
        cLabel : str, optional
            Colour bar label text.

        """
        # Set default plot parameters then update with any input
        plotParams = {}
        plotParams.update(kwargs)

        if grainData is None:
            if mapData is None:
                raise ValueError("Either 'mapData' or 'grainData' must "
                                 "be supplied.")
            else:
                grainData = self.calcGrainAv(mapData, grainIds=grainIds)

        # Check that grains have been detected in the map
        self.checkGrainsDetected()

        if type(grainIds) is int and grainIds == -1:
            grainIds = range(len(self))

        if len(grainData) != len(grainIds):
            raise Exception("Must be 1 value for each grain in grainData.")

        grainOri = np.empty(len(grainIds), dtype=Quat)

        for i, grainId in enumerate(grainIds):
            grain = self[grainId]
            grainOri[i] = grain.refOri

        plot = Quat.plotIPF(grainOri,
                            direction,
                            self.crystalSym,
                            c=grainData,
                            **plotParams)

        return plot
def calc_beta_oris_from_misori(
        alpha_ori: Quat,
        neighbour_oris: List[Quat],
        burg_tol: float = 5) -> Tuple[List[List[Quat]], List[float]]:
    """Calculate the possible beta orientations for a given alpha
    orientation using the misorientation relation to neighbour orientations.

    Parameters
    ----------
    alpha_ori
        A quaternion representing the alpha orientation

    neighbour_oris
        Quaternions representing neighbour grain orientations

    burg_tol
        The threshold misorientation angle to determine neighbour relations

    Returns
    -------
    list of lists of defdap.Quat.quat
        Possible beta orientations, grouped by each neighbour. Any
        neighbour with deviation greater than the tolerance is excluded.
    list of float
        Deviations from perfect Burgers transformation

    """
    burg_tol *= np.pi / 180.
    # This needed to move further up calculation process
    unq_cub_sym_comps = Quat.extract_quat_comps(unq_cub_syms)

    alpha_ori_inv = alpha_ori.conjugate

    beta_oris = []
    beta_devs = []

    for neighbour_ori in neighbour_oris:

        min_misoris, min_cub_sym_idxs = calc_misori_of_variants(
            alpha_ori_inv, neighbour_ori, unq_cub_sym_comps)

        # find the hex symmetries (i, j) from give the minimum
        # deviation from the burgers relation for the minimum store:
        # the deviation, the hex symmetries (i, j) and the cubic
        # symmetry if the deviation is over a threshold then set
        # cubic symmetry to -1
        min_misori_idx = np.unravel_index(np.argmin(min_misoris),
                                          min_misoris.shape)
        burg_dev = min_misoris[min_misori_idx]

        if burg_dev < burg_tol:
            beta_oris.append(
                beta_oris_from_cub_sym(alpha_ori,
                                       min_cub_sym_idxs[min_misori_idx],
                                       int(min_misori_idx[0])))
            beta_devs.append(burg_dev)

    return beta_oris, beta_devs
Ejemplo n.º 6
0
    def buildMisOriList(self, calcAxis=False):
        quatCompsSym = Quat.calcSymEqvs(self.quatList, self.crystalSym)

        if self.refOri is None:
            self.refOri = Quat.calcAverageOri(quatCompsSym)

        misOriArray, minQuatComps = Quat.calcMisOri(quatCompsSym, self.refOri)

        self.averageMisOri = misOriArray.mean()
        self.misOriList = list(misOriArray)

        if calcAxis:
            # Now for axis calulation
            refOriInv = self.refOri.conjugate

            misOriAxis = np.empty((3, minQuatComps.shape[1]))
            Dq = np.empty((4, minQuatComps.shape[1]))

            # refOriInv * minQuat for all points (* is quaternion product)
            # change to minQuat * refOriInv
            Dq[0, :] = (refOriInv[0] * minQuatComps[0, :] - refOriInv[1] * minQuatComps[1, :] -
                        refOriInv[2] * minQuatComps[2, :] - refOriInv[3] * minQuatComps[3, :])

            Dq[1, :] = (refOriInv[1] * minQuatComps[0, :] + refOriInv[0] * minQuatComps[1, :] +
                        refOriInv[3] * minQuatComps[2, :] - refOriInv[2] * minQuatComps[3, :])

            Dq[2, :] = (refOriInv[2] * minQuatComps[0, :] + refOriInv[0] * minQuatComps[2, :] +
                        refOriInv[1] * minQuatComps[3, :] - refOriInv[3] * minQuatComps[1, :])

            Dq[3, :] = (refOriInv[3] * minQuatComps[0, :] + refOriInv[0] * minQuatComps[3, :] +
                        refOriInv[2] * minQuatComps[1, :] - refOriInv[1] * minQuatComps[2, :])

            Dq[:, Dq[0] < 0] = -Dq[:, Dq[0] < 0]

            # numpy broadcasting taking care of different array sizes
            misOriAxis[:, :] = (2 * Dq[1:4, :] * np.arccos(Dq[0, :])) / np.sqrt(1 - np.power(Dq[0, :], 2))

            # hack it back into a list. Need to change self.*List to be arrays, it was a bad decision to
            # make them lists in the beginning
            self.misOriAxisList = []
            for row in misOriAxis.transpose():
                self.misOriAxisList.append(row)
Ejemplo n.º 7
0
 def transformData(self):
     """
     Rotate map by 180 degrees and transform quats
     """
     print("\rTransforming EBSD data...", end="")
     self.eulerAngleArray = self.eulerAngleArray[:, ::-1, ::-1]
     self.bandContrastArray = self.bandContrastArray[::-1, ::-1]
     self.phaseArray = self.phaseArray[::-1, ::-1]
     self.buildQuatArray()
     
     transformQuat = Quat.fromAxisAngle(np.array([0, 0, 1]), np.pi)
     for i in range(self.xDim):
         for j in range(self.yDim):
             self.quatArray[j, i] = self.quatArray[j, i] * transformQuat
     print("\rDone                                               ", end="")
Ejemplo n.º 8
0
    def buildQuatArray(self):
        """
        Build quaternion array
        """
        print("\rBuilding quaternion array...", end="")

        self.checkDataLoaded()

        if self.quatArray is None:
            # create the array of quat objects
            self.quatArray = Quat.createManyQuats(self.eulerAngleArray)

        print("\rDone                                               ", end="")

        return
Ejemplo n.º 9
0
def beta_oris() -> List[Quat]:
    """The 6 possible beta orientations for the `ori_single_valid` fixture."""
    return [
        Quat(0.11460994,  0.97057328,  0.10987647, -0.18105038),
        Quat(0.71894262,  0.52597293, -0.12611599,  0.43654181),
        Quat(0.4812518,   0.86403135, -0.00937589,  0.14750805),
        Quat(0.23608204, -0.12857975,  0.96217918,  0.04408791),
        Quat(0.39243229, -0.10727847, -0.59866151, -0.68999466),
        Quat(0.62851433, -0.23585823,  0.36351768, -0.64590675)
    ]
Ejemplo n.º 10
0
    def test_ori_tol(beta_oris: List[Quat]):
        ori_tol = 5.
        possible_beta_oris = [
            [beta_oris[0] * Quat.fromAxisAngle(np.array([1, 0, 0]),
                                               1.01 * ori_tol * np.pi / 180)],
            [beta_oris[1]],
            [beta_oris[1]],
            [beta_oris[1], beta_oris[3], beta_oris[4]]
        ]
        variant_count = recon.count_beta_variants(
            beta_oris, possible_beta_oris, ori_tol
        )

        expected_variant_count = [0, 3, 0, 1, 1, 0]

        assert all(np.equal(variant_count, expected_variant_count))
Ejemplo n.º 11
0
    def plotIPFMap(self, direction, **kwargs):
        # Set default plot parameters then update with any input
        plotParams = {}
        plotParams.update(kwargs)

        # calculate IPF colours
        IPFcolours = Quat.calcIPFcolours(
            self.quatArray.flatten(),
            direction,
            self.crystalSym
        )
        # reshape back to map shape array
        IPFcolours = np.reshape(IPFcolours, (self.yDim, self.xDim, 3))

        plot = MapPlot.create(self, IPFcolours, **plotParams)

        return plot
def calc_misori_of_variants(
        alpha_ori_inv: Quat, neighbour_ori: Quat,
        unq_cub_sym_comps: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
    """Calculate possible symmetry variants between two orientations.

    Calculate all possible sym variants for misorientation between two
    orientations undergoing a Burgers type transformation. Then calculate
    the misorientation to the nearest cubic symmetry, this is the deviation
    to a perfect Burgers transformation.

    Parameters
    ----------
    alpha_ori_inv
        Inverse of first orientation
    neighbour_ori
        Second orientation
    unq_cub_sym_comps
        Components of the unique cubic symmetries

    Returns
    -------
    min_misoris : np.ndarray 
       The minimum misorientation for each of the possible beta variants - shape (12, 12)

    min_cub_sym_idx : np.ndarray
       The minimum cubic symmetry index for each of the possible variants - shape (12, 12)

    """
    # calculate all possible S^B_m (eqn 11. from [1]) from the
    # measured misorientation from 2 neighbour alpha grains
    # for each S^B_m calculate the 'closest' cubic symmetry
    # (from reduced subset) and the deviation from this symmetry

    # Vectorised calculation of:
    # hex_sym[j].inv * ((neighbour_ori * alpha_ori_inv) * hex_sym[i])
    # labelled: d = h2.inv * (c * h1)
    hex_sym_comps = Quat.extract_quat_comps(hex_syms)
    c = (neighbour_ori * alpha_ori_inv).quatCoef
    h1 = np.repeat(hex_sym_comps, 12, axis=1)  # outer loop
    h2 = np.tile(hex_sym_comps, (1, 12))  # inner loop
    d = np.zeros_like(h1)

    c_dot_h1 = c[1] * h1[1] + c[2] * h1[2] + c[3] * h1[3]
    c_dot_h2 = c[1] * h2[1] + c[2] * h2[2] + c[3] * h2[3]
    h1_dot_h2 = h1[1] * h2[1] + h1[2] * h2[2] + h1[3] * h2[3]

    d[0] = (c[0] * h1[0] * h2[0] - h2[0] * c_dot_h1 + c[0] * h1_dot_h2 +
            h1[0] * c_dot_h2 + h2[1] * (c[2] * h1[3] - c[3] * h1[2]) + h2[2] *
            (c[3] * h1[1] - c[1] * h1[3]) + h2[3] *
            (c[1] * h1[2] - c[2] * h1[1]))
    d[1] = (c[0] * h2[0] * h1[1] + h1[0] * h2[0] * c[1] -
            c[0] * h1[0] * h2[1] + c_dot_h1 * h2[1] + c_dot_h2 * h1[1] -
            h1_dot_h2 * c[1] + h2[0] * (c[2] * h1[3] - c[3] * h1[2]) + c[0] *
            (h1[2] * h2[3] - h1[3] * h2[2]) + h1[0] *
            (c[2] * h2[3] - c[3] * h2[2]))
    d[2] = (c[0] * h2[0] * h1[2] + h1[0] * h2[0] * c[2] -
            c[0] * h1[0] * h2[2] + c_dot_h1 * h2[2] + c_dot_h2 * h1[2] -
            h1_dot_h2 * c[2] + h2[0] * (c[3] * h1[1] - c[1] * h1[3]) + c[0] *
            (h1[3] * h2[1] - h1[1] * h2[3]) + h1[0] *
            (c[3] * h2[1] - c[1] * h2[3]))
    d[3] = (c[0] * h2[0] * h1[3] + h1[0] * h2[0] * c[3] -
            c[0] * h1[0] * h2[3] + c_dot_h1 * h2[3] + c_dot_h2 * h1[3] -
            h1_dot_h2 * c[3] + h2[0] * (c[1] * h1[2] - c[2] * h1[1]) + c[0] *
            (h1[1] * h2[2] - h1[2] * h2[1]) + h1[0] *
            (c[1] * h2[2] - c[2] * h2[1]))

    # Vectorised calculation of:
    # burg_trans * (d * burg_trans.inv)
    # labelled: beta_vars = b * (c * b.inv)
    b = burg_trans.quatCoef
    beta_vars = np.zeros_like(h1)

    b_dot_b = b[1] * b[1] + b[2] * b[2] + b[3] * b[3]
    b_dot_d = b[1] * d[1] + b[2] * d[2] + b[3] * d[3]

    beta_vars[0] = d[0] * (b[0] * b[0] + b_dot_b)
    beta_vars[1] = (d[1] * (b[0] * b[0] - b_dot_b) + 2 * b_dot_d * b[1] +
                    2 * b[0] * (b[2] * d[3] - b[3] * d[2]))
    beta_vars[2] = (d[2] * (b[0] * b[0] - b_dot_b) + 2 * b_dot_d * b[2] +
                    2 * b[0] * (b[3] * d[1] - b[1] * d[3]))
    beta_vars[3] = (d[3] * (b[0] * b[0] - b_dot_b) + 2 * b_dot_d * b[3] +
                    2 * b[0] * (b[1] * d[2] - b[2] * d[1]))

    # calculate misorientation to each of the cubic symmetries
    misoris = np.einsum("ij,ik->jk", beta_vars, unq_cub_sym_comps)
    misoris = np.abs(misoris)
    misoris[misoris > 1] = 1.
    misoris = 2 * np.arccos(misoris)

    # find the cubic symmetry with minimum misorientation for each of
    # the beta misorientation variants
    min_cub_sym_idx = np.argmin(misoris, axis=1)
    min_misoris = misoris[np.arange(144), min_cub_sym_idx]
    # reshape to 12 x 12 for each of the hex sym multiplications
    min_cub_sym_idx = min_cub_sym_idx.reshape((12, 12))
    min_misoris = min_misoris.reshape((12, 12))

    return min_misoris, min_cub_sym_idx
Ejemplo n.º 13
0
    def test_calc(self):
        mock_map = Mock(spec=ebsd.Map)
        mock_map.quatArray = np.array([
            Quat([0.31666724, -0.91588522,  0.13405465, -0.20713635]),
            Quat([0.17612928, -0.20214505, -0.21599713, -0.93886159]),
            Quat([0.24967011, -0.224283,   -0.26045348, -0.90527673]),
            Quat([0.68070418, -0.53435491,  0.23619103, -0.44195072]),
            Quat([0.7293098,  -0.34952765, -0.07857021, -0.58289309]),
            Quat([0.00169551,  0.14777021,  0.12010977,  0.98169992]),
            Quat([0.47365417, -0.20536756, -0.20468846, -0.83161201]),
            Quat([0.78776179, -0.27972705,  0.09209514, -0.54101999]),
            Quat([0.33727506, -0.13311405, -0.13924545, -0.92148624]),
            Quat([0.41137644, -0.85164404,  0.21420115, -0.24411007]),
            Quat([0.26092708, -0.27640969, -0.29049792, -0.87813763]),
            Quat([0.51237664, -0.62480323, -0.16679851, -0.56503925])
        ]).reshape((3, 4))
        mock_map.shape = mock_map.quatArray.shape
        variant_map = np.array([[0,  3, -1,  1],
                                [2,  2,  4,  4],
                                [5, -1,  1,  2]])

        beta_quat_array = recon.construct_beta_quat_array(
            mock_map, variant_map=variant_map
        )
        expected_comps = [
            np.array([0.3586687,  -0.42249045, -0.28570384,  0.78181321]),
            np.array([0.04396812, -0.20834367, -0.45309011,  0.86566105]),
            np.array([1.,          0.,          0.,          0.]),
            np.array([0.13489744, -0.93982694,  0.30093181, -0.08926393]),
            np.array([0.66997125, -0.66745906,  0.24828395,  0.2097427]),
            np.array([0.46111146, -0.72030688, -0.25269131, -0.4524172]),
            np.array([0.31807292,  0.2294237,  -0.791435,    0.46885503]),
            np.array([0.1535714,  -0.26761921, -0.83850223,  0.44912114]),
            np.array([0.51219503,  0.6609771,  -0.31826009, -0.44662741]),
            np.array([1.,          0.,          0.,          0.]),
            np.array([0.26763438, -0.77098237, -0.48040495, -0.32119948]),
            np.array([0.56407948, -0.75208451, -0.06296879,  0.33498979])
        ]

        assert all([np.allclose(quat.quatCoef, row) for quat, row
                    in zip(beta_quat_array.flat, expected_comps)])
Ejemplo n.º 14
0
def ori_quat_list_valid() -> List[Quat]:
    """A list of sample quaternion representing the orientations of grains."""
    return [
        Quat(0.22484510, 0.45464871, -0.70807342, 0.49129550),
        Quat(0.36520321, 0.25903472, -0.40342268, 0.79798357)
    ]
Ejemplo n.º 15
0
def ori_single_valid_3() -> Quat:
    """A single sample quaternion representing the orientation of a grain."""
    return Quat(0.8730071, -0.41360125, -0.02295757, -0.25742097)
Ejemplo n.º 16
0
def ori_single_valid_2() -> Quat:
    """A single sample quaternion representing the orientation of a grain."""
    return Quat(0.11939881, -0.36445855, -0.67237386, -0.63310922)
Ejemplo n.º 17
0
def ori_single_valid() -> Quat:
    """A single sample quaternion representing the orientation of a grain."""
    return Quat(0.22484510, 0.45464871, -0.70807342, 0.49129550)
Ejemplo n.º 18
0
 def plotOriSpread(self, direction=np.array([0, 0, 1]), **kwargs):
     plotParams = {'marker': '.'}
     plotParams.update(kwargs)
     return Quat.plotIPF(self.quatList, direction, self.crystalSym,
                         **plotParams)
import numpy as np
from defdap.quat import Quat

hex_syms = Quat.symEqv("hexagonal")
# subset of hexagonal symmetries that give unique orientations when the
# Burgers transformation is applied
unq_hex_syms = [
    hex_syms[0], hex_syms[5], hex_syms[4], hex_syms[2], hex_syms[10],
    hex_syms[11]
]

cubic_syms = Quat.symEqv("cubic")
# subset of cubic symmetries that give unique orientations when the
# Burgers transformation is applied
unq_cub_syms = [
    cubic_syms[0], cubic_syms[7], cubic_syms[9], cubic_syms[1], cubic_syms[22],
    cubic_syms[16], cubic_syms[12], cubic_syms[15], cubic_syms[4],
    cubic_syms[8], cubic_syms[21], cubic_syms[20]
]

# HCP -> BCC
burg_eulers = np.array([135, 90, 354.74]) * np.pi / 180
burg_trans = Quat.fromEulerAngles(*burg_eulers).conjugate
def calc_beta_oris_from_boundary_misori(
        grain: ebsd.Grain,
        neighbour_network: nx.Graph,
        quat_array: np.ndarray,
        alpha_phase_id: int,
        burg_tol: float = 5
) -> Tuple[List[List[Quat]], List[float], List[Quat]]:
    """Calculate the possible beta orientations for pairs of alpha and
    neighbour orientations using the misorientation relation to neighbour
    orientations.

    Parameters
    ----------
    grain
        The grain currently being reconstructed

    neighbour_network
        A neighbour network mapping grain boundary connectivity

    quat_array
        Array of quaternions, representing the orientations of the pixels of the EBSD map

    burg_tol :
        The threshold misorientation angle to determine neighbour relations

    Returns
    -------
    list of lists of defdap.Quat.quat
        Possible beta orientations, grouped by each neighbour. Any
        neighbour with deviation greater than the tolerance is excluded.
    list of float
        Deviations from perfect Burgers transformation
    list of Quat
        Alpha orientations

    """
    # This needed to move further up calculation process
    unq_cub_sym_comps = Quat.extract_quat_comps(unq_cub_syms)

    beta_oris = []
    beta_devs = []
    alpha_oris = []

    neighbour_grains = neighbour_network.neighbors(grain)
    neighbour_grains = [
        grain for grain in neighbour_grains if grain.phaseID == alpha_phase_id
    ]
    for neighbour_grain in neighbour_grains:

        bseg = neighbour_network[grain][neighbour_grain]['boundary']
        # check sense of bseg
        if grain is bseg.grain1:
            ipoint = 0
        else:
            ipoint = 1

        for boundary_point_pair in bseg.boundaryPointPairsX:
            point = boundary_point_pair[ipoint]
            alpha_ori = quat_array[point[1], point[0]]

            point = boundary_point_pair[ipoint - 1]
            neighbour_ori = quat_array[point[1], point[0]]

            min_misoris, min_cub_sym_idxs = calc_misori_of_variants(
                alpha_ori.conjugate, neighbour_ori, unq_cub_sym_comps)

            # find the hex symmetries (i, j) from give the minimum
            # deviation from the burgers relation for the minimum store:
            # the deviation, the hex symmetries (i, j) and the cubic
            # symmetry if the deviation is over a threshold then set
            # cubic symmetry to -1
            min_misori_idx = np.unravel_index(np.argmin(min_misoris),
                                              min_misoris.shape)
            burg_dev = min_misoris[min_misori_idx]

            if burg_dev < burg_tol / 180 * np.pi:
                beta_oris.append(
                    beta_oris_from_cub_sym(alpha_ori,
                                           min_cub_sym_idxs[min_misori_idx],
                                           int(min_misori_idx[0])))
                beta_devs.append(burg_dev)
                alpha_oris.append(alpha_ori)

    return beta_oris, beta_devs, alpha_oris
Ejemplo n.º 21
0
    def findBoundaries(self, boundDef=10):
        """
        Find grain boundaries

        :param boundDef: critical misorientation
        :type boundDef: float
        """
        self.buildQuatArray()
        print("\rFinding boundaries...", end="")

        syms = Quat.symEqv(self.crystalSym)
        numSyms = len(syms)

        # array to store quat components of initial and symmetric equivalents
        quatComps = np.empty((numSyms, 4, self.yDim, self.xDim))

        # populate with initial quat components
        for i, row in enumerate(self.quatArray):
            for j, quat in enumerate(row):
                quatComps[0, :, i, j] = quat.quatCoef

        # loop of over symmetries and apply to initial quat components
        # (excluding first symmetry as this is the identity transformation)
        for i, sym in enumerate(syms[1:], start=1):
            # sym[i] * quat for all points (* is quaternion product)
            quatComps[i, 0] = (quatComps[0, 0] * sym[0] - quatComps[0, 1] * sym[1] -
                               quatComps[0, 2] * sym[2] - quatComps[0, 3] * sym[3])
            quatComps[i, 1] = (quatComps[0, 0] * sym[1] + quatComps[0, 1] * sym[0] -
                               quatComps[0, 2] * sym[3] + quatComps[0, 3] * sym[2])
            quatComps[i, 2] = (quatComps[0, 0] * sym[2] + quatComps[0, 2] * sym[0] -
                               quatComps[0, 3] * sym[1] + quatComps[0, 1] * sym[3])
            quatComps[i, 3] = (quatComps[0, 0] * sym[3] + quatComps[0, 3] * sym[0] -
                               quatComps[0, 1] * sym[2] + quatComps[0, 2] * sym[1])

            # swap into positve hemisphere if required
            quatComps[i, :, quatComps[i, 0] < 0] *= -1

        # Arrays to store neigbour misorientation in positive x and y direction
        misOrix = np.zeros((numSyms, self.yDim, self.xDim))
        misOriy = np.zeros((numSyms, self.yDim, self.xDim))

        # loop over symmetries calculating misorientation to initial
        for i in range(numSyms):
            for j in range(self.xDim - 1):
                misOrix[i, :, j] = abs(np.einsum("ij,ij->j", quatComps[0, :, :, j], quatComps[i, :, :, j + 1]))

            for j in range(self.yDim - 1):
                misOriy[i, j, :] = abs(np.einsum("ij,ij->j", quatComps[0, :, j, :], quatComps[i, :, j + 1, :]))

        misOrix[misOrix > 1] = 1
        misOriy[misOriy > 1] = 1

        # find min misorientation (max here as misorientaion is cos of this)
        misOrix = np.max(misOrix, axis=0)
        misOriy = np.max(misOriy, axis=0)

        # convert to misorientation in degrees
        misOrix = 360 * np.arccos(misOrix) / np.pi
        misOriy = 360 * np.arccos(misOriy) / np.pi

        # set boundary locations where misOrix or misOriy are greater than set value
        self.boundaries = np.zeros((self.yDim, self.xDim), dtype=int)

        for i in range(self.xDim):
            for j in range(self.yDim):
                if (misOrix[j, i] > boundDef) or (misOriy[j, i] > boundDef):
                    self.boundaries[j, i] = -1

        print("\rDone                                               ", end="")
        return
Ejemplo n.º 22
0
    def calcNye(self):
        """
        Calculates Nye tensor and related GND density for the EBSD map.
        Stores result in self.Nye and self.GND.
        """
        self.buildQuatArray()
        print("\rFinding boundaries...", end="")
        syms = Quat.symEqv(self.crystalSym)
        numSyms = len(syms)

        # array to store quat components of initial and symmetric equivalents
        quatComps = np.empty((numSyms, 4, self.yDim, self.xDim))

        # populate with initial quat components
        for i, row in enumerate(self.quatArray):
            for j, quat in enumerate(row):
                quatComps[0, :, i, j] = quat.quatCoef

        # loop of over symmetries and apply to initial quat components
        # (excluding first symmetry as this is the identity transformation)
        for i, sym in enumerate(syms[1:], start=1):
            # sym[i] * quat for all points (* is quaternion product)
            quatComps[i, 0] = (quatComps[0, 0] * sym[0] - quatComps[0, 1] * sym[1] -
                               quatComps[0, 2] * sym[2] - quatComps[0, 3] * sym[3])
            quatComps[i, 1] = (quatComps[0, 0] * sym[1] + quatComps[0, 1] * sym[0] -
                               quatComps[0, 2] * sym[3] + quatComps[0, 3] * sym[2])
            quatComps[i, 2] = (quatComps[0, 0] * sym[2] + quatComps[0, 2] * sym[0] -
                               quatComps[0, 3] * sym[1] + quatComps[0, 1] * sym[3])
            quatComps[i, 3] = (quatComps[0, 0] * sym[3] + quatComps[0, 3] * sym[0] -
                               quatComps[0, 1] * sym[2] + quatComps[0, 2] * sym[1])

            # swap into positve hemisphere if required
            quatComps[i, :, quatComps[i, 0] < 0] *= -1

        # Arrays to store neigbour misorientation in positive x and y direction
        misOrix = np.zeros((numSyms, self.yDim, self.xDim))
        misOriy = np.zeros((numSyms, self.yDim, self.xDim))

        # loop over symmetries calculating misorientation to initial
        for i in range(numSyms):
            for j in range(self.xDim - 1):
                misOrix[i, :, j] = abs(np.einsum("ij,ij->j", quatComps[0, :, :, j], quatComps[i, :, :, j + 1]))

            for j in range(self.yDim - 1):
                misOriy[i, j, :] = abs(np.einsum("ij,ij->j", quatComps[0, :, j, :], quatComps[i, :, j + 1, :]))

        misOrix[misOrix > 1] = 1
        misOriy[misOriy > 1] = 1

        # find min misorientation (max here as misorientaion is cos of this)
        argmisOrix = np.argmax(misOrix, axis=0)
        argmisOriy = np.argmax(misOriy, axis=0)
        misOrix = np.max(misOrix, axis=0)
        misOriy = np.max(misOriy, axis=0)

        # convert to misorientation in degrees
        misOrix = 360 * np.arccos(misOrix) / np.pi
        misOriy = 360 * np.arccos(misOriy) / np.pi

        # calculate relative elastic distortion tensors at each point in the two directions
        betaderx = np.zeros((3, 3, self.yDim, self.xDim))
        betadery = betaderx
        for i in range(self.xDim - 1):
            for j in range(self.yDim - 1):
                q0x = Quat(quatComps[0, 0, j, i], quatComps[0, 1, j, i],
                           quatComps[0, 2, j, i], quatComps[0, 3, j, i])
                qix = Quat(quatComps[argmisOrix[j, i], 0, j, i + 1],
                           quatComps[argmisOrix[j, i], 1, j, i + 1],
                           quatComps[argmisOrix[j, i], 2, j, i + 1],
                           quatComps[argmisOrix[j, i], 3, j, i + 1])
                misoquatx = qix.conjugate * q0x
                # change stepsize to meters
                betaderx[:, :, j, i] = (Quat.rotMatrix(misoquatx) - np.eye(3)) / self.stepSize / 1e-6
                q0y = Quat(quatComps[0, 0, j, i], quatComps[0, 1, j, i],
                           quatComps[0, 2, j, i], quatComps[0, 3, j, i])
                qiy = Quat(quatComps[argmisOriy[j, i], 0, j + 1, i],
                           quatComps[argmisOriy[j, i], 1, j + 1, i],
                           quatComps[argmisOriy[j, i], 2, j + 1, i],
                           quatComps[argmisOriy[j, i], 3, j + 1, i])
                misoquaty = qiy.conjugate * q0y
                # change stepsize to meters
                betadery[:, :, j, i] = (Quat.rotMatrix(misoquaty) - np.eye(3)) / self.stepSize / 1e-6

        # Calculate the Nye Tensor
        alpha = np.empty((3, 3, self.yDim, self.xDim))
        bavg = 1.4e-10  # Burgers vector
        alpha[0, 2] = (betadery[0, 0] - betaderx[0, 1]) / bavg
        alpha[1, 2] = (betadery[1, 0] - betaderx[1, 1]) / bavg
        alpha[2, 2] = (betadery[2, 0] - betaderx[2, 1]) / bavg
        alpha[:, 1] = betaderx[:, 2] / bavg
        alpha[:, 0] = -1 * betadery[:, 2] / bavg

        # Calculate 3 possible L1 norms of Nye tensor for total
        # disloction density
        alpha_total3 = np.empty((self.yDim, self.xDim))
        alpha_total5 = np.empty((self.yDim, self.xDim))
        alpha_total9 = np.empty((self.yDim, self.xDim))
        alpha_total3 = 30 / 10. *(
                abs(alpha[0, 2]) + abs(alpha[1, 2]) +
                abs(alpha[2, 2])
        )
        alpha_total5 = 30 / 14. * (
                abs(alpha[0, 2]) + abs(alpha[1, 2]) + abs(alpha[2, 2]) +
                abs(alpha[1, 0]) + abs(alpha[0, 1])
        )
        alpha_total9 = 30 / 20. * (
                abs(alpha[0, 2]) + abs(alpha[1, 2]) + abs(alpha[2, 2]) +
                abs(alpha[0, 0]) + abs(alpha[1, 0]) + abs(alpha[2, 0]) +
                abs(alpha[0, 1]) + abs(alpha[1, 1]) + abs(alpha[2, 1])
        )
        alpha_total3[abs(alpha_total3) < 1] = 1e12
        alpha_total5[abs(alpha_total3) < 1] = 1e12
        alpha_total9[abs(alpha_total3) < 1] = 1e12

        # choose from the different alpha_totals according to preference;
        # see Ruggles GND density paper
        self.GND = alpha_total9
        self.Nye = alpha
Ejemplo n.º 23
0
    def calcAverageOri(self):
        quatCompsSym = Quat.calcSymEqvs(self.quatList, self.crystalSym)

        self.refOri = Quat.calcAverageOri(quatCompsSym)
Ejemplo n.º 24
0
 def plotRefOri(self, direction=np.array([0, 0, 1]), **kwargs):
     plotParams = {'marker': '+'}
     plotParams.update(kwargs)
     return Quat.plotIPF([self.refOri], direction, self.crystalSym,
                         **plotParams)