# Amira Abdel-Rahman
# (c) Massachusetts Institute of Technology 2020

function runMetavoxelGPU!(setup,numTimeSteps,latticeSize,displacements,returnEvery,save)
    function initialize!(setup)
        nodes      = setup["nodes"]
        edges      = setup["edges"]

        i=1
        # pre-calculate current position
        for node in nodes
            node["position"]["y"]=node["position"]["y"]+0.0
            node["position"]["z"]=node["position"]["z"]+0.0
            # element=parse(Int,node["id"][2:end])
            N_position[i]=Vector3(node["position"]["x"],node["position"]["y"],node["position"]["z"])
            N_restrained[i]=node["restrained_degrees_of_freedom"][1] ## todo later consider other degrees of freedom
            N_displacement[i]=Vector3(node["displacement"]["x"],node["displacement"]["y"],node["displacement"]["z"])
            N_angle[i]=Vector3(node["angle"]["x"],node["angle"]["y"],node["angle"]["z"])
            N_force[i]=Vector3(node["force"]["x"],node["force"]["y"],node["force"]["z"])
            N_fixedDisplacement[i]=Vector3(node["fixedDisplacement"]["x"],node["fixedDisplacement"]["y"],node["fixedDisplacement"]["z"])
            N_currPosition[i]=Vector3(node["position"]["x"],node["position"]["y"],node["position"]["z"])
            #voxelMaterial(E,mass,nu,rho,zeta,zetaCollision,muStatic,muKinetic,nomSize)

            i=i+1
        end 

        i=1
        # pre-calculate the axis
        for edge in edges
            # element=parse(Int,edge["id"][2:end])

            # find the nodes that the lements connects
            fromNode = nodes[edge["source"]+1]
            toNode = nodes[edge["target"]+1]


            node1 = [fromNode["position"]["x"] fromNode["position"]["y"] fromNode["position"]["z"]]
            node2 = [toNode["position"]["x"] toNode["position"]["y"] toNode["position"]["z"]]

            length=norm(node2-node1)
            axis=normalize(collect(Iterators.flatten(node2-node1)))

            E_source[i]=edge["source"]+1
            E_target[i]=edge["target"]+1
            E_axis[i]=Vector3(axis[1],axis[2],axis[3])
            E_currentRestLength[i]=length #?????? todo change  
                      

            N_edgeID[E_source[i],N_currEdge[E_source[i]]]=i
            N_edgeFirst[E_source[i],N_currEdge[E_source[i]]]=true
            N_currEdge[E_source[i]]+=1

            N_edgeID[E_target[i],N_currEdge[E_target[i]]]=i
            N_edgeFirst[E_target[i],N_currEdge[E_target[i]]]=false
            N_currEdge[E_target[i]]+=1
            
            E=(N_material[E_source[i]].E+N_material[E_target[i]].E)/2.0
            mass=(N_material[E_source[i]].mass+N_material[E_target[i]].mass)/2.0
            nu=(N_material[E_source[i]].nu+N_material[E_target[i]].nu)/2.0
            rho=(N_material[E_source[i]].rho+N_material[E_target[i]].rho)/2.0
            area=edge["material"]["area"]
   
            E_material[i]=edgeMaterial()
            loaded=0.0

            strainRatio=(N_material[E_source[i]].E / N_material[E_target[i]].E)

            # linear=(N_material[E_source[i]].linear && N_material[E_target[i]].linear)
            # poisson=(N_material[E_source[i]].poisson || N_material[E_target[i]].poisson)
            linear=true
            poisson=false
            
            cTE=0.0 #Coefficient of thermal expansion
 

            # epsilonFail=(N_material[E_source[i]].epsilonFail+N_material[E_target[i]].epsilonFail)/2.0 #TODO CHANGE TO SMALLEST

            E_material[i]=edgeMaterial(E,mass,nu,rho,sqrt(area),sqrt(area),length,loaded,strainRatio,linear,poisson,cTE)
            #edgeMaterial(E,mass,nu,rho,b,h,L)


            i=i+1
        end 
    end
    function simulateParallel!(metavoxel,numTimeSteps,dt,returnEvery)
        # initialize(setup)

        for i in 1:numTimeSteps
            #println("Timestep:",i)
            doTimeStep!(metavoxel,dt,i)
            if(mod(i,returnEvery)==0)
                append!(displacements,[Array(metavoxel["N_displacementGPU"])])
            end
        end
    end
    
    ########
    voxCount=0
    linkCount=0
    nodes      = setup["nodes"]
    edges      = setup["edges"]
    voxCount=size(nodes)[1]
    linkCount=size(edges)[1]
    strain =0 #todooo moveeee
    maxNumEdges=10

    ########
    voxCount=0
    linkCount=0
    nodes      = setup["nodes"]
    edges      = setup["edges"]
    voxCount=size(nodes)[1]
    linkCount=size(edges)[1]
    strain =0 #todooo moveeee

    ############# nodes
    N_position=fill(Vector3(),voxCount)
    N_restrained=zeros(Bool, voxCount)
    N_displacement=fill(Vector3(),voxCount)
    N_angle=fill(Vector3(),voxCount)
    N_currPosition=fill(Vector3(),voxCount)
    N_linMom=fill(Vector3(),voxCount)
    N_angMom=fill(Vector3(),voxCount)
    N_intForce=fill(Vector3(),voxCount)
    N_intMoment=fill(Vector3(),voxCount)
    N_moment=fill(Vector3(),voxCount)
    N_force=fill(Vector3(),voxCount)
    N_fixedDisplacement=fill(Vector3(),voxCount)
    N_orient=fill(Quaternion(),voxCount)
    N_edgeID=fill(-1,(voxCount,maxNumEdges))
    N_edgeFirst=fill(true,(voxCount,maxNumEdges))
    N_currEdge=fill(1,voxCount)
    N_material=fill(voxelMaterial(),voxCount)
    N_poissonStrain=fill(Vector3(),voxCount)
    #voxelMaterial(E,mass,nu,rho,zeta,zetaCollision,muStatic,muKinetic,nomSize)

    ############# edges
    E_source=fill(0,linkCount)
    E_target=fill(0,linkCount)
    E_stress=fill(0.0,linkCount)
    E_axis=fill(Vector3(1.0,0.0,0.0),linkCount)
    E_currentRestLength=fill(0.0,linkCount)
    E_pos2=fill(Vector3(),linkCount)
    E_angle1v=fill(Vector3(),linkCount)
    E_angle2v=fill(Vector3(),linkCount)
    E_angle1=fill(Quaternion(),linkCount)
    E_angle2=fill(Quaternion(),linkCount)

    E_intForce1=fill(Vector3(),linkCount)
    E_intMoment1=fill(Vector3(),linkCount) 

    E_intForce2=fill(Vector3(),linkCount)
    E_intMoment2=fill(Vector3(),linkCount)
    E_damp=fill(false,linkCount)
    E_smallAngle=fill(true,linkCount)
    E_material=fill(edgeMaterial(),linkCount)

    E_strain=fill(0.0,linkCount)
    E_maxStrain=fill(0.0,linkCount)
    E_strainOffset=fill(0.0,linkCount)
    E_currentTransverseArea=fill(0.0,linkCount)
    E_currentTransverseStrainSum=fill(0.0,linkCount)# TODO remove ot incorporate


    #################################################################
    initialize!(setup)
    #################################################################

    ########################## turn to cuda arrays
    ############# nodes
    N_positionGPU=      CuArray(N_position)      
    N_restrainedGPU=    CuArray(N_restrained)  
    N_displacementGPU=  CuArray(N_displacement)   
    N_angleGPU=         CuArray(N_angle)       
    N_currPositionGPU=  CuArray(N_currPosition)    
    N_linMomGPU=        CuArray(N_linMom)        
    N_angMomGPU=        CuArray(N_angMom)        
    N_intForceGPU=      CuArray(N_intForce)     
    N_intMomentGPU=     CuArray(N_intMoment)        
    N_momentGPU=        CuArray(N_moment)         
    N_forceGPU=         CuArray(N_force)
    N_fixedDisplacementGPU=CuArray(N_fixedDisplacement)           
    N_orientGPU=        CuArray(N_orient)       
    N_edgeIDGPU=        CuArray(N_edgeID)         
    N_edgeFirstGPU=     CuArray(N_edgeFirst)
    N_materialGPU=      CuArray(N_material)
    N_poissonStrainGPU= CuArray(N_poissonStrain) 


    ############# edges
    E_sourceGPU=                    CuArray(E_source)   
    E_targetGPU=                    CuArray(E_target)
    E_stressGPU=                    CuArray(E_stress)
    E_axisGPU=                      CuArray(E_axis)          
    E_currentRestLengthGPU=         CuArray(E_currentRestLength)
    E_pos2GPU=                      CuArray(E_pos2)
    E_angle1vGPU=                   CuArray(E_angle1v)
    E_angle2vGPU=                   CuArray(E_angle2v)
    E_angle1GPU=                    CuArray(E_angle1)
    E_angle2GPU=                    CuArray(E_angle2)

    E_strainGPU=                    CuArray(E_strain)
    E_maxStrainGPU=                 CuArray(E_maxStrain)
    E_strainOffsetGPU=              CuArray(E_strainOffset)
    E_currentTransverseAreaGPU=     CuArray(E_currentTransverseArea)
    E_currentTransverseStrainSumGPU=CuArray(E_currentTransverseStrainSum)# TODO remove ot incorporate

    E_intForce1GPU=                 CuArray(E_intForce1) 
    E_intMoment1GPU=                CuArray(E_intMoment1)  
    E_intForce2GPU=                 CuArray(E_intForce2) 
    E_intMoment2GPU=                CuArray(E_intMoment2)
    E_dampGPU=                      CuArray(E_damp)
    E_smallAngleGPU=                CuArray(E_smallAngle)
    E_materialGPU=                  CuArray(E_material)



    #########################################
    metavoxel = Dict(
        "N_positionGPU" => N_positionGPU,    
        "N_restrainedGPU" => N_restrainedGPU,  
        "N_displacementGPU" => N_displacementGPU,
        "N_angleGPU" => N_angleGPU,       
        "N_currPositionGPU" => N_currPositionGPU,
        "N_linMomGPU" => N_linMomGPU,      
        "N_angMomGPU" => N_angMomGPU,      
        "N_intForceGPU" => N_intForceGPU,    
        "N_intMomentGPU" => N_intMomentGPU,   
        "N_momentGPU" => N_momentGPU,      
        "N_forceGPU" => N_forceGPU,
        "N_fixedDisplacementGPU"=>N_fixedDisplacementGPU,       
        "N_orientGPU" => N_orientGPU,      
        "N_edgeIDGPU" => N_edgeIDGPU,      
        "N_edgeFirstGPU" => N_edgeFirstGPU,
        "N_materialGPU"=>    N_materialGPU,
        "N_poissonStrainGPU"=> N_poissonStrainGPU,

        "E_sourceGPU" =>E_sourceGPU,                    
        "E_targetGPU" =>E_targetGPU,                    
        "E_stressGPU" =>E_stressGPU,                    
        "E_axisGPU" =>E_axisGPU,                      
        "E_currentRestLengthGPU" =>E_currentRestLengthGPU,         
        "E_pos2GPU" =>E_pos2GPU,                      
        "E_angle1vGPU" =>E_angle1vGPU,                   
        "E_angle2vGPU" =>E_angle2vGPU,                   
        "E_angle1GPU" =>E_angle1GPU,                    
        "E_angle2GPU" =>E_angle2GPU,

        "E_strainGPU" =>E_strainGPU,
        "E_maxStrainGPU" =>E_maxStrainGPU,
        "E_strainOffsetGPU"=>E_strainOffsetGPU,
        "E_currentTransverseAreaGPU" =>E_currentTransverseAreaGPU,
        "E_currentTransverseStrainSumGPU" =>E_currentTransverseStrainSumGPU,

        "E_intForce1GPU" =>E_intForce1GPU,                 
        "E_intMoment1GPU" =>E_intMoment1GPU,                
        "E_intForce2GPU" =>E_intForce2GPU,                 
        "E_intMoment2GPU" =>E_intMoment2GPU,                
        "E_dampGPU" =>E_dampGPU,
        "E_smallAngleGPU" =>E_smallAngleGPU,
        "E_materialGPU" =>E_materialGPU
    )

    #########################################
    
    #todo change to get min stiffness
    dt=0.0251646
    E = setup["edges"][1]["material"]["stiffness"]  # MPa
    s=sqrt(setup["edges"][1]["material"]["area"])
    mass=1  
    # mass=1e-6  
    MaxFreq2=E*s/mass
    dt= 1/(6.283185*sqrt(MaxFreq2))
    println("dt: $dt")
    
    append!(displacements,[Array(metavoxel["N_displacementGPU"])])
    
    t=@timed doTimeStep!(metavoxel,dt,0)
    append!(displacements,[Array(metavoxel["N_displacementGPU"])])
    time=t[2]
    println("first timestep took $time seconds")
    t=@timed simulateParallel!(metavoxel,numTimeSteps-1,dt,returnEvery)
    time=t[2]
    
    if save
        updateDataAndSave!(metavoxel,setup,"./../../json/trialJuliaParallelGPUDynamic.json")
    end
    println("ran latticeSize $latticeSize with $voxCount nodes and $linkCount edges for $numTimeSteps time steps took $time seconds")
    return
end

#################################################################

function doTimeStep!(metavoxel,dt,currentTimeStep)
    # update forces: go through edges, get currentposition from nodes, calc pos2 and update stresses and interior forces of nodes
    run_updateEdges!(dt,currentTimeStep,
        metavoxel["E_sourceGPU"], 
        metavoxel["E_targetGPU"],
        metavoxel["E_stressGPU"],
        metavoxel["E_axisGPU"],
        metavoxel["E_currentRestLengthGPU"],
        metavoxel["E_pos2GPU"],
        metavoxel["E_angle1vGPU"],
        metavoxel["E_angle2vGPU"],
        metavoxel["E_angle1GPU"],
        metavoxel["E_angle2GPU"],
        metavoxel["E_intForce1GPU"],
        metavoxel["E_intMoment1GPU"],
        metavoxel["E_intForce2GPU"],
        metavoxel["E_intMoment2GPU"],
        metavoxel["E_dampGPU"],
        metavoxel["E_smallAngleGPU"],
        metavoxel["E_materialGPU"],
        metavoxel["E_strainGPU"],
        metavoxel["E_maxStrainGPU"],
        metavoxel["E_strainOffsetGPU"],
        metavoxel["E_currentTransverseAreaGPU"],
        metavoxel["E_currentTransverseStrainSumGPU"],
        metavoxel["N_currPositionGPU"],
        metavoxel["N_orientGPU"],
        metavoxel["N_poissonStrainGPU"])
    
    # update forces: go through nodes and update interior force (according to int forces from edges), integrate and update currpos
    run_updateNodes!(dt,currentTimeStep,
        metavoxel["N_positionGPU"], 
        metavoxel["N_restrainedGPU"],
        metavoxel["N_displacementGPU"],
        metavoxel["N_angleGPU"],
        metavoxel["N_currPositionGPU"],
        metavoxel["N_linMomGPU"],
        metavoxel["N_angMomGPU"],
        metavoxel["N_intForceGPU"],
        metavoxel["N_intMomentGPU"],
        metavoxel["N_forceGPU"],
        metavoxel["N_fixedDisplacementGPU"],
        metavoxel["N_momentGPU"],
        metavoxel["N_orientGPU"],
        metavoxel["N_edgeIDGPU"], 
        metavoxel["N_edgeFirstGPU"],
        metavoxel["N_materialGPU"],
        metavoxel["N_poissonStrainGPU"],
        metavoxel["E_intForce1GPU"],
        metavoxel["E_intMoment1GPU"],
        metavoxel["E_intForce2GPU"],
        metavoxel["E_intMoment2GPU"],
        metavoxel["E_axisGPU"],
        metavoxel["E_strainGPU"],
        metavoxel["E_materialGPU"]
        )
    
end

#################################################################

function runMetavoxelGPULive!(setup,folderPath)

    maxNumTimeSteps=setup["numTimeSteps"]
    maxNumFiles=setup["maxNumFiles"]

    saveEvery=round(maxNumTimeSteps/maxNumFiles)
    maxNumFiles=round(maxNumTimeSteps/saveEvery)-1
    setup["maxNumFiles"]=maxNumFiles

    function initialize!(setup)
        nodes      = setup["nodes"]
        edges      = setup["edges"]

        i=1
        # pre-calculate current position
        for node in nodes
            # element=parse(Int,node["id"][2:end])
            node["position"]["y"]=node["position"]["y"]+0.0
            node["position"]["z"]=node["position"]["z"]+0.0
            N_position[i]=Vector3(node["position"]["x"],node["position"]["y"],node["position"]["z"])
            N_restrained[i]=node["restrained_degrees_of_freedom"][1] ## todo later consider other degrees of freedom
            N_displacement[i]=Vector3(node["displacement"]["x"],node["displacement"]["y"],node["displacement"]["z"])
            N_angle[i]=Vector3(node["angle"]["x"],node["angle"]["y"],node["angle"]["z"])
            N_force[i]=Vector3(node["force"]["x"],node["force"]["y"],node["force"]["z"])
            N_fixedDisplacement[i]=Vector3(node["fixedDisplacement"]["x"],node["fixedDisplacement"]["y"],node["fixedDisplacement"]["z"])
            N_currPosition[i]=Vector3(node["position"]["x"],node["position"]["y"],node["position"]["z"])

            E=2000
            E = node["material"]["stiffness"]  # MPa
            nu=0.0
            if haskey(node["material"], "poissonRatio") #todo change material data to nodes
                nu= node["material"]["poissonRatio"]
            end
            # println(nu)
            
            rho=1e3
            rho = node["material"]["density"]  # MPa

            momentInertiaInverse=1.92e-6
            inertia=1/momentInertiaInverse
            zetaInternal=1.0
            zetaGlobal=0.2
            if haskey(setup,"globalDamping")
                zetaGlobal=setup["globalDamping"]
            end
            zetaCollision=0.0
            muStatic= 2.0
            muKinetic= 0.1
            nomSize=round(sqrt(node["material"]["area"] );digits=10)
            mass=round(nomSize*nomSize*nomSize *rho;digits=10)

            # E=1e6
            # nu=0.3
            # rho=1e3
            # mass=1e-6 #nomSize*nomSize*nomSize *rho
            # massInverse=1.0/mass
            # momentInertiaInverse=1.92e-6
            # inertia=1/momentInertiaInverse
            # zetaInternal=1.0
            # zetaGlobal=0.2
            # zetaCollision=0.0
            # muStatic= 2.0
            # muKinetic= 0.1
            # nomSize=0.001
            linear=true
            poisson=false
            if haskey(setup,"linear")
                linear=setup["linear"]
            end
            if haskey(setup,"poisson")
                poisson= setup["poisson"]
            end
            cTE=0.0 #Coefficient of thermal expansion
            if haskey(setup,"thermal") # later change for node matrial data
                if setup["thermal"]
                    cTE=node["material"]["cTE"]
                end
            end
            # print("poisson $poisson")
            # epsilonFail=E*1000.0
            N_material[i]=voxelMaterial(E,mass,nu,rho,zetaInternal,zetaGlobal,zetaCollision,muStatic,muKinetic,nomSize,linear,poisson,cTE)

            #voxelMaterial(E,mass,nu,rho,zeta,zetaCollision,muStatic,muKinetic,nomSize)
            i=i+1
        end 

        i=1
        # pre-calculate the axis
        for edge in edges
            # element=parse(Int,edge["id"][2:end])

            # find the nodes that the lements connects
            fromNode = nodes[edge["source"]+1]
            toNode = nodes[edge["target"]+1]


            node1 = [fromNode["position"]["x"] fromNode["position"]["y"] fromNode["position"]["z"]]
            node2 = [toNode["position"]["x"] toNode["position"]["y"] toNode["position"]["z"]]

            length=norm(node2-node1)
            axis=normalize(collect(Iterators.flatten(node2-node1)))


            E_source[i]=edge["source"]+1
            E_target[i]=edge["target"]+1
            E_axis[i]=Vector3(axis[1],axis[2],axis[3])
            E_currentRestLength[i]=length #?????? todo change
            

            N_edgeID[E_source[i],N_currEdge[E_source[i]]]=i
            N_edgeFirst[E_source[i],N_currEdge[E_source[i]]]=true
            N_currEdge[E_source[i]]+=1

            N_edgeID[E_target[i],N_currEdge[E_target[i]]]=i
            N_edgeFirst[E_target[i],N_currEdge[E_target[i]]]=false
            N_currEdge[E_target[i]]+=1
            
            E=edge["material"]["stiffness"]
            E=(N_material[E_source[i]].E+N_material[E_target[i]].E)/2.0
            mass=(N_material[E_source[i]].mass+N_material[E_target[i]].mass)/2.0
            nu=(N_material[E_source[i]].nu+N_material[E_target[i]].nu)/2.0
            rho=edge["material"]["density"]
            rho=(N_material[E_source[i]].rho+N_material[E_target[i]].rho)/2.0

            
            # E_material[i]=edgeMaterial()

            # E=edge["material"]["stiffness"]
            area=edge["material"]["area"]
            # mass=1e-6
            
            loaded=0.0
            if(haskey(edge, "loaded"))
                loaded=edge["loaded"]
            end

            strainRatio=(N_material[E_source[i]].E / N_material[E_target[i]].E)
            linear=(N_material[E_source[i]].linear && N_material[E_target[i]].linear)
            poisson=(N_material[E_source[i]].poisson || N_material[E_target[i]].poisson)
            # epsilonFail=(N_material[E_source[i]].epsilonFail+N_material[E_target[i]].epsilonFail)/2.0 #TODO CHANGE TO SMALLEST
            b=sqrt(area)
            h=sqrt(area)
            E_currentTransverseArea[i]=b*h

            linear=true
            if haskey(setup,"linear")
                linear=setup["linear"]
            end
            cTE=0.0 #Coefficient of thermal expansion
            if haskey(edge,"cTE")
                cTE=edge["cTE"]
            end
            cTE=N_material[E_source[i]].cTE+N_material[E_target[i]].cTE      

            E_material[i]=edgeMaterial(E,mass,nu,rho,b,h,length,loaded,strainRatio,linear,poisson,cTE)

            if !linear
                plasticModulus=5e5
                yieldStress=1e5
                failureStress=-1.0
                E_material[i]=setModelBilinear(E_material[i], plasticModulus, yieldStress)
                
            end
            
            i=i+1
        end 
    end
    function simulateParallel!(metavoxel,maxNumTimeSteps,dt)
        # initialize(setup)
        for i in 1:maxNumTimeSteps
            doTimeStep!(metavoxel,dt,i)
            if(mod(i,saveEvery)==0)
                #append!(displacements,[Array(metavoxel["N_displacementGPU"])])
                updateDataAndSave!(metavoxel,setup,"$(folderPath)$(numFile).json")
                numFile+=1
                if numFile>maxNumFiles
                    numFile=0
                end
            end
        end
    end
    
    ########
    voxCount=0
    linkCount=0
    nodes      = setup["nodes"]
    edges      = setup["edges"]
    voxCount=size(nodes)[1]
    linkCount=size(edges)[1]
    strain =0 #todooo moveeee
    maxNumEdges=10

    ########
    voxCount=0
    linkCount=0
    nodes      = setup["nodes"]
    edges      = setup["edges"]
    voxCount=size(nodes)[1]
    linkCount=size(edges)[1]
    strain =0 #todooo moveeee

    ############# nodes
    N_position=fill(Vector3(),voxCount)
    N_restrained=zeros(Bool, voxCount)
    N_displacement=fill(Vector3(),voxCount)
    N_angle=fill(Vector3(),voxCount)
    N_currPosition=fill(Vector3(),voxCount)
    N_linMom=fill(Vector3(),voxCount)
    N_angMom=fill(Vector3(),voxCount)
    N_intForce=fill(Vector3(),voxCount)
    N_intMoment=fill(Vector3(),voxCount)
    N_moment=fill(Vector3(),voxCount)
    N_force=fill(Vector3(),voxCount)
    N_fixedDisplacement=fill(Vector3(),voxCount)
    N_orient=fill(Quaternion(),voxCount)
    N_edgeID=fill(-1,(voxCount,maxNumEdges))
    N_edgeFirst=fill(true,(voxCount,maxNumEdges))
    N_currEdge=fill(1,voxCount)
    N_material=fill(voxelMaterial(),voxCount)
    N_poissonStrain=fill(Vector3(),voxCount)
    #voxelMaterial(E,mass,nu,rho,zeta,zetaCollision,muStatic,muKinetic,nomSize)

    ############# edges
    E_source=fill(0,linkCount)
    E_target=fill(0,linkCount)
    E_stress=fill(0.0,linkCount)
    E_axis=fill(Vector3(1.0,0.0,0.0),linkCount)
    E_currentRestLength=fill(0.0,linkCount)
    E_pos2=fill(Vector3(),linkCount)
    E_angle1v=fill(Vector3(),linkCount)
    E_angle2v=fill(Vector3(),linkCount)
    E_angle1=fill(Quaternion(),linkCount)
    E_angle2=fill(Quaternion(),linkCount)

    E_intForce1=fill(Vector3(),linkCount)
    E_intMoment1=fill(Vector3(),linkCount) 

    E_intForce2=fill(Vector3(),linkCount)
    E_intMoment2=fill(Vector3(),linkCount)
    E_damp=fill(false,linkCount)
    E_smallAngle=fill(true,linkCount)
    E_material=fill(edgeMaterial(),linkCount)
    
    E_strain=fill(0.0,linkCount)
    E_maxStrain=fill(0.0,linkCount)
    E_strainOffset=fill(0.0,linkCount)
    E_currentTransverseArea=fill(0.0,linkCount)
    E_currentTransverseStrainSum=fill(0.0,linkCount)# TODO remove ot incorporate


    #################################################################
    initialize!(setup)
    #################################################################

    ########################## turn to cuda arrays
    ############# nodes
    N_positionGPU=      CuArray(N_position)      
    N_restrainedGPU=    CuArray(N_restrained)  
    N_displacementGPU=  CuArray(N_displacement)   
    N_angleGPU=         CuArray(N_angle)       
    N_currPositionGPU=  CuArray(N_currPosition)    
    N_linMomGPU=        CuArray(N_linMom)        
    N_angMomGPU=        CuArray(N_angMom)        
    N_intForceGPU=      CuArray(N_intForce)     
    N_intMomentGPU=     CuArray(N_intMoment)        
    N_momentGPU=        CuArray(N_moment)         
    N_forceGPU=         CuArray(N_force)
    N_fixedDisplacementGPU= CuArray(N_fixedDisplacement)           
    N_orientGPU=        CuArray(N_orient)       
    N_edgeIDGPU=        CuArray(N_edgeID)         
    N_edgeFirstGPU=     CuArray(N_edgeFirst)
    N_materialGPU=      CuArray(N_material)
    N_poissonStrainGPU= CuArray(N_poissonStrain) 


    ############# edges
    E_sourceGPU=                    CuArray(E_source)   
    E_targetGPU=                    CuArray(E_target)
    E_stressGPU=                    CuArray(E_stress)
    E_axisGPU=                      CuArray(E_axis)          
    E_currentRestLengthGPU=         CuArray(E_currentRestLength)
    E_pos2GPU=                      CuArray(E_pos2)
    E_angle1vGPU=                   CuArray(E_angle1v)
    E_angle2vGPU=                   CuArray(E_angle2v)
    E_angle1GPU=                    CuArray(E_angle1)
    E_angle2GPU=                    CuArray(E_angle2)

    E_strainGPU=                    CuArray(E_strain)
    E_maxStrainGPU=                 CuArray(E_maxStrain)
    E_strainOffsetGPU=              CuArray(E_strainOffset)
    E_currentTransverseAreaGPU=     CuArray(E_currentTransverseArea)
    E_currentTransverseStrainSumGPU=CuArray(E_currentTransverseStrainSum)# TODO remove ot incorporate

    E_intForce1GPU=                 CuArray(E_intForce1) 
    E_intMoment1GPU=                CuArray(E_intMoment1)  
    E_intForce2GPU=                 CuArray(E_intForce2) 
    E_intMoment2GPU=                CuArray(E_intMoment2)
    E_dampGPU=                      CuArray(E_damp)
    E_smallAngleGPU=                CuArray(E_smallAngle)
    E_materialGPU=                  CuArray(E_material)


    #########################################
    metavoxel = Dict(
        "N_positionGPU" => N_positionGPU,    
        "N_restrainedGPU" => N_restrainedGPU,  
        "N_displacementGPU" => N_displacementGPU,
        "N_angleGPU" => N_angleGPU,       
        "N_currPositionGPU" => N_currPositionGPU,
        "N_linMomGPU" => N_linMomGPU,      
        "N_angMomGPU" => N_angMomGPU,      
        "N_intForceGPU" => N_intForceGPU,    
        "N_intMomentGPU" => N_intMomentGPU,   
        "N_momentGPU" => N_momentGPU,      
        "N_forceGPU" => N_forceGPU,
        "N_fixedDisplacementGPU"=>N_fixedDisplacementGPU,       
        "N_orientGPU" => N_orientGPU,      
        "N_edgeIDGPU" => N_edgeIDGPU,      
        "N_edgeFirstGPU" => N_edgeFirstGPU,
        "N_materialGPU"=>    N_materialGPU,
        "N_poissonStrainGPU"=> N_poissonStrainGPU,

        "E_sourceGPU" =>E_sourceGPU,                    
        "E_targetGPU" =>E_targetGPU,                    
        "E_stressGPU" =>E_stressGPU,                    
        "E_axisGPU" =>E_axisGPU,                      
        "E_currentRestLengthGPU" =>E_currentRestLengthGPU,         
        "E_pos2GPU" =>E_pos2GPU,                      
        "E_angle1vGPU" =>E_angle1vGPU,                   
        "E_angle2vGPU" =>E_angle2vGPU,                   
        "E_angle1GPU" =>E_angle1GPU,                    
        "E_angle2GPU" =>E_angle2GPU,  

        "E_strainGPU" =>E_strainGPU,
        "E_maxStrainGPU" =>E_maxStrainGPU,
        "E_strainOffsetGPU"=>E_strainOffsetGPU,
        "E_currentTransverseAreaGPU" =>E_currentTransverseAreaGPU,
        "E_currentTransverseStrainSumGPU" =>E_currentTransverseStrainSumGPU,

        "E_intForce1GPU" =>E_intForce1GPU,                 
        "E_intMoment1GPU" =>E_intMoment1GPU,                
        "E_intForce2GPU" =>E_intForce2GPU,                 
        "E_intMoment2GPU" =>E_intMoment2GPU,                
        "E_dampGPU" =>E_dampGPU,
        "E_smallAngleGPU" =>E_smallAngleGPU,
        "E_materialGPU" =>E_materialGPU
    )

    #########################################
    
    #todo make recommended timestep a function
    dt=0.0251646
    E = setup["edges"][1]["material"]["stiffness"]  # MPa
    s=round(sqrt(setup["edges"][1]["material"]["area"] );digits=10)
    s=E_material[1].L
    mass=N_material[1].mass
    MaxFreq2=E*s/mass
    if(setup["poisson"])
        # mat->_eHat*currentTransverseArea/((strain+1.0)*currentRestLength)
        eHat=E_material[1].eHat
        temp=eHat*E_material[1].b*E_material[1].h/((0.0+1.0)*E_material[1].L)
        MaxFreq2=temp/mass
    end
    dt= 1.0/(6.283185*sqrt(MaxFreq2))
    # println("E: $(E_material[1].E)")
    # println("L: $(E_material[1].L)")
    # println("b: $(E_material[1].b)")
    # println("h: $(E_material[1].h)")
    # println("a1: $(E_material[1].a1)")
    # println("a2: $(E_material[1].a2)")
    # println("b1: $(E_material[1].b1)")
    # println("b2: $(E_material[1].b2)")
    # println("b3: $(E_material[1].b3)")
    # println("eHat: $(E_material[1].eHat)")
    println("dt: $dt")
    
    numFile=0
    numSaves=0
    
    
    t=@timed doTimeStep!(metavoxel,dt,0)
    time=t[2]
    println("first timestep took $time seconds")
    t=@timed simulateParallel!(metavoxel,maxNumTimeSteps-1,dt)
    time=t[2]
    
    setup["maxNumFiles"]=numSaves
    

    println("ran $voxCount nodes and $linkCount edges for $maxNumTimeSteps time steps took $time seconds")
    return
end

########################################

function getYoungsModulus(latticeSize,voxelSize,disp,Load,topNodesIndices)
    F=-Load
    l0=voxelSize*latticeSize
    A=l0*l0

    δl1=-mean( x.y for x in disp[topNodesIndices])
        
    stresses=F/A
    strain=δl1/l0
    println("Load=$Load")
    println("stress=$stresses")

    E=stresses/strain 

    return E
end

####################################

function runMetavoxel2DGPULive!(setup,x,nelx,nely,p,EE,nu,maxNumTimeSteps,saveEvery,maxNumFiles,save)
    function initialize!(setup)
        nodes      = setup["nodes"]
        edges      = setup["edges"]
        
        d=10.0

        for ii in 0:(nelx-1)
            for jj in 0:(nely-1)
                
                i=(ii*nely +jj)+1
                nodes[i]=deepcopy(nodes[1])
                
                N_position[i]=Vector3(ii*d,jj*d + 2.0*d ,0.0)
                nodes[i]["position"]["x"]=N_position[i].x
                nodes[i]["position"]["y"]=N_position[i].y
                nodes[i]["position"]["z"]=N_position[i].z
                
                nodes[i]["id"]="n$(i-1)"
                
                if((ii==0))
                #if((ii==0&&jj==0)||(ii==(nelx-1)&&jj==0))
                    N_restrained[i]=true## todo later consider other degrees of freedom
                    nodes[i]["restrained_degrees_of_freedom"]=[true,true,true,true,true,true]
                else
                    N_restrained[i]=false ## todo later consider other degrees of freedom
                    nodes[i]["restrained_degrees_of_freedom"]=[false,false,false,true,true,true]
                end
                
                #if((ii==round(nelx/2)&&jj==(nely-1)))
                #if((ii==round(nelx/2)&&jj==(nely-1))||(ii==round(nelx/2)-1&&jj==(nely-1)))
                if((ii==round(nelx-3)&&jj==(nely-1)))
                    N_force[i]=Vector3(0.0,-100.0,0.0)
                    
                else
                    N_force[i]=Vector3(0.0,0.0,0.0)
                end
                
                nodes[i]["force"]["x"]=N_force[i].x
                nodes[i]["force"]["y"]=N_force[i].y
                nodes[i]["force"]["z"]=N_force[i].z
                
                nodes[i]["displacement"]["x"]=0.0
                nodes[i]["displacement"]["y"]=0.0
                nodes[i]["displacement"]["z"]=0.0
                
                
                
                N_material[i]=voxelMaterial()
                
                if ii==(nelx-1)&& jj==(nely-1)
                    E=EE*x[ii,jj]^p
                    nodes[i]["size"]=x[ii,jj]
                elseif ii==(nelx-1)
                    E=EE*x[ii,jj+1]^p
                    nodes[i]["size"]=x[ii,jj+1]
                elseif jj==(nely-1)
                    E=EE*x[ii+1,jj]^p
                    nodes[i]["size"]=x[ii+1,jj]
                else
                    E=EE*x[ii+1,jj+1]^p
                    nodes[i]["size"]=x[ii+1,jj+1]
                
                end
                
                mass=10
                rho=7.85e-9
                momentInertiaInverse=1.92e-6
                zeta=1.0
                zetaCollision=0.0
                zetaGlobal=0.2
                zetaInternal=1.0
                muStatic= 2.0
                muKinetic= 0.1
                nomSize=1.0
                linear=true
                poisson=false 
                cTE=0.0 #Coefficient of thermal expansion
                N_material[i]=voxelMaterial(E,mass,nu,rho,zetaInternal,zetaGlobal,zetaCollision,muStatic,muKinetic,nomSize,linear,poisson,cTE)

                N_currPosition[i]=deepcopy(N_position[i])
                
                
            end
        end
        
        j=1
        for ii in 0:(nelx-1)
            for jj in 0:(nely-1)
                i=(ii*nely +jj)+1
                if ii<(nelx-1)
                    edges[j]=deepcopy(edges[1])
                    node1 = [ii*d jj*d 0.0]
                    node2 = [(ii+1)*d jj*d 0.0]
                    
                    length=d
                    length=norm(node2-node1)
                    axis=normalize(collect(Iterators.flatten(node2-node1)))
                    
                    E_source[j]=i
                    E_target[j]=((ii+1)*nely +jj)+1
                    
                    edges[j]["id"]="e$(j-1)"
                    
                    edges[j]["source"]=E_source[j]-1
                    edges[j]["target"]=E_target[j]-1
                    E_axis[j]=Vector3(axis[1],axis[2],axis[3])
                    E_currentRestLength[j]=length
                    
                    
                    
                    N_edgeID[E_source[j],N_currEdge[E_source[j]]]=j
                    N_edgeFirst[E_source[j],N_currEdge[E_source[j]]]=true
                    N_currEdge[E_source[j]]+=1

                    N_edgeID[E_target[j],N_currEdge[E_target[j]]]=j
                    N_edgeFirst[E_target[j],N_currEdge[E_target[j]]]=false
                    N_currEdge[E_target[j]]+=1
                    
                    
                    

                    #E=(N_material[E_source[j]].E+N_material[E_target[j]].E)/2.0
                    #mass=(N_material[E_source[j]].mass+N_material[E_target[j]].mass)/2.0
                    #nu=(N_material[E_source[j]].nu+N_material[E_target[j]].nu)/2.0
                    #rho=(N_material[E_source[j]].rho+N_material[E_target[j]].rho)/2.0
                    E=(N_material[E_source[j]].E)/1.0
                    mass=(N_material[E_source[j]].mass)/1.0
                    nu=(N_material[E_source[j]].nu)/1.0
                    rho=(N_material[E_source[j]].rho)/1.0
                    
                    edges[j]["material"]["area"]=(E/EE)^(1/p)
                    
                    E_material[j]=edgeMaterial()
                    loaded=0.0
                    linear=true
                    poisson=false
                    cTE=0.0 #Coefficient of thermal expansion

                    E_material[j]=edgeMaterial(E,mass,nu,rho,2.38,2.38,length,loaded,1.0,linear,poisson,cTE)       
                    
                    j+=1
                end
                if jj<(nely-1)
                    edges[j]=deepcopy(edges[1])
                    node1 = [ii*d jj*d 0.0]
                    node2 = [(ii)*d (jj+1)*d 0.0]
                    
                    length=d
                    length=norm(node2-node1)
                    axis=normalize(collect(Iterators.flatten(node2-node1)))
                    
                    E_source[j]=i
                    E_target[j]=((ii)*nely +(jj+1))+1
                    
                    edges[j]["id"]="e$(j-1)"
                    
                    edges[j]["source"]=E_source[j]-1
                    edges[j]["target"]=E_target[j]-1
                    E_axis[j]=Vector3(axis[1],axis[2],axis[3])
                    E_currentRestLength[j]=length
                    
                    N_edgeID[E_source[j],N_currEdge[E_source[j]]]=j
                    N_edgeFirst[E_source[j],N_currEdge[E_source[j]]]=true
                    N_currEdge[E_source[j]]+=1

                    N_edgeID[E_target[j],N_currEdge[E_target[j]]]=j
                    N_edgeFirst[E_target[j],N_currEdge[E_target[j]]]=false
                    N_currEdge[E_target[j]]+=1

                    #E=(N_material[E_source[j]].E+N_material[E_target[j]].E)/2.0
                    #mass=(N_material[E_source[j]].mass+N_material[E_target[j]].mass)/2.0
                    #nu=(N_material[E_source[j]].nu+N_material[E_target[j]].nu)/2.0
                    #rho=(N_material[E_source[j]].rho+N_material[E_target[j]].rho)/2.0
                    E=(N_material[E_source[j]].E)/1.0
                    mass=(N_material[E_source[j]].mass)/1.0
                    nu=(N_material[E_source[j]].nu)/1.0
                    rho=(N_material[E_source[j]].rho)/1.0
                    edges[j]["material"]["area"]=(E/EE)^(1/p)

                    E_material[j]=edgeMaterial()
                    loaded=0.0
                    poisson=false
                    linear=true
                    cTE=0.0 #Coefficient of thermal expansion
                    E_material[j]=edgeMaterial(E,mass,nu,rho,2.38,2.38,length,loaded,1.0,linear,poisson,cTE) 

                    
                    j+=1
                    
                end
                
                if jj>0&&ii<(nelx-1)
                    edges[j]=deepcopy(edges[1])
                    node1 = [ii*d jj*d 0.0]
                    node2 = [(ii+1)*d (jj-1)*d 0.0]
                    
                    length=d
                    length=norm(node2-node1)
                    axis=normalize(collect(Iterators.flatten(node2-node1)))
                    
                    E_source[j]=i
                    E_target[j]=((ii+1)*nely +(jj-1))+1
                    
                    edges[j]["id"]="e$(j-1)"
                    
                    edges[j]["source"]=E_source[j]-1
                    edges[j]["target"]=E_target[j]-1
                    E_axis[j]=Vector3(axis[1],axis[2],axis[3])
                    E_currentRestLength[j]=length
                    
                    N_edgeID[E_source[j],N_currEdge[E_source[j]]]=j
                    N_edgeFirst[E_source[j],N_currEdge[E_source[j]]]=true
                    N_currEdge[E_source[j]]+=1

                    N_edgeID[E_target[j],N_currEdge[E_target[j]]]=j
                    N_edgeFirst[E_target[j],N_currEdge[E_target[j]]]=false
                    N_currEdge[E_target[j]]+=1

                    #E=(N_material[E_source[j]].E+N_material[E_target[j]].E)/2.0
                    #mass=(N_material[E_source[j]].mass+N_material[E_target[j]].mass)/2.0
                    #nu=(N_material[E_source[j]].nu+N_material[E_target[j]].nu)/2.0
                    #rho=(N_material[E_source[j]].rho+N_material[E_target[j]].rho)/2.0
                    E=(N_material[E_source[j]].E)/1.0
                    mass=(N_material[E_source[j]].mass)/1.0
                    nu=(N_material[E_source[j]].nu)/1.0
                    rho=(N_material[E_source[j]].rho)/1.0
                    
                    edges[j]["material"]["area"]=(E/EE)^(1/p)

                    E_material[j]=edgeMaterial()
                    loaded=0.0
                    linear=true 
                    poisson=false
                    cTE=0.0 #Coefficient of thermal expansion
                    E_material[j]=edgeMaterial(E,mass,nu,rho,2.38,2.38,length,loaded,1.0,linear,poisson,cTE) 

                    
                    j+=1
                    
                end
                
                if jj<(nely-1)&&ii<(nelx-1)
                    edges[j]=deepcopy(edges[1])
                    node1 = [ii*d jj*d 0.0]
                    node2 = [(ii+1)*d (jj+1)*d 0.0]
                    
                    length=d
                    length=norm(node2-node1)
                    axis=normalize(collect(Iterators.flatten(node2-node1)))
                    
                    E_source[j]=i
                    E_target[j]=((ii+1)*nely +(jj+1))+1
                    
                    edges[j]["id"]="e$(j-1)"
                    
                    edges[j]["source"]=E_source[j]-1
                    edges[j]["target"]=E_target[j]-1
                    E_axis[j]=Vector3(axis[1],axis[2],axis[3])
                    E_currentRestLength[j]=length
                    
                    N_edgeID[E_source[j],N_currEdge[E_source[j]]]=j
                    N_edgeFirst[E_source[j],N_currEdge[E_source[j]]]=true
                    N_currEdge[E_source[j]]+=1

                    N_edgeID[E_target[j],N_currEdge[E_target[j]]]=j
                    N_edgeFirst[E_target[j],N_currEdge[E_target[j]]]=false
                    N_currEdge[E_target[j]]+=1

                    #E=(N_material[E_source[j]].E+N_material[E_target[j]].E)/2.0
                    #mass=(N_material[E_source[j]].mass+N_material[E_target[j]].mass)/2.0
                    #nu=(N_material[E_source[j]].nu+N_material[E_target[j]].nu)/2.0
                    #rho=(N_material[E_source[j]].rho+N_material[E_target[j]].rho)/2.0
                    E=(N_material[E_source[j]].E)/1.0
                    mass=(N_material[E_source[j]].mass)/1.0
                    nu=(N_material[E_source[j]].nu)/1.0
                    rho=(N_material[E_source[j]].rho)/1.0
                    
                    edges[j]["material"]["area"]=(E/EE)^(1/p)

                    E_material[j]=edgeMaterial()
                    loaded=0.0
                    linear=true
                    poisson=false
                    cTE=0.0 #Coefficient of thermal expansion
                    E_material[j]=edgeMaterial(E,mass,nu,rho,2.38,2.38,length,loaded,1.0,linear,poisson,cTE) 

                    
                    j+=1
                    
                end
                
            end
        end
    end
    function simulateParallel!(metavoxel,maxNumTimeSteps,dt,saveEvery)
        # initialize(setup)

        for i in 1:maxNumTimeSteps
            doTimeStep!(metavoxel,dt,i)
            if(mod(i,saveEvery)==0&&save)
                updateDataAndSave!(metavoxel,setup,"./../../json/live/$(numFile).json")
                numFile+=1
                if numFile>maxNumFiles
                    numFile=0
                end
            end
        end
    end
    
    ########

    maxNumEdges=10

    ########
    #nelx=20
    #nely=10
    ########
    voxCount=(nelx)*(nely)
    linkCount=(nelx-1)*(nely)+(nelx)*(nely-1)
    linkCount=(nelx-1)*(nely)+(nelx)*(nely-1)+(nelx-1)*(nely-1)
    linkCount=(nelx-1)*(nely)+(nelx)*(nely-1)+(nelx-1)*(nely-1)+(nelx-1)*(nely-1) 
    setup["nodes"]      =  fill(deepcopy(setup["nodes"][1]),voxCount)
    setup["edges"]      =  fill(deepcopy(setup["edges"][1]),linkCount)
    strain =0 #todooo moveeee

    ############# nodes
    N_position=fill(Vector3(),voxCount)
    N_restrained=zeros(Bool, voxCount)
    N_displacement=fill(Vector3(),voxCount)
    N_angle=fill(Vector3(),voxCount)
    N_currPosition=fill(Vector3(),voxCount)
    N_linMom=fill(Vector3(),voxCount)
    N_angMom=fill(Vector3(),voxCount)
    N_intForce=fill(Vector3(),voxCount)
    N_intMoment=fill(Vector3(),voxCount)
    N_moment=fill(Vector3(),voxCount)
    N_force=fill(Vector3(),voxCount)
    N_fixedDisplacement=fill(Vector3(),voxCount)
    N_orient=fill(Quaternion(),voxCount)
    N_edgeID=fill(-1,(voxCount,maxNumEdges))
    N_edgeFirst=fill(true,(voxCount,maxNumEdges))
    N_currEdge=fill(1,voxCount)
    N_material=fill(voxelMaterial(),voxCount)
    N_poissonStrain=fill(Vector3(),voxCount)
    #voxelMaterial(E,mass,nu,rho,zeta,zetaCollision,muStatic,muKinetic,nomSize)

    ############# edges
    E_source=fill(0,linkCount)
    E_target=fill(0,linkCount)
    E_stress=fill(0.0,linkCount)
    E_axis=fill(Vector3(1.0,0.0,0.0),linkCount)
    E_currentRestLength=fill(0.0,linkCount)
    E_pos2=fill(Vector3(),linkCount)
    E_angle1v=fill(Vector3(),linkCount)
    E_angle2v=fill(Vector3(),linkCount)
    E_angle1=fill(Quaternion(),linkCount)
    E_angle2=fill(Quaternion(),linkCount)

    E_intForce1=fill(Vector3(),linkCount)
    E_intMoment1=fill(Vector3(),linkCount) 

    E_intForce2=fill(Vector3(),linkCount)
    E_intMoment2=fill(Vector3(),linkCount)
    E_damp=fill(false,linkCount)
    E_smallAngle=fill(true,linkCount)
    E_material=fill(edgeMaterial(),linkCount)

    E_strain=fill(0.0,linkCount)
    E_maxStrain=fill(0.0,linkCount)
    E_strainOffset=fill(0.0,linkCount)
    E_currentTransverseArea=fill(0.0,linkCount)
    E_currentTransverseStrainSum=fill(0.0,linkCount)# TODO remove ot incorporate


    #################################################################
    initialize!(setup)
    #################################################################

    ########################## turn to cuda arrays
    ############# nodes
    N_positionGPU=      CuArray(N_position)      
    N_restrainedGPU=    CuArray(N_restrained)  
    N_displacementGPU=  CuArray(N_displacement)   
    N_angleGPU=         CuArray(N_angle)       
    N_currPositionGPU=  CuArray(N_currPosition)    
    N_linMomGPU=        CuArray(N_linMom)        
    N_angMomGPU=        CuArray(N_angMom)        
    N_intForceGPU=      CuArray(N_intForce)     
    N_intMomentGPU=     CuArray(N_intMoment)        
    N_momentGPU=        CuArray(N_moment)         
    N_forceGPU=         CuArray(N_force)
    N_fixedDisplacementGPU=CuArray(N_fixedDisplacement)           
    N_orientGPU=        CuArray(N_orient)       
    N_edgeIDGPU=        CuArray(N_edgeID)         
    N_edgeFirstGPU=     CuArray(N_edgeFirst)
    N_materialGPU=      CuArray(N_material)
    N_poissonStrainGPU= CuArray(N_poissonStrain)


    ############# edges
    E_sourceGPU=                    CuArray(E_source)   
    E_targetGPU=                    CuArray(E_target)
    E_stressGPU=                    CuArray(E_stress)
    E_axisGPU=                      CuArray(E_axis)          
    E_currentRestLengthGPU=         CuArray(E_currentRestLength)
    E_pos2GPU=                      CuArray(E_pos2)
    E_angle1vGPU=                   CuArray(E_angle1v)
    E_angle2vGPU=                   CuArray(E_angle2v)
    E_angle1GPU=                    CuArray(E_angle1)
    E_angle2GPU=                    CuArray(E_angle2)

    E_strainGPU=                    CuArray(E_strain)
    E_maxStrainGPU=                 CuArray(E_maxStrain)
    E_strainOffsetGPU=              CuArray(E_strainOffset)
    E_currentTransverseAreaGPU=     CuArray(E_currentTransverseArea)
    E_currentTransverseStrainSumGPU=CuArray(E_currentTransverseStrainSum)# TODO remove ot incorporate

    E_intForce1GPU=                 CuArray(E_intForce1) 
    E_intMoment1GPU=                CuArray(E_intMoment1)  
    E_intForce2GPU=                 CuArray(E_intForce2) 
    E_intMoment2GPU=                CuArray(E_intMoment2)
    E_dampGPU=                      CuArray(E_damp)
    E_smallAngleGPU=                CuArray(E_smallAngle)
    E_materialGPU=                  CuArray(E_material)


    #########################################
    metavoxel = Dict(
        "N_positionGPU" => N_positionGPU,    
        "N_restrainedGPU" => N_restrainedGPU,  
        "N_displacementGPU" => N_displacementGPU,
        "N_angleGPU" => N_angleGPU,       
        "N_currPositionGPU" => N_currPositionGPU,
        "N_linMomGPU" => N_linMomGPU,      
        "N_angMomGPU" => N_angMomGPU,      
        "N_intForceGPU" => N_intForceGPU,    
        "N_intMomentGPU" => N_intMomentGPU,   
        "N_momentGPU" => N_momentGPU,      
        "N_forceGPU" => N_forceGPU,
        "N_fixedDisplacementGPU"=>N_fixedDisplacementGPU,       
        "N_orientGPU" => N_orientGPU,      
        "N_edgeIDGPU" => N_edgeIDGPU,      
        "N_edgeFirstGPU" => N_edgeFirstGPU,
        "N_materialGPU"=>    N_materialGPU,
        "N_poissonStrainGPU"=> N_poissonStrainGPU,

        "E_sourceGPU" =>E_sourceGPU,                    
        "E_targetGPU" =>E_targetGPU,                    
        "E_stressGPU" =>E_stressGPU,                    
        "E_axisGPU" =>E_axisGPU,                      
        "E_currentRestLengthGPU" =>E_currentRestLengthGPU,         
        "E_pos2GPU" =>E_pos2GPU,                      
        "E_angle1vGPU" =>E_angle1vGPU,                   
        "E_angle2vGPU" =>E_angle2vGPU,                   
        "E_angle1GPU" =>E_angle1GPU,                    
        "E_angle2GPU" =>E_angle2GPU,

        "E_strainGPU" =>E_strainGPU,
        "E_maxStrainGPU" =>E_maxStrainGPU,
        "E_strainOffsetGPU"=>E_strainOffsetGPU,
        "E_currentTransverseAreaGPU" =>E_currentTransverseAreaGPU,
        "E_currentTransverseStrainSumGPU" =>E_currentTransverseStrainSumGPU,

        "E_intForce1GPU" =>E_intForce1GPU,                 
        "E_intMoment1GPU" =>E_intMoment1GPU,                
        "E_intForce2GPU" =>E_intForce2GPU,                 
        "E_intMoment2GPU" =>E_intMoment2GPU,                
        "E_dampGPU" =>E_dampGPU,
        "E_smallAngleGPU"=>E_smallAngleGPU,
        "E_materialGPU" =>E_materialGPU
    )

    #########################################
    

    dt=0.0251646
    s=2.38
    E=EE*maximum(x)
    mass=1
    # mass=1e-6  
    MaxFreq2=E*s/mass
    dt= 1/(6.283185*sqrt(MaxFreq2))
    
    numFile=0
    numSaves=0
    
    
    t=@timed doTimeStep!(metavoxel,dt,0)
    time=t[2]
    if save
        println("dt: $dt")
        println("first timestep took $time seconds")
    end
    t=@timed simulateParallel!(metavoxel,maxNumTimeSteps-1,dt,saveEvery)
    time=t[2]
    
    setup["maxNumFiles"]=numSaves
    
    if save
        println("ran $voxCount nodes and $linkCount edges for $maxNumTimeSteps time steps took $time seconds")
    end
    return metavoxel,Array(metavoxel["N_displacementGPU"])
end