A grain boundary is stored as short segments between neighbouring EBSD measurements that belong to different grains. Most properties come in pairs: two pixels, grains, phases, and the misorientation between them. The remaining properties describe the segment's geometry and its place in the boundary network.
This page assumes that the map has already been divided into grains as in Grain Reconstruction. Select Grain Boundaries covers phase and grain selection. Grain Boundaries explains the physical meaning of a boundary trace in a two-dimensional section.
|
property |
meaning |
property |
meaning |
|
ebsdId |
neighbouring pixel IDs |
grainId |
neighbouring grain IDs |
|
phaseId |
neighbouring phase IDs |
misorientation |
rotation across the segment |
|
F |
endpoint vertex IDs |
direction |
segment direction |
|
midPoint |
segment midpoint |
segment length |
|
|
signed segment curvature |
triplePoints |
triple-point list |
|
|
chainId |
chain label |
chainSize |
number of segments in the chain |
|
arcLength |
distance along the chain |
chainLength |
total chain length |
|
isClosed |
whether the chain closes |
junctionId |
junction vertex IDs |
|
componentId |
connected-component label |
componentSize |
number of segments in the component |
close all;
% load the example map in its specimen plotting frame
plottingConvention.default('y↑→x');
mtexdata twins silent;
ebsd.prop = rmfield(ebsd.prop,{'error','bands'});
% reconstruct the grains
[grains,ebsd] = calcGrains(ebsd,'angle',10*degree,'minPixel',3);Preserve the segment-to-pixel relation
This page uses ebsdId to inspect the pixel pair beside each segment. Boundary simplification and refinement change the segment count, and a resampled segment no longer lies between one specific pixel pair. The smoothBoundary options 'noSimplify' and 'noRefine' preserve that relation while smoothing.
grains = grains.smoothBoundary(5,'noSimplify','noRefine');
% draw the grains and overlay the complete boundary network
plot(grains,'FaceColor',[0.9 0.9 0.9],'micronbar','off');
gB = grains.boundary;
hold on;
plot(gB,'lineWidth',2);
hold off;
The thick black lines show that the boundary is a network of individual segments. Each segment separates one pair of neighbouring measurements.
The two sides of a segment
ebsdId, grainId, and phaseId are \(N \times 2\) matrices. Each row records what lies on the two sides of one segment. Consider the boundary of one small grain:
gB4 = grains(4).boundarygB4 = grainBoundary (y↑→x)
Segments length mineral 1 mineral 2
8 1.3 µm Magnesium MagnesiumThe object summary reports eight segments. Its ebsdId property is therefore an 8 by 2 matrix.
gB4.ebsdIdans =
1103 1104
966 967
966 829
965 828
965 964
965 1102
1103 1102
1103 1240These values are measurement IDs, not positions in the current list. IDs and list positions differ as soon as measurements have been removed from the map. Indexing by ID must say so explicitly.
ebsd('id',gB4.ebsdId)ans = EBSDsquare (y↑→x, row←col↑)
Phase Orientations Mineral Color Symmetry Crystal reference frame
1 16 (100%) Magnesium LightSkyBlue 6/mmm X||a*, Y||b, Z||c
Id Phase orientation bc bs mad oldId grainId
1103 1 (114.7°,15.7°,218.5°) 182 174 0.5 1011 4
966 1 (113.7°,15.5°,219.3°) 164 158 0.5 1010 4
966 1 (113.7°,15.5°,219.3°) 164 158 0.5 1010 4
965 1 (115.3°,15.6°,218.2°) 170 176 0.7 843 4
965 1 (115.3°,15.6°,218.2°) 170 176 0.7 843 4
965 1 (115.3°,15.6°,218.2°) 170 176 0.7 843 4
1103 1 (114.7°,15.7°,218.5°) 182 174 0.5 1011 4
1103 1 (114.7°,15.7°,218.5°) 182 174 0.5 1011 4
1104 1 (4.7°,80.8°,195.4°) 174 181 0.5 1178 42
967 1 (4.5°,80.7°,195.2°) 168 171 0.3 1177 42
829 1 (4.6°,80.7°,195.4°) 156 160 0.4 1009 42
828 1 (4.9°,80.4°,195.2°) 167 170 0.5 842 42
964 1 (4.9°,80.4°,195.2°) 176 196 0.5 676 42
1102 1 (4.4°,80.5°,195.1°) 174 197 0.3 844 42
1102 1 (4.4°,80.5°,195.1°) 174 197 0.3 844 42
1240 1 (4.4°,80.5°,195.2°) 176 168 0.4 1012 42
Scan unit : um
X × Y × Z : [1.8 → 2.7] × [1.2 → 2.1] × [0 → 0]
Normal vector: (0,0,1)
Square grid :8 × 2The grain IDs on the two sides reveal the local grain arrangement.
gB4.grainIdans =
4 42
4 42
4 42
4 42
4 42
4 42
4 42
4 42Grain 4 has grain 42 on the far side of all eight segments. Grain 4 is therefore an inclusion: it is entirely surrounded by one other grain.
plot(grains(4),'FaceColor','DarkBlue','micronbar','off');
hold on;
plot(grains(42),'FaceColor','LightCoral');
hold off;
The dark-blue inclusion sits wholly inside the coral grain. The plot is the spatial counterpart of the repeated pair [4 42] above.
From segments to chains
A chain is a maximal run of segments laid end to end. It runs from one junction to the next and never passes through a junction. Every segment belongs to exactly one chain. The same two grains lie on its two sides along the entire chain.
A junction is a vertex where the number of meeting segments is not two. It need not be a triple point. For example, the end of a boundary at the map rim is a junction but not a point where three real grains meet.
The inclusion boundary reaches no junction and closes onto itself. Its eight segments consequently form one closed chain.
chainSummary = table(gB4.chainId(1),gB4.chainSize(1),gB4.isClosed(1),...
'VariableNames',{'chainId','chainSize','isClosed'})chainSummary =
1×3 table
chainId chainSize isClosed
_______ _________ ________
1 8 true
arcLength is the cumulative length from the start of a chain to the end of each segment. chainLength repeats the total length on every segment, so either property remains aligned with the boundary list during indexing.
The misorientation across a segment
A segment's misorientation is the rotation from the orientation of its second pixel to that of its first. It follows the column order of ebsdId and can be reproduced from the two pixel orientations.
gB4(1).misorientation
inv(ebsd('id',gB4.ebsdId(1,2)).orientations) .* ...
ebsd('id',gB4.ebsdId(1,1)).orientationsans = misorientation (Magnesium → Magnesium)
antipodal: true
Bunge Euler angles in degree
phi1 Phi phi2
329.875 86.401 150.169
ans = misorientation (Magnesium → Magnesium)
Bunge Euler angles in degree
phi1 Phi phi2
329.875 86.401 150.169The two rotations agree, but only the stored one has its antipodal flag set. A boundary has no preferred side, so MTEX treats that rotation and its inverse as the same boundary misorientation. The rotation computed directly from two ordered orientations retains its direction.
A list of misorientations is meaningful only when every segment relates the same two phases. Select the phase pair before analysing angles or axes.
gB_Mg = gB('Magnesium','Magnesium');
% plot the misorientation angle on each magnesium boundary segment
moriAngle = gB_Mg.misorientation.angle ./ degree;
plot(gB_Mg,moriAngle,'lineWidth',4,'micronbar','off');
mtexColorbar('title','misorientation angle (°)');
Long runs share nearly the same colour, as expected for segments along one twin boundary. Most non-twin boundaries form the contrasting shorter runs.
The dominant magnesium twin has a misorientation angle of 86.3 degrees. In this map, 52.8 percent of the magnesium segments are within 3 degrees of that angle, and the median angle is 84.3 degrees.
fractionNearTwin = mean(abs(moriAngle - 86.3) < 3)
medianMoriAngle = median(moriAngle)fractionNearTwin =
0.5280
medianMoriAngle =
84.2547The high fraction confirms that this specimen is dominated by twin boundaries. Twinning develops that selection.
Which way a segment runs
direction is the direction of the segment trace. Its angle to the misorientation axis is used when classifying tilt and twist boundaries. See Twist and Tilt. Compute the axis in specimen coordinates from the two pixel orientations. The stored misorientation alone no longer retains that specimen-frame information.
% compute misorientation axes in specimen coordinates
ori = ebsd('id',gB_Mg.ebsdId).orientations;
axes = axis(ori(:,1),ori(:,2),'antipodal');
% plot the angle between each axis and its raw segment direction
rawAxisTraceAngle = angle(gB_Mg.direction,axes) ./ degree;
plot(gB_Mg,rawAxisTraceAngle,'lineWidth',4,'micronbar','off');
mtexColorbar('title','axis-to-trace angle (°)');
The colour flickers along boundaries that are straight on the scale of several pixels. This is the pixel staircase: one segment has only a few possible grid directions, regardless of the direction of the underlying boundary.
calcMeanDirection uses a window along each chain and never crosses a junction. An argument of 4 includes four neighbouring segments on each side of the segment.
meanDirection = gB_Mg.calcMeanDirection(4);
meanAxisTraceAngle = angle(meanDirection,axes) ./ degree;
plot(gB_Mg,meanAxisTraceAngle,'lineWidth',4,'micronbar','off');
mtexColorbar('title','axis-to-mean-trace angle (°)');
The flicker is suppressed, so nearby segments on one boundary now carry similar colours. The values differ from boundary to boundary, which is the information the flicker was hiding. The median angle is 43.2 degrees, and 10.5 percent of the segments lie below 20 degrees. The misorientation axes are therefore mostly not aligned with the traces in this section.
medianAxisTraceAngle = median(meanAxisTraceAngle)
fractionBelow20 = mean(meanAxisTraceAngle < 20)medianAxisTraceAngle =
43.2434
fractionBelow20 =
0.1053Be careful with what follows from that comparison. A trace is not a plane. It is the one direction of the boundary plane revealed by the section, while the inclination remains unknown. An axis parallel to the trace lies in the boundary plane and identifies a tilt boundary. A large angle to the trace settles nothing on its own. Twist and Tilt takes this further.
Where a segment is
midPoint gives the segment position as a vector3d. It supplies the anchor for quantities drawn at a segment, such as the misorientation axes computed above.
plot(grains,'FaceColor',[0.9 0.9 0.9],...
'faceAlpha',0.3,'micronbar','off');
hold on;
quiver(gB_Mg(1:3:end),axes(1:3:end),...
'color','black','autoScaleFactor',0.6);
hold off;
The black arrows are anchored at every third magnesium boundary midpoint. Their positions come from the boundary segments, while their directions come from the two neighbouring pixel orientations.
The same midpoint property supports spatial selection.
pos = gB_Mg.midPoint;
isTop = pos.y > 30;
plot(grains,'FaceColor',[0.9 0.9 0.9],...
'faceAlpha',0.3,'micronbar','off');
hold on;
plot(gB_Mg(isTop),'lineWidth',3,'lineColor','red');
plot(gB_Mg(~isTop),'lineWidth',3,'lineColor','blue');
hold off;
Red marks segments above \(y = 30\) micrometres, and blue marks the rest. Select Grain Boundaries develops this indexing pattern for other per-segment properties.
Two lengths are easy to confuse. length(gB_Mg) is the number of segments, whereas segLength(gB_Mg) contains the length of each segment in µm. Their sum is the total length of the selected boundary list.
totalBoundaryLength = sum(gB_Mg.segLength)totalBoundaryLength =
722.2508Chains and connected components
A chain stops at a junction. A connected component does not: it contains every segment reachable through touching segments, including branches through junctions. One component can therefore contain several chains.
componentId labels each connected group. componentSize repeats the number of segments in that component on every row. It is a segment count, not a geometric length.
The next example first selects segments close to the magnesium twin relation, then colours each segment by the size of its component.
CS = ebsd.CS;
twinning = orientation.map(Miller(1,-1,0,1,CS),Miller(1,0,-1,-1,CS),...
Miller(0,1,-1,1,CS,'uvw'),Miller(1,-1,0,1,CS,'uvw'));
gBTwin = gB(gB.isTwinning(twinning));
plot(grains,'FaceColor',[0.9 0.9 0.9],...
'faceAlpha',0.25,'micronbar','off');
hold on;
plot(gBTwin,gBTwin.componentSize,'lineWidth',4);
hold off;
mtexColorbar('title','segments per component');
Long twin lamellae appear as large connected components. Short isolated matches appear in the low end of the colour scale and may be accidental.
numTwinComponents = max(gBTwin.componentId);
componentSummary = table(length(gBTwin),numTwinComponents,...
'VariableNames',{'twinSegments','components'})componentSummary =
1×2 table
twinSegments components
____________ __________
1648 54The 1648 selected segments form 54 components. A twin lamella crossing a grain is one long component. A few isolated segments that happen to meet the twin criterion form a short component, which is why component labels are useful.
A component-scale straightness descriptor
A simple straightness descriptor divides the maximum distance between two component midpoints by the component's total segment length. Its value approaches 1 for one straight lamella. It decreases when a component meanders or branches. This is a user-defined descriptor, not a built-in MTEX property.
componentId = gBTwin.componentId;
numComponents = max(componentId);
span = zeros(numComponents,1);
for k = 1:numComponents
x = gBTwin.midPoint.x(componentId == k);
y = gBTwin.midPoint.y(componentId == k);
distanceSquared = (x-x.').^2 + (y-y.').^2;
span(k) = sqrt(max(distanceSquared(:)));
end
componentLength = accumarray(componentId,gBTwin.segLength,...
[numComponents,1],@sum);
straightness = span ./ componentLength;
plot(grains,'FaceColor',[0.9 0.9 0.9],...
'faceAlpha',0.25,'micronbar','off');
hold on;
plot(gBTwin,straightness(componentId),'lineWidth',4);
hold off;
mtexColorMap blue2red;
mtexColorbar('title','component straightness');
Straight components are red, while meandering or branched components are blue. The values reach 0.97 and have a median of 0.66.
Define a large component here as one containing more than 50 segments. The 13 large components have a median of 0.48, compared with 0.67 for the rest. A large component in this map is often several lamellae meeting inside one grain, rather than one long straight lamella.
componentSegmentCount = accumarray(componentId,1);
isLarge = componentSegmentCount > 50;
straightnessSummary = table(max(straightness),median(straightness),...
sum(isLarge),median(straightness(isLarge)),...
median(straightness(~isLarge)),...
'VariableNames',{'maximum','median','largeComponents',...
'largeMedian','otherMedian'})straightnessSummary =
1×5 table
maximum median largeComponents largeMedian otherMedian
_______ _______ _______________ ___________ ___________
0.96667 0.65528 13 0.48004 0.66692This descriptor separates single straight lamellae from branched networks, rather than twins from misindexing. It is still worth checking for the latter. A few segments selected by accident, for example because of pseudosymmetry, form neither a lamella nor a branched twin network.
Next
Misorientations at Grain Boundaries develops the angle and axis on each side of a boundary. Curvature uses chain order for signed curvature, and Triple Points develops the junctions where three real grains meet.
Further reading
- V. Randle, The Measurement of Grain Boundary Geometry, Institute of Physics, 1993. This monograph connects measurable interface geometry with material properties.
- A. P. Sutton, E. P. Banks, and A. R. Warwick, The five-dimensional parameter space of grain boundaries, Proceedings of the Royal Society A 471 (2015), 20150442. It formalises the three misorientation and two boundary- plane degrees of freedom.
- G. S. Rohrer, Measuring and Interpreting the Structure of Grain-Boundary Networks, Journal of the American Ceramic Society 94 (2011), 633-646. This review connects two-dimensional boundary maps with three-dimensional interface networks.
Citing this page.
This page is part of the documentation of
MTEX, a free and open
source MATLAB toolbox for analyzing and modeling crystallographic textures.
It was written by The MTEX Developers and is published at
https://mtex-toolbox.github.io/BoundaryProperties.html.
If you use MTEX, or reuse text or figures from this page, in your research,
please cite
F. Bachmann, R. Hielscher, H. Schaeben: Texture Analysis with MTEX - Free and Open Source Software Toolbox, Solid State Phenomena 160 (2010), 63-68. 10.4028/www.scientific.net/SSP.160.63
BibTeX
@article{bachmann2010mtex,
author = {F. Bachmann and R. Hielscher and H. Schaeben},
title = {Texture Analysis with MTEX - Free and Open Source Software Toolbox},
journal = {Solid State Phenomena},
volume = {160},
pages = {63-68},
year = {2010},
doi = {10.4028/www.scientific.net/SSP.160.63},
url = {https://doi.org/10.4028/www.scientific.net/SSP.160.63}
}
Other papers describing specific MTEX methods are listed under Publications — please cite the one that best fits your application. The MTEX source code is licensed under the GNU General Public License v2.0; the text and figures of this documentation are licensed under CC BY 4.0, which permits reuse — including by automated systems — provided The MTEX Developers and this page are credited.