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:

  1. Maximum output displacement due to input load

  2. Constrained maximum volume

  3. Spring attached to output

  4. (Optional) spring attached to input

However, this problem formulation only works with

  1. Spring with positive output stiffness

  2. Non-design domains at the input and output

  3. Active volume constraint

  4. 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)

Gallery generated by Sphinx-Gallery