Merging Grains edit page

Merging grains may be useful when reconstructing parent grain structures, i.e., before phase transition or before twinning. In this section we will use a twinning example for illustration. Lets start by importing some Magnesium data and reconstructing the grain structure:

% load some example data
mtexdata twins silent

% segment grains
[grains,ebsd.grainId,ebsd.mis2mean] = calcGrains(ebsd('indexed'),'angle',5*degree);

% remove two pixel grains
ebsd(grains(grains.grainSize<=2)) = [];
[grains,ebsd.grainId,ebsd.mis2mean] = calcGrains(ebsd('indexed'),'angle',5*degree,'removeQuadruplePoints');

% smooth them
grains = grains.smooth(5);

% visualize the grains
plot(grains,grains.meanOrientation)

Next we identify all twinning boundaries

% define twinning misorientation
CS = grains.CS;
twinning = orientation.map(Miller(0,1,-1,-2,CS),Miller(0,-1,1,-2,CS),...
  Miller(2,-1,-1,0,CS),Miller(2,-1,-1,0,CS));

% extract all Magnesium Magnesium grain boundaries
gB = grains.boundary('Magnesium','Magnesium');

% and check which of them are twinning boundaries with threshold 5 degree
isTwinning = angle(gB.misorientation,twinning) < 5*degree;
twinBoundary = gB(isTwinning)

% plot the twinning boundaries
hold on
plot(twinBoundary,'linecolor','w','linewidth',4,'displayName','twin boundary')
hold off
twinBoundary = grainBoundary
 
 Segments  length  mineral 1  mineral 2
     1649  361 µm  Magnesium  Magnesium

Merge grains along boundaries

The command merge will merge grains together that have a common boundary which is specified as the second argument. In our example we want to merge all grains that have a common twinning boundary so we do

[mergedGrains,parentId] = merge(grains,twinBoundary);

% plot the merged grains
hold on
plot(mergedGrains.boundary,'linecolor','k','linewidth',2.5,'linestyle','-',...
  'displayName','merged grains')
hold off

Grain relationships

The second output argument paraentId of merge is a list with the same size as the child grains which indicates for each child grain into which parent grain it has been merged. The id of the common grain is usually different from the ids of the merged grains and can be found by

mergedGrains(16).id
ans =
    16

Hence, we can find all childs of grain 16 by

childs = grains(parentId == mergedGrains(16).id)
childs = grain2d
 
 Phase  Grains  Pixels    Mineral  Symmetry  Crystal reference frame
     1       8    1698  Magnesium     6/mmm       X||a*, Y||b, Z||c*
 
 boundary segments: 442 (103 µm)
 inner boundary segments: 0 (0 µm)
 triple points: 25
 
 Id   Phase   Pixels   phi1   Phi   phi2          GOS
  6       1       40    178    90    236   0.00755032
 14       1      254     81    25    187    0.0113603
 17       1        4     80    26    189    0.0135724
 19       1       38     95   145    186   0.00600972
 24       1      774    178    90    235    0.0102946
 28       1       45     80    25    188    0.0108147
 29       1      293     94   145    185   0.00913161
 33       1      250    179    89    235    0.0107569

Estimate twin area fraction

Determining which of the measured grains are original grains and which are twins is a tough problem. Here we make a very simple assumption by labeling those areas as twins that make up less than half of the merged (original) parent grain

% extract grain area for faster access
gArea = grains.area;

% loop over mergedGrains and determine children that are not twins
isTwin = true(grains.length,1);
for i = 1:mergedGrains.length

  % get child ids
   childId = find(parentId==i);

   % cluster grains of similar orientations
   [fId,center] = calcCluster(grains.meanOrientation(childId),'maxAngle',...
       15*degree,'method','hierarchical','silent');

   % compute area of each cluster
   clusterArea = accumarray(fId,gArea(childId));

   % label the grains of largest cluster as original grain
   [~,fParent] = max(clusterArea);
   isTwin(childId(fId==fParent)) = false;
end

% compute the area fraction of twins
sum(area(grains(isTwin)))/sum(area(grains)) * 100

% visualize the result
close all
plot(grains(~isTwin),'FaceColor','darkgray','displayName','not twin')
hold on
plot(grains(isTwin),'FaceColor','red','displayName','twin')
hold on
plot(mergedGrains.boundary,'linecolor','k','linewidth',2,'linestyle','-',...
  'displayName','merged grains')
mtexTitle('twin id')
ans =
   16.6212

The parentId may also be used to compute properties of the parent grains by averaging over the corresponding child grain properties. This can be done with the MATLAB command accumarray

% this averages the GOS of the child grains into the parent grains
mergedGrains.prop.GOS = accumarray(parentId,grains.GOS,size(mergedGrains),@mean);

% visualize the result
close all
plot(grains,grains.GOS ./ degree)
hold on
plot(mergedGrains.boundary,'lineColor','white','lineWidth',2)
mtexTitle('original GOS')

nextAxis(1,2)
plot(mergedGrains,mergedGrains.GOS  ./ degree)
mtexTitle('merged GOS')
mtexColorbar
setColorRange([0,1.5])

The above result is a bit unrealistic since the averages are computed between the childs ignoring their relative areas. A better approach is to compute a weighted average by the following lines.

% extract GOS and area
childGOS = grains.GOS;
childArea = grains.area;

% compute the weighted averages
mergedGrains.prop.GOS = accumarray(parentId,1:length(grains),size(mergedGrains),...
  @(id) nanmeanWeights(childGOS(id),childArea(id)));

nextAxis(1,3), hold on
plot(mergedGrains,mergedGrains.GOS  ./ degree)

Setting Up the EBSD Data for the Merged Grains

Note that the Id's of the merged grains does not fit the grainIds stored in the initial ebsd variable. As a consequence, the following command will not give the right result

close all
plot(mergedGrains(16).boundary,'linewidth',2)
hold on
plot(ebsd(mergedGrains(16)),ebsd(mergedGrains(16)).orientations)
hold off

In order to update the grainId in the ebsd variable to the merged grains, we proceed as follows.

% copy ebsd data into a new variable to not change the old data
ebsd_merged = ebsd;

% update the grainIds to the parentIds
ebsd_merged('indexed').grainId = parentId(grains.id2ind(ebsd('indexed').grainId))
ebsd_merged = EBSD
 
 Phase  Orientations     Mineral         Color  Symmetry  Crystal reference frame
     0     46 (0.2%)  notIndexed                                                 
     1  22794 (100%)   Magnesium  LightSkyBlue     6/mmm       X||a*, Y||b, Z||c*
 
 Properties: bands, bc, bs, error, mad, x, y, grainId, mis2mean
 Scan unit : um

Now the variable ebsd_merged can be indexed by the merged grains, i.e.

plot(ebsd_merged(mergedGrains(16)),ebsd_merged(mergedGrains(16)).orientations)
hold on
plot(mergedGrains(16).boundary,'linewidth',2)
hold off