function FEM(data_FEM,ρ) # FEM solver for 2D elements # Author: JV Carstensen, CEE, MIT (JK Guest, Civil Eng, JHU) # Revised: Oct 26 2017, JVC # Revised: Amira Abdel-Rahman # ======================================================================= # ---- READ IN DATA ----------------------------------------------------- # read in external file E,f,dis,idb,ien,iplane,ndf,nel,nen,nnp,nsd,snu,t,xn,Ke0 = data_FEM; g=zeros(size(dis)) g.=dis # ---- NUMBER THE EQUATIONS --------------------------------------------- id,neq = number_eq(idb,ndf,nnp); # ---- FORM THE ELEMENT STIFFNESS MATRICES ------------------------------ nee = ndf*nen; # number of element equations Ke = zeros(nee,nee,nel); Imx_nsd = Matrix(1I, nsd, nsd); zero_nsd = zeros(nsd,nsd); for i = 1:nel Ke[:,:,i] .= (ρ[i]).*Ke_quad4(iplane,E,snu,t,nee,nen,nsd,ndf,ien[:,i],xn,Imx_nsd,zero_nsd); end # ---- PERFORM GLOBAL TO LOCAL MAPPING ---------------------------------- LM = zeros(nee,nel); for i = 1:nel LM[:,i] = get_local_id(id,ien[:,i],ndf,nee,nen); end # ---- IF THERE IS FREE DEGREES OF FREEDOM, THEN SOLVE THE EQUILIBRIUM -- if (neq > 0) # get global force vector (line 275) F = globalF(f,g,id,ien,Ke,LM,ndf,nee,nel,nen,neq,nnp); # solve Kd - F = 0 (line 521) d,K = solveEQ(F,LM,Ke,nee,nel,neq); end # display(Ke) de,Fe=post_processing(d,E,g,id,ien,Ke,ndf,nee,nel,nen,nnp) return K,F,d,de,Fe end # ======================================================================= # ======================================================================= function add_d2dcomp(dcomp,d,id,ndf,nnp) # function that adds the displacements of the free degrees of freedom to # the nodal displacements # ----------------------------------------------------------------------- # dcomp(ndf,nnp) = nodal displacements # d(neq,1) = displacement at free degrees of freedom # id(ndf,nnp) = equation numbers of degrees of freedom # ndf = number of degrees of freedom per node # nnp = number of nodal points #------------------------------------------------------------------------ # loop over nodes and degrees of freedom for n=1:nnp for i=1:ndf # if it is a free dof then add the global displacement if (id[i,n]>0) dcomp[i,n] = dcomp[i,n]+d[Int(id[i,n])]; end end end return dcomp end # ======================================================================= # ======================================================================= function add_loads_to_force(F,f,id,ndf,nnp) # function that adds nodal forces to the global force vector # ----------------------------------------------------------------------- # F(neq,1) = global force vector # f(ndf,nnp) = prescribed nodal forces # id(ndf,nnp) = equation numbers of degrees of freedom # ndf = number of degrees of freedom per node # nnp = number of nodal points #------------------------------------------------------------------------ # loop over nodes and degrees of freedom for n = 1:nnp for i = 1:ndf # get the global equation number M = Int(id[i,n]); # if free degree of freedom, then add nodal load to global force # vector if (M > 0) F[M] = F[M]+f[i,n]; end end end return F end # ======================================================================= # ======================================================================= function addforce(F,Fe,LM,nee) # function that adds element forces to the global force vector # ----------------------------------------------------------------------- # F(neq,1) = global force vector # Fe(nee,1) = element force vector # LM(nee,nel) = global to local map for the element # nee = number of element equations # ======================================================================= # loop over rows of Fe for i = 1:nee # get the global equation number for local equation i M = Int(LM[i]); # if free dof (eqn number > 0) add to F vector if (M > 0) F[M]=F[M]+Fe[i]; end end return F end # ======================================================================= # ======================================================================= function addstiff(K,Ke,LM,nee) # function that adds the element stiffness matrices to the global # stiffness matrix # ----------------------------------------------------------------------- # K(neq,neq) = global stiffness matrix # Ke(nee,nee,1) = element stiffness matrix # LM(nee,nel) = global to local map for the element # nee = number of element equations # ======================================================================= # loop over rows of Ke for i = 1:nee # loop over columns of Ke for j = 1:nee Mr = Int(LM[i]); Mc = Int(LM[j]); if (Mr > 0 && Mc > 0) # if equation #'s are non-zero add element contribution to the # stiffness matrix K[Mr,Mc] = K[Mr,Mc]+Ke[i,j]; end end end return K end # ======================================================================= # ======================================================================= function B_2d_elastic(dNdx,dNdy,nen,ndf) # Computes the strain-displacement matrix for an elastic 2d problem # ----------------------------------------------------------------------- # B(3,nen*ndf) = strain displacement matrix at current gauss point # dNdx = derivative of shape function at current gauss point # dNdy = derivative of shape function at current gauss point # nen = number of nodes per element # ndf = number of degrees of freedom per node # # ======================================================================= # Initialize B = zeros(3,nen*ndf); # Compute B for i = 1:nen B[:,(i-1)*ndf+1:ndf*i] = [ dNdx[i] 0 0 dNdy[i] dNdy[i] dNdx[i] ]; end # ======================================================================= return B end # ======================================================================= # ======================================================================= function D_2d(E,snu,iplane,nstr) # Computes the elastic constitutive matrix for a 2d problem # ----------------------------------------------------------------------- # E = Young's modulus # snu = Poisson's ratio # iplane = 1 - plane strain, 2 - plane stress # nstr = number of independent stress components # # D(nstr,nstr) = elastic constitutive matrix for element e # # ======================================================================= # initialize and define D D = zeros(Int(nstr),Int(nstr)); if (iplane == 2) # plane stress coeff = E/(1-(snu*snu)); D[1,1] = coeff; D[2,2] = D[1,1]; D[1,2] = coeff*snu; D[2,1] = D[1,2]; D[3,3] = coeff*(1-snu)/2; elseif (iplane == 1) # plane strain coeff = E/((1+snu)*(1-2*snu)); D[1,1] = coeff*(1-snu); D[2,2] = D[1,1]; D[1,2] = coeff*snu; D[2,1] = D[1,2]; D[3,3] = coeff*(1-2*snu)/2; end return D end # ======================================================================= # ======================================================================= function get_de_from_dcomp(dcomp,ien,ndf,nen) # extracts element displacement vector from complete displacement vector #------------------------------------------------------------------------ # dcomp(ndf,nnp) = nodal displacements # ien(nen,1) = element connectivity # ndf = number of degrees of freedom per node # nen = number of element equations # # de(nen,1) = element displacements # #------------------------------------------------------------------------ de = zeros((nen-1)*ndf+ndf,1); # loop over number of element nodes for i = 1:nen # loop over number of degrees of freedom per node for j = 1:ndf # get the local element number and place displacement in de leq = (i-1)*ndf+j; de[leq] = dcomp[j,ien[i]]; end end return de end # ======================================================================= # ======================================================================= function gausspoints_quad4(ndf,nsd) # function that defines integration points and weigths for quad4 elements #------------------------------------------------------------------------ # ndf = number of degrees of freedom per node # nsd = number of spacial dimensions # # Nint = number of gauss points in x and y # point = location of gauss points in x and y # weight = weigthing of gauss points for numerical integration # nstre = number of independent stress components # nstr1 = total number of stress components # ngp = number of gauss points per element # #------------------------------------------------------------------------ Nint=zeros(Int,2) Nint[1] = 2; # number of gauss points in x Nint[2] = 2; # number of gauss points in y # total number of gauss points ngp = Nint[1]*Nint[2]; # Initialize point = zeros(Nint[2],Nint[1]); weight = zeros(Nint[2],Nint[1]); weight .= point; # get gauss point locations and weights for integration point[1,1] = (-0.577350269189626); point[1,2] = (-0.577350269189626); point[2,1] = ( 0.577350269189626); point[2,2] = ( 0.577350269189626); weight .= weight .+1; # define number of individual stress/strain components nstre = nsd*(ndf+1)/2; # define number of stress components in each element (4 in 2d) nstr1 = 4; return Nint,point,weight,nstre,nstr1,ngp end # ======================================================================= # ======================================================================= function get_local_id(id,ien,ndf,nee,nen) # functions that performs the global to local mapping of equation numbers # ----------------------------------------------------------------------- # id(ndf,nnp) = equation numbers of degrees of freedom # ien(nen,1) = element connectivity # ndf = number of degrees of freedom per node # nee = number of element equations # nen = number of element equations # # LM(nee,1) = global to local map for element # ======================================================================= # initialize global-local mapping matrix LM = zeros(nee,1); # initialize local equation number counter k = 0; # loop over element nodes for i = 1:nen # loop over degrees of freedom at each node for j = 1:ndf # update counter and prescribe global equation number k = k+1; LM[k] = id[j,ien[i]]; end end return LM end # ======================================================================= # ======================================================================= function globalF(f,g,id,ien,Ke,LM,ndf,nee,nel,nen,neq,nnp) # function that assembles the global load vector # ----------------------------------------------------------------------- # id(ndf,nnp) = equation numbers of degrees of freedom # f(ndf,nnp) = prescribed nodal forces # g(ndf,nnp) = prescribed nodal displacements # ien(nen,nel) = element connectivities # Ke(nee,nee,nel) = element stiffness matrices # ndf = number of degrees of freedom per node # nee = number of element equations # nel = number of elements # nen = number of element equations # neq = number of equations # nnp = number of nodal points # # F(neq,1) = global force vector # ======================================================================= # initialize F = zeros(neq,1); # Insert applied loads into F F = add_loads_to_force(F,f,id,ndf,nnp); # Compute forces from applied displacements (ds~=0) and insert into F Fse = zeros(nee,nel); # loop over elements for i = 1:nel # get dse for current element dse = get_de_from_dcomp(g,ien[:,i],ndf,nen); # compute element force Fse[:,i] = -Ke[:,:,i]*dse; # assemble elem force into global force vector F = addforce(F,Fse[:,i],LM[:,i],nee); end return F end # ======================================================================= # ======================================================================= function jacobian_2d(dNdr,dNds,xn,nen,Imx,Jaco) # computes the Jacobian, its determinate and inverse at current gauss # point in element e # ----------------------------------------------------------------------- # dNdr = derivative of shape function at current gauss point # dNds = derivative of shape function at current gauss point # xn(nsd,nen) = nodal coordinates for element e # nen = number of nodes per element # Imx = eye(nsd) # Jaco = zeros(nsd) # # Jaco = Jacobian matrix for current gauss point # detJ = determinant of the Jacobian for current gauss point # InvJ = inverse of the Jacobian matrix for current gauss point # ======================================================================= # Add to Jacobian for i = 1:nen #for j = 1:nsd Jaco[1,1] = Jaco[1,1]+dNdr[i]*xn[1,i]; Jaco[1,2] = Jaco[1,2]+dNdr[i]*xn[2,i]; Jaco[2,1] = Jaco[2,1]+dNds[i]*xn[1,i]; Jaco[2,2] = Jaco[2,2]+dNds[i]*xn[2,i]; #end end # Find its determinant detJ = det(Jaco); # Find its inverse invJ = Imx/Jaco; return Jaco,detJ,invJ end # ======================================================================= # ======================================================================= function ke_elem_quad4(Nint,point,weight,ien, xn,ndf,nen,nee,t,D,Imx_nsd,zero_nsd) # loops over element gauss points and computes element stiffness matrix # ----------------------------------------------------------------------- # Nint = number of gauss points in x and y # point = location of gauss points # weight = weigthing of gauss points in numerical integration # ien(nen,nel) = element connectivity # xn(nsd,nnp) = nodal coordinates # nen = number of nodes per element # nee = number of element equations # t = element thickness # D = elastic constititive matrix for the element # Imx = eye(nsd) # Jaco = zeros(nsd) # # kee(nee,nee,1) = element stiffness matrix for quad 4 element # ======================================================================= # ======================================================================= # Initialize kee = zeros(nee,nee); coeff = 0; for i = 1:Nint[1] ri = point[i,1]; wi = weight[i,1]; for j = 1:Nint[2] sj = point[j,2]; wj = weight[j,2]; sN,dNdx,dNdy,detJ,invJ = shape_quad4(ri,sj,xn,ien,nen,Imx_nsd,zero_nsd); B = B_2d_elastic(dNdx,dNdy,nen,ndf); coeff = t*wi*wj*detJ; # Compute kee = kee + transpose(B)*D*B*coeff kee .= kee .+(B')*D*B*coeff; end end return kee end # ======================================================================= # ======================================================================= function Ke_quad4(iplane,E,snu,t,nee,nen,nsd,ndf,ien,xn, Imx_nsd,zero_nsd) # computes element stiffness matrix # ----------------------------------------------------------------------- # iplane = 1 - plane strain, 2 - plane stress # E = Young's modulus # snu = Poisson's ratio # t = element thickness # nee = number of element equations # nen = number of nodes per element # nsd = number of spacial dimmensions # ndf = number of degrees of freedom # weight = weigthing of gauss points in numerical integration # ien(nen,nel) = element connectivity # xn(nsd,nnp) = nodal coordinates # Imx = eye(nsd) # Jaco = zeros(nsd) # # kee(nee,nee,1) = element stiffness matrix for quad 4 element # ======================================================================= # Define location and weights of integration points Nint,point,weight,nstre,nstr1,ngp = gausspoints_quad4(ndf,nsd); # get constitutive matrix nstr = nsd*(nsd+1)/2; D = D_2d(E,snu,iplane,nstr); # Compute element matrix kee = ke_elem_quad4(Nint,point,weight,ien,xn,ndf,nen,nee,t,D,Imx_nsd,zero_nsd); return kee end # ======================================================================= # ======================================================================= function number_eq(idb,ndf,nnp) # function that numbers the unknown degrees of freedom (equations) # ----------------------------------------------------------------------- # idb(ndf,nnp) = 1 if the degree of freedom is prescribed, 0 otherwise # ndf = number of degrees of freedom per node # nnp = number of nodal points # # id(ndf,nnp) = equation numbers of degrees of freedom # neq = number of equations (tot number of degrees of freedom) # ======================================================================= # initialize id and neq id = zeros(ndf,nnp); neq = 0; # loop over nodes for n = 1:nnp # loop over degrees of freedom for i = 1:ndf if idb[i,n] == 0 # udate # of equations neq = neq + 1; # if no prescribed displacement at dof i of node n # => give an equation # different from 0 id[i,n] = neq; end end end return id,neq end # ======================================================================= # ======================================================================= function solveEQ(F,LM,Ke,nee,nel,neq) # function that solves the equilibrium condition # ----------------------------------------------------------------------- # F(neq,1) = global force vector # LM(nee,nel) = global to local maps # Ke(nee,nee,nel) = element stiffness matrices # nee = number of element equations # nel = number of elements # neq = number of equations # # d(neq,1) = displacements at free degrees of freedom # ======================================================================= # assemble global stiffness matrix # K = zeros(neq,neq); # Use 'sparse' for more efficient memory usage K=spzeros(neq,neq) for i = 1:nel K = addstiff(K,Ke[:,:,i],LM[:,i],nee); end # solve the equlibrium d = K\F; return d,K end # ======================================================================= # ======================================================================= function shape_quad4(r,s,xn,ien,nen, Imx_nsd,zero_nsd) # computes the shape functions for quad4 element e at gauss point (r,s) # ----------------------------------------------------------------------- # r = gauss point coordinate in parent domain # s = gauss point coordinate in parent domain # xn(nsd,nnp) = nodal coordinates # ien(nen,1) = element connectivity for element e # nen = number of nodes per element # Imx = eye(nsd) # Jaco = zeros(nsd) # # sN(nen,1) = shape functions at current gauss point # dNdx(nen,1) = shape function derivatives at current gauss point in # (x,y) # dNdy(nen,1) = shape function derivatives at current gauss point in # (x,y) # detJ = determinant of the Jacobian for current gauss point # InvJ = inverse of the Jacobian matrix for current gauss point # # ======================================================================= # Initialize dNdx = zeros(nen,1); dNdy = zeros(nen,1); dNdy .= dNdx; # compute shape functions at(r,s) sN,dNdr,dNds = shapefunct_quad4(r,s); # compute Jacobian, its deterninant and inverse Jaco,detJ,invJ = jacobian_2d(dNdr,dNds,xn[:,ien],nen,Imx_nsd,zero_nsd); # compute the derivatives of the shape functions for i = 1:nen dNdx[i] = invJ[1,1]*dNdr[i]+invJ[1,2]*dNds[i]; dNdy[i] = invJ[2,1]*dNdr[i]+invJ[2,2]*dNds[i]; end return sN,dNdx,dNdy,detJ,invJ end # ======================================================================= # ======================================================================= function shapefunct_quad4(r,s) # evaluates the shape functions and their derivatives at point (s,r) # ----------------------------------------------------------------------- # r = gauss point coordinate in parent domain # s = gauss point coordinate in parent domain # # sN(nen,1) = shape functions at current gauss point # dNdr(nen,1) = shape function derivatives at current gauss point in # (r,s) # dNds(nen,1) = shape function derivatives at current gauss point in # (r,s) # # ======================================================================= # Initialize sN = zeros(4,1); dNdr = zeros(4,1); dNds = zeros(4,1); dNdr .= sN; dNds .= sN; # Shape functions sN[1] = (1-r)*(1-s); sN[2] = (1+r)*(1-s); sN[3] = (1+r)*(1+s); sN[4] = (1-r)*(1+s); sN .= 0.25*sN; # Derivatives dNdr[1] = -(1-s); dNdr[2] = (1-s); dNdr[3] = (1+s); dNdr[4] = -(1+s); dNdr .= 0.25*dNdr; dNds[1] = -(1-r); dNds[2] = -(1+r); dNds[3] = (1+r); dNds[4] = (1-r); dNds .= 0.25*dNds; return sN,dNdr,dNds end # ======================================================================= # ======================================================================= function post_processing(d,E,g,id,ien,Ke,ndf,nee,nel,nen,nnp) # function that performs post processing for truss elements # ----------------------------------------------------------------------- # A(nel,1) = cross-sectional area of elements # d(neq,1) = displacements at free degrees of freedom # E(nel,1) = Young's modulus of elements # g(ndf,nnp) = prescribed nodal displacements # id(ndf,nnp) = equation numbers of degrees of freedom # ien(nen,nel) = element connectivities # Ke(nee,nee,nel) = element stiffness matrices # ndf = number of degrees of freedom per node # nee = number of element equations # nel = number of elements # nen = number of element equations # nnp = number of nodes # Te(nee,nee,nel) = element transformation matrices # # dcomp(ndf,nnp) = nodal displacements # axial(nel,1) = axial element forces # stress(nel,1) = element stresses # strain(nel,1) = element strains # Fe(nee,nel) = element forces # ======================================================================= # get the total displacement of the structure in matrix form dcomp(nsd,nnp) dcomp = add_d2dcomp(g,d,id,ndf,nnp); # initalize evaluation of global element forces Fe, local element forces # fe, axial forces, element stresses and strains Fe = zeros(nee,nel); de = zeros(nee,nel); fe = zeros(1*nen,nel); # element local force vector axial = zeros(nel,1); stress = zeros(nel,1); strain = zeros(nel,1); # element axial, stress, strain # loop over elements for i=1:nel # get the element displacaments de[:,i] = get_de_from_dcomp(dcomp,ien[:,i],ndf,nen); # compute the element forces Fe[:,i] = Ke[:,:,i]*de[:,i]; end # display("Fe") # display(Fe) return de,Fe end