Exporting Grains edit page

Grains are geometry and data at the same time, and consequently there is no single file format that captures all of them. What one wants to export is usually one of three things - the grain wise table of properties, the mean orientations, or the polygons that make up the grain boundaries. This page shows all three. Exporting the picture rather than the numbers is covered in Exporting Plots.

Throughout this page we work with the Forsterite data set.

plottingConvention.default('y↑→x');
mtexdata forsterite silent

grains = calcGrains(ebsd('indexed'),'angle',10*degree);
grains = smoothBoundary(grains,5);

% remove the very small grains
grains = grains(grains.numPixel > 10)
grains = grain2d (y↑→x)
 
 Phase  Grains  Pixels     Mineral  Symmetry         Color
     1     443  151150  Forsterite       mmm  LightSkyBlue
     2     184   25465   Enstatite       mmm  DarkSeaGreen
     3     107    6962    Diopside     12/m1     Goldenrod
 
 boundary segments: 38616 (1.6e+06 µm)
 inner boundary segments: 156 (7018 µm)
 triple points: 3370
 
 Properties: meanRotation, GOS

The grain table

Every scalar grain property can be collected into a MATLAB table and written by writetable to a spreadsheet or a csv file. This is the most useful format for further statistical analysis outside of MTEX.

T = table(grains.id, grains.phase, grains.numPixel, grains.area, ...
  grains.perimeter, grains.equivalentRadius, grains.GOS./degree, ...
  'VariableNames',{'id','phase','numPixel','area','perimeter',...
  'equivalentRadius','GOS'});

head(T)
id    phase    numPixel       area       perimeter    equivalentRadius      GOS  
    __    _____    ________    __________    _________    ________________    _______
    19      1         34            91250     1462.3           170.43         0.24256
    21      2         16            51473     1099.2              128         0.62956
    22      1         15            50640     996.04           126.96         0.42433
    27      1         19            55714       1056           133.17         0.17977
    30      1         26            72940     1156.8           152.37         0.28391
    44      1         51       2.0553e+05     2204.5           255.78         0.19108
    53      1         49       1.4694e+05     1605.6           216.27         0.48932
    54      1         23            66472     1081.2           145.46          0.4652

Adding the mean orientation as three Euler angle columns makes the table self contained. Since Euler angles only make sense within one crystal symmetry we restrict ourselves to the Forsterite grains from here on.

grains = grains('Forsterite');
T = T(T.phase == grains.phase(1),:);

[phi1,Phi,phi2] = grains.meanOrientation.Euler;

T.phi1 = phi1./degree;
T.Phi  = Phi./degree;
T.phi2 = phi2./degree;

% we write into the temporary folder to not pollute the MTEX folder
fname = fullfile(tempdir,'grains.csv');
writetable(T,fname)

and reading it back is a one liner

head(readtable(fname))
id    phase    numPixel       area       perimeter    equivalentRadius      GOS       phi1      Phi       phi2 
    __    _____    ________    __________    _________    ________________    _______    ______    ______    ______
    19      1         34            91250     1462.3           170.43         0.24256    2.5813    161.36    269.26
    22      1         15            50640     996.04           126.96         0.42433    133.37    168.62    247.12
    27      1         19            55714       1056           133.17         0.17977    138.49    91.149    267.06
    30      1         26            72940     1156.8           152.37         0.28391    33.863    82.646    349.95
    44      1         51       2.0553e+05     2204.5           255.78         0.19108    123.16    80.504    260.91
    53      1         49       1.4694e+05     1605.6           216.27         0.48932    165.83    107.25    260.95
    54      1         23            66472     1081.2           145.46          0.4652    166.09    111.76    264.03
    55      1         44       1.3889e+05     1615.5           210.26         0.62373    139.92    82.051    260.12

Mean orientations

If only the orientations are of interest, the command export is more convenient, since it knows about the Euler angle conventions. It is described in detail in Exporting Crystal Orientations.

export(grains.meanOrientation,fullfile(tempdir,'grainOri.txt'))

export also takes a struct of additional columns, which is the natural place for the grain properties that belong to each orientation

S.area = grains.area;
S.GOS = grains.GOS ./ degree;

export(grains.meanOrientation,fullfile(tempdir,'grainOri.txt'),S)

fid = fopen(fullfile(tempdir,'grainOri.txt'));
for k = 1:3, disp(fgetl(fid)); end
fclose(fid);
phi1         Phi        phi2        area         GOS
 2.58129      161.36     269.258       91250    0.242557
 133.373     168.617     247.116     50640.4    0.424334

For crystal plasticity codes the command export_VPSC writes the VPSC texture format, with the grain areas as weights

export_VPSC(grains.meanOrientation,...
  fullfile(tempdir,'grains.tex'),'weights',grains.area)

The grain geometry

The outline of a grain is a closed polygon. The property grains.poly holds, for each grain, the list of vertex indices that walks around it. These indices refer to grains.allV, the complete vertex list - not to grains.V, which is the shorter list of vertices actually in use.

V = grains.allV;

poly = grains(1).poly{1};

size(poly)
ans =
     1    31

Hence the coordinates of the first grain are

xy = [V(poly).x, V(poly).y];

xy(1:5,:)
ans =
   1.0e+04 *
    0.0100    1.1800
    0.0058    1.1808
    0.0017    1.1817
   -0.0025    1.1825
   -0.0025    1.1775

Writing all of them into one text file, one grain after the other, is a short loop. We use the grain id as a separator so that the file can be split up again.

fname = fullfile(tempdir,'grainPolygons.txt');
fid = fopen(fname,'w');
fprintf(fid,'%% grainId x y\n');
for k = 1:min(length(grains),50)
  p = grains(k).poly{1};
  fprintf(fid,'%d %f %f\n',[repmat(double(grains.id(k)),1,numel(p)); ...
    V(p).x.'; V(p).y.']);
end
fclose(fid);

fid = fopen(fname);
for k = 1:4, disp(fgetl(fid)); end
fclose(fid);
% grainId x y
19 100.000000 11800.000000
19 58.333333 11808.333333
19 16.666667 11816.666667

The individual boundary segments, together with the ids of the two grains they separate and their misorientation, are reached through grains.boundary

gB = grains.boundary('Forsterite','Forsterite');

TB = table(gB.grainId(:,1), gB.grainId(:,2), gB.segLength, ...
  gB.misorientation.angle./degree, ...
  'VariableNames',{'grainA','grainB','segLength','misAngle'});

head(TB)
grainA    grainB    segLength    misAngle
    ______    ______    _________    ________
     2675      2691          50       60.549 
     2159      2170          50       70.835 
     2450      2507      70.711       90.587 
     2453      2507      55.902       59.531 
     1768      1810      53.033       60.026 
     1768      1810      53.033       60.026 
     1599      1670      39.278       60.336 
     1599      1670      32.044       60.336

Saving in MTEX' own format

Finally, if the data is only meant to be read back by MTEX, the ordinary MATLAB save is both lossless and by far the simplest option

save(fullfile(tempdir,'grains.mat'),'grains','ebsd')

Note that a .mat file is not readable by other software and is not guaranteed to load in a future MTEX version - for archiving, prefer one of the ASCII formats above.

% clean up
delete(fullfile(tempdir,'grains.csv'))
delete(fullfile(tempdir,'grainOri.txt'))
delete(fullfile(tempdir,'grains.tex'))
delete(fullfile(tempdir,'grainPolygons.txt'))
delete(fullfile(tempdir,'grains.mat'))