diff --git a/zUtil_Strain2/gtStrainGrainsGeneral.m b/zUtil_Strain2/gtStrainGrainsGeneral.m new file mode 100644 index 0000000000000000000000000000000000000000..23bcd0340630e0e9c6e5eb63cdac93c59a2a17c8 --- /dev/null +++ b/zUtil_Strain2/gtStrainGrainsGeneral.m @@ -0,0 +1,89 @@ +function grain = gtStrainGrainsGeneral(grain, cryst, energy, Bmat, stdlim) +% GTSTRAINGRAINSGENERAL grain = gtStrainGrainsGeneral(grain, cryst, energy, +% Bmat, stdlim) +% +% Runs a strain calculation for all the grains in the cell input 'grain', +% and puts the results in the field grain{i}.strain. +% Needs the 'cryst' data as in the output of Indexter +% (in file 4_grains/phase_##/index.mat) and the beam energy in keV. +% It filters outliers based on their principal strains being away from the +% median value of all grains more than 'stdlim' times its std. +% The fields 'Bmat' and 'stdlim' are optional. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Set input +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +tic + +lambda = gtConvEnergyToWavelength(energy); + +if ~exist('Bmat','var') || isempty(Bmat) + Bmat = gtCrystHKL2CartesianMatrix(cryst.latticepar); +end + +if ~exist('stdlim','var') || isempty(stdlim) + stdlim = Inf; +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Loop through grains +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +parfor ii = 1:length(grain) + + % Deformation tensor + DT = gtStrainSingleGrain(grain{ii}, cryst, lambda, Bmat); + grain{ii}.strain.DT = DT; + + % Strain tensor - symmetric part + grain{ii}.strain.ST = 0.5*(DT + DT'); + + % Rotation tensor - skew part + grain{ii}.strain.ROT = 0.5*(DT - DT'); + + + % Principal strains - eigenvalues and eigenvectors + [ivec, ival] = eig(grain{ii}.strain.ST); + + [ivalsort, ivalinds] = sort([ival(1,1), ival(2,2), ival(3,3)], 2, 'descend'); + + % principal strain values ordered + grain{ii}.strain.pstrain = ivalsort; + + % principal strain directions in columns + grain{ii}.strain.pstraindir = ivec(:, ivalinds); + +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Filter outliers +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +med_ps = median(gtIndexAllGrainValues(grain,'strain','pstrain',1)); +std_ps = std(gtIndexAllGrainValues(grain,'strain','pstrain',1)); + + +parfor ii = 1:length(grain) + + if (grain{ii}.strain.pstrain(1) > med_ps + stdlim*std_ps) || ... + (grain{ii}.strain.pstrain(1) < med_ps - stdlim*std_ps) + + grain{ii}.strain.DT(:) = NaN; + grain{ii}.strain.ST(:) = NaN; + grain{ii}.strain.ROT(:) = NaN; + grain{ii}.strain.pstrain(:) = NaN; + grain{ii}.strain.pstraindir(:) = NaN; + + end + +end + + +toc + + +end % of function \ No newline at end of file diff --git a/zUtil_Strain2/gtStrainPlaneNormals.m b/zUtil_Strain2/gtStrainPlaneNormals.m new file mode 100644 index 0000000000000000000000000000000000000000..47f87ae257290a5e38f1d0d7af35821dfcc35ffe --- /dev/null +++ b/zUtil_Strain2/gtStrainPlaneNormals.m @@ -0,0 +1,86 @@ +function [pldef, drel] = gtStrainPlaneNormals(pl, G) +% FUNCTION [pldef, drel] = gtStrainPlaneNormals(pl, G) +% +% Returns the new orientations of plane normals 'pl' after a deformation +% described by the deformation tensor 'G' (9 distinct components). +% Also returns the elongations 'drel' due to the deformation in the +% directions of the plane normals. In a crystal this equals to the ratios +% of the d-spacings after and before the deformation. +% The results are exact, no linear approximation is used. +% The 'pl' and 'G' coordinates must be given in the same reference frame. +% + + +% Get an arbitrary vector e1 perpendicular to n: +[~, maxcomp] = max(abs(pl), [], 1); + + +ee = zeros(3,size(pl,2)*3); + +zero = zeros(1,size(pl,2)); + +ee(:,1:3:end) = [ pl(2,:); -pl(1,:); zero]; +ee(:,2:3:end) = [ zero; pl(3,:); -pl(2,:)]; +ee(:,3:3:end) = [-pl(3,:); zero; pl(1,:)]; + +ind2 = (0:size(pl,2)-1)*3 + maxcomp; +e1 = ee(:,ind2); + + +% Cross product of e1 and pl: e2=cross(e1,pl) +e2 = [pl(2,:).*e1(3,:) - pl(3,:).*e1(2,:); + pl(3,:).*e1(1,:) - pl(1,:).*e1(3,:); + pl(1,:).*e1(2,:) - pl(2,:).*e1(1,:)]; + + +% Check +% ne2 = sqrt(sum(e2.*e2, 1)); +% e2n = e2./ne2([1 1 1],:); +% +% ne1 = sqrt(sum(e1.*e1, 1)); +% e1n = e1./ne1([1 1 1],:); +% +% cross(e1n,e2n) - pl + + +% check flip e2 if needed to get them right handed: +% ch = [e1(2,:).*e2(3,:) - e1(3,:).*e2(2,:); +% e1(3,:).*e2(1,:) - e1(1,:).*e2(3,:); +% e1(1,:).*e2(2,:) - e1(2,:).*e2(1,:)]; +% sum(ch.*pl, 1) < 0 + + +% e-s in deformed state: +e1def = G*e1 + e1; +e2def = G*e2 + e2; + + +% new deformed pl +pldef = [e1def(2,:).*e2def(3,:) - e1def(3,:).*e2def(2,:); + e1def(3,:).*e2def(1,:) - e1def(1,:).*e2def(3,:); + e1def(1,:).*e2def(2,:) - e1def(2,:).*e2def(1,:)]; + + +% normalise pldef +normpldef = sqrt(sum(pldef.*pldef, 1)); +pldef = pldef./normpldef([1 1 1],:); + + +% check flip +% if sum(pldef.*pl, 1) < 0 +% pldef = ...-pldef...; +% end +%sum(pldef.*pl, 1) >= 0 + + + +% new relative d-spacing (exact calculation, non-linear in the G components) +fvec = pl + G*pl; % change along original plane normal + +% new normalised d-spacing +drel = sum(pldef.*fvec, 1); + + + +end % of function + diff --git a/zUtil_Strain2/gtStrainSingleGrain.m b/zUtil_Strain2/gtStrainSingleGrain.m new file mode 100644 index 0000000000000000000000000000000000000000..0243d67cc97e5ae2b6200a52af49ad2adde20f35 --- /dev/null +++ b/zUtil_Strain2/gtStrainSingleGrain.m @@ -0,0 +1,146 @@ +function DT = gtStrainSingleGrain(grain, cryst, lambda, Bmat) +% GTSTRAINSINGLEGRAIN DT = gtStrainSingleGrain(grain, cryst, lambda, Bmat) +% +% Computes the deformation tensor 'DT' for a single indexed grain +% (crystal system is irrelevant). The 9 deformation matrix components are +% computed in the same reference as the plane normals. +% The deformation components are found by fitting all the interplanar +% angles and distances observed (no weighting of the equations). No linear +% approximation is used. +% If 'Bmat' is empty, it will be calculated inside the function. +% + + +% All measured signed hkl-s in the grain +shkl = []; +for ii = 1:length(grain.shklind) + shkl(:,ii) = cryst.shklfam{grain.hklind(ii)}(grain.shklind(ii),:)'; +end + + +% Measured d-spacings +dsp = 0.5*lambda./sind(grain.theta); + +% D-spacings in the undeformed reference crystal +dsp_ref = cryst.dspacing(grain.hklind); + +% Required relative elongations to achieve unstrained state from deformed state +elo_ref = dsp_ref./dsp; + + +% Get B matrix for hkl -> Cartesian coordinate transforms +if isempty(Bmat) + Bmat = gtCrystHKL2CartesianMatrix(cryst.latticepar); +end + +% Cartesian coordinates of plane normals in the unstrained reference crystal +% (output plane normals are normalized) +pl_ref = gtCrystHKL2Cartesian(shkl, Bmat); + +% All combinations of plane normal dot products in the unstrained reference crystal +dotpro_ref = abs(pl_ref'*pl_ref); +dotpro_ref(dotpro_ref>1) = 1; % correction for numerical errors + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Optimization +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Initial guess values for fitting the 9 components +scguess = [0 0 0 0 0 0 0 0 0]; + +% Lower and upper bounds for the components solution +boundslow = -0.1*ones(1,9); +boundshigh = 0.1*ones(1,9); + + +% Optimization parameters +par.tolx = 1e-6; % precision for components (1e-6 is reasonable and fast) +par.tolfun = 1e-12; % optimization function target value (should be very low) +par.typicalx = 1e-5*ones(1,9); % typical component values for calculating gradients + + + +% Find the "undeformation tensor" components 'sc' that would bring the +% observed, deformed grain into the undeformed reference state. +% Using Matlab's built-in non-linear least square solver. +scopt = lsqnonlin( @(sc) sfDeviation(sc, grain.pl, elo_ref, dotpro_ref),... + scguess, boundslow, boundshigh,... + optimset('TolX',par.tolx, 'TolFun',par.tolfun,... + 'TypicalX',par.typicalx, 'FunValCheck','off', 'Display','off')); + +%[scopt, resnorm, residual, exitflag, optimout, lagrange, jacobian] = ... +% lsqnonlin( @(sc) sfDeviation(sc, grain.pl, elo_ref, dotpro_ref),... +% scguess, boundslow, boundshigh,... +% optimset('TolX',par.tolx, 'TolFun',par.tolfun,... +% 'TypicalX',par.typicalx, 'FunValCheck','off', 'Display','off')); + + +% "Undeformation tensor" +DTundef = reshape(scopt,3,3); + +I = [1 0 0; 0 1 0; 0 0 1]; + +% The real deformation tensor is a kind of inverse of DTundef +DT = inv(I + DTundef) - I; + + +end % of main function + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Sub-function +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Gives a vector of the deviations in interplanar angles and distances +% as a function of the deformation components 'sc'. These differences +% should be eliminated by finding the right 'sc'. + + +function dev = sfDeviation(sc, pl0, elo_ref, dotpro_ref) + + DT = reshape(sc,3,3); + + % 'pl0' is the as observed plane normals; 'pl' is 'pl0' after + % deformation 'DT' + [pl, elo] = gtStrainPlaneNormals(pl0, DT); + + + % Difference in elongation between computed and reference + elodiff = elo - elo_ref; + + + % Difference in dot products between computed and reference + dotprodiff = abs(pl'*pl) - dotpro_ref; + + % Take upper triangle of matrix + inds = triu(true(size(dotprodiff)),1); + dotprodiff = dotprodiff(inds); + + + % All differences in one vector + dev = [elodiff(:); dotprodiff]; + +end + + + + + + + + + + + + + + + + + + +