% Source script in matlab/octave language for the 3D peeling problem of the
% non-smooth post-it with frictionnal contact, as described
% in the article
% David Dureisseix, Paul Larousse, Anthony Gravouil, Jean Di Stasio, 
% An explicit dynamics framework suited to highly non-smooth interface behaviors, 
% to appear in Journal of Theoretical, Computational and Applied Mechanics, 2024
% https://hal.science/hal-04146784
% https://doi.org/10.46298/jtcam.11611
%
% It uses the matlabEF subroutines
% for reading the input FE data file (AVS UCD ASCII format) Peeling3Dv7.inp
% and for building the FE matrices K1 and M1.
% This open library is available at https://github.com/dureisse/matlabEF.git
% and https://hal.science/hal-04647638.
% Then, a dedicated solver is used for obtaining the solution.
%
% Working versions: matlab2024a update 4, or GNU Octave version 9.1.0
%
clear all
close all

% ============================================================================
function [rn,rt,rY,alpha,ddstatus] = resolvecv63( ...
          gap,rY,vnfree,vtfree,H,alpha, ...
          lambda,rYmax,nalpha,mu, ...
          weightedpastrn,weightedpastrt)

% non-localite en temps lors de la rupture

    % Comportement
    if (gap > 0)
        ddstatus = 'decolle';
        % Pas d'interaction
           % Check pour voir si on peut arriver la avec une rupture
           if (rY > 0)
               disp('ON ROMP EN DEPLACEMENT.... PAS PREVU')
               error('pb 1')
           end
        rY = 0;  % Eventuellement updater alpha ?? depend de pb1
        rn = 0;
        rt = 0*vtfree; % vtfree pour avoir la bonne taille
    else
        rntilde = - (1/H)*vnfree;
        rttilde = - (1/H)*vtfree;
        rntilde2 = rntilde + weightedpastrn;
        rttilde2 = rttilde + weightedpastrt;
        rr = seuil4_0(rntilde2,norm(rttilde2),lambda);
        rYtilde = max(rY,alpha*max(rntilde,0));
        rYtilde = min(rYtilde,rYmax);
        if (rr < rYtilde)
            ddstatus = 'colle';
            % Collage, qu'on soit avant collé ou pas
            rn = rntilde;
            rt = rttilde;
            rY = rYtilde;
        else
            % On passe en contact
            if (rY > 0) 
                % il y a rupture, alpha decroit
                alpha = alpha * exp(-1/nalpha);
            end
            rY = 0;
            rn = max(rntilde,0.); % solution du LCP, pas forcement 0
            if (rn > 0)
                % contact actif
                if (norm(rttilde) > mu*rn)
                    ddstatus = 'sliding';
                    % sliding, ici rttile != 0
                    rt = mu*rn*rttilde/norm(rttilde);
                else
                    ddstatus = 'sticking';
                    % sticking
                    rt = rttilde;
                end
            else
                ddstatus = 'release contact';
                % contact inactif
                rt = 0*vtfree; % vtfree pour avoir la bonne taille
            end

            % On decide ici de ne pas updater rY !
              % check
              rr = seuil4_0(rn,norm(rt),lambda);
              rrY = max(rY,alpha*max(rn,0));
              rrY = min(rrY,rYmax); 
              if (rr < rY)
                  error('pb4 pas forcement erreur')
              end

        end
    end

end
% ============================================================================
function [r] = seuil4_0(x,y,lambda)
% Inputs
%   x    (usuellement r_n)
%   y    (usuellement norm(r_t)) >=0
%  lambda   dans ]0,1] pour differencier n et t. si =1, traites pareils
% Output  
%   r    valeur du seuil d'impulsion

r = sqrt(min(x,0)^2 + (y/lambda)^2);

return
end
% ============================================================================

% Default parameters
path(path,'../matlabEF')
path(path,'../matlabEF2')
path(path,'../matlabUtils')
GlobalVar;
mode1 = 'TRID';
idim = 3;
LTRAC1 = false;

if (1 == 1)

% Pre-processing: read input data
% """""""""""""""""""""""""""""""
xcrit1 = 0.0001;
file1 = 'Peeling3Dv7.inp'; % input data from AVS UCD ASCII file
[xcoor1,ListMesh1,ListChpo1,ListnMesh1,ListChml1, ...
  ListCara1,error1] = ReadMergeAVS2(xcrit1,file1,3);
mail1 = ListMesh1{1}; % volumic mesh
surf1a = ListMesh1{2}; % interface 1 mesh
surf1b = ListMesh1{3}; % interface 2 mesh
  nsurf1a = ChangeMesh2(surf1a,'POI1'); % interface 1 cloud of points
  nsurf1b = ChangeMesh2(surf1b,'POI1'); % interface 2 cloud of points
% merging the two interfaces
surf1 = surf1a; surf1{1}.MAIL = [surf1a{1}.MAIL ; surf1b{1}.MAIL];
clear ListMesh1;
nmail1 = ListnMesh1{1}; % volume cloud of points
clear ListnMesh1;
chpo1 = ListChpo1{1}; % nodal input data field 
clear ListChpo1;
cham1 = ListChml1{1}; % element input data field
clear ListChml1;
clear ListCara1;

% Convert to matrices for the volume operators
% """""""""""""""""""
% Models
[modl1,intg1] = ModlIntg13(mail1,'ELASTIQUE','RIGIDITE',mode1,idim);
intgno2 = SegmentIntgNo(mail1,'nodes'); % nodal integration
[modl2,intg2] = ModlIntg13(mail1,'ELASTIQUE','MASSE',mode1,idim,intgno2);
% Materials: convert contant-per-element field to integration points
mater1 = ChmlToCham(cham1,mail1,intg1);
mater2 = ChmlToCham(cham1,mail1,intg2);
% Elementary stiffnes matrices
rigi1 = Rigi9(modl1,mater1,mail1,intg1,xcoor1,mode1);
% Elementary lumped mass matrices
mass2 = Mass7(modl2,mater2,mail1,intg2,xcoor1,mode1);
% Assembling
numer1 = nmail1{1}.MAIL'; % node ordering
  numer1a = nsurf1a{1}.MAIL';
  which1a = findoccur(numer1a,numer1); % mapping node numbers
  numer1b = nsurf1b{1}.MAIL';
  which1b = findoccur(numer1b,numer1); % mapping node numbers
[listDdlDual1,listDdlPrim1] = ListDdlModl2(modl1);
[listComDual1,listComPrim1] = ListCompModl3(modl1);                           
[mapddlDual1,mapddlPrim1] = MapddlRigi2(modl1,mail1, ...
                                        numer1,listDdlDual1, ...
                                        numer1,listDdlPrim1);
K1 = RigiToMatrix(rigi1,modl1,mail1, ...
                  numer1,mapddlDual1,listDdlDual1, ...
                  numer1,mapddlPrim1,listDdlPrim1);
[listDdlDual2,listDdlPrim2] = ListDdlModl2(modl2);
[listComDual2,listComPrim2] = ListCompModl3(modl2);                           
[mapddlDual2,mapddlPrim2] = MapddlRigi2(modl2,mail1, ...
                                        numer1,listDdlDual2, ...
                                        numer1,listDdlPrim2);
M1 = RigiToMatrix(mass2,modl2,mail1, ...
                  numer1,mapddlDual2,listDdlDual2, ...
                  numer1,mapddlPrim2,listDdlPrim2);
% vector of lumped mass coefficients
M1 = full(diag(M1));

% Find dofs with prescribed displacement
U1 = ChpoToVect3(chpo1,nmail1,numer1,mapddlPrim1,listDdlPrim1);
ddlud = find(U1 > 0.5);

% prescribed forces
F2 = ChpoToVect3(chpo1,nmail1,numer1,mapddlDual1,[{'FX'} {'FY'} {'FZ'}]);
% normal vector coordinates
NO1 = ChpoToVect3(chpo1,nmail1,numer1,mapddlPrim1,[{'NX'} {'NY'} {'NZ'}]);
% potential contact nodes (total interface, and for each interface)
NOC1 = ChpoToVect3(chpo1,nmail1,numer1,numer1',[{'SCAL'}]); % integration weight values
nocon1 = find(NOC1 > 1e-6); % non-zero weight means potential contact node
ws1 = NOC1(nocon1); % integration interface nodal weight 
  NOC1a = 0.*NOC1; NOC1a(which1a) = NOC1(which1a);
  NOC1b = 0.*NOC1; NOC1b(which1b) = NOC1(which1b);
  nocon1a = find(NOC1a > 1e-6);
  which2a = findoccur(nocon1a,nocon1); % mapping interface 1 to total interface
  nocon1b = find(NOC1b > 1e-6);
  which2b = findoccur(nocon1b,nocon1); % mapping interface 2 to total interface

% Local basis at potenrial contact nodes n1,t1,t2
[n1 t1 t2] = BaseLocaleC(NO1,mapddlPrim1,nocon1);

%    save Peeling3Dv7prepro.mat nocon1 n1 t1 t2 ws1 ...
%         mapddlPrim1 F2 ddlud K1 M1 which2a which2b;
%    keyboard
else
    load Peeling3Dv7prepro.mat;
end

% Processing stage
% """"""""""""""""
% As entries are required:
%   nocon1(nbnc) = list of node numbers for the potential contact nodes
%   n1(nbnc,3) = coordinates of the unitary normal to the contact for each node
%   t1(nbnc,3) = coordinates of the first unitary tangent to the contact for each node
%   t2(nbnc,3) = coordinates of the second unitary tangent to the contact for each node
%   ws1(nbnc) = interface nodal integration weight
%   mapddlPrim1(nddl/3,3) = for each component (UX,UY,UZ) of each node, its number in the list of dofs
%   F2(nddl,1) = external general nodal forces (unitary resultant)
%   ddlud(nddld) = list of prescribed dofs by Dirichlet boundary condition
%   K1(nddl,nddl) = stiffness matrix
%   M1(nddl) = lumped mass matrix coefficient list

nbnc = length(nocon1); % number of potential contact nodes
nddl = length(M1); % number of total dofs (with boundary conditioned ones)

% Precompute the inverse of the lumped mass matrix coefficient list
M1inv = M1 .^ -1;

T1 = 16.; % total time studied
% Time grid with discretization c = 4
h = T1/32000; % time step size
% Time grid with discretization c = 3
h = T1/24000; % time step size
% Time grid with discretization c = 1
h = T1/8000; % time step size
lt1 = [0:h:T1]; % time steps
nt1 = length(lt1);

% Loading amplitude evolution
Fd = 0.0018;
FEXT1 = 0.5*Fd*(1. - cos(pi*lt1/T1));
FEXT2 = 0*lt1;
FEXT2(1:ceil(3*nt1/4)-1) = FEXT1(1:ceil(3*nt1/4)-1);
FEXT2(ceil(3*nt1/4):end) = max(FEXT2);
    if LTRAC1
    figure(1)
    p = plot(lt1,FEXT2,'k-');
    p(1).LineWidth = 2;
    ax = gca;
    ax.XAxis.FontSize = 12;
    ax.YAxis.FontSize = 12;
    xlabel('time','FontSize',12)
    ylabel('external force repartition','FontSize',12)
    end

% Interface constitutive law parameters
nalpha = inf;   % no efficiency decrease 
RY0    = 1.e-4; % initial strength
RYmax  = RY0;   % maximum value for cicatrisation
lambda = 0.8;   % influence of tangential component
tauY   = 0.2;   % time non-locality
mu     = 0.1;   % friction coefficient

% Precompute Delassus operator
% (since spherical per contact node, store only the scalar value in H0)
H0 = zeros(nbnc,1);
% Loop on contact nodes
for ic = 1:nbnc
    ddlnoc = mapddlPrim1(nocon1(ic),:);
    n1t = n1(ic,:); % normal vector
    t1t = t1(ic,:); % tangential vector 1
    t2t = t2(ic,:); % tangential vector 2
    base1t = [n1t ; t1t ; t2t]; % local basis
    H = base1t * diag(M1inv(ddlnoc)) * base1t';
    H0(ic) = H(1,1);
end

% Time non-locality weights weightt(nbtack)
ntback = ceil(tauY/h); % upper integer = number of past time increments
if (ntback <= 1)
    % tauY <= h
    disp('Warning: too coarse discretization or no time non-locality')
    weightt = [1]; ntback = 1;
else
    weightt = ones(1,ntback-1);
    weightt = [weightt (tauY-h*(ntback-1))/h];
end
% To store past history
pastrn = zeros(1,ntback-1); % for normal impulse
pastrt = zeros(2,ntback-1); % for tangential impulse

% To store intermediate results
  lCrack1 = zeros(1,nt1); % total cracked surface ratio
  lCrack1a = zeros(1,nt1); % cracked surface 1 ratio
  lCrack1b = zeros(1,nt1); % cracked surface 2 ratio

disp('Resolution')
% Initial conditions
i = 1;
    U = zeros(nddl,1);
    U(ddlud,1) = 0;
    V = zeros(nddl,1);
    V(ddlud,1) = 0;
    alpha = ones(1,nbnc); % starting with maximal cicatrisation efficiency
    RY = RY0*ones(1,nbnc); % initial frature strength
    g0 = -1e-8; % initial gap (small negative to avoid grazing contact status)
    W1 = 0.;

      lCrack1(i) = sum(ws1(find(RY < 1e-6))) / sum(ws1);
      lCrack1a(i) = sum(ws1(find(RY(which2a) < 1e-6))) / sum(ws1(which2a));
      lCrack1b(i) = sum(ws1(find(RY(which2b) < 1e-6))) / sum(ws1(which2b));

% Loop on time steps
for i = 2:length(lt1)-1
%    disp([' time step ' int2str(i)])

%   Explicit configuration
    U = U + h*V;

    FF = FEXT2(i)*F2 - K1*U;
    Vfree = V + h*M1inv .* FF;
    R = zeros(nddl,1);

    % Loop on contact/interfacial nodes
    for ic = 1:nbnc

        ddlnoc = mapddlPrim1(nocon1(ic),:); % correponding dofs
        n1t = n1(ic,:);
        t1t = t1(ic,:);
        t2t = t2(ic,:);
        tt = [t1t ; t2t];
        Hc = H0(ic)*ws1(ic);

        % Local treatment of the node
        gapc = g0 + n1t * U(ddlnoc,1);
        RYc = RY(ic);
        vfreec = Vfree(ddlnoc,:);
        vnfreec = n1t*vfreec;
        vtfreec = tt*vfreec;
        alphac = alpha(ic);

        weightedpastrn = weightt(2:end) * pastrn';
        weightedpastrt = weightt(2:end) * pastrt';
        weightedpastrt = weightedpastrt';
        [rnc,rtc,RYc,alphac,ddstatus] = resolvecv63( ...
            gapc,RYc,vnfreec,vtfreec,Hc,alphac, ...
            lambda,RYmax,nalpha,mu, ...
            weightedpastrn,weightedpastrt);
        if (ntback > 1)
            pastrn = [rnc pastrn(:,1:end-1)]; % on stocke l'histoire actuelle
            pastrt = [rtc pastrt(:,1:end-1)]; % on stocke l'histoire actuelle
        end
        alpha(ic) = alphac;
        RY(ic) = RYc;
        R(ddlnoc,1) = R(ddlnoc,1) + ws1(ic)*(n1t'*rnc + tt'*rtc);
    end

%   post-process the dynamics
    V = Vfree + M1inv .* R;
    V(ddlud,:) = 0;

      lCrack1(i) = sum(ws1(find(RY < 1e-6))) / sum(ws1);
      lCrack1a(i) = sum(ws1(find(RY(which2a) < 1e-6))) / sum(ws1(which2a));
      lCrack1b(i) = sum(ws1(find(RY(which2b) < 1e-6))) / sum(ws1(which2b));

end

    if LTRAC1
    figure(2)
    p = plot(lt1(1:end-1),lCrack1(1:end-1),'-k' , ...
             lt1(1:end-1),lCrack1a(1:end-1),'--k', ...
             lt1(1:end-1),lCrack1b(1:end-1),'-.k');
    legend('total interface','interface 1','interface 2', ...
           'Location','southeast','Fontsize',6)
    p(1).LineWidth = 2;
    p(2).LineWidth = 2;
    p(3).LineWidth = 2;
    ax = gca;
    ax.XAxis.FontSize = 12;
    ax.YAxis.FontSize = 12;
    xlabel('time','FontSize',12)
    ylabel('fraction of cracked surface','FontSize',12)
    end

