%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Steven Huth, Sandra Sindt, 2018 % % the program asks for a folder that contains textfiles with data % all the distance and force data are read from each textfile % % the hertz model is fit to the data using a lsqcurvefit to get a first estimation of % the contact point, a region from shortly before the contact point until % the end of the data array is further examined % % the hertz model is fitted repeatedly to the data, starting with a small part % around the contact point and increasing the uper fitting with each fit % % the young's modulus of each part of the curve is calculated and computed % vs the fit range (which is equivalent to the indentation depth + contact point) % a stdfilt is applied to this E-d curve in order to find the first plateau % (where E values are constant) % % from this plateau region, the E value that was the result of the fit with % lowest squared sum of residuals (chi squared) was chosen % % results are written into the chosen folder into a new folder close all clear all %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % input parameters to be scpecified by user % format of the textfiles to be read num_col=10; %number of columns format=[]; for i=1:num_col format=[format,'%s']; end d_num=1; % number of the column containing the position of the indenter in meter F_num=2; % number of the column containing Force data in newton header=79; %number of lines in the header del=' '; % delimiter between columns '\t' for tab model_func=inline('p(1)*((d-p(2)).*(d>p(2))).^1.5', 'p', 'd'); % elastic model opt=optimset('Display','off','TolFun',1E-20,'TolX',1e-20); % fitting optionen R=10.91E-6; %indenter radius in m poisson=0.5; %poisson ratio E_exp=0; % if a young's modulus is expected, it can be given as a start value for the fitting process startval(1)=4*sqrt(R)*E_exp/(3*(1-poisson^2)); startval(2)=0; %start value for the contact point thresh_std=5; % threshhold of the stdfilt (in %) points_std=5; % amount of points considered for the stdfilt %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% path = uigetdir('select folder with force curve data'); folder = dir([path '\*.txt']); files = {folder.name}; if length(files)>1 % creates all folders and writes user input ino 'parameters.txt' [~,filename,~]=fileparts(files{1}); datestring=datestr(datetime,'yyyy-mm-dd_hh.MM'); results_path=[path '\hertz_results_' datestring '\']; mkdir([results_path 'saturated\E_vs_d']) mkdir([results_path 'saturated\force_curves']) mkdir([results_path 'saturated\Chi']) mkdir([results_path 'not_saturated\E_vs_d']) mkdir([results_path 'not_saturated\Chi']) mkdir([results_path 'not_saturated\force_curves']) fid=fopen([results_path 'parameters.txt'],'w'); fprintf(fid, '%s\t%g\n', 'Indenter Radius', R); fprintf(fid, '%s\t%f\n', 'Poisson Value', poisson); fprintf(fid, '%s\t%g\t%g\n', 'Start Values',startval); fprintf(fid, '%s\t%f\n', 'thresh_std', thresh_std); fprintf(fid, '%s\t%f\n', 'points_std', points_std); fclose(fid); fid2=fopen([results_path 'results.txt'],'w'); fprintf(fid2,'%s\t%s\t%s\t%s\t%s\t%s\t%s\n', 'Youngs Modulus [kPa]', 'Chi', 'Contact Point [µm]', 'Indentation Depth [µm]', 'Saturated', 'Position', 'File'); sat_count=[0 0]; for k=1:length(files) [~,filename,~]=fileparts(files{k}); fid=fopen([path '\' files{k}],'r'); data=textscan(fid, format,'headerlines', header, 'delimiter',del); fclose(fid); dist=str2double(data{d_num}); d=flipud(dist); d=d-d(1); %d now contains the indenter distance (relative to the first data point) in m % F=str2double(data{F_num}); % F contains Force data in N %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% [P_fit, ~,~]=lsqcurvefit(model_func, startval, d, F, [], [], opt); contact_index=find(d>=P_fit(2),1); start=contact_index-round((length(d)-contact_index)/2); if start<=0 start=11; end start_ind=start-10; d(1:start_ind)=[]; F(1:start_ind)=[]; % one fit is performed and d index that belongs to the resulting contact point % (P_fit(2)) is found. The beginning of the data set up to shortly before the contact % point is dropped num_of_steps=30; steps=round(length(d)/num_of_steps); if steps < 1 steps = 1; end % num_of_steps gives the amount of fits and resulting data points in the E-d-curve. % higher numbers give finer results, but take more computation % time count=0; chi_kurz=[]; chi=[]; E=[]; contact=[]; ind_depth=[]; for i = 1:steps:length(d) count=count+1; [P_fit, ~,~]=lsqcurvefit(model_func, startval, d(1:i), F(1:i), [], [], opt); chi(count)=sum((F-model_func(P_fit,d)).^2)/(length(d)-2); %squared sum of residuals for the entire curve (not only up to fit range) E(count)=3*1E-3*(1-poisson^2)*P_fit(1)/(4*sqrt(R)); % Young's modulus in kPa contact(count)=P_fit(2); % contact point in m ind_depth(count)=d(i); % indentation depth end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% local_std = stdfilt(E, ones(points_std, 1)'); plateau_index = find(local_std/max(local_std) < thresh_std/100); % the 5% are a threshold, which can be adjusted maxfilt=find(local_std==max(local_std),1); plateau_index(plateau_index<=maxfilt)=[]; plateau_index(find(diff(plateau_index)>1,1)+1:end)=[]; %plateaus in the E data are found; only the first consecutive %array of E-data in a plateau after the maximum of the E-d %curve (after contact point) is taken into consideration if ~isempty(plateau_index) sat_count(1)=sat_count(1)+1; best_index=find(min(chi))+plateau_index(1)-1; saturation=1; sat_string='saturated\'; else sat_count(2)=sat_count(2)+1; best_index=size(E,2); saturation=0; sat_string='not_saturated\'; end E_best=E(best_index); chi_best=chi(best_index); contact_best=contact(best_index); ind_depth=(ind_depth-contact_best)*1E6; fprintf(fid2,'%f\t%g\t%g\t%g\t%i\t%i\t%s\n', E_best, chi_best, contact_best, ind_depth(best_index), saturation, str2double(filename(end-3:end-1)), filename); %best index is the index where chi is minimal in the plateau %region. if there is no plateau detected, the last data point %is selected %the last 4 numbers of the filename are taken tio be a position index %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% figure('visible','off') set(gcf,'CreateFcn','set(gcf,''Visible'',''on'')'); plot(ind_depth,E, 'bo') hold on plot(ind_depth(plateau_index),E(plateau_index),'ro'); if ~isempty(plateau_index) plot(ind_depth(best_index),E_best, 'gx'); end title(filename); ylabel('Youngs Modulus [kPa]') xlabel('Indentation Depth [µm]') saveas(gcf,[results_path sat_string 'E_vs_d\' filename '.tif']); savefig([results_path sat_string 'E_vs_d\' filename '.fig']); hold off i=((best_index-1)*steps)+1; [P_fit, ~,~]=lsqcurvefit(model_func, startval, d(1:i), F(1:i), [], [], opt); f=figure('visible','off'); set(gcf,'CreateFcn','set(gcf,''Visible'',''on'')'); plot(d,F,'x', d,model_func(P_fit,d)) %force curve with hertz plot y=get(gca,'ylim'); hold on line([d(i) d(i)],y); % fit range plot(P_fit(2),F(find(d>=P_fit(2),1)),'ro') % contact point xlabel('Distance [m]') ylabel('Force [N]') title([num2str(E_best) ',' num2str(chi_best)]) saveas(f,[results_path sat_string 'force_curves\' filename '.tif']); savefig([results_path sat_string 'force_curves\' filename '.fig']); % E-d-curve with marked plateau and best value as well as force curve % with best fit range and contact point are plotted and saved f=figure('visible','off'); set(gcf,'CreateFcn','set(gcf,''Visible'',''on'')'); depth=d-contact_best; plot(depth,F,'x', depth,model_func(P_fit,d)) %force curve with hertz plot y=get(gca,'ylim'); hold on line([depth(i) depth(i)],y); % fit range plot(P_fit(2)-contact_best,F(find(d>=P_fit(2),1)),'ro') % contact point xlabel('Indentation Depth [m]') ylabel('Force [N]') title([num2str(E_best) ',' num2str(chi_best)]) saveas(f,[results_path sat_string filename '.tif']); savefig([results_path sat_string filename '.fig']); % E-d-curve with marked plateau and best value as well as force curve % with best fit range and contact point are plotted and saved end fclose(fid2); fid=fopen([results_path 'number_of_saturated_curves.txt'],'w'); fprintf(fid, '%s\t%s\n', 'saturated', 'not saturated'); fprintf(fid, '%i\t%i\n', sat_count(1), sat_count(2)); fclose(fid); end