Anl_Nonlinear Class

This MATLAB class implements the solution of a nonlinear incremental- iterative analysis. It is designed to handle nonlinear structural analysis using various solution methods, including load control, displacement control, and arc-length methods.

The code was adapted from the Anl_Nonlinear class from NUMA-TF (https://gitlab.com/rafaelrangel/numa-tf, Accessed on February 1st, 2023) In the reference code, the stiffness matrix and the internal force vector are computed through different methods. Here both are computed using the same function. Another change that was done was related to the input variable to those methods, now the increment of the displacement vector associated to the iteration is used as an input. To consider the possibility of material nonlinearity, it was added a method to update the state variables after the convergence of the iterative process. It was also added the displacement control method.

Contents

Methods

Author

Danilo Cavalcanti

Version History

Version 1.00.

Class definition

classdef Anl_Nonlinear < Anl

Public properties

    properties (SetAccess = public, GetAccess = public)
        method     = 0;   % Flag for solution method
        adjustStep = 0;   % Flag for type of increment size adjustment
        increment  = 0;   % Initial increment of load ratio
        max_lratio = 0;   % Limit value of load ratio
        max_step   = 0;   % Maximum number of steps
        max_iter   = 0;   % Maximum number of iterations in each step
        trg_iter   = 0;   % Desired number of iterations in each step
        tol        = 0;   % Numerical tolerance for convergence
        ctrlNode   = 0;   % Control node (for displacement control method)
        ctrlDof    = 0;   % Control dof (for displacement control method)
        incrSign   = 0;   % Sign of the increment of displacement
        plotNd     = 1;   % Node that will be plotted the dof
        plotDof    = 1;   % DOF (ux,uy) that will plotted
        Uplot      = [];  % Matrix of nodal displacement vectors of all steps/modes
        lbdplot    = [];  % Vector of load ratios of all steps
    end

Constructor method

    methods
        %------------------------------------------------------------------
        function anl = Anl_Nonlinear(method,adjustStep,increment,max_lratio,max_step,max_iter,trg_iter,tol)
            anl = anl@Anl('Nonlinear');

            % Default analysis configuration
            if nargin == 1
                anl.method     = 'LoadControl';
                anl.adjustStep = false;
                anl.increment  = 0.1;
                anl.max_lratio = 0.5;
                anl.max_step   = 40;
                anl.max_iter   = 10;
                anl.trg_iter   = 3;
                anl.tol        = 0.00001;

            % Given analysis configuration
            else
                anl.method     = method;
                anl.adjustStep = adjustStep;
                anl.increment  = increment;
                anl.max_lratio = max_lratio;
                anl.max_step   = max_step;
                anl.max_iter   = max_iter;
                anl.trg_iter   = trg_iter;
                anl.tol        = tol;
            end
        end
    end

Public methods

    methods
        %------------------------------------------------------------------
        % Executes the nonlinear analysis process, handles iterative
        % steps, convergence checks, and state variables updates. It also
        % plots the load-displacement curve upon completion.
        function run(this,mdl)
            % Initialize model object
            mdl.preComputations();

            disp("*** Performing nonlinear analysis...")

            % Initialize results
            this.lbdplot = zeros(this.max_step+1,1);
            this.Uplot   = zeros(this.max_step+1,1);

            % Initialize data for first step
            step = 0;  % step number
            lbd  = 0;  % total load ratio (lambda)
            sign = 1;  % sign of predicted increment of load ratio

            % Initialize vector of total nodal displacements
            U = mdl.U;

            % Initialize vector of total increment displacement
            D_U = zeros(mdl.ndof,1);

            % Start incremental process
            while (step < this.max_step)
                step = step + 1;

                % Tangent stiffness matrix
                [K,~,~,Fref] = mdl.globalMatrices(U);

                % Tangent increment of displacements for predicted solution
                d_Up0 = this.solveSystem(mdl,K,Fref,U);

                if (step == 1)
                    % Initial increment of load ratio for predicted solution
                    if strcmp(this.method,'DisplacementControl')
                        d_lbd0 = this.predictedIncrement(mdl,sign,1,1,0.0,0.0,D_U,d_Up0,Fref);
                    else
                        d_lbd0 = this.increment;
                    end

                    % Set previous tangent increment of displacements as current increment
                    d_Up0_old = d_Up0;

                    % Store squared value of the norm of tangent increment of displacements
                    n2 = d_Up0(mdl.doffree)' * d_Up0(mdl.doffree);
                else
                    % Generalized Stiffness Parameter
                    GSP = n2 / (d_Up0(mdl.doffree)' * d_Up0_old(mdl.doffree));

                    % Adjust increment sign
                    if (GSP < 0)
                        sign = -sign;
                    end

                    % Adjustment factor of increment size
                    if (this.adjustStep == true)
                        J = sqrt(this.trg_iter/iter);
                    else
                        J = 1;
                    end

                    % Predicted increment of load ratio
                    d_lbd0 = this.predictedIncrement(mdl,sign,J,GSP,D_lbd,d_lbd0,D_U,d_Up0,Fref);
                end

                % Limit increment of load ratio to make total load ratio smaller than maximum value
                if ((this.max_lratio > 0.0 && lbd + d_lbd0 > this.max_lratio) ||...
                    (this.max_lratio < 0.0 && lbd + d_lbd0 < this.max_lratio))
                    d_lbd0 = this.max_lratio - lbd;
                end

                % Increments of load ratio and displacements for predicted solution
                d_lbd = d_lbd0;
                d_U0  = d_lbd0 * d_Up0;
                d_U   = d_U0;

                % Initialize incremental values of load ratio and displacements for current step
                D_lbd = d_lbd;
                D_U   = d_U;

                % Update total values of load ratio and displacements
                lbd = lbd + d_lbd;
                U   = U   + d_U;

                % Start iterative process
                iter = 1;
                conv = 0;

                while (conv == 0 && iter <= this.max_iter)
                    % Vector of external and internal forces
                    Fext = lbd * Fref;
                    [K,~,Fint] = mdl.globalMatrices(U);

                    % Vector of unbalanced forces
                    R = Fext - Fint;

                    % Check convergence
                    unbNorm = norm(R(mdl.doffree));
                    forNorm = norm(Fref(mdl.doffree));
                    conv = (unbNorm == 0 || forNorm == 0 || unbNorm/forNorm < this.tol);
                    if conv == 1
                        break;
                    end

                    % Start/keep corrector phase
                    iter = iter + 1;

                    % Tangent and residual increments of displacements
                    d_Up = this.solveSystem(mdl,K,Fref);
                    d_Ur = this.solveSystem(mdl,K,R);

                    % Corrected increment of load ratio
                    d_lbd = this.correctedIncrement(mdl,d_lbd0,D_lbd,d_Up0_old,d_U0,d_Up,d_Ur,D_U,Fref,R);
                    if (~isreal(d_lbd))
                        conv = -1;
                        break;
                    end

                    % Corrected increment of displacements
                    d_U = d_lbd * d_Up + d_Ur;

                    % Increments of load ratio and displacements for current step
                    D_lbd = D_lbd + d_lbd;
                    D_U   = D_U   + d_U;

                    % Total values of load ratio and displacements
                    lbd = lbd + d_lbd;
                    U   = U   + d_U;
                end

                % Check for convergence fail or complex value of increment
                if (conv == 0)
                    disp('Convergence not achieved!');
                    this.plotCurves();
                    return;
                elseif (conv == -1)
                    disp('Unable to compute load increment!');
                    return;
                end
                fprintf('Step: %d | Iter: %d | ratio: %.2f\n',step,iter,lbd);

                % Update state variables
                mdl.updateStateVar();

                % Store step results
                this.lbdplot(step+1) = lbd;
                this.Uplot(step+1) = U(mdl.ID(this.plotNd,this.plotDof));

                % Store predicted tangent increment of displacements for next step
                if (step ~= 1)
                    d_Up0_old = d_Up0;
                end

                % Check if maximum load ratio was reached
                if ((this.max_lratio >= 0 && lbd >= 0.999*this.max_lratio) ||...
                    (this.max_lratio <= 0 && lbd <= 0.999*this.max_lratio))
                    break;
                end
            end

            % Clean unused steps
            if (step < this.max_step)
                this.lbdplot = this.lbdplot(1:step+1);
                this.Uplot = this.Uplot(1:step+1);
            end

            this.plotCurves();
            mdl.U = U;

            disp("*** Analysis completed!");
        end

        %------------------------------------------------------------------
        % Partition and solve a linear system of equations.
        %  f --> free d.o.f. (natural B.C. - unknown)
        %  c --> constrained d.o.f. (essential B.C. - known)
        %
        % [ Kff Kfs ] * [ Uf ] = [ Fext ]
        % [ Ksf Kss ]   [ Us ] = [   R  ]
        %
        function [U,Fext] = solveSystem(~,mdl,K,Fext,U)
            if nargin < 5
                U = zeros(mdl.ndof,1);
            end

            % Partition system of equations
            Kff = K(mdl.doffree,mdl.doffree);
            Ff  = Fext(mdl.doffree);

            % Solve system of equilibrium equations
            Uf = Kff \ Ff;

            % Displacement vector
            U(mdl.doffree) = Uf;
        end

        %------------------------------------------------------------------
        % Compute inrement of load ratio for the predicted solution
        % (first iteration)
        function d_lbd0 = predictedIncrement(this,mdl,sign,J,GSP,D_lbd,d_lbd0,D_U,d_Up0,Pref)
            % Extract free d.o.f. components
            Pref  = Pref(mdl.doffree);
            D_U   = D_U(mdl.doffree);
            d_Up0 = d_Up0(mdl.doffree);

            % LCM: Load Increment
            if strcmp(this.method,'LoadControl')
                d_lbd0 = J * abs(d_lbd0);

            % DCM: Displacement Increment
            elseif strcmp(this.method,'DisplacementControl')
                d_lbd0 = J * sign * this.increment / d_Up0(this.ctrlDof);
                return  % Sign change must not be applied

            % WCM: Work Increment
            elseif strcmp(this.method,'WorkControl')
                d_lbd0 = J * sqrt(abs((D_lbd*Pref'*D_U)/(Pref'*d_Up0)));

            % ALCM_FNP: Cylindrical Arc-Length Increment
            elseif strcmp(this.method,'ArcLengthFNPControl')
                d_lbd0 = J * sqrt((D_U'*D_U)/(d_Up0'*d_Up0));

            % ALCM_UNP: Cylindrical Arc-Length Increment
            elseif strcmp(this.method,'ArcLengthUNPControl')
                d_lbd0 = J * sqrt((D_U'*D_U)/(d_Up0'*d_Up0));

            % ALCM_CYL: Cylindrical Arc-Length Increment
            elseif strcmp(this.method,'ArcLengthCylControl')
                d_lbd0 = J * sqrt((D_U'*D_U)/(d_Up0'*d_Up0));

            % ALCM_SPH: Spherical Arc-Length Increment
            elseif strcmp(this.method,'ArcLengthSPHControl')
                d_lbd0 = J * sqrt((D_U'*D_U + D_lbd^2*(Pref'*Pref)) / (d_Up0'*d_Up0 + Pref'*Pref));

            % MNCM: Cylindrical Arc-Length Increment
            elseif strcmp(this.method,'MinimumNorm')
                d_lbd0 = J * sqrt((D_U'*D_U)/(d_Up0'*d_Up0));

            % ORCM: Cylindrical Arc-Length Increment
            elseif strcmp(this.method,'OrthogonalResidual')
                d_lbd0 = J * sqrt((D_U'*D_U)/(d_Up0'*d_Up0));

            % GDCM: GSP criteria
            elseif strcmp(this.method,'GeneralizedDisplacement')
                d_lbd0 = J * sqrt(abs(GSP)) * this.increment;
            end

            % Apply increment sign
            d_lbd0 = sign * d_lbd0;
        end

        %--------------------------------------------------------------------------
        % Compute inrement of load ratio for the corrected solutions
        % (iterations to correct predicted solution).
        function d_lbd = correctedIncrement(this,mdl,d_lbd0,D_lbd,d_Up0,d_U0,d_Up,d_Ur,D_U,Pref,R)
            % Extract free d.o.f. components
            d_Up0 = d_Up0(mdl.doffree);
            d_U0  = d_U0(mdl.doffree);
            d_Up  = d_Up(mdl.doffree);
            d_Ur  = d_Ur(mdl.doffree);
            D_U   = D_U(mdl.doffree);
            Pref  = Pref(mdl.doffree);
            R     = R(mdl.doffree);

            % LCM
            if strcmp(this.method,'LoadControl')
                d_lbd = 0;

            % DCM
            elseif strcmp(this.method,'DisplacementControl')
                d_lbd = -d_Ur(this.ctrlDof)/d_Up(this.ctrlDof);

            % WCM
            elseif strcmp(this.method,'WorkControl')
                d_lbd = -(Pref'*d_Ur)/(Pref'*d_Up);

            % ALCM_FNP
            elseif strcmp(this.method,'ArcLengthFNPControl')
                d_lbd = -(d_Ur'*d_U0)/(d_Up'*d_U0 + d_lbd0*(Pref'*Pref));

            % ALCM_UNP
            elseif strcmp(this.method,'ArcLengthUNPControl')
                d_lbd = -(d_Ur'*D_U)/(d_Up'*D_U + D_lbd*(Pref'*Pref));

            % ALCM_CYL
            elseif strcmp(this.method,'ArcLengthCylControl')
                a = d_Up'*d_Up;
                b = d_Up'*(d_Ur + D_U);
                c = d_Ur'*(d_Ur + 2*D_U);
                s = sign(D_U'*d_Up);
                d_lbd = -b/a + s*sqrt((b/a)^2 - c/a);

            % ALCM_SPH
            elseif strcmp(this.method,'ArcLengthSPHControl')
                a = d_Up'*d_Up + Pref'*Pref;
                b = d_Up'*(d_Ur + D_U) + D_lbd*(Pref'*Pref);
                c = d_Ur'*(d_Ur + 2*D_U);
                s = sign(D_U'*d_Up);
                d_lbd = -b/a + s*sqrt((b/a)^2 - c/a);

            % MNCM
            elseif strcmp(this.method,'MinimumNorm')
                d_lbd = -(d_Up'*d_Ur)/(d_Up'*d_Up);

            % ORCM
            elseif strcmp(this.method,'OrthogonalResidual')
                d_lbd = -(R'*D_U)/(Pref'*D_U);

            % GDCM
            elseif strcmp(this.method,'GeneralizedDisplacement')
                d_lbd = -(d_Up0'*d_Ur)/(d_Up0'*d_Up);
            end
        end

        %------------------------------------------------------------------
        % Sets the node and degree of freedom to be plotted
        function setPlotDof(this,nd,dof)
            this.plotNd  = nd;
            this.plotDof = dof;
        end

        %------------------------------------------------------------------
        % Plots the load-displacement curve for the analysis
        function plotCurves(this)
            figure;
            hold on, box on, grid on, axis on;
            plot(this.Uplot, this.lbdplot, 'o-k');
            xlabel('Displacement (mm)', 'Interpreter', 'latex');
            ylabel('Load factor', 'Interpreter', 'latex');
            xaxisproperties= get(gca, 'XAxis');
            xaxisproperties.TickLabelInterpreter = 'latex';
            yaxisproperties= get(gca, 'YAxis');
            yaxisproperties.TickLabelInterpreter = 'latex';
            set(gca,'FontSize', 14);
        end
    end
end