Skip to content
Snippets Groups Projects
Commit d67e3b32 authored by Sam Calisch's avatar Sam Calisch
Browse files

cleaned up code, added chamfer

parent 9ddd2d8d
Branches
No related tags found
No related merge requests found
Pipeline #
flexure/figure_1.png

66.4 KiB

...@@ -5,6 +5,8 @@ import argparse ...@@ -5,6 +5,8 @@ import argparse
from pyframe3dd.frame3dd import write_frame3dd_file, read_lowest_mode, read_frame3dd_displacements, compute_mass from pyframe3dd.frame3dd import write_frame3dd_file, read_lowest_mode, read_frame3dd_displacements, compute_mass
from pyframe3dd.util import magnitudes, close from pyframe3dd.util import magnitudes, close
import subprocess import subprocess
import matplotlib.pyplot as plt
plt.style.use('bmh')
def plot_connections(nodes,beamsets): def plot_connections(nodes,beamsets):
#for debug only, this is slow! #for debug only, this is slow!
...@@ -45,49 +47,61 @@ def clean_up_frame3dd(filename): ...@@ -45,49 +47,61 @@ def clean_up_frame3dd(filename):
def build(args): def build(args):
#return nodes,rods as numpy arrays #return nodes,rods as numpy arrays
dxy = args.attach_radius/sqrt(2) dxy = args.attach_radius/sqrt(2)
nodes = array([[dxy,dxy,0],[-dxy,dxy,0],[-dxy,-dxy,0],[dxy,-dxy,0]])
if args.flexure_type == 'mirrored': solid_beams = array([ [0,1],[1,2],[2,3],[3,0],[0,2],[1,3] ])
nodes = args.l*array([[0,0,0],[0,1,0],[-1,1,0],[1,0,0],[1,-1,0]]) #one side of flexure plate
beams = array([[0,1],[1,2],[0,3],[3,4]])
nodes += array([dxy,dxy,0]) #offset by attachment radius
elif args.flexure_type == 'cyclic':
nodes = args.l*array([[0,0,0],[0,1,0],[-1,1,0]]) + array([dxy,dxy,0])
beams = array([[0,1],[1,2],[3,4]])
nodes = vstack((nodes, args.l*array([[1,1,0],[1,0,0]]) + array([dxy,-dxy,0])))
nodes = vstack((nodes,array([[-n[0],-n[1],0] for n in nodes]))) #append reflection
beams = vstack((beams, beams + 5))
z_os = array([0,0,.5*args.sep]) z_os = array([0,0,.5*args.sep])
nodes = vstack(( nodes+z_os, nodes-z_os )) nodes = vstack(( nodes+z_os, nodes-z_os ))
beams = vstack((beams, beams + 10)) solid_beams = vstack((solid_beams, solid_beams + 4))
solid_beams = vstack((solid_beams, array([
solid_nodes = array([ [dxy,-dxy,.5*args.sep],[-dxy,dxy,.5*args.sep],[dxy,-dxy,-.5*args.sep],[-dxy,dxy,-.5*args.sep] ]) [0,4],[1,5],[2,6],[3,7],
nodes = vstack((nodes, solid_nodes)) [0,5],[1,6],[2,7],[3,4],
solid_beams = array([ [0,7],[1,4],[2,5],[3,6] ])))
[0,5],[0,10],[5,15],[10,15],#[0,15],[5,10], #sensor nodes
[0,20],[0,21],[5,20],[5,21],[20,21], nodes = vstack((nodes, array([ [args.sensor_radius,0,0],[0,args.sensor_radius,0],[-args.sensor_radius,0,0],[0,-args.sensor_radius,0] ])))
[10,22],[10,23],[15,22],[15,23],[22,23], solid_beams = vstack((solid_beams, array([ [0,8],[3,8],[0,9],[1,9],[1,10],[2,10],[2,11],[3,11] ]) ))
[20,22],[21,23],#[20,23],[21,22], solid_beams = vstack((solid_beams, array([ [4,8],[7,8],[4,9],[5,9],[5,10],[6,10],[6,11],[7,11] ]) ))
[0,22],[0,23],[5,22],[5,23],
[10,20],[10,21],[15,20],[15,21]
])
if args.flexure_type == 'cyclic': if args.flexure_type == 'cyclic':
beams = vstack((beams, array([[4,20],[9,21],[14,22],[19,23]]))) if args.chamfer > 0:
l = args.l; ch = args.chamfer
#chamfer the flexure
flexure_nodes = array([[0,l-ch,0],[-ch,l,0],[-l,l,0]]) + array([dxy,dxy,0])
flexure_nodes = vstack(( flexure_nodes, array([[l-ch,0,0],[l,ch,0],[l,l,0]]) + array([dxy,-dxy,0]) ))
#append reflection
flexure_nodes = vstack((flexure_nodes,array([[-n[0],-n[1],0] for n in flexure_nodes])))
flexure_beams = array([[0,12],[12,13],[13,14],[3,15],[15,16],[16,17],[2,18],[18,19],[19,20],[1,21],[21,22],[22,23]])
#append both plates
flexure_nodes = vstack(( flexure_nodes + z_os, flexure_nodes - z_os))
flexure_beams = vstack(( flexure_beams, array([[4,24],[24,25],[25,26],[7,27],[27,28],[28,29],[6,30],[30,31],[31,32],[5,33],[33,34],[34,35]]) ))
fixed_nodes = [14,17,20,23,26,29,32,35]
else:
flexure_nodes = args.l*array([[0,1,0],[-1,1,0]]) + array([dxy,dxy,0])
flexure_nodes = vstack(( flexure_nodes, args.l*array([[1,0,0],[1,1,0]]) + array([dxy,-dxy,0]) ))
#append reflection
flexure_nodes = vstack((flexure_nodes,array([[-n[0],-n[1],0] for n in flexure_nodes])))
flexure_beams = array([[0,12],[12,13],[3,14],[14,15],[2,16],[16,17],[1,18],[18,19]])
#append both plates
flexure_nodes = vstack(( flexure_nodes + z_os, flexure_nodes - z_os))
flexure_beams = vstack(( flexure_beams, array([[4,20],[20,21],[7,22],[22,23],[6,24],[24,25],[5,26],[26,27]]) ))
fixed_nodes = [13,15,17,19,21,23,25,27]
elif args.flexure_type == 'mirrored':
flexure_nodes = args.l*array([[0,1,0],[-1,1,0]]) + array([dxy,dxy,0])
flexure_nodes = vstack(( flexure_nodes, args.l*array([[1,0,0],[1,-1,0]]) + array([dxy,dxy,0]) ))
#append reflection
flexure_nodes = vstack((flexure_nodes,array([[-n[0],-n[1],0] for n in flexure_nodes])))
flexure_beams = array([[0,12],[12,13],[0,14],[14,15],[2,16],[16,17],[2,18],[18,19]])
#append both plates
flexure_nodes = vstack(( flexure_nodes + z_os, flexure_nodes - z_os))
flexure_beams = vstack(( flexure_beams, array([[4,20],[20,21],[4,22],[22,23],[6,24],[24,25],[6,26],[26,27]]) ))
fixed_nodes = [13,15,17,19,21,23,25,27]
nodes = vstack((nodes, array([ [args.sensor_radius,0,0],[0,args.sensor_radius,0],[-args.sensor_radius,0,0],[0,-args.sensor_radius,0] ])))
solid_beams = vstack(( solid_beams, array([
[24,0],[24,10],[24,20],[24,22],
[25,0],[25,10],[25,21],[25,23],
[26,5],[26,15],[26,21],[26,23],
[27,5],[27,15],[27,20],[27,22]
])))
return nodes, beams, solid_beams nodes = vstack((nodes, flexure_nodes))
return nodes, flexure_beams, solid_beams, fixed_nodes
def run_simulation(args): def run_simulation(args):
#set up simulation #set up simulation
nodes,beams,solid_beams = build(args) nodes,beams,solid_beams,fixed_nodes = build(args)
global_args = { global_args = {
'n_modes':args.n_modes,'length_scaling':args.length_scaling,'exagerration':10, 'n_modes':args.n_modes,'length_scaling':args.length_scaling,'exagerration':10,
'zoom_scale':2.,'node_radius':zeros(shape(nodes)[0]), 'zoom_scale':2.,'node_radius':zeros(shape(nodes)[0]),
...@@ -98,15 +112,14 @@ def run_simulation(args): ...@@ -98,15 +112,14 @@ def run_simulation(args):
(beams,{'E':args.E,'nu':args.nu,'rho':args.rho,'cross_section':'rectangular','d2':args.w,'d1':args.t,'roll':0.,'loads':[],'beam_divisions':args.bd,'prestresses':[]}), (beams,{'E':args.E,'nu':args.nu,'rho':args.rho,'cross_section':'rectangular','d2':args.w,'d1':args.t,'roll':0.,'loads':[],'beam_divisions':args.bd,'prestresses':[]}),
(solid_beams,{'E':10*args.E,'nu':args.nu,'rho':args.rho,'cross_section':'rectangular','d1':.003,'d2':.003,'roll':0.,'loads':[],'beam_divisions':1,'prestresses':[]}) (solid_beams,{'E':10*args.E,'nu':args.nu,'rho':args.rho,'cross_section':'rectangular','d1':.003,'d2':.003,'roll':0.,'loads':[],'beam_divisions':1,'prestresses':[]})
] ]
if args.flexure_type == 'mirrored':
fixed_nodes = [2,4,7,9,12,14,17,19]
elif args.flexure_type == 'cyclic':
fixed_nodes = [2,3,7,8,12,13,17,18]
constraints = [{'node':node,'DOF':dof,'value':0} for dof in [0,1,2,3,4,5] for node in fixed_nodes] constraints = [{'node':node,'DOF':dof,'value':0} for dof in [0,1,2,3,4,5] for node in fixed_nodes]
loaded_nodes = [0,5,10,15,20,21,22,23] #loaded_nodes = [0,5,10,15,20,21,22,23]
sensor_nodes = [24,25,26,27] #sensor_nodes = [24,25,26,27]
loaded_nodes = range(8)
sensor_nodes = [8,9,10,11]
results = [] results = []
for force_dof in [0,1,2]: for force_dof in [0,1,2]:
...@@ -125,7 +138,7 @@ def run_simulation(args): ...@@ -125,7 +138,7 @@ def run_simulation(args):
loads = [{'node':n,'DOF':torque_dof,'value':torque_force if nodes[n][2]>0 else -torque_force} for n in loaded_nodes] loads = [{'node':n,'DOF':torque_dof,'value':torque_force if nodes[n][2]>0 else -torque_force} for n in loaded_nodes]
run_frame3dd(args,nodes,global_args,beam_sets,constraints,loads) run_frame3dd(args,nodes,global_args,beam_sets,constraints,loads)
disps = read_frame3dd_displacements(global_args['frame3dd_filename']) disps = read_frame3dd_displacements(global_args['frame3dd_filename'])
moving_sensor_nodes = [24,26] if torque_dof==0 else [25,27] moving_sensor_nodes = [8,10] if torque_dof==0 else [9,11]
#print disps[sensor_nodes] #print disps[sensor_nodes]
#axis = (array([0,0,0]), array([0,-1,0]) if torque_dof==0 else array([1,0,0]) ) #axis = (array([0,0,0]), array([0,-1,0]) if torque_dof==0 else array([1,0,0]) )
...@@ -159,45 +172,9 @@ def run_simulation(args): ...@@ -159,45 +172,9 @@ def run_simulation(args):
#todo: plot displacements vs. design parameters #todo: plot displacements vs. design parameters
return results return results
def find_stability_threshold(args):
#out loop of simulations to determine the buckling load
lower = 0 #lower bound
upper = 10*args.force_res #initial upper bound before bracketing
bracketed=False
#actually not necessary, but fun to have the unloaded frequency
args.force = lower
res = run_simulation(args)
freqs = [res['fundamental_frequency']]
forces = [args.force]
i = 0
while not bracketed:
print lower,upper,bracketed,res['fundamental_frequency']
args.force = upper
res = run_simulation(args); i += 1
if res['fundamental_frequency']<0:
bracketed=True
else:
freqs.append(res['fundamental_frequency'])
forces.append(args.force)
lower = upper
upper = 2*upper
while (upper-lower > args.force_res):
print lower,upper,bracketed
args.force = .5*(upper+lower)
res = run_simulation(args); i += 1
if res['fundamental_frequency']>0:
freqs.append(res['fundamental_frequency'])
forces.append(args.force)
lower = .5*(upper+lower)
else:
upper = .5*(upper+lower)
return forces,freqs,res
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('-M','--mode',choices=('simulate','search', 'visualize'), required=True) parser.add_argument('-M','--mode',choices=('simulate','graph', 'visualize'), required=True)
parser.add_argument('-flexure_type','--flexure_type',choices=('cyclic','mirrored'), required=True) parser.add_argument('-flexure_type','--flexure_type',choices=('cyclic','mirrored'), required=True)
parser.add_argument('-Q','--quiet',action='store_true',help='Whether to suppress frame3dd output') parser.add_argument('-Q','--quiet',action='store_true',help='Whether to suppress frame3dd output')
parser.add_argument("-f","--force", type=double, default=.1, help="force to apply (N)") parser.add_argument("-f","--force", type=double, default=.1, help="force to apply (N)")
...@@ -208,8 +185,9 @@ if __name__ == '__main__': ...@@ -208,8 +185,9 @@ if __name__ == '__main__':
parser.add_argument("-t","--t", type=double, default=.0023, help="thickness of flexure material (m)") parser.add_argument("-t","--t", type=double, default=.0023, help="thickness of flexure material (m)")
parser.add_argument("-l","--l", type=double, default=.0068, help="length of flexure segment (m)") parser.add_argument("-l","--l", type=double, default=.0068, help="length of flexure segment (m)")
parser.add_argument("-attach_radius","--attach_radius", type=double, default=.0043, help="distance from z axis to flexure attachment (m)") parser.add_argument("-attach_radius","--attach_radius", type=double, default=.0043, help="distance from z axis to flexure attachment (m)")
parser.add_argument("-sep","--sep", type=double, default=.025, help="flexure plate z separation (m)") parser.add_argument("-sep","--sep", type=double, default=.0185, help="flexure plate z separation (m)")
parser.add_argument("-sensor_radius","--sensor_radius", type=double, default=.012, help="distance from rotation axis to sensor (m)") parser.add_argument("-sensor_radius","--sensor_radius", type=double, default=.012, help="distance from rotation axis to sensor (m)")
parser.add_argument("-chamfer","--chamfer", type=double, default=.001, help="chamfer length for flexure (m), zero for no chamfer")
parser.add_argument("-bd","--bd", type=int, default=1, help='how many divisions for each rod, useful in buckling analysis') parser.add_argument("-bd","--bd", type=int, default=1, help='how many divisions for each rod, useful in buckling analysis')
parser.add_argument("-E","--E", type=double, default=70e9, help="Young's Modulus of laminate") parser.add_argument("-E","--E", type=double, default=70e9, help="Young's Modulus of laminate")
...@@ -220,21 +198,83 @@ if __name__ == '__main__': ...@@ -220,21 +198,83 @@ if __name__ == '__main__':
parser.add_argument("-ls","--length_scaling", type=double, default=1., help="Scale factor to keep numbers commesurate") parser.add_argument("-ls","--length_scaling", type=double, default=1., help="Scale factor to keep numbers commesurate")
args = parser.parse_args() args = parser.parse_args()
if args.mode=='search': #if args.mode=='search':
forces,freqs,last_res = find_stability_threshold(args) # forces,freqs,last_res = find_stability_threshold(args)
print "Fundamental frequency: %.3f Hz"%(freqs[-1]) # print "Fundamental frequency: %.3f Hz"%(freqs[-1])
print "Critical force: %.3f N"%(forces[-1]) # print "Critical force: %.3f N"%(forces[-1])
print "Critical stress: %.3f MPa"%(last_res['stress']/1e6) # print "Critical stress: %.3f MPa"%(last_res['stress']/1e6)
if args.mode == 'graph':
ws = linspace(.000, .9*args.l, 10)
res = {}
for wi in ws:
#args.w = wi
args.chamfer = wi
res[wi] = run_simulation(args)
X = [1e6*res[wi][0]['displacement'] for wi in ws]
Y = [1e6*res[wi][1]['displacement'] for wi in ws]
Z = [1e6*res[wi][2]['displacement'] for wi in ws]
rX = [1e6*res[wi][3]['displacement'] for wi in ws]
rY = [1e6*res[wi][4]['displacement'] for wi in ws]
rZ = [1e6*res[wi][5]['displacement'] for wi in ws]
print ws,X,Y,Z
plt.plot( 1e3*ws, X, label='X' )
plt.plot( 1e3*ws, Y, label='Y' )
plt.plot( 1e3*ws, Z, label='Z' )
plt.plot( 1e3*ws, rX, label='rX' )
plt.plot( 1e3*ws, rY, label='rY' )
plt.plot( 1e3*ws, rZ, label='rZ' )
plt.ylabel('displacement at sensor (microns)')
plt.xlabel('chamfer width (mm)')
plt.xlim([1e3*ws[0],1e3*ws[-1]])
plt.legend(loc='upper right')
plt.show()
elif args.mode=='simulate': elif args.mode=='simulate':
res = run_simulation(args) res = run_simulation(args)
print res print res
#print "Fundamental frequency: %.3f Hz"%res['fundamental_frequency'] #print "Fundamental frequency: %.3f Hz"%res['fundamental_frequency']
#print "Stress: %.3f MPa"%(res['stress']/1e6) #print "Stress: %.3f MPa"%(res['stress']/1e6)
elif args.mode=='visualize': elif args.mode=='visualize':
nodes,rods,solid_beams = build(args) nodes,rods,solid_beams,fixed_nodes = build(args)
plot_connections(nodes,[rods,solid_beams]) plot_connections(nodes,[rods,solid_beams])
else: else:
assert(0) #should not be here assert(0) #should not be here
'''
def find_stability_threshold(args):
#out loop of simulations to determine the buckling load
lower = 0 #lower bound
upper = 10*args.force_res #initial upper bound before bracketing
bracketed=False
#actually not necessary, but fun to have the unloaded frequency
args.force = lower
res = run_simulation(args)
freqs = [res['fundamental_frequency']]
forces = [args.force]
i = 0
while not bracketed:
print lower,upper,bracketed,res['fundamental_frequency']
args.force = upper
res = run_simulation(args); i += 1
if res['fundamental_frequency']<0:
bracketed=True
else:
freqs.append(res['fundamental_frequency'])
forces.append(args.force)
lower = upper
upper = 2*upper
while (upper-lower > args.force_res):
print lower,upper,bracketed
args.force = .5*(upper+lower)
res = run_simulation(args); i += 1
if res['fundamental_frequency']>0:
freqs.append(res['fundamental_frequency'])
forces.append(args.force)
lower = .5*(upper+lower)
else:
upper = .5*(upper+lower)
return forces,freqs,res
'''
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment