Note
Go to the end to download the full example code.
Compliant mechanism (springs)
Example showing the (in)efficacy of design of a compliant mechanism inverter using the traditional method of springs
The topology optimization considers:
Maximum output displacement due to input load
Constrained maximum volume
Spring attached to output
(Optional) spring attached to input
However, this problem formulation only works with
Spring with positive output stiffness
Non-design domains at the input and output
Active volume constraint
Input spring stiffness not equal to output spring stiffness
Even with these items taken into consideration, convergence is still troublesome as the inverter requires the displacement (initially positive) to pass trough zero in order to become negative. Other formulations offer better convergence properties, such as using mechanism modes and constraint modes as is done in Compliant mechanism (static condensation) or purely based on compliance values in Compliant mechanism.
The spring stiffnesses are added as a constant matrix using the pymoto.AssembleStiffness module.
- References:
Bendsoe, M. P., & Sigmund, O. (2003). Topology optimization: theory, methods, and applications. Springer Science & Business Media. DOI: https://doi.org/10.1007/978-3-662-05086-6
34 import numpy as np
35 from scipy.sparse import csc_matrix
36 import pymoto as pym
37
38 # Problem settings
39 nx, ny = 80, 80 # Domain size
40 xmin, filter_radius, volfrac = 1e-6, 2, 0.3 # Density settings
41 nu, E = 0.3, 100 # Material properties
42
43 # Spring stiffnesses for the virtual springs at the input and output
44 input_spring_stiffness = 5.0
45 output_spring_stiffness = 15.0
46
47 nondesigndomain_size = 4 # Size of the non-design domain at the input and output
48
49 use_volume_constraint = True
50
51 if __name__ == "__main__":
52 # Set up the domain
53 domain = pym.VoxelDomain(nx, ny)
54
55 # Node and dof groups
56 nodes_left = domain.nodes[0, :].flatten()
57 nodes_right = domain.nodes[-1, :].flatten()
58
59 dofs_right = domain.get_dofnumber(nodes_right, [0, 1], ndof=2).flatten()
60 dofs_left_x = 2*nodes_left
61 dof_input = 2*nodes_left[0] + 1 # Input dof for mechanism
62 dof_output = 2*nodes_left[-1] + 1 # Output dof for mechanism
63
64 fixed_dofs = np.union1d(dofs_left_x, dofs_right)
65
66 # Setup rhs for input force
67 f = np.zeros(domain.nnodes * 2, dtype=float)
68 f[dof_input] = 1.0
69
70 # Signal with design variables
71 s_x = pym.Signal('x', state=volfrac * np.ones(domain.nel))
72
73 # Setup optimization problem
74 with pym.Network() as fn:
75 # Setup non-design domains
76 input_domain = domain.elements[:nondesigndomain_size, :nondesigndomain_size]
77 output_domain = domain.elements[:nondesigndomain_size, -nondesigndomain_size:]
78 non_design_domain = np.union1d(input_domain, output_domain)
79 s_xnondes = pym.SetValue(indices=non_design_domain, value=1.0)(s_x)
80
81 # Density filtering
82 s_xfilt = pym.DensityFilter(domain, radius=filter_radius)(s_xnondes)
83 s_xfilt.tag = 'Filtered density'
84
85 # Plot the design
86 pym.PlotDomain(domain, saveto="out/design")(s_xfilt)
87
88 # SIMP penalization
89 s_xsimp = pym.MathExpression(f"{xmin} + {1 - xmin}*inp0^3")(s_xfilt)
90
91 # Assembly (with added constant stiffness for springs)
92 istiff = np.array([dof_input, dof_output])
93 sstiff = np.array([input_spring_stiffness, output_spring_stiffness])
94 K_const = csc_matrix((sstiff, (istiff, istiff)), shape=(domain.nnodes*2, domain.nnodes*2))
95 s_K = pym.AssembleStiffness(domain, e_modulus=E, poisson_ratio=nu, bc=fixed_dofs, add_constant=K_const)(s_xsimp)
96
97 # Solve for the displacements
98 s_u = pym.LinSolve()(s_K, f)
99
100 # Objective is the displacement at the output
101 s_gobj = pym.Scaling(scaling=10.0)(s_u[dof_output])
102 s_gobj.tag = "Objective"
103
104 # List of optimization responses: first the objective and all others the constraints
105 optimization_responses = [s_gobj]
106
107 # Add volume constraint if requested
108 if use_volume_constraint:
109 # Volume
110 s_vol = pym.EinSum('i->')(s_xfilt)
111 s_vol.tag = "Volume"
112
113 # Volume constraint
114 s_gvol = pym.Scaling(scaling=10.0, maxval=volfrac * domain.nel)(s_vol)
115 s_gvol.tag = "Volume constraint"
116
117 optimization_responses.append(s_gvol)
118
119 # Plot iteration history
120 pym.PlotIter()(*optimization_responses)
121
122 # Optimization
123 pym.minimize_mma(s_x, optimization_responses, verbosity=2)