Skip to content
Snippets Groups Projects
compliant_mechanisms.jl 12.94 KiB
# Amira Abdel-Rahman
# (c) Massachusetts Institute of Technology 2020 

function getDataFromSetup3D(setup,scale)
    # -----------------------------------------------------------------------
    # A(nel,1)        = cross-sectional area of elements
    # E(nel,1)        = Young's modulus of elements
    # f(ndf,nnp)      = prescribed nodal forces
    # g(ndf,nnp)      = prescribed nodal displacements
    # idb(ndf,nnp)    = 1 if the degree of freedom is prescribed, 0 otherwise
    # ien(nen,nel)    = element connectivities
    # ndf             = number of degrees of freedom per node
    # nel             = number of elements
    # nen             = number of element equations
    # nnp             = number of nodes
    # nsd             = number of spacial dimensions
    # xn(nsd,nnp)     = nodal coordinates

    # =======================================================================
    nodes=setup["nodes"]
    edges=setup["edges"]


    # ---- MESH -------------------------------------------------------------
    nsd =  3;  # number of spacial dimensions
    ndf =  3;  # number of degrees of freedom per node 
    nen =  2;  # number of element nodes

    nel =  length(edges);  # number of elements 
    nnp =  length(nodes);  # number of nodal points


    # ---- NODAL COORDINATES ------------------------------------------------
    # xn(i,N) = coordinate i for node N
    # N = 1,...,nnp
    # i = 1,...,nsd
    # -----------------------------------------------------------------------

    xn         = zeros(nsd,nnp);
    for i in 1:nnp
        xn[1:nsd, i]  = [(nodes[i]["position"]["x"]/scale)  (nodes[i]["position"]["y"]/scale) (nodes[i]["position"]["z"]/scale)]';
    end
    
    # ---- NODAL COORDINATES ------------------------------------------------
    # ien(a,e) = N
    # N: global node number - N = 1,...,nnp
    # e: element number     - e = 1,...,nel
    # a: local node number  - a = 1,...,nen
    # -----------------------------------------------------------------------

    ien = zeros(nen,nel);
    for i in 1:nel
        ien[1:2,i]  = [(edges[i]["source"]+1)   (edges[i]["target"]+1)]' ;
    end


    len=zeros(nel);
    for i in 1:nel
        x1=(nodes[(edges[i]["source"]+1)]["position"]["x"]/scale)
        x2=(nodes[(edges[i]["target"]+1)]["position"]["x"]/scale)

        y1=(nodes[(edges[i]["source"]+1)]["position"]["y"]/scale)
        y2=(nodes[(edges[i]["target"]+1)]["position"]["y"]/scale)

        z1=(nodes[(edges[i]["source"]+1)]["position"]["z"]/scale)
        z2=(nodes[(edges[i]["target"]+1)]["position"]["z"]/scale)

        len[i]  = sqrt((x1-x2)^2+(y1-y2)^2+(z1-z2)^2);
    end
    

    # ---- MATERIAL PROPERTIES ----------------------------------------------
    # E(e) = E_mat
    # e: element number     - e = 1,...,nel
    # -----------------------------------------------------------------------
    E_mat = 29000*1000;         # Young's modulus # lbf/in^2
    E = E_mat*ones(nel,1);
    # todo change to make it parameter

    # ---- GEOMETRIC PROPERTIES ---------------------------------------------
    # A(e) = A_bar
    # e: element number     - e = 1,...,nel
    # -----------------------------------------------------------------------
    #A = ones(nel);
    #A[:] .= 400; # mm^2

    # ---- BOUNDARY CONDITIONS ----------------------------------------------
    # prescribed displacement flags (essential boundary condition)
    #
    # idb(i,N) = 1 if the degree of freedom i of the node N is prescribed
    #          = 0 otherwise
    #
    # 1) initialize idb to 0
    # 2) enter the flag for prescribed displacement boundary conditions
    # -----------------------------------------------------------------------
    idb = zeros(ndf,nnp);
    for i in 1:nnp
        if nodes[i]["restrained_degrees_of_freedom"][1]
            idb[1,i]=1; 
        end
        if nodes[i]["restrained_degrees_of_freedom"][2]
            idb[2,i]=1; 
        end
        if nodes[i]["restrained_degrees_of_freedom"][3]
            idb[3,i]=1; 
        end
    end


    # ---- BOUNDARY CONDITIONS: PRESCRIBED NODAL DISPLACEMENTS --------------
    # g(i,N) = prescribed displacement magnitude
    # N = 1,...,nnp
    # i = 1,...,nsd
    #
    # 1) initialize g to 0
    # 2) enter the values
    # -----------------------------------------------------------------------
    g = zeros(ndf,nnp);


    # ---- BOUNDARY CONDITIONS: PRESCRIBED NODAL FORCES ---------------------
    # f(i,N) = prescribed force magnitude
    # N = 1,...,nnp
    # i = 1,...,nsd
    #
    # 1) initialize f to 0
    # 2) enter the values
    # -----------------------------------------------------------------------
    P = 20.0* 1000;     # lbf
    f = zeros(ndf,nnp);
    for i in 1:nnp
        #inverter
        f[1,i] = nodes[i]["force"]["x"]*P;
        f[2,i] = nodes[i]["force"]["y"]*P;
        f[3,i] = nodes[i]["force"]["z"]*P;
    end

    Ls=[]
    for i in 1:nnp
        if (nodes[i]["fixedDisplacement"]["x"]!=0)||(nodes[i]["fixedDisplacement"]["y"]!=0)||(nodes[i]["fixedDisplacement"]["z"]!=0)
            L=zeros(ndf,nnp);
            L[1,i] = nodes[i]["fixedDisplacement"]["x"];
            L[2,i] = nodes[i]["fixedDisplacement"]["y"];
            L[3,i] = nodes[i]["fixedDisplacement"]["z"];
            append!(Ls,[L]);
        end
    end
    
    A=ones(nel)
    
    # ---- NUMBER THE EQUATIONS ---------------------------------------------
    # line 380
    id,neq = number_eq(idb,ndf,nnp);
    
    # ---- FORM THE ELEMENT STIFFNESS MATRICES ------------------------------
    # line 324
    nee = ndf*nen;                   # number of element equations
    
    Ke  = zeros(nee,nee,nel);
    Te  = zeros(nen*1,nen*nsd,nel);  # *1 is specific to truss
    for i = 1:nel
        Ke[:,:,i],Te[:,:,i] = Ke_truss(A[i],E[i],ien[:,i],nee,nsd,xn);
    end    

    return E,f,g,idb,ien,ndf,nel,nen,nnp,nsd,xn,len,Ke,Te,Ls
end

# =======================================================================

function getSensitivites(Ke0,dcomp,ien,nel,len,λ)

    dfdA=zeros(nel)
    dgdA=zeros(nel)


    for i in 1:nel
        de=[dcomp[1,Int(ien[1,i])],dcomp[2,Int(ien[1,i])],dcomp[3,Int(ien[1,i])] ,dcomp[1,Int(ien[2,i])] ,dcomp[2,Int(ien[2,i])] ,dcomp[3,Int(ien[2,i])]]
        λe=[λ[1,Int(ien[1,i])],λ[2,Int(ien[1,i])],λ[3,Int(ien[1,i])] ,λ[1,Int(ien[2,i])] ,λ[2,Int(ien[2,i])],λ[3,Int(ien[2,i])] ]
        # println(de)
        # println(Ke0[:,:,i])
        dKedA=Ke0[:,:,i]
        dgdA[i]=(-1.0 .*λe)'*dKedA*de
        dfdA[i]=len[i]
    end
    return dfdA,dgdA
end

# =======================================================================

function getSensitivitesSIMP(Ke0,dcomp,ien,nel,len,λ,η,X)

    dfdA=zeros(nel)
    dgdA=zeros(nel)


    for i in 1:nel
        de=[dcomp[1,Int(ien[1,i])],dcomp[2,Int(ien[1,i])],dcomp[3,Int(ien[1,i])] ,dcomp[1,Int(ien[2,i])] ,dcomp[2,Int(ien[2,i])] ,dcomp[3,Int(ien[2,i])]]
        λe=[λ[1,Int(ien[1,i])],λ[2,Int(ien[1,i])],λ[3,Int(ien[1,i])] ,λ[1,Int(ien[2,i])] ,λ[2,Int(ien[2,i])],λ[3,Int(ien[2,i])] ]
        # println(de)
        # println(Ke0[:,:,i])
        # dKedA=Ke0[:,:,i]
        dKedA=Ke0[:,:,i].*(η).*X[i]^(η-1)
        dgdA[i]=(-1.0 .*λe)'*dKedA*de
        dfdA[i]=len[i]
    end
    return dfdA,dgdA
end


function getAdjoint(K,dcomp,L,free)
    # L1=L[:,free]
    L1=L[free]
    λ1=K\L1[:]
    λ=zeros(size(dcomp))
    # λ[:,free].=reshape(λ1,size(L1))
    λ[free].=reshape(λ1,size(L1))
    return λ,L1,λ1
end
# =======================================================================

function optimizeCompliantMechanism1(problem,Ls,free,dmax,totalVolFactor,maxeval=500)
    
    E,f,g,idb,ien,ndf,nel,nen,nnp,nsd,xn,len,Ke,Te=problem
    
    nel=length(len)
    η=3.0
    
    
    function FA(x::Vector, grad::Vector)

        K,F,d,stress,dcomp,g=FEM_truss(problem,x);
        λ,L1,λ1=getAdjoint(copy(K),copy(dcomp),copy(Ls[1]),copy(free))

        grad[:] .=getSensitivites(Ke,dcomp,ien,nel,len,copy(λ))[2]

        # g=(L1[:]'*d)[1] .- dmax - (λ1'*(K*d -F))[1]

        goal=L1[:]'*d -λ1'*K*d +λ1'*F .-dmax
        # display(goal )
        # display(sum(x) )
        
        return goal[1]
    end

    function GA(x::Vector, grad::Vector)
        grad[:] .=len[:]
        return (sum(x .* len )) - totalVolFactor
    end
    FA(ones(nel), fill(0.25,nel))
    GA(ones(nel), fill(0.25,nel))
    

    opt = Opt(:LD_MMA, nel)
    opt.lower_bounds = fill(1e-3,nel)
    opt.xtol_rel = 1e-6
    opt.maxeval = maxeval

    opt.min_objective = FA
    inequality_constraint!(opt, (x,gg) -> GA(x,gg), 1e-6)

    display(@time (minf,minx,ret) = optimize(opt, ones(nel)))
    numevals = opt.numevals # the number of function evaluations
    display("got $minf after $numevals iterations (returned $ret)")
    
    return minx
end
# =======================================================================

function optimizeCompliantMechanism(problem,Ls,free,dmax,amax,maxeval=500,SIMP=false)
    
    E,f,g,idb,ien,ndf,nel,nen,nnp,nsd,xn,len,Ke,Te=problem
    
    nel=length(len)

    η=3.0
    
    function GA(x::Vector, grad::Vector,num::Int)

        K,F,d,stress,dcomp,g=FEM_truss(problem,x);
        λ,L1,λ1=getAdjoint(copy(K),copy(dcomp),copy(Ls[num]),copy(free))

        grad[:] .=getSensitivites(Ke,dcomp,ien,nel,len,copy(λ))[2]

        # g=(L1[:]'*d)[1] .- dmax - (λ1'*(K*d -F))[1]

        goal=L1[:]'*d -λ1'*K*d +λ1'*F .-dmax
        # display(goal )
        # display(sum(x) )
        
        return goal[1]
    end

    function GASIMP(x::Vector, grad::Vector,num::Int)

        K,F,d,stress,dcomp,g=FEM_truss(problem,x.^η);
        λ,L1,λ1=getAdjoint(copy(K),copy(dcomp),copy(Ls[num]),copy(free))

        grad[:] .=getSensitivitesSIMP(Ke,dcomp,ien,nel,len,copy(λ),η,x)[2]

        # g=(L1[:]'*d)[1] .- dmax - (λ1'*(K*d -F))[1]

        goal=L1[:]'*d -λ1'*K*d +λ1'*F .-dmax
        # display(goal )
        # display(sum(x) )
        
        return goal[1]
    end

    function FA(x::Vector, grad::Vector)
        grad[:] .=len[:]
        return (sum(x .* len )) 
    end

    function FASIMP(x::Vector, grad::Vector)
        grad[:] .=len[:]
        return (sum(x.^η .* len )) 
    end
    

    opt = Opt(:LD_MMA, nel)
    opt.lower_bounds = fill(1e-6,nel)
    opt.upper_bounds = fill(amax,nel)
    opt.xtol_rel = 1e-6
    opt.maxeval = maxeval

    if (SIMP)
        opt.min_objective = FASIMP
        println(size(Ls)[1])
        for i =1:size(Ls)[1]
            inequality_constraint!(opt, (x,gg) -> GASIMP(x,gg,i), 1e-6)
        end
    else
        opt.min_objective = FA
        println(size(Ls)[1])
        for i =1:size(Ls)[1]
            inequality_constraint!(opt, (x,gg) -> GA(x,gg,i), 1e-6)
        end
    end

    display(@time (minf,minx,ret) = optimize(opt, ones(nel).*amax*0.5))
    numevals = opt.numevals # the number of function evaluations
    display("got $minf after $numevals iterations (returned $ret)")
    
    return minx
end
# =======================================================================

function simulateAndExport(setup,X,dcomp,fileName,threshold)
    nodes=setup["nodes"]
    edges=setup["edges"]

    nodesNew=[]
    edgesNew=[]

    nel =  length(edges);  # number of elements 
    nnp =  length(nodes);  # number of nodal points

    for i in 1:nnp
        nodes[i]["in"]=false
        nodes[i]["displacement"]["x"]=dcomp[1,i];
        nodes[i]["displacement"]["y"]=dcomp[2,i];
        nodes[i]["displacement"]["z"]=dcomp[3,i]; 
    end

    idCount=0
    for i in 1:nel
        if X[i]>threshold
            if (!nodes[(edges[i]["source"]+1)]["in"])
                nodes[(edges[i]["source"]+1)]["in"]=true
                nodes[(edges[i]["source"]+1)]["id"]="n$(idCount)"
                nodes[(edges[i]["source"]+1)]["idd"]=idCount
                append!(nodesNew,[nodes[(edges[i]["source"]+1)]])
                id1=idCount
                idCount=idCount+1
            else
                id1=nodes[(edges[i]["source"]+1)]["idd"]
            end

            if (!nodes[(edges[i]["target"]+1)]["in"])
                nodes[(edges[i]["target"]+1)]["in"]=true
                nodes[(edges[i]["target"]+1)]["id"]="n$(idCount)"
                nodes[(edges[i]["target"]+1)]["idd"]=idCount
                append!(nodesNew,[nodes[(edges[i]["target"]+1)]])
                id2=idCount
                idCount=idCount+1
            else
                id2=nodes[(edges[i]["target"]+1)]["idd"]
            end
            edges[i]["source"]=id1
            edges[i]["target"]=id2

            append!(edgesNew,[edges[i]])

        end
    end


    setup["nodes"]=nodesNew;
    setup["edges"]=edgesNew;

    stringdata = JSON.json(setup)# pass data as a json string (how it shall be displayed in a file)

    # write the file with the stringdata variable information
    open(fileName, "w") do f
            write(f, stringdata)
    end


    # E,f,g,idb,ien,ndf,nel,nen,nnp,nsd,xn,len,Ke,Te,Ls=getDataFromSetup3D(setup,scale);

    # problem1=E,f,g,idb,ien,ndf,nel,nen,nnp,nsd,xn,len,Ke,Te;

    # nel=length(edgesNew)

    # display(a)

    # X=ones(nel)
    # K,F,d,stress,dcomp,g=FEM_truss(problem1,X);
    # display(dcomp)
    

    # display(plotTrussDeformed3D(problem1,copy(X),scale,0.1,1))


    
end