Source code for topopt.mechanisms.problems

"""Compliant mechanism synthesis problems using topology optimization."""

import numpy
import scipy.sparse

from ..problems import ElasticityProblem
from .boundary_conditions import MechanismSynthesisBoundaryConditions
from ..utils import deleterowcol


[docs]class MechanismSynthesisProblem(ElasticityProblem): r""" Topology optimization problem to generate compliant mechanisms. :math:`\begin{aligned} \max_{\boldsymbol{\rho}} \quad & \{u_{\text{out}}=\mathbf{l}^{T} \mathbf{u}\}\\ \textrm{subject to}: \quad & \mathbf{K}\mathbf{u} = \mathbf{f}_\text{in}\\ & \sum_{e=1}^N v_e\rho_e \leq V_\text{frac}, \quad 0 < \rho_\min \leq \rho_e \leq 1, \quad e=1, \dots, N.\\ \end{aligned}` where :math:`\mathbf{l}` is a vector with the value 1 at the degree(s) of freedom corresponding to the output point and with zeros at all other places. Attributes ---------- spring_stiffnesses: numpy.ndarray The spring stiffnesses of the actuator and output displacement. Emin: float The minimum stiffness of elements. Emax: float The maximum stiffness of elements. """
[docs] @staticmethod def lk(E: float = 1.0, nu: float = 0.3) -> numpy.ndarray: """ Build the element stiffness matrix. Parameters ---------- E: Young's modulus of the material. nu: Poisson's ratio of the material. Returns ------- The element stiffness matrix for the material. """ return ElasticityProblem.lk(1e0, nu)
[docs] def __init__( self, bc: MechanismSynthesisBoundaryConditions, penalty: float): """ Create the topology optimization problem. Parameters ---------- nelx: Number of elements in the x direction. nely: Number of elements in the x direction. penalty: Penalty value used to penalize fractional densities in SIMP. bc: Boundary conditions of the problem. """ super().__init__(bc, penalty) self.Emin = 1e-6 # Minimum stiffness of elements self.Emax = 1e2 # Maximum stiffness of elements # Spring stiffnesses for the actuator and output displacement self.spring_stiffnesses = numpy.full( numpy.nonzero(self.f)[0].shape, 10.0)
[docs] def build_K(self, xPhys: numpy.ndarray, remove_constrained: bool = True ) -> scipy.sparse.coo.coo_matrix: """ Build the stiffness matrix for the problem. Parameters ---------- xPhys: The element densisities used to build the stiffness matrix. remove_constrained: Should the constrained nodes be removed? Returns ------- The stiffness matrix for the mesh. """ # Build the stiffness matrix using inheritance K = super().build_K(xPhys, remove_constrained=False).tocsc() # Add spring stiffnesses spring_ids = numpy.nonzero(self.f)[0] K[spring_ids, spring_ids] += self.spring_stiffnesses # K = (K.T + K) / 2. # Make sure the stiffness matrix is symmetric # Remove constrained dofs from matrix and convert to coo if remove_constrained: K = deleterowcol(K, self.fixed, self.fixed) return K.tocoo()
[docs] def compute_objective(self, xPhys: numpy.ndarray, dobj: numpy.ndarray ) -> float: r""" Compute the objective and gradient of the mechanism synthesis problem. The objective is :math:`u_{\text{out}}=\mathbf{l}^{T} \mathbf{u}` where :math:`\mathbf{l}` is a vector with the value 1 at the degree(s) of freedom corresponding to the output point and with zeros at all other places. The gradient of the objective is :math:`\begin{align} u_\text{out} &= \mathbf{l}^T\mathbf{u} = \mathbf{l}^T\mathbf{u} + \boldsymbol{\lambda}^T(\mathbf{K}\mathbf{u} - \mathbf{f})\\ \frac{\partial u_\text{out}}{\partial \rho_e} &= (\mathbf{K}\boldsymbol{\lambda} + \mathbf{l})^T \frac{\partial \mathbf u}{\partial \rho_e} + \boldsymbol{\lambda}^T\frac{\partial \mathbf K}{\partial \rho_e} \mathbf{u} = \boldsymbol{\lambda}^T\frac{\partial \mathbf K}{\partial \rho_e} \mathbf{u} \end{align}` where :math:`\mathbf{K}\boldsymbol{\lambda} = -\mathbf{l}`. Parameters ---------- xPhys: The density design variables. dobj: The gradient of the objective to compute. Returns ------- The objective of the compliant mechanism synthesis problem. """ # Setup and solve FE problem self.update_displacements(xPhys) u = self.u[:, 0][self.edofMat].reshape(-1, 8) # Displacement λ = self.u[:, 1][self.edofMat].reshape(-1, 8) # Fixed vector (Kλ = -l) obj = self.f[:, 1].T @ self.u[:, 0] self.obje[:] = (λ @ self.KE * u).sum(1) self.compute_young_moduli(xPhys, dobj) # Stores the derivative in dobj dobj *= -self.obje return obj