├── Abstract.pdf ├── Analysis 1.mat ├── CalcStressStrainWithYield.m ├── Driver.m ├── Example_Test.xlsx ├── FindYieldStart.m ├── FindYield_v2.m ├── ISS 1.png ├── LoadTest.m ├── MyHistResults.m ├── MyHistSearch.m ├── MyPlotSearch.m ├── NIAnalyzeSearch.m ├── README.md ├── RunMe.m ├── SearchExplorer.m ├── SingleSearchAllSegments.m ├── Spherical Nanoindentation Stress-Strain Analysis in MATLAB.pdf ├── filterResults.m ├── license.txt ├── mypolyfit.m ├── rsquare.m ├── smoothstrain.m └── subslider.m /Abstract.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsweaver/Spin/9e26712f00a9933e7582a743f1e30567cbf2fadb/Abstract.pdf -------------------------------------------------------------------------------- /Analysis 1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsweaver/Spin/9e26712f00a9933e7582a743f1e30567cbf2fadb/Analysis 1.mat -------------------------------------------------------------------------------- /CalcStressStrainWithYield.m: -------------------------------------------------------------------------------- 1 | 2 | function StressStrainResults = CalcStressStrainWithYield(TestData, SR, Plastic) 3 | 4 | StressStrainResults.contact_radius = NaN; % contact radius, nm 5 | StressStrainResults.Stress = NaN; % indentation stress, GPa 6 | StressStrainResults.Strain = NaN; % indentation strain 7 | StressStrainResults.Yield_Strength = NaN; % indentation yield strength, GPa 8 | StressStrainResults.Yield_Strain = NaN; % indentation stain at yield, used for plots 9 | StressStrainResults.Hardening = NaN; % hardening fits GPa/ nm/nm 10 | StressStrainResults.HardeningStartEnd = NaN; % indicies for hardening fits 11 | StressStrainResults.h_new = NaN; % zero-point corrected displacement, nm 12 | StressStrainResults.P_new = NaN; % zero-point corrected load, mN 13 | StressStrainResults.popin_YN = NaN; % indication if a pop-in is detected, 0 = pop-in, 1 = no pop-in 14 | StressStrainResults.fullH_YN = NaN; % indication if there was enough data for the desired hardening fits, 0 = yes, 1 = no 15 | StressStrainResults.PopinStressStrain = NaN; % stress and strains related to the pop-in event 16 | StressStrainResults.h_sample = NaN; % the sample displacement corrected for the elastic displacment of the tip, used for indentation strain, nm 17 | StressStrainResults.E_ind = NaN; % indentation modulus, GPa, not the sample or effective modulus 18 | StressStrainResults.H_ind = NaN; % indentation work hardening for first fit 19 | StressStrainResults.H_ind2 = NaN; % indentation work hardening for second fit 20 | 21 | 22 | h = TestData.Data(:,7); % displacement(nm) 23 | P = TestData.Data(:,8); % Load (mN) 24 | S = TestData.Data(:,9); % Harmonic Stiffness (mN/nm) 25 | % note these may or may not be harmonic corrected depending on CSM variable, see LoadTest.m 26 | 27 | h_star = SR.h_star; 28 | P_star = SR.P_star; 29 | 30 | h_new = h-h_star; % total Displacement (nm) 31 | P_new = P-P_star; % load (mN) 32 | StressStrainResults.h_new = h_new; 33 | StressStrainResults.P_new = P_new; 34 | 35 | if Plastic.Eassume == 0; % uses the modulus regression analysis value for calculation of indentation stress-strain curve 36 | E_star = SR.E_star; 37 | else % if you want to force a modulus for the calculation of the stress-strain curve, not recommended 38 | E_sample = Plastic.Eassume; % assumed Young's modulus, GPa 39 | E_star = ((1-TestData.nui^2)/TestData.Ei + (1-TestData.nus^2)/E_sample)^-1; 40 | end 41 | 42 | a = S./(2*E_star)*1e6; % contact radius (nm) 43 | StressStrainResults.contact_radius = a; 44 | % he = 3/2*P_new./S; % elastic displacement, (nm) 45 | % R_star = a.^2/he; % effective radius, (nm) 46 | 47 | vi = TestData.nui; % indenter Poisson's ratio 48 | Ei = TestData.Ei; % indenter Young's modulus 49 | 50 | hi = 3/4*(1-vi^2)/Ei*P_new./a.*10^6; % elastic displacement of the indenter tip (nm) 51 | 52 | h_sample = h_new - hi; % sample displacement corrected for the elastic displacement of the indenter tip 53 | StressStrainResults.h_sample = h_sample; % used for calculation of indentation strain 54 | 55 | E_ind = (1/E_star - (1-vi^2)/Ei)^-1; % indentation modulus (GPa) 56 | StressStrainResults.E_ind = E_ind; 57 | Stress = P_new./(pi*a.^2)*1e6; % indentation stress, GPa 58 | Strain = 4/(3*pi)*h_sample./a; % indentation strain, this is strain corrected for the indenter tip displacement 59 | StressStrainResults.Stress = Stress; 60 | StressStrainResults.Strain = Strain; 61 | 62 | % Yeild calculation 63 | SegmentEnd = SR.segment_end; % end of zero-point and modulus regression segments 64 | [Plastic_window, Pop] = FindYieldStart(Stress, Strain, SegmentEnd, E_ind, Plastic); 65 | [yield_stress, yield_strain, Hardening, Plastic_window] = FindYield_v2(Stress, Strain, SegmentEnd, Plastic_window, E_ind, Plastic); 66 | 67 | StressStrainResults.Yield_Strength = yield_stress; 68 | StressStrainResults.Yield_Strain = yield_strain; 69 | StressStrainResults.YieldStartEnd = Plastic_window.yield_win_indices; 70 | StressStrainResults.Hardening = Hardening; 71 | StressStrainResults.H_ind = Hardening(1); 72 | StressStrainResults.H_ind2 = Hardening(3); 73 | StressStrainResults.HardeningStartEnd = [Plastic_window.min_point; Plastic_window.max_point; Plastic_window.max_point2]; 74 | StressStrainResults.popin_YN = Plastic_window.popsuccess; 75 | StressStrainResults.fullH_YN = [Plastic_window.Hsuccess; Plastic_window.Hsuccess2]; 76 | StressStrainResults.PopinStressStrain = Pop; 77 | end -------------------------------------------------------------------------------- /Driver.m: -------------------------------------------------------------------------------- 1 | function[TestData, FitResults] = Driver(file, sheet, radius, vs, seg_sizes, skip, CSM, SearchSegEnd) 2 | 3 | TestData = LoadTest(file, sheet, radius, vs, skip, CSM); 4 | FitResults = SingleSearchAllSegments(seg_sizes, TestData, SearchSegEnd); 5 | end -------------------------------------------------------------------------------- /Example_Test.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsweaver/Spin/9e26712f00a9933e7582a743f1e30567cbf2fadb/Example_Test.xlsx -------------------------------------------------------------------------------- /FindYieldStart.m: -------------------------------------------------------------------------------- 1 | function [Plastic_window, Pop] = FindYieldStart(Stress, Strain, end_segment, E_ind, Plastic) 2 | 3 | % checks to see if there is a pop-in based on a strain jump 4 | % determines the indices for yield point and hardening fits 5 | 6 | 7 | Plastic_window.max_point = NaN; 8 | Plastic_window.max_point2 = NaN; 9 | Plastic_window.min_point = NaN; 10 | Plastic_window.popsuccess = NaN; % 0 when there is a pop-in detected, 1 for no pop-in 11 | Plastic_window.Hsuccess = NaN; % 0 when there is enough data for the offsets used, 1 when there is not 12 | Plastic_window.Hsuccess2 = NaN; % 0 when there is enough data for the offsets used, 1 when there is not 13 | Plastic_window.yield_win_indices = NaN; % yield point window indices calculated in FindYield.m 14 | 15 | % Pop-in stresses, strains, and indices 16 | Pop.Stress = NaN; 17 | Pop.Strain = NaN; 18 | Pop.index = NaN; 19 | Pop.Stress_after = NaN; 20 | Pop.Strain_after = NaN; 21 | Pop.index_after = NaN; 22 | 23 | % check for pop-in 24 | end_search = length(Strain) - Plastic.smooth_window; % limit search so that smoothstrain.m will not give errors 25 | pop_window = Plastic.pop_window; 26 | change = Strain(end_segment+pop_window:end_search) - Strain(end_segment:end_search-pop_window); % change in strain between n+3 and n 27 | % change has the same indexing as Strain only without the last three points 28 | pindex = find(change > Plastic.pop_in, 1); %pop-in threshold, first occurence, pindex is the first point, n, in the pop-in from n:n+3 29 | popsuccess = isempty(pindex); 30 | Plastic_window.popsuccess = popsuccess; 31 | 32 | % determine min_point 33 | if popsuccess==0 % pop-in detected 34 | stress_index = pindex -1 + end_segment; 35 | Pop.index = [stress_index:stress_index+pop_window]; 36 | Pop.Stress = Stress(Pop.index); 37 | Pop.Strain = Strain(Pop.index); 38 | [~, k] = min(Stress(stress_index:end_search)); % find the minimum stress after the pop-in, can limit the search to right after the pop-in 39 | min_stress_index = k -1 + stress_index; % put the index back to the same indexing as Stress 40 | 41 | dstrain = Plastic.C_dstrain * (Strain(stress_index + pop_window) - Strain(min_stress_index)); 42 | % C_dstrain = 0, starts fit immediately after the popin 43 | % C_dstrain = 1, starts fit as soon as the strain is greater than the strain at the pop-in cliff 44 | % the initial recovery after a pop-in is very high, this may or may not 45 | % be desirable for the back extrapolation 46 | mind_point = find(Strain(min_stress_index:end_search) > (Strain(stress_index + pop_window) - dstrain), 1); % start of back extrapolation fit 47 | min_point = mind_point -1 + min_stress_index; % put the indexing back to the same as Stress and Strain 48 | Pop.Stress_after = Stress(min_point); 49 | Pop.Strain_after = Strain(min_point); 50 | Pop.index_after = min_point; 51 | else 52 | % use offset strain to find the start of the hardening fit 53 | f1 = E_ind .* (Strain(end_segment:end_search) - Plastic.H_offset(1)); % offset line using modulus slope 54 | mind_point = find ( (f1 - Stress(end_segment:end_search)) > 0, 1 ); % first point after the offset line 55 | min_point = mind_point -1 + end_segment; % put the indexing back to the same as Stress and Strain 56 | end 57 | 58 | % determine max_point and max_point2 59 | f2 = E_ind .* (Strain(end_segment:end_search) - Plastic.H_offset(2)); % offset line using modulus slope 60 | i2 = find ( (f2 - Stress(end_segment:end_search)) > 0, 1 ); % first point after the offset line 61 | success_i2 = isempty(i2); 62 | Plastic_window.Hsuccess = success_i2; % indicates whether there is enough data to reach the specifed offset 63 | 64 | if success_i2 == 0; % there is enough data 65 | max_point = i2 -1 + end_segment; % end of back extrapolation/hardening fit 66 | % find the last data point based on another offset strain 67 | f3 = E_ind .* (Strain(end_segment:end_search) - Plastic.H_offset2); % offset line using modulus slope 68 | i3 = find ( (f3 - Stress(end_segment:end_search)) > 0, 1 ); % first point after the offset line 69 | success_i3 = isempty(i3); 70 | Plastic_window.Hsuccess2 = success_i3; % indicates whether there is enough data to reach the specifed offset 71 | 72 | if success_i3 == 0; % there is enough data 73 | max_point2 = i3 - 1 + end_segment; 74 | else % not enough data 75 | max_point2 = end_search; %end of data 76 | end 77 | else % not enough data 78 | max_point = end_search; % end of data 79 | max_point2 = NaN; 80 | Plastic_window.H_success2 = NaN; 81 | end 82 | 83 | % These are the indices for the two hardening fits 84 | % H1 (min_point:max_point) and H2 (max_point:max_point2) 85 | Plastic_window.max_point = max_point; 86 | Plastic_window.max_point2 = max_point2; 87 | Plastic_window.min_point = min_point; 88 | 89 | 90 | end -------------------------------------------------------------------------------- /FindYield_v2.m: -------------------------------------------------------------------------------- 1 | function [yield_stress, yield_strain, har, Plastic_window] = FindYield_v2(Stress, strain, segment_end, Plastic_window, E_ind, Plastic) 2 | 3 | % calculates the yield point based on two outcomes: 4 | % Outcome 1 (when a pop-in is detected): the intersection of a back extrapolated linear fit in the post elastic regime with an offset modulus line. 5 | % Outcome 2 (no pop-in detected): different options are avaialbe for the offset yield strength 6 | % 7 | % also calculates linear work hardening fits 8 | 9 | plastic_start = Plastic_window.min_point; 10 | plastic_end = Plastic_window.max_point; 11 | plastic_end2 = Plastic_window.max_point2; 12 | 13 | % smoothed for hardening/back extrapolation fit 14 | SStrain = smoothstrain(plastic_start, plastic_end, strain, Plastic.smooth_window); 15 | % Strain should be the same size as strain 16 | 17 | % 1st hardening/ back extrapolation linear fit 18 | p = mypolyfit(SStrain, Stress(plastic_start:plastic_end),1); 19 | a = p(1); % hardening slope 20 | b = p(2); % y-intercept 21 | 22 | % 2nd hardening fit 23 | if isnan(Plastic_window.Hsuccess2) == 0; 24 | SStrain2 = smoothstrain(plastic_end, plastic_end2, strain, Plastic.smooth_window); 25 | p2 = mypolyfit(SStrain2, Stress(plastic_end:plastic_end2),1); 26 | a2 = p2(1); % hardening slope 27 | b2 = p2(2); % y-intercept 28 | else 29 | a2 = NaN; 30 | b2 = NaN; 31 | end 32 | 33 | har = [a, b, a2, b2]; % linear hardening fits 34 | 35 | 36 | if Plastic_window.popsuccess==0 % pop-in occured, use back extrapolated method 37 | % intersection of offset line and back extrapolated line 38 | yield_strain = (E_ind*Plastic.YS_offset + p(2)) / (E_ind - p(1)); 39 | yield_stress = E_ind*(yield_strain - Plastic.YS_offset); 40 | Plastic_window.yield_win_indices = [NaN NaN]; 41 | 42 | else % no pop-in, 43 | % use the modulus slope and YS_window to define two offset lines (f1 and f2) which 44 | % are used for the Yind calculation 45 | c1 = 1 - Plastic.YS_window(1); c2 = 1 + Plastic.YS_window(2); 46 | f1 = E_ind .* (strain(segment_end:end) - c1 * Plastic.YS_offset); 47 | f2 = E_ind .* (strain(segment_end:end) - c2 * Plastic.YS_offset); 48 | 49 | windmin = find ( (f1 - Stress(segment_end:end)) < 0, 1, 'Last'); 50 | windmax = find ( (f2 - Stress(segment_end:end)) < 0, 1, 'Last'); 51 | 52 | in1 = segment_end + windmin - 1; 53 | in2 = segment_end + windmax - 1; 54 | 55 | Yield_Strain = strain (in1 : in2); % all values in the window 56 | Yield_Stress = Stress (in1 : in2); 57 | Plastic_window.yield_win_indices = [in1 in2]; 58 | 59 | % different methods for determining offset yield strength 60 | switch Plastic.method 61 | case 'max' % maximum stress inside the window 62 | yield_strain = max (Yield_Strain); 63 | yield_stress = max (Yield_Stress); 64 | case 'median' % median stress inside the window 65 | yield_strain = median (Yield_Strain); 66 | yield_stress = median (Yield_Stress); 67 | case 'linear' % linear fit fo data inside window and intersection with YS offset line 68 | p3 = mypolyfit(Yield_Strain, Yield_Stress,1); 69 | xx = strain(segment_end:end); 70 | intX(1) = (p3(2) + E_ind*(Plastic.YS_offset))/(E_ind - p3(1)); 71 | intX(2) = p3(1)*intX(1) + p3(2); 72 | yield_strain = intX(1); 73 | yield_stress = intX(2); 74 | case 'mean' % mean stress inside the window 75 | yield_strain = mean (Yield_Strain); 76 | yield_stress = mean (Yield_Stress); 77 | end 78 | 79 | end 80 | 81 | end -------------------------------------------------------------------------------- /ISS 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsweaver/Spin/9e26712f00a9933e7582a743f1e30567cbf2fadb/ISS 1.png -------------------------------------------------------------------------------- /LoadTest.m: -------------------------------------------------------------------------------- 1 | function [TestData] = LoadTest(filename, sheet, radius, vs, skip, CSM) 2 | 3 | [num, txt] = xlsread(filename, sheet); 4 | 5 | TestData.Filename = filename; 6 | TestData.Sheet = sheet; 7 | TestData.StiffnessSegmentStart = NaN; 8 | TestData.LoadSegmentEnd = NaN; 9 | TestData.Data = NaN; 10 | TestData.IndenterRadius = radius; 11 | TestData.nui = 0.07; % Poisson's ratio of indenter, 0.07 for diamond 12 | TestData.Ei = 1140; % Young's modulus of indenter, 1140 GPa for diamond 13 | TestData.nus = vs; 14 | TestData.skip = skip; 15 | 16 | if isempty(num) 17 | warning('%s contains no data', sheet); 18 | return 19 | end 20 | 21 | % Finds where unloading starts 22 | HoldSegmentII = size(num, 1); 23 | for ii=1:size(txt,1) 24 | if strcmpi(txt(ii), 'Hold Segment Type') == 1 % e.g., 'End Of Loading Marker' the exact text depends on the version of NanoSuite 25 | HoldSegmentII=ii-2; 26 | break; 27 | end 28 | end 29 | 30 | T = num(1:HoldSegmentII, 1:6); % num columns 1:6 are loaded 31 | 32 | TestData.LoadSegmentEnd = HoldSegmentII; 33 | if CSM == 1; % apply CSM corrections 34 | h = T(:,2) + T(:,5).*sqrt(2); % displacement(nm) 35 | P = T(:,3) + T(:,6).*sqrt(2).*10^-3; % Load (mN) 36 | S0 = T(:,4); % Harmonic Stiffness (N/m) 37 | S1 = S0./1e6; % To convert S from N/m into mN/nm 38 | K = 0.6524; 39 | m = 1.5; 40 | S2 = 1/sqrt(2*pi).*P./T(:,5).*(1/K)^(1/m) .* (1 - (1 - 2*sqrt(2).*T(:,5).*S1./P).^(1/m)); 41 | 42 | % find the last imaginary data point for S2. 43 | im = imag(S2); 44 | imd = find(im~=0,1,'last'); 45 | if isempty(imd) == 1 46 | imd = 0; 47 | end 48 | TestData.StiffnessSegmentStart = imd +1; 49 | 50 | T(1:end, 7:9) = [h, P, S2]; 51 | end 52 | 53 | if CSM == 0; % no CSM corrections 54 | TestData.StiffnessSegmentStart = 1; 55 | T(1:end, 7:8) = T(:, 2:3); 56 | T(1:end, 9) = T(:,4)./1e6; % To convert S from N/m into mN/nm 57 | end 58 | 59 | if CSM == 2; % only h and P CSM corrections 60 | TestData.StiffnessSegmentStart = 1; 61 | h = T(:,2) + T(:,5).*sqrt(2); % displacement(nm) 62 | P = T(:,3) + T(:,6).*sqrt(2).*10^-3; % Load (mN) 63 | S0 = T(:,4); % Harmonic Stiffness (N/m) 64 | S1 = S0./1e6; % To convert S from N/m into mN/nm 65 | T(1:end, 7:9) = [h, P, S1]; 66 | end 67 | 68 | TestData.Data = T; 69 | 70 | end 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /MyHistResults.m: -------------------------------------------------------------------------------- 1 | function [Estat, Ystat, Hstat, Hstat2] = MyHistResults(SR, SSR) 2 | 3 | % other variables can be added such as the zero-point correction 4 | 5 | E = [SR.E_sample]; % Sample modulus 6 | 7 | Estat.mean = mean(E); 8 | Estat.median = median(E); 9 | Estat.stdev = std(E); 10 | Estat.min = min(E); 11 | Estat.max = max(E); 12 | 13 | YS = [SSR.Yield_Strength]; % Indentation yield strength 14 | 15 | Ystat.mean = mean(YS); 16 | Ystat.median = median(YS); 17 | Ystat.stdev = std(YS); 18 | Ystat.min = min(YS); 19 | Ystat.max = max(YS); 20 | 21 | H = [SSR.H_ind]; % First hardening slope 22 | 23 | Hstat.mean = mean(H); 24 | Hstat.median = median(H); 25 | Hstat.stdev = std(H); 26 | Hstat.min = min(H); 27 | Hstat.max = max(H); 28 | 29 | H2 = [SSR.H_ind2]; % Second hardening slope 30 | 31 | Hstat2.mean = mean(H2); 32 | Hstat2.median = median(H2); 33 | Hstat2.stdev = std(H2); 34 | Hstat2.min = min(H2); 35 | Hstat2.max = max(H2); 36 | 37 | end 38 | 39 | -------------------------------------------------------------------------------- /MyHistSearch.m: -------------------------------------------------------------------------------- 1 | function [histy] = MyHistSearch(SearchResults, bins) 2 | 3 | histy.Estar = NaN; 4 | histy.Esample = NaN; 5 | histy.ModLength = NaN; 6 | histy.Fit1R2 = NaN; 7 | histy.Fit1AAR = NaN; 8 | histy.Fit1MAR = NaN; 9 | histy.Fit2R2 = NaN; 10 | histy.Fit2AAR = NaN; 11 | histy.Fit2MAR = NaN; 12 | histy.Hr = NaN; 13 | histy.Fit3R2 = NaN; 14 | histy.hchange = NaN; 15 | histy.Pchange = NaN; 16 | histy.Fit4AAR = NaN; 17 | histy.Fit4MAR = NaN; 18 | histy.P_star = NaN; 19 | histy.h_star = NaN; 20 | histy.dP = NaN; 21 | histy.dH = NaN; 22 | 23 | 24 | %% the histogram plots 25 | Fit1 = [SearchResults.Fit1]; 26 | Fit2 = [SearchResults.Fit2]; 27 | Fit3 = [SearchResults.Fit3]; 28 | Fit4 = [SearchResults.Fit4]; 29 | 30 | subplot(4,4,1); % E_star & E_sample 31 | [N, X] = hist([SearchResults.E_star], bins); 32 | histy.Estar = [N; X]; 33 | plot(X,N,'b.-'); 34 | hold on 35 | [N, X] = hist([SearchResults.E_sample], bins); 36 | histy.Esample = [N; X]; 37 | plot(X,N,'g.-'); 38 | xlabel('Modulus [GPa]', 'FontWeight', 'Bold'); 39 | legend('effective','sample','Location','NorthWest'); 40 | hold off 41 | 42 | subplot(4,4,2) % Fit1 & Fit2 & Fit3 R2 43 | [N, X] = hist([Fit1.Rsquared], bins); 44 | plot(X,N,'b.-'); 45 | histy.Fit1R2 = [N; X]; 46 | hold on 47 | [N, X] = hist([Fit2.Rsquared], bins); 48 | histy.Fit2R2 = [N; X]; 49 | plot(X,N,'g.-'); 50 | [N, X] = hist([Fit3.Rsquared], bins); 51 | histy.Fit3R2 = [N; X]; 52 | plot(X,N,'r.-'); 53 | xlabel('R^2', 'FontWeight', 'Bold'); 54 | legend('Fit1','Fit2', 'Fit3','Location','NorthWest'); 55 | hold off 56 | 57 | subplot(4,4,3); % Modulus Segment Length 58 | [N, X] = hist([SearchResults.modulus_length], bins); 59 | histy.ModLength = [N; X]; 60 | plot(X,N,'b.-'); 61 | xlabel('Modulus Length [#points]', 'FontWeight', 'Bold'); 62 | 63 | subplot(4,4,4); % Hr - residual height 64 | [N, X] = hist(real([Fit2.y_intercept]), bins); 65 | histy.Hr = [N; X]; 66 | plot(X, N, 'b.-'); 67 | xlabel('H_r [nm]', 'FontWeight', 'Bold'); 68 | 69 | subplot(4,4,5); % Fit1 AAR 70 | [N, X] = hist([Fit1.AverageAbsoluteResidual], bins); 71 | histy.Fit1AAR = [N; X]; 72 | plot(X,N,'b.-'); 73 | xlabel('Fit1 Avg.Abs.Res.', 'FontWeight', 'Bold'); 74 | 75 | subplot(4,4,6); % Fit1 MAR 76 | [N, X] = hist([Fit1.MaxAbsoluteResidual], bins); 77 | histy.Fit1MAR = [N; X]; 78 | plot(X,N,'b.-'); 79 | xlabel('Fit1 Max.Abs.Res.', 'FontWeight', 'Bold'); 80 | 81 | subplot(4,4,7); % Fit2 AAR 82 | [N, X] = hist([Fit2.AverageAbsoluteResidual], bins); 83 | histy.Fit2AAR = [N; X]; 84 | plot(X,N,'b.-'); 85 | xlabel('Fit2 Avg.Abs.Res.', 'FontWeight', 'Bold'); 86 | 87 | subplot(4,4,8); % Fit2 MAR 88 | [N, X] = hist([Fit2.MaxAbsoluteResidual], bins); 89 | histy.Fit2MAR = [N; X]; 90 | plot(X,N,'b.-'); 91 | xlabel('Fit2 Max.Abs.Res.', 'FontWeight', 'Bold'); 92 | 93 | subplot(4,4,9); % h_change 94 | [N, X] = hist([SearchResults.h_change], bins); 95 | histy.hchange = [N; X]; 96 | plot(X,N,'b.-'); 97 | xlabel('h change [nm/nm]', 'FontWeight', 'Bold'); 98 | 99 | subplot(4,4,10); % P_change 100 | [N, X] = hist([SearchResults.p_change], bins); 101 | histy.Pchange = [N; X]; 102 | plot(X,N,'b.-'); 103 | xlabel('P change [mN/mN]', 'FontWeight', 'Bold'); 104 | 105 | subplot(4,4,11); % Fit4 Avg. Abs. Residual 106 | [N, X] = hist([Fit4.AverageAbsoluteResidual], bins); 107 | histy.Fit4AAR = [N; X]; 108 | plot(X, N, 'b.-'); 109 | xlabel('Fit4 Avg.Abs.Res.', 'FontWeight', 'Bold'); 110 | 111 | subplot(4,4,12); % Fit4 MaxAbsResidual 112 | [N, X] = hist([Fit4.MaxAbsoluteResidual], bins); 113 | histy.Fit4MAR = [N; X]; 114 | plot(X,N,'b.-'); 115 | xlabel('Fit4 Max.Abs.Res.', 'FontWeight', 'Bold'); 116 | 117 | subplot(4,4,13); % P_star 118 | [N, X] = hist([SearchResults.P_star], bins); 119 | histy.P_star = [N; X]; 120 | plot(X,N,'b.-'); 121 | xlabel('P^* [mN]', 'FontWeight', 'Bold'); 122 | 123 | subplot(4,4,14); % h_star 124 | [N, X] = hist([SearchResults.h_star], bins); 125 | histy.h_star = [N; X]; 126 | plot(X,N,'b.-'); 127 | xlabel('h^* [nm]', 'FontWeight', 'Bold'); 128 | 129 | subplot(4,4,15); % dP 130 | [N, X] = hist([SearchResults.dP], bins); 131 | histy.dP = [N; X]; 132 | plot(X,N,'b.-'); 133 | xlabel('dP [mN]', 'FontWeight', 'Bold'); 134 | 135 | subplot(4,4,16); % dH 136 | [N, X] = hist([SearchResults.dH], bins); 137 | histy.dH = [N; X]; 138 | plot(X,N,'b.-'); 139 | xlabel('dH [nm]', 'FontWeight', 'Bold'); 140 | 141 | end 142 | -------------------------------------------------------------------------------- /MyPlotSearch.m: -------------------------------------------------------------------------------- 1 | function [h, SearchResults, num_points] = MyPlotSearch(FR, filt) 2 | 3 | %% Filter FR 4 | [SearchResults, num_points] = filterResults(FR, filt); 5 | 6 | Fit4 = [SearchResults.Fit4]; 7 | 8 | %% 3D scatter plot w/ colorbar (4 variabless) 9 | % x y z, color 10 | % maybe use 'patch' for large dataset plots 11 | % the variables used for scatter3 must match the variables in SearchExplorer.m 12 | % e.g. index = find(p(1) == [Fit4.AverageAbsoluteResidual] & p(2) == [FR.E_star] & p(3) == [FR.h_change], 1, 'first'); 13 | 14 | h = scatter3([Fit4.AverageAbsoluteResidual], [SearchResults.E_star], [SearchResults.h_change],10, [Fit4.MaxAbsoluteResidual], 'filled'); 15 | cmap=[1 0 0; 1 1 0; 0 1 0; 0 1 1; 0 0 1]; 16 | colormap(cmap); 17 | t = colorbar; 18 | set(get(t,'ylabel'),'string','Fit4 Max Abs Residual','FontWeight', 'Bold'); 19 | grid on; 20 | xlabel('Fit4 Average Abs Residual', 'FontWeight', 'Bold'); 21 | ylabel('E_e_f_f', 'FontWeight', 'Bold'); 22 | zlabel('h-change', 'FontWeight', 'Bold'); 23 | axis square; 24 | 25 | end -------------------------------------------------------------------------------- /NIAnalyzeSearch.m: -------------------------------------------------------------------------------- 1 | function [analysis, success] = NIAnalyzeSearch(TestData, segment_start, segment_end) 2 | 3 | success = 0; % if success = 0, the trial is not recorded 4 | 5 | minR21 = TestData.skip(1); % min R2 for zero point fit for success 6 | seg_overlap = TestData.skip(2); % min segment overlap percent for success 7 | % this function will also kick out (success = 0) when Fit 2 slope is negative 8 | 9 | analysis.segment_start = segment_start; % starting point for the zero-point regression analysis 10 | analysis.segment_end = segment_end; % end point of the zero-point and modulus regression analysis 11 | analysis.Fit1 = NaN; % zero point regression analysis 12 | analysis.Fit2 = NaN; % modulus regression analysis 13 | analysis.h_star = NaN; % zero-point displacement correction, nm 14 | analysis.P_star = NaN; % zero-point load correction, nm 15 | analysis.E_star = NaN; % effective modulus, GPa 16 | analysis.E_sample = NaN; % Young's modulus assuming the sample is isotropic, GPa 17 | analysis.h_change = NaN; % nomralized displacemnt between the start of the zero-point segment and the first positive point after the zero-point correction is applied 18 | analysis.p_change = NaN; % nomralized load between the start of the zero-point segment and the first positive point after the zero-point correction is applied 19 | analysis.dH = NaN; % displacement difference between the first modulus regression data point and the origin 20 | analysis.dP = NaN; % load difference between the first modulus regression data point and the origin 21 | analysis.segment_length = segment_end - segment_start + 1; % number of points for the zero point regression 22 | analysis.modulus_start = NaN; % index of the first point used for the modulus regression 23 | analysis.modulus_length = NaN; % number of points in the modulus regression 24 | analysis.Fit3 = NaN; % elastic stress-strain data regression 25 | analysis.Fit4 = NaN; % modulus line and elastic stress-strain data regression 26 | 27 | 28 | 29 | Ri = TestData.IndenterRadius; % indenter radius, nm 30 | Ei = TestData.Ei; % indenter Young's modulus, GPa 31 | nui = TestData.nui; % indenter Poisson's ratio 32 | nus = TestData.nus; % sample Poisson's ratio 33 | 34 | % these are the displacement, load, and contact stiffness from LoadTest.m 35 | % they may or may not be harmonic corrected depending on CSM variable, see LoadTest.m 36 | h = TestData.Data(:,7); 37 | P = TestData.Data(:,8); 38 | S = TestData.Data(:,9); 39 | 40 | A = P-2/3*S.*h; 41 | 42 | ws = warning('off','all'); 43 | 44 | S_elast=S(segment_start:segment_end); % S in the elastic part 45 | A_elast=A(segment_start:segment_end); % A in the elastic part 46 | 47 | p = mypolyfit(S_elast,A_elast,1); % zero point regression 48 | Fit1.slope = p(1) ; 49 | Fit1.y_intercept = p(2); 50 | Output = polyval(p, S_elast); 51 | [R21, ~, ~, AAR1, MAR1] = rsquare(A_elast, Output); 52 | 53 | if (R21 < minR21) % check the r-squared of the zero point regression 54 | return; 55 | end 56 | 57 | Fit1.Rsquared = R21; 58 | Fit1.AverageAbsoluteResidual = AAR1; 59 | Fit1.MaxAbsoluteResidual = MAR1; 60 | analysis.Fit1 = Fit1; 61 | 62 | 63 | % Calculate h_star and P_star 64 | h_star = -3/2*Fit1.slope; 65 | P_star = Fit1.y_intercept; 66 | analysis.h_star = h_star; 67 | analysis.P_star = P_star; 68 | 69 | h_new = h-h_star; % Displacement (nm) 70 | P_new = P-P_star; % Load (mN) 71 | 72 | h_new_fit = h_new(segment_start:segment_end); 73 | P_new_fit = P_new(segment_start:segment_end); 74 | 75 | % find the negative values that result from the zero-point correction. 76 | first_pos_ii = find(P_new_fit > 0, 1, 'first'); 77 | 78 | % displacement and load used for the modulus regression 79 | h_new_fit = h_new_fit(first_pos_ii:end); 80 | P_new_fit = P_new_fit(first_pos_ii:end); 81 | 82 | percent = length(h_new_fit)/length(h_new(segment_start:segment_end)); % percentatge of data left over after zero-point correction for the modulus fit 83 | modulus_start = first_pos_ii -1 + segment_start; 84 | 85 | dH = h_new(modulus_start); % same as looking at h(modulus_start) - h_star 86 | dP = P_new(modulus_start); % same as looking at P(modulus_start) - P_star 87 | % these variables are a measure of how close the elastic segment is to 88 | % the origin 89 | 90 | analysis.dH = dH; 91 | analysis.dP = dP; 92 | 93 | % check overlap criteria 94 | if(percent < seg_overlap) 95 | return; 96 | end 97 | 98 | P_new_23 = P_new_fit.^(2/3); 99 | 100 | p = mypolyfit(P_new_23, h_new_fit, 1); % modulus regression 101 | 102 | if (p(1) <= 0) % if the slope is negative, E_star is imaginary 103 | return; 104 | end 105 | 106 | Fit2.slope = p(1); 107 | Fit2.y_intercept = p(2); 108 | % Evaluate Fit assuming y_intercept is zero. Fit2 parameters then emphasize data 109 | % which is linear and goes through the origin. 110 | Output = polyval([p(1) 0], P_new_23); 111 | [R22, ~, ~, AAR2, MAR2] = rsquare(h_new_fit, Output); 112 | 113 | Fit2.Rsquared = R22; 114 | Fit2.AverageAbsoluteResidual = AAR2; 115 | Fit2.MaxAbsoluteResidual = MAR2; 116 | analysis.Fit2 = Fit2; 117 | 118 | analysis.h_change = (h(modulus_start) - h(segment_start)) / (h(segment_end) - h(segment_start)); % overlap metric 119 | analysis.p_change = (P(modulus_start) - P(segment_start)) / (P(segment_end) - P(segment_start)); % overlap metric 120 | analysis.modulus_start = modulus_start; 121 | analysis.modulus_length = length(P_new_fit); 122 | 123 | % Find E* from slope2 & y_intercept2 124 | E_star=(3/4)/(Fit2.slope^(3/2) * Ri^(1/2))*1e6; % in GPa 125 | E_sample=(1-nus^2)*(1/E_star-(1-nui^2)/Ei)^-1; % in GPa 126 | analysis.E_star = E_star; 127 | analysis.E_sample = E_sample; 128 | 129 | 130 | % Get the R square for the linear portion of the stress strain curve 131 | % he = 3/2*P_new./S; % (nm) 132 | a = S./(2*E_star)*1e6; % (nm) area of contact 133 | % R_star=a.^2/he; % (nm) 134 | 135 | Stress=P_new./(pi*a.^2)*1e6; % GPa 136 | Strain=4/(3*pi)*h_new./a; 137 | 138 | % Evaluate elastic stress-strain fit 139 | stress_E = Stress(modulus_start:segment_end); % actual elastic stress data 140 | strain_E = Strain(modulus_start:segment_end); 141 | p = mypolyfit(strain_E, stress_E, 1); 142 | % Evaluate Fit assuming y_intercept is zero. Fit3 parameters then emphasize data 143 | % which is linear and goes through the origin. 144 | Output = polyval([p(1) 0], strain_E); 145 | [R23, ~, ~, AAR3, MAR3] = rsquare(stress_E, Output); % how linear is the segment 146 | 147 | Fit3.Rsquared = R23; 148 | Fit3.AverageAbsoluteResidual = AAR3; 149 | Fit3.MaxAbsoluteResidual = MAR3; 150 | analysis.Fit3 = Fit3; 151 | 152 | stress_Estar = E_star.*Strain(modulus_start:segment_end); % elastic stress data asumming S=Estar*e 153 | [~, ~, ~, AAR4, MAR4] = rsquare(stress_E, stress_Estar); % how close to the effective modulus line is the data 154 | 155 | Fit4.Rsquared = NaN; 156 | Fit4.AverageAbsoluteResidual = AAR4; 157 | Fit4.MaxAbsoluteResidual = MAR4; 158 | analysis.Fit4 = Fit4; 159 | 160 | % No errors, set success to 1 161 | success = 1; 162 | 163 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spin 2 | Spherical nanoindentation stress-strain analysis 3 | 4 | ## Please read [Spherical Nanoindentation Stress-Strain Analysis in MATLAB.pdf](https://github.com/jsweaver/Spin/blob/master/Spherical%20Nanoindentation%20Stress-Strain%20Analysis%20in%20MATLAB.pdf) for an introduction to the code. ## 5 | 6 | ## Helpful References: ## 7 | 8 | (1) Kalidindi and Pathak. (2008) Acta Materialia. http://dx.doi.org/10.1016/j.actamat.2008.03.036 9 | (2) Vachhani et al. (2013) Acta Materialia. http://dx.doi.org/10.1016/j.actamat.2013.03.005 10 | (3) Pathak and Kalidindi. (2015) Materials Science and Engineering R. http://dx.doi.org/10.1016/j.mser.2015.02.001 11 | (4) Weaver et al. (2016) Acta Materialia. (Supplemental Material) http://dx.doi.org/10.1016/j.actamat.2016.06.053 12 | 13 | ## Purpose and Intended Use: ## 14 | 15 | The main reason for writing this code was to make the determination of the zero-point correction and indentation stress-strain curves more robust by semi-automating the analysis, developing metrics for determining appropriate answers, and providing some estimate of the uncertainty 16 | 17 | of the appropriate answer including measurements from the indentation stress-strain curve (e.g. indentation yield strength). 18 | 19 | The intended use is that the user would select a representative answer for each test and include the statistics of multiple appropriate answers for one test when determining the final answer, values, or properties. 20 | 21 | ## Short Discription of Functions ## 22 | 23 | **RunME.m** - load, analyze, plot, save data with this script. Most of the parameters which require adjusting are set in this script. 24 | 25 | **LoadTest.m** - imports nanoindentation in excel format. Important to set/check the indenter properties, the "End of Test" marker, and correct columns of raw data. CSM corrections are also calculated in here. 26 | 27 | **smoothstrain.m** - applies a moving average to the hardening fit stress-strain data. Not always used. 28 | 29 | **filterResults.m** - cuts down the results based on different criteria. A new criterion can be added by coping the 'case' logic used for other variables. 30 | 31 | **CalcStressStrainWithYield.m** - here is where the indentation stress-strain curve is calculated 32 | 33 | **FindYield_v2.m** - function for determining the yield point and hardening slopes. 34 | 35 | **FindYieldStart.m** - function for determining if a pop-in occurs and some markers needed for determining the yield point 36 | 37 | **MyPlotSearch.m** - plotting function for the 3-D scatter plot of the results. In order to change the axes of the 3-D plot, modify this code and in SearchExplorer.m 38 | 39 | **NIAnalyzeSearch.m** - this function does the zero=point and modulus regression analyses. It gets called many times so try not to add more to it. A speed up in computation might come from better coding with this function and the sub functions it calls. 40 | 41 | **MyHistSearch.m** - plotting function for histograms of relevant variables in the results. Viewing this data can be helpful for updating the filter to determine the results. 42 | 43 | **SearchExplorer.m** - interactive plotting function for the 3D scatter plot for the results. *Important* - the scaling of the indentation stress-strain curve plot is done manually because it always causes problems. Find the variables: mstrain and mstress and adjust accordingly. 44 | 45 | **subslider.m** - creates a subplot of historgrams for many vairables. A NewFilt variable can be created based on the slider values of all the variables in the plot. 46 | 47 | **MyHistResults.m** - spits out the statistics for indentation properties for the saved analyses. 48 | 49 | Shouldn't have to touch these... 50 | 51 | **Driver.m** 52 | **SingleSearchAllSegments.m** 53 | **mypolyfit.m** 54 | **rsquare.m** 55 | -------------------------------------------------------------------------------- /RunMe.m: -------------------------------------------------------------------------------- 1 | % This script is used to change variables and run the main functions for the analysis 2 | % Please see ReadMe.txt before begining 3 | % Errors often occur when... 4 | % 1)trying to load data that doesn't exist or is in the wrong format 5 | % 2)there are no answers left over after filtering 6 | 7 | %% Step 1. Load data and perform zero-point and modulus regression analyses for a large array of segments 8 | clc 9 | folder = []; % e.g., 'C:\folder\subfolder\', requires \ at the end 10 | file = 'Example_Test.xlsx'; % use appropriate .xls or .xlsx 11 | % excel file should have segment type, time, displacement, load, harmonics 12 | % stiffness, harmonic displacement, and harmonic load in that order 13 | tnum = '1'; % used for saving and reading the test number 14 | sheet = ['Test 00', tnum]; % name of sheet in file, add or remove a zero depending on tnum 15 | 16 | seg_sizes = [20:20:500]; % an array of segment sizes for which to analyze, use a minimum number for speed 17 | SearchSegEnd = 1500; % cut off no. of data points, analyzes data up to this value 18 | % SearchSegEnd must be <= EndLoadSegment Marker in raw data or an error will occur 19 | 20 | Rind = 100000; % indenter radius in nm, ie. 100000 (100um), 16500 (16.5um) 21 | vs = 0.3; % sample Poisson ratio, only matters if you want to compute the sample modulus 22 | skip = [0.3 0.1]; % aborts and skips over any analysis with Fit1 R2 < skip(1) AND length(Fit2)/length(Fit1) < skip(2) 23 | % modulus also has to be real value 24 | CSM = 0; % choose whether to apply CSM corrections (CSM = 1) or not (CSM = 0) or just Pact and hact (CSM = 2) 25 | % these are based on Vachhani et al. (2013) Acta Materialia http://dx.doi.org/10.1016/j.actamat.2013.03.005 26 | 27 | % Zero-point and modulus regression analysis of a single test 28 | [TestData, FitResults] = Driver([folder, file], sheet, Rind, vs, seg_sizes, skip, CSM, SearchSegEnd); 29 | % Fit Results contains all the regression analyses 30 | % TestData contains the raw and CSM corrected data 31 | 32 | %% Step 2. Filter down FitResults based on different criteron 33 | 34 | % see filterResults.m for a full list of Filt 'variables' and [value formats] 35 | % anything you want can be added as long as it is saved in FitResults 36 | Filt ={... 37 | 'Modulus', [300 340];... 38 | 'R21', [0.7 1.0];... 39 | 'R22', [0.7 1.0] ;... 40 | 'R23', [0.7 1.0];... 41 | % 'P*', 0.1;... 42 | % 'h*', 15;... 43 | 'dP', [-0.5 0.5];... 44 | 'dH', [-0.5 0.5];... 45 | 'h_change', [-0.7 0.7];.... 46 | }; 47 | 48 | bins = 20; % number of bins for the historgram plots 49 | 50 | % Plastic contains all the parameters for determing yield strength (Yind) and 51 | % hardening fits. These are not standardized. See FindYieldStart.m and 52 | % FindYield_v2.m for details of variables and calculations. 53 | 54 | Plastic.method = 'linear'; % method for finding Yind, see FindYield_v2.m for details and options 55 | Plastic.YS_offset = 0.002; % offset strain for Yind 56 | Plastic.H_offset = [0.003 0.02]; % offset strains for first hardneing fit 57 | Plastic.H_offset2 = 0.25; % max offset strain for 2nd hardening fit (start of fit is the end of the first hardening fit) 58 | Plastic.YS_window = [0 4]; % +/- faction of YS_offset in which to calc median stress and strain 59 | Plastic.pop_in = Inf; % threshold strain burst to consider a pop-in, use Inf to ignore this 60 | % if a pop-in is recorded, Yind is then determined from a back-extrapolation, 61 | Plastic.pop_window = 3; % number of data points for calculating strain burst (i.e. n+3 - n) 62 | Plastic.C_dstrain = 0; % tuning variable for the start of the back-extrapolation after a pop-in 63 | Plastic.smooth_window = 0; % +/- number of points for movering average on strain, used for hardening fits, use 0 to ignore 64 | 65 | BEuler=[0 0 0]'; % bunge-euler angles, shows up on plots, useful when doing single grain indents 66 | 67 | Plastic.Eassume = 0; % forced Young's modulus for determing the contact radius, use 0 to ignore 68 | % this will alter the indentation stress-strain curve, only recomended for troubleshooting 69 | 70 | % Filters through FitResults and plots histograms of SearchResults and 3-D interactive plot for selecting an answer 71 | [SearchResults, npoints, HistSearchResults] = SearchExplorer(TestData, FitResults, Filt, Plastic, BEuler, bins); 72 | 73 | % 3-D interactive plot instructions 74 | % click on any point in Figure (1), then click on the "Plot ISS Analysis" button to see the answer 75 | % recording this answer, click on "Save FR & ISS" 76 | % recording all the answer, click on "Save All ISS" 77 | %% Step 2.2 after you have NewFilt 78 | 79 | [SearchResults, npoints, HistSearchResults] = SearchExplorer(TestData, FitResults, NewFilt, Plastic, BEuler, bins); 80 | 81 | 82 | %% Step 3. Save your work as .mat and .png 83 | % you must 84 | % (1) Calculate the stress-strain curves for all the SearchResults you see as acceptable answers. Click on the "Save All ISS" button in Figure 1 85 | % (2) Select, calculate, and plot a single representative answer. Click on "Save FR & ISS" button to save the currently selected data point. 86 | % Keep the plot for this answer up, Click on "Plot ISS Analysis" 87 | % and close all the other figures. 88 | 89 | % computes the statistics of the indentation properties for the SearchResults 90 | [Estat, Ystat, Hstat, Hstat2] = MyHistResults(SearchResults, Stress_Strain_Search_Results) 91 | 92 | % save workspace and the ISS plot in the same folder as the raw data 93 | % caution!!! this will overwrite data if files with the same name already exists 94 | save([folder,'Analysis ' tnum]) % saves your entire workspace 95 | set(gcf,'PaperPositionMode','auto') % makes the plots full screen 96 | saveas(gcf,[folder, 'ISS ' tnum], 'png') % grabs the current plot, close others you don't want 97 | -------------------------------------------------------------------------------- /SearchExplorer.m: -------------------------------------------------------------------------------- 1 | function [SearchResults, npoints, HistSearchResults] = SearchExplorer(TestData, FR, filt, Plastic, BEuler, bins) 2 | 3 | 4 | 5 | index = 1; 6 | 7 | figure(1) % scatter plot 8 | [h, SearchResults, npoints] = MyPlotSearch(FR, filt); % filter results and scatter plot 9 | 10 | SZ = get(0,'Screensize'); 11 | SZ(2) = SZ(2) + 50; 12 | SZ(4) = SZ(4) - 130; 13 | 14 | Fit4 = [FR.Fit4]; 15 | %% grab the index of the datapoint and print these variables on the plot 16 | function txt = hitme(gcbo,eventdata) % prints results for mouse cliced data point 17 | p = get(eventdata, 'Position'); 18 | % index variables must match the variables of the scatter plot in MyPlotSearch.m 19 | % e.g., h = scatter3([Fit4.AverageAbsoluteResidual], [SearchResults.E_star], [SearchResults.h_change],10, [Fit4.MaxAbsoluteResidual], 'filled'); 20 | 21 | index = find(p(1) == [Fit4.AverageAbsoluteResidual] & p(2) == [FR.E_star] & p(3) == [FR.h_change], 1, 'first'); 22 | 23 | % txt box with data when you use the cursor to select an analysis 24 | labels = ['Start: %d\n',... 25 | 'End: %d\n'... 26 | 'Length: %d\n',... 27 | 'Modulus Start: %d\n',... 28 | 'Modulus Length: %d\n',... 29 | 'h*: %0.4g\n',... 30 | 'P*: %0.4g\n',... 31 | 'h_ch: %0.4g\n',... 32 | 'p_ch: %0.4g\n',... 33 | 'dH: %0.4g\n',... 34 | 'dP: %0.4g\n',... 35 | 'H_r: %0.4g\n',... 36 | 'Fit1.R2: %0.3g\n',... 37 | 'Fit2.R2: %0.3g\n',... 38 | 'Fit3.R2: %0.3g\n',... 39 | 'E_eff: %0.3g\n',... 40 | 'E_s: %0.3g']; 41 | txt = sprintf(labels, ... 42 | FR(index).segment_start, ... 43 | FR(index).segment_end, ... 44 | FR(index).segment_length, ... 45 | FR(index).modulus_start, ... 46 | FR(index).modulus_length, ... 47 | FR(index).h_star, ... 48 | FR(index).P_star, ... 49 | FR(index).h_change,... 50 | FR(index).p_change,... 51 | FR(index).dH,... 52 | FR(index).dP,... 53 | FR(index).Fit2.y_intercept,... 54 | FR(index).Fit1.Rsquared, ... 55 | FR(index).Fit2.Rsquared, ... 56 | FR(index).Fit3.Rsquared,... 57 | FR(index).E_star, ... 58 | FR(index).E_sample); 59 | end 60 | %% plot the stress-strain curve and 4 other plots 61 | function plotss(a,b) % plots the stress-strain curve for the analysis selcted with the cursor 62 | SSR = CalcStressStrainWithYield(TestData, FR(index), Plastic); 63 | 64 | figure() 65 | set(gcf, 'Position', SZ) % make fullscreen 66 | subplot(2,4,[1,2,5,6]) % stress-strain curve 67 | hold on 68 | 69 | mstrain = max(real(SSR.Strain)); 70 | mstress = max(real(SSR.Stress)); 71 | temp = [0 mstrain]; % for line plotting 72 | plot(SSR.Strain, SSR.Stress,'b.', 'markersize', 10); % stress-strain data 73 | plot(temp,[SSR.E_ind].*temp,'color',[0.5 0.5 0.5],'LineStyle','-','linewidth',2) % modulus line 74 | plot(temp, [SSR.E_ind].*(temp - Plastic.YS_offset),'color',[0.5 0.5 0.5],'LineStyle','--','linewidth',2); % strain offset line 75 | plot(SSR.Strain(FR(index).segment_start:FR(index).segment_end), SSR.Stress(FR(index).segment_start:FR(index).segment_end), 'g.','markersize', 10); % modulus fit data 76 | 77 | % any of these can be commented out if they are needed on the plot, 78 | if isnan(SSR.YieldStartEnd) == 0; % if it exists 79 | plot(SSR.Yield_Strain, SSR.Yield_Strength, 'r.', 'markersize', 35); % yield point 80 | plot(SSR.Strain(SSR.YieldStartEnd), SSR.Stress(SSR.YieldStartEnd),'r^','MarkerSize',15) % yield point data 81 | end 82 | if isnan(SSR.popin_YN) == 0; % if it exists 83 | plot(SSR.Yield_Strain, SSR.Yield_Strength, 'r.', 'markersize', 35); % yield point 84 | end 85 | if isnan(SSR.HardeningStartEnd(2)) == 0; % 1st hardening fit if it exists 86 | plot(temp, ([SSR.Hardening(1)].*temp + SSR.Hardening(2)), 'k--', 'linewidth', 2) % int. hardening slope line 87 | plot(SSR.Strain(SSR.HardeningStartEnd(1):SSR.HardeningStartEnd(2)), SSR.Stress(SSR.HardeningStartEnd(1):SSR.HardeningStartEnd(2)), 'k.','markersize', 10); % int. hardening slope data 88 | end 89 | % if isnan(SSR.Hardening(3)) == 0; % 2nd hardening fit if it exists 90 | % plot(temp, ([SSR.Hardening(3)].*temp + SSR.Hardening(4)), 'm--', 'linewidth', 2) % 2nd hardening slope line 91 | % plot(SSR.Strain(SSR.HardeningStartEnd(2):SSR.HardeningStartEnd(3)), SSR.Stress(SSR.HardeningStartEnd(2):SSR.HardeningStartEnd(3)), 'm.','markersize', 10); % 2nd hardening slope data 92 | % end 93 | 94 | xlabel('Indentation Strain ','fontsize',13) 95 | ylabel ('Indentation Stress [GPa]','fontsize',13) 96 | 97 | % legend entries are not complete 98 | legend('Stress-Strain','Modulus Line', '0.2% Offset', 'Modulus Fit Data', 'Location', 'SOUTHEAST'); 99 | 100 | %% use for manual scaling 101 | mstrain = 0.05; 102 | % mstress = 3.0; 103 | 104 | xlim([0 mstrain + mstrain/20]) 105 | ylim([0 mstress + mstress/20]) 106 | 107 | %% 108 | 109 | Eexp = num2str(FR(index).E_sample); 110 | Euler1 = num2str(BEuler(1,:)); 111 | Euler2 = num2str(BEuler(2,:)); 112 | Euler3 = num2str(BEuler(3,:)); 113 | YS = num2str(SSR.Yield_Strength); 114 | H = num2str(SSR.Hardening(1)); 115 | tl=['Es=',Eexp,'; ','Bunge=',Euler1,', ',Euler2,', ',Euler3,'; ','Strength=',YS,'; ','Hardening=',H]; 116 | title(tl, 'fontsize',13) 117 | grid on; 118 | 119 | % rename variables for easy reference 120 | Load = TestData.Data(:,8); 121 | Displ = TestData.Data(:,7); 122 | S = TestData.Data(:,9); 123 | % note these may or may not be harmonic corrected depending on CSM variable, see LoadTest.m 124 | segment_start = FR(index).segment_start; 125 | segment_end = FR(index).segment_end; 126 | 127 | subplot(2,4,3) % load vs displ 128 | hold on 129 | 130 | plot(Displ, Load, 'b.'); 131 | plot(Displ(segment_start:segment_end), Load(segment_start:segment_end), 'g.'); 132 | legend('Raw Data','0 Pt. Data','Location','NorthWest'); 133 | xlabel('displacement / nm'); 134 | ylabel('load / mN'); 135 | title('Load Vs. Displacement'); 136 | hold off 137 | 138 | subplot(2,4,4) % Zero Point Fit 139 | Y = Load - 2/3.*S.*Displ; 140 | plot(S(1:segment_end+50), Y(1:segment_end+50),'b.'); 141 | hold on 142 | plot(S(segment_start:segment_end), Y(segment_start:segment_end),'g*'); 143 | legend('Raw Data','0 Pt. Data'); 144 | xlabel('S'); 145 | ylabel('P2/3-Sh'); 146 | title('Zero Point Fit') 147 | 148 | subplot(2,4,7) % Modulus Fit 149 | modulus_start = FR(index).modulus_start; 150 | P23 = (SSR.P_new).^(2/3); 151 | plot(P23(modulus_start:segment_end+50), SSR.h_new(modulus_start:segment_end+50), 'b.') 152 | hold on 153 | plot(P23(modulus_start:segment_end), SSR.h_new(modulus_start:segment_end), 'g.') 154 | xlabel('P 2/3') 155 | ylabel('h') 156 | legend('Data','Elastic','Location','NorthWest') 157 | title('Modulus Fit') 158 | hold off 159 | 160 | subplot(2,4,8) % contact radius vs. strain 161 | plot(SSR.Strain, SSR.contact_radius, 'b.') 162 | hold on 163 | plot(SSR.Strain(modulus_start:segment_end), SSR.contact_radius(modulus_start:segment_end), 'g.') 164 | xlim([0, mstrain + mstrain/20]); 165 | YL = ylim; 166 | ylim([0 YL(2)]); 167 | xlabel('Strain'); 168 | ylabel('Contact Radius / nm'); 169 | legend('Data','Elastic','Location','SouthEast'); 170 | title('Strain Vs. Contact Radius') 171 | grid on 172 | hold off 173 | 174 | end 175 | %% save the FitResult and stress-strain analysis 176 | function savess(a,b) % save the data for the analysis selcted with the cursor 177 | StressStrain = CalcStressStrainWithYield(TestData, FR(index), Plastic); 178 | sht.StressStrainResult = StressStrain; 179 | sht.FitResult = FR(index); 180 | sht.Index = index; 181 | assignin('base', 'Stress_Strain_Analysis', sht); 182 | end 183 | %% save all the stress-strain curves of SearchResults 184 | % 1 to 1 correpondence with SearchResults 185 | function savessall(a,b) 186 | for ii = 1:npoints(end); 187 | [StressStrainSearchResults(ii)] = CalcStressStrainWithYield(TestData, SearchResults(ii), Plastic); 188 | end 189 | sht = StressStrainSearchResults; 190 | assignin('base', 'Stress_Strain_Search_Results', sht); 191 | end 192 | %% 193 | dcm_obj = datacursormode(gcf); 194 | set(dcm_obj, 'enable', 'on'); 195 | set(dcm_obj,'UpdateFcn',@hitme); 196 | set(gcf, 'Position', SZ/1.3); 197 | 198 | w = findobj('Tag','figpanel'); 199 | pos = get(gcf,'Position'); 200 | set(w,'Position',[20 pos(4)-230 200 60]); 201 | 202 | uicontrol('Style', 'pushbutton',... 203 | 'String', 'Plot ISS Analysis',... 204 | 'Position', [20 240 160 30],... 205 | 'Callback', @plotss); 206 | 207 | uicontrol('Style', 'pushbutton',... 208 | 'String', 'Save FR & ISS',... 209 | 'Position', [20 140 160 30],... 210 | 'Callback', @savess); 211 | 212 | uicontrol('Style', 'pushbutton',... 213 | 'String', 'Save All ISS',... 214 | 'Position', [20 40 160 30],... 215 | 'Callback', @savessall); 216 | 217 | set(figure(1), 'Position', SZ); 218 | 219 | figure(2) % histograms 220 | % bins is number of bins for histograms 221 | [HistSearchResults] = MyHistSearch(SearchResults, bins); 222 | 223 | set(figure(2), 'Position', SZ); 224 | 225 | figure(3) % histograms with sliders 226 | subslider(HistSearchResults) 227 | 228 | set(figure(3), 'Position', SZ); 229 | 230 | 231 | end 232 | -------------------------------------------------------------------------------- /SingleSearchAllSegments.m: -------------------------------------------------------------------------------- 1 | function [FitResults] = SingleSearchAllSegments(seg_sizes, TestData, SearchSegEnd) 2 | AnalysisCell = cell(10,1); 3 | count = 1; 4 | SSegEnd = zeros(length(seg_sizes),1); 5 | for ii=1:length(seg_sizes) % iterate of all segment sizes 6 | SSegEnd(ii) = SearchSegEnd - seg_sizes(ii); 7 | for j=TestData.StiffnessSegmentStart:SSegEnd(ii) % iterate through the data 8 | start = j; 9 | stop = start + seg_sizes(ii); 10 | [analysis, success]= NIAnalyzeSearch(TestData, start, stop); 11 | if success==1 12 | AnalysisCell{count,1}=analysis; 13 | count = count + 1; 14 | end 15 | end 16 | end 17 | 18 | FitResults = AnalysisCell{1,1}; % sets FitResults to have the same structure as Analysis; 19 | 20 | for k = 1:size(AnalysisCell) 21 | FitResults(k)=AnalysisCell{k}; 22 | end 23 | 24 | end 25 | 26 | -------------------------------------------------------------------------------- /Spherical Nanoindentation Stress-Strain Analysis in MATLAB.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsweaver/Spin/9e26712f00a9933e7582a743f1e30567cbf2fadb/Spherical Nanoindentation Stress-Strain Analysis in MATLAB.pdf -------------------------------------------------------------------------------- /filterResults.m: -------------------------------------------------------------------------------- 1 | function [GoodR, numResults] = filterResults(FR, fil) 2 | 3 | % fil is a cell array with a string and number 4 | % number is always listed lower bound, upper bound 5 | % each row is a new fil 6 | % column 1 is the text, column 2 is the number or two number array 7 | 8 | % all the variables which can be used are calculated and saved in 9 | % FitResults 10 | 11 | % pay close attention to how the rqsuared and residuals are calculated in 12 | % rsquare.m and how rsquare.m is used. Sometimes the y-intercept is not 13 | % used in order to measure the goodness of fit forced through the origin. 14 | 15 | p = length(FR); 16 | [m, ~]= size(fil); 17 | numResults = zeros(m+1,1); 18 | numResults(1) = p; 19 | 20 | for ii = 1:m 21 | switch fil{ii,1} 22 | case 'R21' % r-squared of the zero point regression 23 | Fit1 = [FR.Fit1]; 24 | GoodR = FR([Fit1.Rsquared] >= fil{ii,2}(1) & [Fit1.Rsquared] <= fil{ii,2}(2)); 25 | case 'AAR1' % average absolute residual of zero point regression 26 | Fit1 = [FR.Fit1]; 27 | GoodR = FR([Fit1.AverageAbsoluteResidual] >= fil{ii,2}(1) & [Fit1.AverageAbsoluteResidual] <= fil{ii,2}(2)); 28 | case 'MAR1' % maximum absolute residual of zero point regression 29 | Fit1 = [FR.Fit1]; 30 | GoodR = FR([Fit1.MaxAbsoluteResidual] >= fil{ii,2}(1) & [Fit1.MaxAbsoluteResidual] <= fil{ii,2}(2)); 31 | case 'R22' % r-squared of the modulus regression 32 | Fit2 = [FR.Fit2]; 33 | GoodR =FR([Fit2.Rsquared] >= fil{ii,2}(1) & [Fit2.Rsquared] <= fil{ii,2}(2)); 34 | case 'AAR2' % average absolute residual of modulus regression 35 | Fit2 = [FR.Fit2]; 36 | GoodR = FR([Fit2.AverageAbsoluteResidual] >= fil{ii,2}(1) & [Fit2.AverageAbsoluteResidual] <= fil{ii,2}(2)); 37 | case 'MAR2' % maximum absolute residual of modulus regression 38 | Fit2 = [FR.Fit2]; 39 | GoodR = FR([Fit2.MaxAbsoluteResidual] >= fil{ii,2}(1) & [Fit2.MaxAbsoluteResidual] <= fil{ii,2}(2)); 40 | case 'Modulus' % effective modulus, E_eff or E^* 41 | GoodR = FR([FR.E_star] >= fil{ii,2}(1) & [FR.E_star] <= fil{ii,2}(2)); 42 | case 'R23' % r-squared of the elastic indentation stress-strain data 43 | Fit3 = [FR.Fit3]; 44 | GoodR = FR([Fit3.Rsquared] >= fil{ii,2}(1) & [Fit3.Rsquared] <= fil{ii,2}(2)); 45 | case 'Hr' % residual displacement, from the modulus regression 46 | Fit2 = [FR.Fit2]; 47 | GoodR = FR([Fit2.y_intercept] >= fil{ii,2}(1) & [Fit2.y_intercept] <= fil{ii,2}(2)); 48 | case 'ModLength' % number of data points for the modulus regression 49 | GoodR = FR([FR.modulus_length] >= fil{ii,2}(1) & [FR.modulus_length] <= fil{ii,2}(2)); 50 | % only needs a lower bound 51 | case 'h_change' % normalized displacement between the start of the zero-point segment and the first positive point after the zero-point correction is applied 52 | GoodR = FR([FR.h_change] >= fil{ii,2}(1) & [FR.h_change] <= fil{ii,2}(2)); 53 | case 'p_change' % normalized load between the start of the zero-point segment and the first positive point after the zero-point correction is applied 54 | GoodR = FR([FR.p_change] >= fil{ii,2}(1) & [FR.p_change] <= fil{ii,2}(2)); 55 | case 'dP' % load difference between the first modulus regression data point and the origin 56 | GoodR = FR([FR.dP] >= fil{ii,2}(1) & [FR.dP] <= fil{ii,2}(2)); 57 | case 'dH' % displacement difference between the first modulus regression data point and the origin 58 | GoodR = FR([FR.dH] >= fil{ii,2}(1) & [FR.dH] <= fil{ii,2}(2)); 59 | case 'AAR4' % average absolute residual between the modulus line and elastic stress-strain data 60 | Fit4 = [FR.Fit4]; 61 | GoodR = FR([Fit4.AverageAbsoluteResidual] >= fil{ii,2}(1) & [Fit4.AverageAbsoluteResidual] <= fil{ii,2}(2)); 62 | case 'MAR4' % average absolute residual between the modulus line and elastic stress-strain data 63 | Fit4 = [FR.Fit4]; 64 | GoodR = FR([Fit4.MaxAbsoluteResidual] >= fil{ii,2}(1) & [Fit4.MaxAbsoluteResidual] <= fil{ii,2}(2)); 65 | case 'h*' % zero-point displacement correction, from zero-point regression 66 | GoodR = FR([FR.h_star] >= fil{ii,2}(1) & [FR.h_star] <= fil{ii,2}(2)); 67 | case 'P*' % zero-point load correction, from zero-point regression 68 | GoodR = FR([FR.P_star] >= fil{ii,2}(1) & [FR.P_star] <= fil{ii,2}(2)); 69 | case 'ModStart' % position of first data point in the modulus regression 70 | GoodR = FR([FR.modulus_start] >= fil{ii,2}(1) & [FR.modulus_start] <= fil{ii,2}(2)); 71 | case 'SegStart' % position of first data point in the zero point regression 72 | GoodR = FR([FR.segment_start] >= fil{ii,2}(1) & [FR.segment_start] <= fil{ii,2}(2)); 73 | 74 | end 75 | numResults(ii+1) = length(GoodR); 76 | FR = GoodR; 77 | end 78 | end -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsweaver/Spin/9e26712f00a9933e7582a743f1e30567cbf2fadb/license.txt -------------------------------------------------------------------------------- /mypolyfit.m: -------------------------------------------------------------------------------- 1 | function [p,S,mu] = mypolyfit(x,y,n) 2 | %POLYFIT Fit polynomial to data. 3 | % P = POLYFIT(X,Y,N) finds the coefficients of a polynomial P(X) of 4 | % degree N that fits the data Y best in a least-squares sense. P is a 5 | % row vector of length N+1 containing the polynomial coefficients in 6 | % descending powers, P(1)*X^N + P(2)*X^(N-1) +...+ P(N)*X + P(N+1). 7 | % 8 | % [P,S] = POLYFIT(X,Y,N) returns the polynomial coefficients P and a 9 | % structure S for use with POLYVAL to obtain error estimates for 10 | % predictions. S contains fields for the triangular factor (R) from a QR 11 | % decomposition of the Vandermonde matrix of X, the degrees of freedom 12 | % (df), and the norm of the residuals (normr). If the data Y are random, 13 | % an estimate of the covariance matrix of P is (Rinv*Rinv')*normr^2/df, 14 | % where Rinv is the inverse of R. 15 | % 16 | % [P,S,MU] = POLYFIT(X,Y,N) finds the coefficients of a polynomial in 17 | % XHAT = (X-MU(1))/MU(2) where MU(1) = MEAN(X) and MU(2) = STD(X). This 18 | % centering and scaling transformation improves the numerical properties 19 | % of both the polynomial and the fitting algorithm. 20 | % 21 | % Warning messages result if N is >= length(X), if X has repeated, or 22 | % nearly repeated, points, or if X might need centering and scaling. 23 | % 24 | % Class support for inputs X,Y: 25 | % float: double, single 26 | % 27 | % See also POLY, POLYVAL, ROOTS, LSCOV. 28 | 29 | % Copyright 1984-2010 The MathWorks, Inc. 30 | % $Revision: 5.17.4.13 $ $Date: 2010/11/17 11:29:32 $ 31 | 32 | % The regression problem is formulated in matrix format as: 33 | % 34 | % y = V*p or 35 | % 36 | % 3 2 37 | % y = [x x x 1] [p3 38 | % p2 39 | % p1 40 | % p0] 41 | % 42 | % where the vector p contains the coefficients to be found. For a 43 | % 7th order polynomial, matrix V would be: 44 | % 45 | % V = [x.^7 x.^6 x.^5 x.^4 x.^3 x.^2 x ones(size(x))]; 46 | 47 | if ~isequal(size(x),size(y)) 48 | %error(message('MATLAB:polyfit:XYSizeMismatch')) 49 | error('MATLAB:polyfit:XYSizeMismatch','MATLAB:polyfit:XYSizeMismatch') 50 | end 51 | 52 | x = x(:); 53 | y = y(:); 54 | 55 | if nargout > 2 56 | mu = [mean(x); std(x)]; 57 | x = (x - mu(1))/mu(2); 58 | end 59 | 60 | % Construct Vandermonde matrix. 61 | V(:,n+1) = ones(length(x),1,class(x)); 62 | for j = n:-1:1 63 | V(:,j) = x.*V(:,j+1); 64 | end 65 | 66 | % Solve least squares problem. 67 | [Q,R] = qr(V,0); 68 | p = R\(Q'*y); % Same as p = V\y; 69 | if size(R,2) > size(R,1) 70 | %warning(message('MATLAB:polyfit:PolyNotUnique')) 71 | warning('MATLAB:polyfit:PolyNotUnique','MATLAB:polyfit:PolyNotUnique') 72 | % elseif warnIfLargeConditionNumber(R) 73 | % if nargout > 2 74 | % warning(message('MATLAB:polyfit:RepeatedPoints')); 75 | % else 76 | % warning('MATLAB:polyfit:RepeatedPointsOrRescale', ... 77 | % ['Polynomial is badly conditioned. Add points with distinct X\n' ... 78 | % ' values, reduce the degree of the polynomial, or try centering\n' ... 79 | % ' and scaling as described in HELP POLYFIT.']); 80 | % end 81 | end 82 | 83 | if nargout > 1 84 | r = y - V*p; 85 | % S is a structure containing three elements: the triangular factor from a 86 | % QR decomposition of the Vandermonde matrix, the degrees of freedom and 87 | % the norm of the residuals. 88 | S.R = R; 89 | S.df = max(0,length(y) - (n+1)); 90 | S.normr = norm(r); 91 | end 92 | 93 | p = p.'; % Polynomial coefficients are row vectors by convention. 94 | 95 | function flag = warnIfLargeConditionNumber(R) 96 | if isa(R, 'double') 97 | flag = (condest(R) > 1e+10); 98 | else 99 | flag = (condest(R) > 1e+05); 100 | end 101 | -------------------------------------------------------------------------------- /rsquare.m: -------------------------------------------------------------------------------- 1 | function [R2, AbsRes, NormRes, AvgAbsRes, MaxAbsRes] = rsquare(y,yhat) 2 | % PURPOSE: calculate r square using data y and estimates yhat 3 | % ------------------------------------------------------------------- 4 | % USAGE: R2 = rsquare(y,yhat) 5 | % where: 6 | % y are the original values as vector or 2D matrix and 7 | % yhat are the estimates calculated from y using a regression, given in 8 | % the same form (vector or raster) as y 9 | % ------------------------------------------------------------------------- 10 | % OUTPUTS: 11 | % R2 is the r square value calculated using 1-SS_E/SS_T 12 | % ------------------------------------------------------------------- 13 | % Note: NaNs in either y or yhat are deleted from both sets. 14 | % 15 | % Felix Hebeler, Geography Dept., University Zurich, Feb 2007 16 | 17 | if nargin ~= 2 18 | error('This function needs some exactly 2 input arguments!'); 19 | end 20 | 21 | % reshape if 2d matrix 22 | yhat=reshape(yhat,1,size(yhat,1)*size(yhat,2)); 23 | y=reshape(y,1,size(y,1)*size(y,2)); 24 | 25 | % delete NaNs 26 | while sum(isnan(y))~=0 || sum(isnan(yhat))~=0 27 | if sum(isnan(y)) >= sum(isnan(yhat)) 28 | yhat(isnan(y))=[]; 29 | y(isnan(y))=[]; 30 | else 31 | y(isnan(yhat))=[]; 32 | yhat(isnan(yhat))=[]; 33 | end 34 | end 35 | 36 | % 1 - SSe/SSt 37 | SSe = sum( (y-yhat).^2 ); 38 | SSt = sum( (y-mean(y)).^2 ); 39 | R2 = 1 - ( SSe / SSt ); 40 | 41 | AbsRes = sum(abs(y-yhat)); 42 | NormRes = sqrt(SSe); 43 | AvgAbsRes = AbsRes/length(y); 44 | MaxAbsRes = max(abs(y-yhat)); 45 | 46 | % R2 = sum((yhat-mean(y)).^2) / sum( (y-mean(y)).^2 ) ; 47 | 48 | %if R2<0 || R2>1 49 | % error(['R^2 of ',num2str(R2),' : yhat does not appear to be the estimate of y from a regression.']) 50 | %end -------------------------------------------------------------------------------- /smoothstrain.m: -------------------------------------------------------------------------------- 1 | function y = smoothstrain(mnpt, mxpt, S, jj) 2 | 3 | % function applies moving average to variable S over mnpt:maxpt 4 | % assuming S has data to the left and right of mnpt and mxpt which can be 5 | % used in the calculation of moving average 6 | 7 | % j is +/- window for average 8 | sumab = 0; 9 | for ii = 1:jj 10 | sa = S(mnpt+ii:mxpt+ii); % S(mnpt:mxpt) shifted by +ii 11 | sb = S(mnpt-ii:mxpt-ii); % S(mnpt:mxpt) shifted by -ii 12 | scount = sa + sb; % sum of S shifts 13 | sumab = scount + sumab; % sum up S shifts for each iteration 14 | end 15 | 16 | y = (S(mnpt:mxpt) + sumab)/ (2*jj+1); 17 | % avearge of S 18 | -------------------------------------------------------------------------------- /subslider.m: -------------------------------------------------------------------------------- 1 | function subslider(histy) 2 | clf 3 | % x and y are the histogram data: x is variable, y is the count/frequency 4 | 5 | % order and variables in x and y should match 6 | % order and variable labels in lbd should also match 7 | % labels must be exact text for filter called variables 8 | % any 16 variables or more I guess could go in this plot 9 | 10 | x = [... 11 | histy.Estar(2,:)',... %row 1 of plots 12 | histy.Fit1R2(2,:)',... 13 | histy.ModLength(2,:)',... 14 | histy.Fit2R2(2,:)',... 15 | histy.Fit1AAR(2,:)',... % row 2 of plots 16 | histy.Fit1MAR(2,:)',... 17 | histy.Fit2AAR(2,:)',... 18 | histy.Fit2MAR(2,:)',... 19 | histy.hchange(2,:)',... % row 3 of plots 20 | histy.Fit3R2(2,:)',... 21 | histy.Fit4AAR(2,:)',... 22 | histy.Fit4MAR(2,:)',... 23 | histy.P_star(2,:)',... % row 4 of plots 24 | histy.h_star(2,:)',... 25 | histy.dP(2,:)',... 26 | histy.dH(2,:)',... 27 | ]; 28 | xlb = {'Modulus';... 29 | 'R21';... 30 | 'ModLength';... 31 | 'R22';... 32 | 33 | 'AAR1';... 34 | 'MAR1';... 35 | 'AAR2';... 36 | 'MAR2';... 37 | 38 | 'h_change';... 39 | 'R23';... 40 | 'AAR4';... 41 | 'MAR4';... 42 | 43 | 'P*';... 44 | 'h*';... 45 | 'dP';... 46 | 'dH'}; 47 | 48 | y = [... 49 | histy.Estar(1,:)',... 50 | histy.Fit1R2(1,:)',... 51 | histy.ModLength(1,:)',... 52 | histy.Fit2R2(1,:)',... 53 | histy.Fit1AAR(1,:)',... 54 | histy.Fit1MAR(1,:)',... 55 | histy.Fit2AAR(1,:)',... 56 | histy.Fit2MAR(1,:)',... 57 | histy.hchange(1,:)',... 58 | histy.Fit3R2(1,:)',... 59 | histy.Fit4AAR(1,:)',... 60 | histy.Fit4MAR(1,:)',... 61 | histy.P_star(1,:)',... 62 | histy.h_star(1,:)',... 63 | histy.dP(1,:)',... 64 | histy.dH(1,:)',... 65 | ]; 66 | 67 | for ii = 1:16 % number of plots 68 | 69 | h(ii) = subplot(4,4,ii); 70 | plot(x(:,ii),y(:,ii),'b.-'); 71 | axis([min(x(:,ii)), max(x(:,ii)), min(y(:,ii)), max(y(:,ii))]) 72 | xlabel(xlb{ii}); 73 | offset = -0.01; 74 | wd = 0.01; 75 | 76 | % left slider, low 77 | hslide(ii) = uicontrol('style','slider'); 78 | set(hslide(ii),'units',get(h(ii),'units')) 79 | subpos = get(h(ii),'position'); 80 | % position left, bot, width, height 81 | slidepos = [subpos(1)+2.8*offset subpos(2)+offset wd subpos(4)-2*offset]; 82 | set(hslide(ii),'position',slidepos); 83 | set(hslide(ii),'Callback',@SliderCallback); 84 | set(hslide(ii),'Min',min(x(:,ii)),'Max',max(x(:,ii)),'Value',min(x(:,ii))); 85 | 86 | % right slider, high 87 | hslide(ii+16) = uicontrol('style','slider'); 88 | set(hslide(ii+16),'units',get(h(ii),'units')) 89 | subpos = get(h(ii),'position'); 90 | % position left, bot, width, height 91 | slidepos = [subpos(1)+subpos(3)-offset-wd subpos(2)+offset wd subpos(4)-2*offset]; 92 | set(hslide(ii+16),'position',slidepos); 93 | set(hslide(ii+16),'Callback',@SliderCallback); 94 | set(hslide(ii+16),'Min',min(x(:,ii)),'Max',max(x(:,ii)),'Value',max(x(:,ii))); 95 | end 96 | 97 | function SliderCallback(~,~) 98 | for ii = 1:16 99 | % update plots 100 | SliderValue_L = get(hslide(ii),'Value'); 101 | SliderValue_R = get(hslide(ii+16),'Value'); 102 | new_data = ( x(:,ii) >= SliderValue_L & x(:,ii) <= SliderValue_R ); 103 | newy_data = y(new_data,ii); 104 | newx_data = x(new_data,ii); 105 | subplot(4,4,ii) 106 | plot(newx_data, newy_data,'b.-'); 107 | axis([min(x(:,ii)), max(x(:,ii)), min(y(:,ii)), max(y(:,ii))]) 108 | xlabel(xlb{ii}); 109 | end 110 | end 111 | 112 | uicontrol('style','pushbutton','String',... 113 | 'Save New Filter',... 114 | 'Position', [20 40 160 30],... 115 | 'Callback',@UpdateFilter); 116 | 117 | function UpdateFilter(~,~) 118 | for ii = 1:16 119 | bw = abs(x(1,ii)-x(2,ii)); % histogram bin width 120 | tol = 0.05*bw; % tolerance 121 | newslider(ii) = get(hslide(ii),'Value') - (bw/2 + tol); 122 | newslider(ii+16) = get(hslide(ii+16),'Value') + (bw/2 + tol); 123 | end 124 | filt_n = [newslider(1:16); newslider(17:32)]; 125 | filt = [xlb, num2cell(filt_n,1)']; 126 | assignin('base', 'NewFilt', filt); 127 | end 128 | 129 | 130 | end 131 | 132 | --------------------------------------------------------------------------------