-
Amira Abdel-Rahman authoredAmira Abdel-Rahman authored
parallelFEA.jl 39.50 KiB
# 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