# Amira Abdel-Rahman # (c) Massachusetts Institute of Technology 2020 using LinearAlgebra using Plots import JSON using StaticArrays, Rotations # BASED ON https://github.com/jonhiller/Voxelyze function simulateParallel(setup,numTimeSteps,dt,static=true,saveInterval=10) initialize(setup) for i in 1:numTimeSteps # println("Timestep:",i) doTimeStep(setup,dt,static,i,saveInterval); end end function initialize(setup) nodes = setup["nodes"] edges = setup["edges"] # pre-calculate current position for node in nodes # element=parse(Int,node["id"][2:end]) append!(N_position,[[node["position"]["x"] node["position"]["y"] node["position"]["z"]]]) append!(N_degrees_of_freedom,[node["degrees_of_freedom"]]) append!(N_restrained_degrees_of_freedom, [node["restrained_degrees_of_freedom"]]) append!(N_displacement,[[node["displacement"]["x"] node["displacement"]["y"] node["displacement"]["z"]]]) append!(N_angle,[[node["angle"]["x"] node["angle"]["y"] node["angle"]["z"]]]) append!(N_force,[[node["force"]["x"] node["force"]["y"] node["force"]["z"]]]) append!(N_currPosition,[[node["position"]["x"] node["position"]["y"] node["position"]["z"]]]) append!(N_orient,[Quat(1.0,0.0,0.0,0.0)])#quat append!(N_linMom,[[0 0 0]]) append!(N_angMom,[[0 0 0]]) append!(N_intForce,[[0 0 0]]) append!(N_intMoment,[[0 0 0]]) append!(N_moment,[[0 0 0]]) # for dynamic simulations append!(N_posTimeSteps,[[]]) append!(N_angTimeSteps,[[]]) end # 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))) append!(E_source,[edge["source"]+1]) append!(E_target,[edge["target"]+1]) append!(E_area,[edge["area"]]) append!(E_density,[edge["density"]]) append!(E_stiffness,[edge["stiffness"]]) append!(E_stress,[0]) append!(E_axis,[axis]) append!(E_currentRestLength,[length]) append!(E_pos2,[[0 0 0]]) append!(E_angle1v,[[0 0 0]]) append!(E_angle2v,[[0 0 0]]) append!(E_angle1,[Quat(1.0,0,0,0)]) #quat append!(E_angle2,[Quat(1.0,0,0,0)]) #quat append!(E_currentTransverseStrainSum,[0]) # for dynamic simulations append!(E_stressTimeSteps,[[]]) end end function doTimeStep(setup,dt,static,currentTimeStep,saveInterval) nodes = setup["nodes"] edges = setup["edges"] voxCount=size(nodes)[1] linkCount=size(edges)[1] if dt==0 return true elseif (dt<0) dt = recommendedTimeStep() end # if (collisions) updateCollisions(); collisions=false # Euler integration: Diverged = false # for edge in edges for i in 1:linkCount # 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"]] # updateForces(setup,edge,node1,node2,static)# element numbers?? updateForces(setup,i,static)# element numbers?? # todo: update forces and whatever if axialStrain(true) > 100 Diverged = true; # catch divergent condition! (if any thread sets true we will fail, so don't need mutex... end end if Diverged println("Divergedd!!!!!") return false end for i in 1:voxCount timeStep(dt,i,static,currentTimeStep) # timeStep(dt,node,static,currentTimeStep) if(!static&& currentTimeStep%saveInterval==0) append!(N_posTimeSteps[i],[N_displacement[i]]) append!(N_angTimeSteps[i],[N_angle[i]]) end # todo: update linMom,angMom, orient and whatever end currentTimeStep = currentTimeStep+dt return true end function updateForces(setup,edge,static=true) # pVNeg=new THREE.Vector3(node1.position.x,node1.position.y,node1.position.z); # pVPos=new THREE.Vector3(node2.position.x,node2.position.y,node2.position.z); # currentRestLength=pVPos.clone().sub(pVNeg).length(); # edge.currentRestLength=currentRestLength; # todo make sure updated node1=E_source[edge] node2=E_target[edge] currentRestLength=E_currentRestLength[edge] pVNeg=copy(N_currPosition[node1])# todo change to be linked to edge not node pVPos=copy(N_currPosition[node2])# todo change to be linked to edge not node # Vec3D<double> three oldPos2 = copy(E_pos2[edge]) oldAngle1v = copy(E_angle1v[edge]) oldAngle2v = copy(E_angle2v[edge])# remember the positions/angles from last timestep to calculate velocity # var oldAngle1v=new THREE.Vector3(node1.angle.x,node1.angle.y,node1.angle.z);//? # var oldAngle2v=new THREE.Vector3(node2.angle.x,node2.angle.y,node2.angle.z); //?? totalRot= orientLink(edge) # sets pos2, angle1, angle2 /*restLength*/ dPos2=0.5*(copy(E_pos2[edge])-oldPos2) dAngle1=0.5*(copy(E_angle1v[edge])-oldAngle1v) dAngle2=0.5*(copy(E_angle2v[edge])-oldAngle2v) # if volume effects... # if (!mat->isXyzIndependent() || currentTransverseStrainSum != 0) # updateTransverseInfo(); //currentTransverseStrainSum != 0 catches when we disable poissons mid-simulation _stress=updateStrain((E_pos2[edge][1]/E_currentRestLength[edge]),E_stiffness[edge]) # var _stress=updateStrain(1.0); E_stress[edge] = _stress if !static append!(E_stressTimeSteps[edge],[_stress]) end ######### check this if setup["viz"]["minStress"]>_stress setup["viz"]["minStress"]=_stress elseif setup["viz"]["maxStress"]<_stress setup["viz"]["maxStress"]=_stress end # if (isFailed()){forceNeg = forcePos = momentNeg = momentPos = Vec3D<double>(0,0,0); return;} # var b1=mat->_b1, b2=mat->_b2, b3=mat->_b3, a2=mat->_a2; //local copies //todo get from where i had l = currentRestLength # ?? rho = E_density[edge] A = E_area[edge] E = E_stiffness[edge] # youngs modulus G=1.0 # todo shear_modulus ixx = 1.0 # todo section ixx I=1.0 iyy = 1.0 # todo section.iyy// # var l0=length.dataSync(); J=1.0;# todo check # var l02 = l0 * l0; # var l03 = l0 * l0 * l0; b1= 12*E*I/(l*l*l) b2= 6*E*I/(l*l) b3= 2*E*I/(l) a1= E*A/l a2= G*J/l nu=0 b1= 5e6 b2= 1.25e7 b3= 2.08333e+07 a1= E*A/l a2= 1.04167e+07 L = 5; a1 = E*L # EA/L : Units of N/m a2 = E * L*L*L / (12.0*(1+nu)) # GJ/L : Units of N-m b1 = E*L # 12EI/L^3 : Units of N/m b2 = E*L*L/2.0 # 6EI/L^2 : Units of N (or N-m/m: torque related to linear distance) b3 = E*L*L*L/6.0 # 2EI/L : Units of N-m # console.log("currentRestLength:"+currentRestLength); # console.log("b1:"+b1/10e6); # console.log("b2:"+b2/10e7); # console.log("b3:"+b3/10e7); # console.log("a2:"+a2/10e7); # var b1= 5e6; # var b2= 1.25e7; # var b3= 2.08333e+07; # var a1= E*A/l; # var a2= 1.04167e+07; currentTransverseArea=25.0 # todo ?? later change currentTransverseArea=A # Beam equations. All relevant terms are here, even though some are zero for small angle and others are zero for large angle (profiled as negligible performance penalty) forceNeg = [(_stress*currentTransverseArea) (b1*E_pos2[edge][2]-b2*(E_angle1v[edge][3] + E_angle2v[edge][3])) (b1*E_pos2[edge][3] + b2*(E_angle1v[edge][2] + E_angle2v[edge][2]))] # Use Curstress instead of -a1*Pos2.x to account for non-linear deformation forcePos = -forceNeg; momentNeg = [(a2*(E_angle2v[edge][1]-E_angle1v[edge][1])) (-b2*E_pos2[edge][3]-b3*(2*E_angle1v[edge][2]+E_angle2v[edge][2])) (b2*E_pos2[edge][2] - b3*(2*E_angle1v[edge][3] + E_angle2v[edge][3]))] momentPos = [(a2*(E_angle1v[edge][1]-E_angle2v[edge][1])) (-b2*E_pos2[edge][3]- b3*(E_angle1v[edge][2]+2*E_angle2v[edge][2])) (b2*E_pos2[edge][2] - b3*(E_angle1v[edge][3] + 2*E_angle2v[edge][3]))] # //local damping: # if (isLocalVelocityValid()){ //if we don't have the basis for a good damping calculation, don't do any damping. # float sqA1=mat->_sqA1, sqA2xIp=mat->_sqA2xIp,sqB1=mat->_sqB1, sqB2xFMp=mat->_sqB2xFMp, sqB3xIp=mat->_sqB3xIp; # Vec3D<double> posCalc( sqA1*dPos2.x, # sqB1*dPos2.y - sqB2xFMp*(dAngle1.z+dAngle2.z), # sqB1*dPos2.z + sqB2xFMp*(dAngle1.y+dAngle2.y)); # forceNeg += pVNeg->dampingMultiplier()*posCalc; # forcePos -= pVPos->dampingMultiplier()*posCalc; # momentNeg -= 0.5*pVNeg->dampingMultiplier()*Vec3D<>( -sqA2xIp*(dAngle2.x - dAngle1.x), # sqB2xFMp*dPos2.z + sqB3xIp*(2*dAngle1.y + dAngle2.y), # -sqB2xFMp*dPos2.y + sqB3xIp*(2*dAngle1.z + dAngle2.z)); # momentPos -= 0.5*pVPos->dampingMultiplier()*Vec3D<>( sqA2xIp*(dAngle2.x - dAngle1.x), # sqB2xFMp*dPos2.z + sqB3xIp*(dAngle1.y + 2*dAngle2.y), # -sqB2xFMp*dPos2.y + sqB3xIp*(dAngle1.z + 2*dAngle2.z)); # } # else setBoolState(LOCAL_VELOCITY_VALID, true); //we're good for next go-around unless something changes # transform forces and moments to local voxel coordinates smallAngle=false # ?? todo check if !smallAngle # ?? chech forceNeg = RotateVec3DInv(E_angle1[edge],forceNeg) momentNeg = RotateVec3DInv(E_angle1[edge],momentNeg) end forcePos = RotateVec3DInv(E_angle2[edge],forcePos); momentPos = RotateVec3DInv(E_angle2[edge],momentPos); # println(momentPos) forceNeg =toAxisOriginalVector3(forceNeg,E_axis[edge]); forcePos =toAxisOriginalVector3(forcePos,E_axis[edge]); momentNeg=toAxisOriginalQuat(momentNeg,E_axis[edge]);# TODOO CHECKKKKKK momentPos=toAxisOriginalQuat(momentPos,E_axis[edge]); # println(momentPos[2]," ",momentPos[3]," ",momentPos[4]," ",momentPos[1]," ") N_intForce[node1] =N_intForce[node1] +(forceNeg) ; N_intForce[node2] =N_intForce[node2] +(forcePos) ; # println(N_intMoment[node2]) N_intMoment[node1]=[(N_intMoment[node1][1]+momentNeg[2]) (N_intMoment[node1][2]+momentNeg[3]) (N_intMoment[node2][3]+momentPos[4])]; N_intMoment[node2]=[(N_intMoment[node2][1]+momentPos[2]) (N_intMoment[node2][2]+momentPos[3]) (N_intMoment[node2][3]+momentPos[4])]; # println(N_intMoment[node2]) # assert(!(forceNeg.x != forceNeg.x) || !(forceNeg.y != forceNeg.y) || !(forceNeg.z != forceNeg.z)); //assert non QNAN # assert(!(forcePos.x != forcePos.x) || !(forcePos.y != forcePos.y) || !(forcePos.z != forcePos.z)); //assert non QNAN end function orientLink( edge) # updates pos2, angle1, angle2, and smallAngle //Quat3D<double> /*double restLength*/ node1=E_source[edge] node2=E_target[edge] currentRestLength=E_currentRestLength[edge] pVNeg=copy(N_currPosition[node1])# todo change to be linked to edge not node pVPos=copy(N_currPosition[node2])# todo change to be linked to edge not node pos2 = toAxisXVector3(pVPos-pVNeg,E_axis[edge]) # digit truncation happens here... # pos2.x = Math.round(pos2.x * 1e4) / 1e4; angle1 = toAxisXQuat(N_orient[node1],E_axis[edge]) angle2 = toAxisXQuat(N_orient[node2],E_axis[edge]) # println(angle1[2]," ",angle1[3]," ",angle1[4]," ",angle1[1]) totalRot = conjugate(angle1) #keep track of the total rotation of this bond (after toAxisX()) # Quat3D<double> # println(totalRot.x," ",totalRot.y," ",totalRot.z," ",totalRot.w) pos2 = RotateVec3D(totalRot,pos2) # angle2 = copy(totalRot) .* angle2 # todo .* angle2=Quat(angle2.w*totalRot.w,angle2.x*totalRot.x,angle2.y*totalRot.y,angle2.z*totalRot.z) angle1 = Quat(1.0,0.0,0.0,0.0)#new THREE.Quaternion() #zero for now... # small angle approximation? # var SmallTurn = ((Math.abs(pos2.z)+Math.abs(pos2.y))/pos2.x); # var ExtendPerc = (Math.abs(1-pos2.x/currentRestLength)); # if (!smallAngle /*&& angle2.IsSmallAngle()*/ && SmallTurn < SA_BOND_BEND_RAD && ExtendPerc < SA_BOND_EXT_PERC){ # smallAngle = true; # setBoolState(LOCAL_VELOCITY_VALID, false); # } # else if (smallAngle && (/*!angle2.IsSmallishAngle() || */SmallTurn > HYSTERESIS_FACTOR*SA_BOND_BEND_RAD || ExtendPerc > HYSTERESIS_FACTOR*SA_BOND_EXT_PERC)){ # smallAngle = false; # setBoolState(LOCAL_VELOCITY_VALID, false); # } smallAngle=true #todo later remove if (smallAngle) #Align so Angle1 is all zeros pos2[1] =pos2[1]- currentRestLength #only valid for small angles else #Large angle. Align so that Pos2.y, Pos2.z are zero. # FromAngleToPosX(angle1,pos2) #get the angle to align Pos2 with the X axis # totalRot = angle1.clone().multiply(totalRot) #update our total rotation to reflect this # angle2 = angle1.clone().multiply( angle2) #rotate angle2 # pos2 = new THREE.Vector3(pos2.length() - currentRestLength, 0, 0); end angle1v = ToRotationVector(angle1) angle2v = ToRotationVector(angle2) # assert(!(angle1v.x != angle1v.x) || !(angle1v.y != angle1v.y) || !(angle1v.z != angle1v.z)); # assert non QNAN # assert(!(angle2v.x != angle2v.x) || !(angle2v.y != angle2v.y) || !(angle2v.z != angle2v.z)); # assert non QNAN E_pos2[edge]=copy(pos2) E_angle1v[edge]=copy(angle1v) E_angle2v[edge]=copy(angle2v) E_angle1[edge]=copy(angle1) E_angle2[edge]=copy(angle2) return totalRot end function RotateVec3D(a, f) fx= (f[1]==-0) ? 0 : f[1] fy= (f[2]==-0) ? 0 : f[2] fz= (f[3]==-0) ? 0 : f[3] # fx= f[1] # fy= f[2] # fz= f[3] tw = fx*a.x + fy*a.y + fz*a.z tx = fx*a.w - fy*a.z + fz*a.y ty = fx*a.z + fy*a.w - fz*a.x tz = -fx*a.y + fy*a.x + fz*a.w return [(a.w*tx+a.x*tw+a.y*tz-a.z*ty) (a.w*ty-a.x*tz+a.y*tw+a.z*tx) (a.w*tz+a.x*ty-a.y*tx + a.z*tw)] end #!< Returns a vector representing the specified vector "f" rotated by this quaternion. @param[in] f The vector to transform. function RotateVec3DInv(a, f) fx=f[1] fy=f[2] fz=f[3] tw = a.x*fx + a.y*fy + a.z*fz tx = a.w*fx - a.y*fz + a.z*fy ty = a.w*fy + a.x*fz - a.z*fx tz = a.w*fz - a.x*fy + a.y*fx return [(tw*a.x + tx*a.w + ty*a.z - tz*a.y) (tw*a.y - tx*a.z + ty*a.w + tz*a.x) (tw*a.z + tx*a.y - ty*a.x + tz*a.w)] end #!< Returns a vector representing the specified vector "f" rotated by the inverse of this quaternion. This is the opposite of RotateVec3D. @param[in] f The vector to transform. function setFromUnitVectors(vFrom, vTo ) # assumes direction vectors vFrom and vTo are normalized EPS = 0.000001; r = dot(vFrom,vTo)+1 if r < EPS r = 0; if abs( vFrom.x ) > abs( vFrom.z ) qx = - vFrom[2] qy = vFrom[1] qz = 0 qw = r else qx = 0 qy = - vFrom[3] qz = vFrom[2] qw = r end else # crossVectors( vFrom, vTo ); // inlined to avoid cyclic dependency on Vector3 qx = vFrom[2] * vTo[3] - vFrom[3] * vTo[2] qy = vFrom[3] * vTo[1] - vFrom[1] * vTo[3] qz = vFrom[1] * vTo[2] - vFrom[2] * vTo[1] qw = r end qx= (qx==-0) ? 0 : qx qy= (qy==-0) ? 0 : qy qz= (qz==-0) ? 0 : qz qw= (qw==-0) ? 0 : qw nn=normalize(collect(Iterators.flatten([qw,qx,qy,qz]))) return [nn[1] nn[2] nn[3] nn[4]] # return normalizeQ(Quat(qw,qx,qy,qz)) # return Quat(nn[1], nn[2], nn[3], nn[4]) end function normalizeQ(q) l = norm(q) if l === 0 qx = 0 qy = 0 qz = 0 qw = 1 else l = 1 / l qx = q.x * l qy = q.y * l qz = q.z * l qw = q.w * l end return Quat(qw,qx,qy,qz) end function conjugate(q) x= (-q.x==-0) ? 0 : -q.x y= (-q.y==-0) ? 0 : -q.y z= (-q.z==-0) ? 0 : -q.z return Quat(q.w, x, y, z) end #Returns a quaternion that is the conjugate of this quaternion. This quaternion is not modified. function applyQuaternion(q1,q2) x = q1[2] y = q1[3] z = q1[4] w = q1[1] qx = q2[2] qy = q2[3] qz = q2[4] qw = q2[1] # calculate quat * vector ix = qw * x + qy * z - qz * y iy = qw * y + qz * x - qx * z iz = qw * z + qx * y - qy * x iw = - qx * x - qy * y - qz * z # calculate result * inverse quat xx = ix * qw + iw * - qx + iy * - qz - iz * - qy yy = iy * qw + iw * - qy + iz * - qx - ix * - qz zz = iz * qw + iw * - qz + ix * - qy - iy * - qx mm=normalize(collect(Iterators.flatten([xx yy zz]))) return [mm[1] mm[2] mm[3]] end function applyQuaternion1(e,q2) x = e[1] y = e[2] z = e[3] qx = q2[2] qy = q2[3] qz = q2[4] qw = q2[1] # calculate quat * vector ix = qw * x + qy * z - qz * y iy = qw * y + qz * x - qx * z iz = qw * z + qx * y - qy * x iw = - qx * x - qy * y - qz * z # calculate result * inverse quat xx = ix * qw + iw * - qx + iy * - qz - iz * - qy yy = iy * qw + iw * - qy + iz * - qx - ix * - qz zz = iz * qw + iw * - qz + ix * - qy - iy * - qx return [xx yy zz] end function setQuaternionFromEuler(euler) x=euler[1] y=euler[2] z=euler[3] c1 = cos( x / 2 ) c2 = cos( y / 2 ) c3 = cos( z / 2 ) s1 = sin( x / 2 ) s2 = sin( y / 2 ) s3 = sin( z / 2 ) x = s1 * c2 * c3 + c1 * s2 * s3 y = c1 * s2 * c3 - s1 * c2 * s3 z = c1 * c2 * s3 + s1 * s2 * c3 w = c1 * c2 * c3 - s1 * s2 * s3 return [w x y z] end function quatToMatrix( quaternion ) te = zeros(16) x = quaternion[2] y = quaternion[3] z = quaternion[4] w = quaternion[1] x2 = x + x y2 = y + y z2 = z + z xx = x * x2 xy = x * y2 xz = x * z2 yy = y * y2 yz = y * z2 zz = z * z2 wx = w * x2 wy = w * y2 wz = w * z2 sx = 1 sy = 1 sz = 1 te[ 1 ] = ( 1 - ( yy + zz ) ) * sx te[ 2 ] = ( xy + wz ) * sx te[ 3 ] = ( xz - wy ) * sx te[ 4 ] = 0; te[ 5 ] = ( xy - wz ) * sy te[ 6 ] = ( 1 - ( xx + zz ) ) * sy te[ 7 ] = ( yz + wx ) * sy te[ 8 ] = 0; te[ 9 ] = ( xz + wy ) * sz te[ 10 ] = ( yz - wx ) * sz te[ 11 ] = ( 1 - ( xx + yy ) ) * sz te[ 12 ] = 0 te[ 13 ] = 0 #position.x; te[ 14 ] = 0 #position.y; te[ 15 ] = 0 #position.z; te[ 16 ] = 1 return te end function setFromRotationMatrix(m) te = m m11 = (te[ 1 ]== -0.0) ? 0.0 : te[ 1 ] m12 = (te[ 5 ]== -0.0) ? 0.0 : te[ 5 ] m13 = (te[ 9 ]== -0.0) ? 0.0 : te[ 9 ] m21 = (te[ 2 ]== -0.0) ? 0.0 : te[ 2 ] m22 = (te[ 6 ]== -0.0) ? 0.0 : te[ 6 ] m23 = (te[ 10]== -0.0) ? 0.0 : te[ 10] m31 = (te[ 3 ]== -0.0) ? 0.0 : te[ 3 ] m32 = (te[ 7 ]== -0.0) ? 0.0 : te[ 7 ] m33 = (te[ 11]== -0.0) ? 0.0 : te[ 11] m11 = te[ 1 ] m12 = te[ 5 ] m13 = te[ 9 ] m21 = te[ 2 ] m22 = te[ 6 ] m23 = te[ 10] m31 = te[ 3 ] m32 = te[ 7 ] m33 = te[ 11] y = asin( clamp( m13, - 1, 1 ) ) if ( abs( m13 ) < 0.9999999 ) x = atan( - m23, m33 ) z = atan( - m12, m11 )#-m12, m11 # if(m23==0.0) # x = atan( m23, m33 ) # end # if(m12==0.0) # z = atan( m12, m11 ) # end else x = atan( m32, m22 ) z = 0; end return [x y z] end function toAxisOriginalVector3(pV,axis) # xaxis=[1 0 0] # vector=copy(axis) # vector=normalize(collect(Iterators.flatten(vector))) # p = SVector(pV[1],pV[2], pV[3]) # q=setFromUnitVectors(xaxis, vector) # v= q * p # return [v[1] v[2] v[3]] xaxis=[1 0 0] vector=copy(axis) vector=normalize(collect(Iterators.flatten(vector))) p = SVector(pV[1],pV[2], pV[3]) q=setFromUnitVectors(xaxis, vector) qq=Quat(q[1],q[2],q[3],q[4]) d=17 qw=round(q[1], digits=d) qx=round(q[2], digits=d) qy=round(q[3], digits=d) qz=round(q[4], digits=d) rot=setFromRotationMatrix(copy(quatToMatrix( copy([qw qx qy qz]) ))) return applyQuaternion1( copy(pV) ,setQuaternionFromEuler(copy(rot)) ) end function toAxisXVector3(pV,axis) #TODO CHANGE # xaxis=[1 0 0] # vector=copy(axis) # vector=normalize(collect(Iterators.flatten(vector))) # p = SVector(pV[1],pV[2], pV[3]) # q=setFromUnitVectors(vector,xaxis) # v= q * p # return [v[1] v[2] v[3]] xaxis=[1 0 0] vector=copy(axis) vector=normalize(collect(Iterators.flatten(vector))) p = SVector(pV[1],pV[2], pV[3]) q=setFromUnitVectors(vector,xaxis) qq=Quat(q[1],q[2],q[3],q[4]) d=17 qw=round(q[1], digits=d) qx=round(q[2], digits=d) qy=round(q[3], digits=d) qz=round(q[4], digits=d) rot=setFromRotationMatrix(copy(quatToMatrix( copy([qw qx qy qz]) ))) return applyQuaternion1( copy(pV) ,setQuaternionFromEuler(copy(rot)) ) end #transforms a vec3D in the original orientation of the bond to that as if the bond was in +X direction function toAxisOriginalQuat(pQ,axis) # xaxis=[1 0 0] # vector=copy(axis) # vector=normalize(collect(Iterators.flatten(vector))) # p = SVector(pQ[1],pQ[2], pQ[3]) # q=setFromUnitVectors(xaxis, vector) # v=q * p # return Quat(1.0,v[1],v[2],v[3]) xaxis=[1 0 0] vector=copy(axis) vector=normalize(collect(Iterators.flatten(vector))) p = SVector(pQ[1],pQ[2], pQ[3]) q=setFromUnitVectors(xaxis,vector) qq=Quat(q[1],q[2],q[3],q[4]) d=17 qw=round(q[1], digits=d) qx=round(q[2], digits=d) qy=round(q[3], digits=d) qz=round(q[4], digits=d) rot=setFromRotationMatrix(copy(quatToMatrix( copy([qw qx qy qz]) ))) v=applyQuaternion1( copy([pQ[1] pQ[2] pQ[3]]) ,setQuaternionFromEuler(copy(rot)) ) return [1.0 v[1] v[2] v[3]] end function toAxisXQuat(pQ,axis) # xaxis=[1 0 0] # vector=copy(axis) # vector=normalize(collect(Iterators.flatten(vector))) # p = SVector(q.x,q.y, q.z) # q=setFromUnitVectors(vector,xaxis) # v=q * p # return Quat(q.w,v[1],v[2],v[3]) xaxis=[1 0 0] vector=copy(axis) vector=normalize(collect(Iterators.flatten(vector))) p = SVector(pQ.x,pQ.y, pQ.z) q=setFromUnitVectors(vector,xaxis) qq=Quat(q[1],q[2],q[3],q[4]) d=17 qw=round(q[1], digits=d) qx=round(q[2], digits=d) qy=round(q[3], digits=d) qz=round(q[4], digits=d) rot=setFromRotationMatrix(copy(quatToMatrix( copy([qw qx qy qz]) ))) v=applyQuaternion1( copy([pQ.x pQ.y pQ.z ]) ,setQuaternionFromEuler(copy(rot)) ) return Quat(1.0,v[1],v[2],v[3]) # return [1.0 v[1] v[2] v[3]] end #transforms a vec3D in the original orientation of the bond to that as if the bond was in +X direction function ToRotationVector(a) if (a.w >= 1.0 || a.w <= -1.0) return [0 0 0] end squareLength = 1.0-a.w*a.w; # because x*x + y*y + z*z + w*w = 1.0, but more susceptible to w noise (when SLTHRESH_ACOS2SQRT= 2.4e-3; # SquareLength threshhold for when we can use square root optimization for acos. From SquareLength = 1-w*w. (calculate according to 1.0-W_THRESH_ACOS2SQRT*W_THRESH_ACOS2SQRT if (squareLength < SLTHRESH_ACOS2SQRT) # ??????? return [a.x a.y a.z] *(2.0*sqrt((2-2*a.w)/squareLength)); # acos(w) = sqrt(2*(1-x)) for w close to 1. for w=0.001, error is 1.317e-6 else return [a.x a.y a.z] * (2.0*acos(a.w)/sqrt(squareLength)); end end # !< Returns a rotation vector representing this quaternion rotation. Adapted from http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/ function FromRotationVector(VecIn) theta=VecIn/2.0 thetaMag2=norm(theta)*norm(theta) DBL_EPSILONx24 =5.328e-15 if thetaMag2*thetaMag2 < DBL_EPSILONx24 qw=1.0 - 0.5*thetaMag2 s=1.0 - thetaMag2 / 6.0 else thetaMag = sqrt(thetaMag2) qw=cos(thetaMag) s=sin(thetaMag) / thetaMag end qx=theta[1]*s qy=theta[2]*s qz=theta[3]*s; return Quat(qw,qx,qy,qz) end function FromAngleToPosX(a, RotateFrom) #highly optimized at the expense of readability SMALL_ANGLE_RAD= 1.732e-2 # Angles less than this get small angle approximations. To get: Root solve atan(t)/t-1+MAX_ERROR_PERCENT. From: MAX_ERROR_PERCENT = (t-atan(t))/t SMALL_ANGLE_W =0.9999625 # quaternion W value corresponding to a SMALL_ANGLE_RAD. To calculate, cos(SMALL_ANGLE_RAD*0.5). W_THRESH_ACOS2SQRT= 0.9988 # Threshhold of w above which we can approximate acos(w) with sqrt(2-2w). To get: Root solve 1-sqrt(2-2wt)/acos(wt) - MAX_ERROR_PERCENT. From MAX_ERROR_PERCENT = (acos(wt)-sqrt(2-2wt))/acos(wt) if (RotateFrom[1]==0 && RotateFrom[2]==0 && RotateFrom[3]==0) return 0 #leave off if it slows down too much!! end # Catch and handle small angle: YoverX = RotateFrom[2]/RotateFrom[1] ZoverX = RotateFrom[3]/RotateFrom[1] if (YoverX<SMALL_ANGLE_RAD && YoverX>-SMALL_ANGLE_RAD && ZoverX<SMALL_ANGLE_RAD && ZoverX>-SMALL_ANGLE_RAD) # ??? //Intercept small angle and zero angle ax=0 ay=0.5*ZoverX az=-0.5*YoverX aw = 1+0.5*(-a.y*a.y-a.z*a.z) # w=sqrt(1-x*x-y*y), small angle sqrt(1+x) ~= 1+x/2 at x near zero. return Quat(aw,ax,ay,az) end # more accurate non-small angle: RotFromNorm = copy(RotateFrom) RotFromNorm=normalize(collect(Iterators.flatten(RotFromNorm))) # Normalize the input... theta = acos(RotFromNorm[1]) # because RotFromNorm is normalized, 1,0,0 is normalized, and A.B = |A||B|cos(theta) = cos(theta) if(theta >(π-DISCARD_ANGLE_RAD)) # ?????? aw=0 ax=0 ay=1 az=0 return Quat(aw,ax,ay,az) end # 180 degree rotation (about y axis, since the vector must be pointing in -x direction AxisMagInv = 1.0/sqrt(RotFromNorm[3]*RotFromNorm[3]+RotFromNorm[2]*RotFromNorm[2]) # Here theta is the angle, axis is RotFromNorm.Cross(Vec3D(1,0,0)) = Vec3D(0, RotFromNorm.z/AxisMagInv, -RotFromNorm.y/AxisMagInv), which is still normalized. (super rolled together) aa = 0.5*theta s = sin(a) aw= cos(aa) ax= 0 ay= RotFromNorm[3]*AxisMagInv*s az = -RotFromNorm[2]*AxisMagInv*s # angle axis function, reduced return Quat(aw,ax,ay,az) end # !< Overwrites this quaternion with the calculated rotation that would transform the specified RotateFrom vector to point in the positve X direction. Note: function changes this quaternion. @param[in] RotateFrom An arbitrary direction vector. Does not need to be normalized. function axialStrain(positiveEnd) # strainRatio = pVPos->material()->E/pVNeg->material()->E; strainRatio=1.0 return positiveEnd ? (2.0 *strain*strainRatio/(1.0+strainRatio)) : (2.0*strain/(1.0+strainRatio)) end function updateStrain( axialStrain,E) # ?from where strain strain = axialStrain # redundant? currentTransverseStrainSum=0.0 # ??? todo linear=true maxStrain=100000000000000000000;# ?? todo later change if linear if axialStrain > maxStrain maxStrain = axialStrain # remember this maximum for easy reference end return stress(axialStrain,E) else if (axialStrain > maxStrain) # if new territory on the stress/strain curve maxStrain = axialStrain # remember this maximum for easy reference returnStress = stress(axialStrain,E) # ??currentTransverseStrainSum if (nu != 0.0) strainOffset = maxStrain-stress(axialStrain,E)/(_eHat*(1-nu)) # precalculate strain offset for when we back off else strainOffset = maxStrain-returnStress/E # precalculate strain offset for when we back off end else # backed off a non-linear material, therefore in linear region. relativeStrain = axialStrain-strainOffset # treat the material as linear with a strain offset according to the maximum plastic deformation if (nu != 0.0) returnStress = stress(relativeStrain,E) else returnStress = E*relativeStrain end end return returnStress end end function stress( strain , E ) #end,transverseStrainSum, forceLinear){ # reference: http://www.colorado.edu/engineering/CAS/courses.d/Structures.d/IAST.Lect05.d/IAST.Lect05.pdf page 10 # if (isFailed(strain)) return 0.0f; //if a failure point is set and exceeded, we've broken! # var E =setup.edges[0].stiffness; //todo change later to material ?? # var E=1000000;//todo change later to material ?? # var scaleFactor=1; return E*strain; # # if (strain <= strainData[1] || linear || forceLinear){ //for compression/first segment and linear materials (forced or otherwise), simple calculation # if (nu==0.0) return E*strain; # else return _eHat*((1-nu)*strain + nu*transverseStrainSum); # else return eHat()*((1-nu)*strain + nu*transverseStrainSum); # # } # //the non-linear feature with non-zero poissons ratio is currently experimental # int DataCount = modelDataPoints(); # for (int i=2; i<DataCount; i++){ //go through each segment in the material model (skipping the first segment because it has already been handled. # if (strain <= strainData[i] || i==DataCount-1){ //if in the segment ending with this point (or if this is the last point extrapolate out) # float Perc = (strain-strainData[i-1])/(strainData[i]-strainData[i-1]); # float basicStress = stressData[i-1] + Perc*(stressData[i]-stressData[i-1]); # if (nu==0.0f) return basicStress; # else { //accounting for volumetric effects # float modulus = (stressData[i]-stressData[i-1])/(strainData[i]-strainData[i-1]); # float modulusHat = modulus/((1-2*nu)*(1+nu)); # float effectiveStrain = basicStress/modulus; //this is the strain at which a simple linear stress strain line would hit this point at the definied modulus # float effectiveTransverseStrainSum = transverseStrainSum*(effectiveStrain/strain); # return modulusHat*((1-nu)*effectiveStrain + nu*effectiveTransverseStrainSum); # } # } # } # assert(false); //should never reach this point # return 0.0f; end function ToEulerAngles(q) # TODO I THINK WRONG # roll (x-axis rotation) sinr_cosp = (2 * (q.w * q.x + q.y * q.z) )== -0.0 ? 0.0 : (2 * (q.w * q.x + q.y * q.z) ) cosr_cosp = (1 - 2 * (q.x * q.x + q.y * q.y))== -0.0 ? 0.0 : (1 - 2 * (q.x * q.x + q.y * q.y)) roll = atan(sinr_cosp, cosr_cosp) # pitch (y-axis rotation) sinp = 2 * (q.w * q.y - q.z * q.x) if (abs(sinp) >= 1) pitch = copysign(π / 2, sinp) # use 90 degrees if out of range else pitch = asin(sinp) end # yaw (z-axis rotation) siny_cosp = 2 * (q.w * q.z + q.x * q.y) cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z) yaw = atan(siny_cosp, cosy_cosp) return [roll pitch yaw] end # ToEulerAngles(Quat(1.0,1.0,1.0,1.0)) # http://klas-physics.googlecode.com/svn/trunk/src/general/Integrator.cpp (reference) function timeStep(dt,node,static,currentTimeStep) previousDt = dt linMom=copy(N_linMom[node]) angMom=copy(N_angMom[node]) orient=copy(N_orient[node]) pos=copy(N_currPosition[node]) if (dt == 0.0) return 0 end if(all(N_restrained_degrees_of_freedom[node] .>=1)) # pos = originalPosition() + ext->translation(); # orient = ext->rotationQuat(); # haltMotion(); # # pos=copy(N_position[node]) # # node.currPosition=pos.clone(); # # linMom = new THREE.Vector3(0,0,0); # # angMom = new THREE.Vector3(0,0,0); # # node.displacement={x:0,y:0,z:0}; # node.orient=orient.clone(); # node.linMom=linMom.clone(); # node.angMom=angMom.clone(); return 0 end # Translation curForce = force(node,static,currentTimeStep) # var fricForce = curForce.clone(); # if (isFloorEnabled()) floorForce(dt, &curForce); //floor force needs dt to calculate threshold to "stop" a slow voxel into static friction. # fricForce = curForce - fricForce; # assert(!(curForce.x != curForce.x) || !(curForce.y != curForce.y) || !(curForce.z != curForce.z)); //assert non QNAN linMom=linMom+curForce*dt massInverse=8e-6 # todo ?? later change translate=linMom*(dt*massInverse) # ??massInverse # // we need to check for friction conditions here (after calculating the translation) and stop things accordingly # if (isFloorEnabled() && floorPenetration() >= 0){ //we must catch a slowing voxel here since it all boils down to needing access to the dt of this timestep. # double work = fricForce.x*translate.x + fricForce.y*translate.y; //F dot disp # double hKe = 0.5*mat->_massInverse*(linMom.x*linMom.x + linMom.y*linMom.y); //horizontal kinetic energy # if(hKe + work <= 0) setFloorStaticFriction(true); //this checks for a change of direction according to the work-energy principle # if (isFloorStaticFriction()){ //if we're in a state of static friction, zero out all horizontal motion # linMom.x = linMom.y = 0; # translate.x = translate.y = 0; # } # } # else setFloorStaticFriction(false); pos=pos+translate N_currPosition[node]=copy(pos) N_displacement[node]=N_displacement[node]+translate # Rotation curMoment = moment(node) angMom=angMom+curMoment*dt momentInertiaInverse=1.0 # todo ?? later change # orient=orient.*FromRotationVector(copy(angMom)*(dt*momentInertiaInverse)) temp=FromRotationVector(copy(angMom)*(dt*momentInertiaInverse)) orient=Quat(orient.w*temp.w,orient.x*temp.x,orient.y*temp.y,orient.z*temp.z) # orient.multiply(FromRotationVector(angMom.clone().multiplyScalar((dt*momentInertiaInverse)))) # tupdate the orientation //momentInertiaInverse N_orient[node]=copy(orient) eul = ToEulerAngles(orient) # TODO I THINK WRONG N_angle[node]=copy(eul) N_linMom[node]=copy(linMom) N_angMom[node]=copy(angMom) # if (ext){//?? todo fix # var size = 1;//change # if (ext->isFixed(X_TRANSLATE)) {pos.x = ix*size + ext->translation().x; linMom.x=0;} # if (ext->isFixed(Y_TRANSLATE)) {pos.y = iy*size + ext->translation().y; linMom.y=0;} # if (ext->isFixed(Z_TRANSLATE)) {pos.z = iz*size + ext->translation().z; linMom.z=0;} # if (ext->isFixedAnyRotation()){ //if any rotation fixed, all are fixed # if (ext->isFixedAllRotation()){ # orient = ext->rotationQuat(); # angMom = Vec3D<double>(); # } # else { //partial fixes: slow! # Vec3D<double> tmpRotVec = orient.ToRotationVector(); # if (ext->isFixed(X_ROTATE)){ tmpRotVec.x=0; angMom.x=0;} # if (ext->isFixed(Y_ROTATE)){ tmpRotVec.y=0; angMom.y=0;} # if (ext->isFixed(Z_ROTATE)){ tmpRotVec.z=0; angMom.z=0;} # orient.FromRotationVector(tmpRotVec); # } # } # } # poissonsStrainInvalid = true; end function force(node,static,currentTimeStep) # forces from internal bonds totalForce=[0 0 0] # new THREE.Vector3(node.force.x,node.force.y,node.force.z); # todo totalForce=totalForce+N_intForce[node] # for (int i=0; i<6; i++){ # if (links[i]) totalForce += links[i]->force(isNegative((linkDirection)i)); # total force in LCS # } totalForce = RotateVec3D(N_orient[node],totalForce); # from local to global coordinates # assert(!(totalForce.x != totalForce.x) || !(totalForce.y != totalForce.y) || !(totalForce.z != totalForce.z)); //assert non QNAN # other forces if(static) totalForce=totalForce+N_force[node] # }else if(currentTimeStep<50){ # totalForce.add(new THREE.Vector3(node.force.x,node.force.y,node.force.z)); else # var ex=0.1; # if(node.force.y!=0){ # var f=400*Math.sin(currentTimeStep*ex); # totalForce.add(new THREE.Vector3(0,f,0)); # } x=N_position[node][3] t=currentTimeStep wave=getForce(x,t) totalForce=totalForce+[0 wave 0] end # if (externalExists()) totalForce += external()->force(); //external forces # totalForce -= velocity()*mat->globalDampingTranslateC(); //global damping f-cv # totalForce.z += mat->gravityForce(); //gravity, according to f=mg # if (isCollisionsEnabled()){ # for (std::vector<CVX_Collision*>::iterator it=colWatch->begin(); it!=colWatch->end(); it++){ # totalForce -= (*it)->contactForce(this); # } # } # todo make internal forces 0 again N_intForce[node]=[0 0 0] # do i really need it? # node.force.x=0; # node.force.y=0; # node.force.z=0; return totalForce end function moment(node) #moments from internal bonds totalMoment=[0 0 0] # for (int i=0; i<6; i++){ # if (links[i]) totalMoment += links[i]->moment(isNegative((linkDirection)i)); //total force in LCS # } totalMoment=totalMoment+N_intMoment[node] totalMoment = RotateVec3D(N_orient[node],totalMoment); totalMoment=totalMoment+N_moment[node] #other moments # if (externalExists()) totalMoment += external()->moment(); //external moments # totalMoment -= angularVelocity()*mat->globalDampingRotateC(); //global damping N_intMoment[node]=[0 0 0] # do i really need it? return totalMoment end function updateDataAndSave(setup,fileName) nodes = setup["nodes"] edges = setup["edges"] voxCount=size(nodes)[1] linkCount=size(edges)[1] i=1 for edge in edges edge["stress"]=E_stress[i] i=i+1 end i=1 for node in nodes node["displacement"]["x"]=N_displacement[i][1] node["displacement"]["y"]=N_displacement[i][2] node["displacement"]["z"]=N_displacement[i][3] node["angle"]["x"]=N_angle[i][1] node["angle"]["y"]=N_angle[i][2] node["angle"]["z"]=N_angle[i][3] i=i+1 end # pass data as a json string (how it shall be displayed in a file) stringdata = JSON.json(setup) # write the file with the stringdata variable information open(fileName, "w") do f write(f, stringdata) end end ############################################################################################### latticeSize=9 numTimeSteps=200 save=false setup = Dict() open("../json/setupTestUni9.json", "r") do f global setup dicttxt = String(read(f)) # file information to string setup=JSON.parse(dicttxt) # parse and transform data end setup=setup["setup"] ############# nodes N_position=[] N_degrees_of_freedom=[] N_restrained_degrees_of_freedom=[] N_displacement=[] N_angle=[] N_currPosition=[] N_linMom=[] N_angMom=[] N_intForce=[] N_intMoment=[] N_moment=[] N_posTimeSteps=[] N_angTimeSteps=[] N_force=[] N_orient=[] ############# edges E_source=[] E_target=[] E_area=[] E_density=[] E_stiffness=[] E_stress=[] E_axis=[] E_currentRestLength=[] E_pos2=[] E_angle1v=[] E_angle2v=[] E_angle1=[] E_angle2=[] E_currentTransverseStrainSum=[] E_stressTimeSteps=[] ######## voxCount=0 linkCount=0 nodes = setup["nodes"] edges = setup["edges"] voxCount=size(nodes)[1] linkCount=size(edges)[1] strain =0 #todooo moveeee ######## ######## ######## ######## # println(toAxisXVector3([-5,0,-5 ],[-0.7071067811865475,0,-0.7071067811865475 ])) # println(toAxisXVector3([5,-5,0 ],[0.7071067811865475,-0.7071067811865475,0 ])) # println(toAxisXVector3([-5,5,0 ],[-0.7071067811865475,0.7071067811865475,0 ])) # println(toAxisXVector3([-5,-5,0 ],[-0.7071067811865475,-0.7071067811865475,0 ])) # println(toAxisXVector3([5,0,5 ],[0.7071067811865475,0,0.7071067811865475 ])) # println(toAxisXVector3([0,5,5 ],[0,0.7071067811865475,0.7071067811865475 ])) # println() # println(toAxisOriginalVector3([1 0 0],[0 0 1])) # println(toAxisOriginalVector3([0 1 0],[0 0 1])) # println(toAxisOriginalVector3([0 0 1],[0 0 1])) # println() # println(toAxisXVector3([1 0 0],[0 0 1])) # println(toAxisXVector3([0 1 0],[0 0 1])) # println(toAxisXVector3([0 0 1],[0 0 1])) # println() dt=0.0251646 t=@timed simulateParallel(setup,numTimeSteps,dt,true,10) time=t[2] println("ran latticeSize $latticeSize with $voxCount voxels and $linkCount edges for $numTimeSteps time steps took $time seconds") # setup["animation"]["exaggeration"]=75444 if save updateDataAndSave(setup,"../json/trialJuliaParallel.json") end