Merging Grains edit page

Merging grains may be usefull when reconstructing parent grain structures, i.e., before phase transistion or before twinning. In this section we will use a twinning example for illustration. Lets start by importing some Magenesium 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

Next we identify all twinning boundaries

% define twinning misorientation
CS = grains.CS;
twinning =,1,-1,-2,CS),Miller(0,-1,1,-2,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  mineral 1  mineral 2
     1649  Magnesium  Magnesium

Merge grains along boundaries

The command merge will merge grains together that have a commong 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
  '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

ans =

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       6    1458  Magnesium     6/mmm       X||a*, Y||b, Z||c*
 boundary segments: 397
 inner boundary segments: 0
 triple points: 24
 Id   Phase   Pixels   phi1   Phi   phi2          GOS
 13       1        9    153    19    204   0.00500566
 28       1       92    155    19    202   0.00709688
 30       1      560     30    75    194    0.0128674
 32       1      422    154    18    203   0.00755127
 34       1      345     30    75    195    0.0126356
 35       1       30    159    18    199   0.00963634

Estimate twin area fraction

Determining which of the measured grains are orginal 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',...

   % 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;

% 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
hold on
  'displayName','merged grains')
mtexTitle('twin id')
ans =

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),@nanmean);

% visualize the result
close all
plot(grains,grains.GOS ./ degree)
hold on
mtexTitle('original GOS')

plot(mergedGrains,mergedGrains.GOS  ./ degree)
mtexTitle('merged GOS')

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(2), hold on
plot(mergedGrains,mergedGrains.GOS  ./ degree)
setColorRange equal

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
hold on
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.

hold on
hold off