← Back to LocalConstraints2 documentation
LocalConstraints2 - Source Code¶
File: userfiles/SpeedReducer/subsystem2/LocalConstraints2.py
# Copyright (C) The DistributedDesignOptimizer Contributors
# Licensed under the GNU General Public License v3.0. See LICENSE file for details.
"""Local constraints module for Speed Reducer subsystem 2.
Defines the stress, deflection, and geometric constraints for shaft 2
in the Speed Reducer problem.
"""
import math
from typing import List
from Distributed_Design_Optimizer.subsystem import LocalSubSystemBasis
from Distributed_Design_Optimizer.subsystem.optimization.designproblem import LocalConstraintsInterface
from Distributed_Design_Optimizer.subsystem.tools import ScalerBasis, ScalerConstraint
class LocalConstraints2(LocalConstraintsInterface):
"""Local constraints class for Speed Reducer subsystem 2.
Evaluates shaft 2 stress, deflection, and mounting constraints.
Attributes:
None specific to this class; inherits from LocalConstraintsInterface.
"""
def __init__(self) -> None:
"""Initialize LocalConstraints2 instance."""
pass
def evaluateEqualityLocalConstraints(self, subsystem: LocalSubSystemBasis) -> None:
"""Evaluate equality constraints for subsystem 2.
Args:
subsystem: The local subsystem basis containing state information.
"""
responses: List[float] = subsystem.get_Responses_Unscaled() # unscaled values
scalers: List[ScalerBasis] = subsystem.get_Scalers()
equality_unscaled = []
# append any equality Local constraints to this list using the responses
################################################################
### USER CODE: Equality constraints ###
################################################################
# equality_unscaled.append(responses[...])
# equality_unscaled.append(responses[...])
# scale equality Local constraint evaluation
scl: List[ScalerConstraint] = [scalers[6]]
# equality: List[float] = [scl[i].transform(equality_unscaled[i]) for i in range(len(equality_unscaled))]
# or if no Equality Local constraints exist:
equality = None
# equality Local constraints needs to be a scaled01 quantity
################################################################
### END USER CODE ###
################################################################
subsystem.set_EqualityLocalConstraintsValue(equality)
def evaluate_Jacobian_EqualityLocalConstraints(self, subsystem: LocalSubSystemBasis) -> None:
"""Evaluate the Jacobian of the local equality constraints.
Args:
subsystem: The local subsystem basis containing state information.
"""
return None
def evaluate_Hessians_EqualityLocalConstraints(self, subsystem: LocalSubSystemBasis) -> None:
"""Evaluate the Hessians of the local equality constraints.
Args:
subsystem: The local subsystem basis containing state information.
"""
return None
def evaluateInEqualityLocalConstraints(self, subsystem: LocalSubSystemBasis) -> None:
"""Evaluate inequality constraints for subsystem 2.
Computes 3 shaft-related constraints on stress and geometry.
Args:
subsystem: The local subsystem basis containing state information.
"""
responses: List[float] = subsystem.get_Responses_Unscaled() # unscaled values
scalers: List[ScalerBasis] = subsystem.get_Scalers()
inequality_unscaled = []
# append any inequality Local constraints to this list using the responses
################################################################
### USER CODE: Inequality constraints ###
################################################################
inequality_unscaled.append(((1.93 * (responses[3]**3))/(responses[1] * responses[2] * (responses[4]**4))) - 1.0)
inequality_unscaled.append((math.sqrt(((745 * responses[3]) / (responses[1] * responses[2]))**2 + 157.5E+6)
/ (85.0 * (responses[4]**3))) - 1.0)
inequality_unscaled.append(((1.1 * responses[4] + 1.9) / responses[3]) - 1.0)
# scale the inequality Local constraint evaluation
scl: List[ScalerConstraint] = scalers[7:10]
inequality: List[float] = [scl[i].transform(inequality_unscaled[i]) for i in range(len(inequality_unscaled))]
# inequality Local constraints needs to be a scaled01 quantity
################################################################
### END USER CODE ###
################################################################
subsystem.set_InequalityLocalConstraintsValue(inequality)
def evaluate_Jacobian_InEqualityLocalConstraints(self, subsystem: LocalSubSystemBasis) -> None:
"""Evaluate the Jacobian of the local inequality constraints.
Args:
subsystem: The local subsystem basis containing state information.
"""
# === Tutorial: scaled <-> unscaled Jacobian (chain rule) ===================
# The optimizer works in SCALED [0, 1] design-variable space, so each Jacobian
# row must be d(scaled constraint) / d(scaled design variables). Every affine
# scaler has a constant slope get_scale() = d(scaled)/d(unscaled). The three
# constraints are written in RESPONSES r = [x1, x2, x3, x5, x7]; the design
# variables are ordered [x5, x7, x1, x2, x3], so we differentiate w.r.t. the
# responses, gather into design-variable order, then convert component-wise:
# dg_s/ds_j = get_scale(constraint) * dg/dx_u[j] / get_scale(dv_scaler_j)
# Return None instead to let the framework fall back to finite differences.
# ===========================================================================
responses: List[float] = subsystem.get_Responses_Unscaled() # unscaled values
scalers: List[ScalerBasis] = subsystem.get_Scalers()
des_var: List[float] = subsystem.get_DesignVariables_Unscaled() # unscaled values
################################################################
### USER CODE: Jacobian of inequality constraints ###
################################################################
# Constraints (unscaled), responses r = [x1, x2, x3, x5, x7]:
# g0 = 1.93*x5^3 / (x2 x3 x7^4) - 1 (monomial)
# g1 = sqrt((745 x5/(x2 x3))^2 + 157.5e6) / (85 x7^3) - 1 (sqrt composite)
# g2 = (1.1 x7 + 1.9)/x5 - 1 (sum of monomials)
dv_to_resp: List[int] = [3, 4, 0, 1, 2]
r: List[float] = list(responses)
n_r: int = len(r)
def mono_grad(coeff: float, p: List[int]) -> List[float]:
M = coeff
for k in range(n_r):
M *= r[k]**p[k]
return [p[k] / r[k] * M for k in range(n_r)]
# g0: monomial in r=[x1,x2,x3,x5,x7]
grad0 = mono_grad(1.93, [0, -1, -1, 3, -4])
# g1 = T * w - 1, with u = 745 x5/(x2 x3), T = sqrt(u^2 + 157.5e6),
# w = (1/85) x7^-3. u = 745 r1^-1 r2^-1 r3^1 (r1=x2, r2=x3, r3=x5).
u = 745.0 * r[3] / (r[1] * r[2])
T = math.sqrt(u**2 + 157.5E+6)
w = (1.0 / 85.0) * r[4]**-3
grad1 = [0.0] * n_r
grad1[1] = w * (u / T) * (-u / r[1]) # dg1/dx2
grad1[2] = w * (u / T) * (-u / r[2]) # dg1/dx3
grad1[3] = w * (u / T) * (u / r[3]) # dg1/dx5
grad1[4] = T * (-3.0 / 85.0) * r[4]**-4 # dg1/dx7
# g2 = 1.1*x7/x5 + 1.9/x5 - 1 (two monomials)
grad2a = mono_grad(1.1, [0, 0, 0, -1, 1])
grad2b = mono_grad(1.9, [0, 0, 0, -1, 0])
grad2 = [grad2a[k] + grad2b[k] for k in range(n_r)]
grads = [grad0, grad1, grad2]
constraint_scalers: List[ScalerBasis] = scalers[7:10]
n_dv: int = len(des_var)
jacobian: List[List[float]] = []
for row in range(3):
scale_c = constraint_scalers[row].get_scale()
jacobian.append([scale_c * grads[row][dv_to_resp[j]] / scalers[j].get_scale()
for j in range(n_dv)])
# jacobian must be a scaled01 quantity (w.r.t. scaled01 design variables)
################################################################
### END USER CODE ###
################################################################
return jacobian
def evaluate_Hessians_InEqualityLocalConstraints(self, subsystem: LocalSubSystemBasis) -> None:
"""Evaluate the Hessians of the local inequality constraints.
Args:
subsystem: The local subsystem basis containing state information.
"""
# === Tutorial: scaled <-> unscaled Hessians (chain rule, 2nd order) ========
# The optimizer works in SCALED [0, 1] space, so each constraint Hessian must
# be d^2(scaled constraint) / d(scaled design vars)^2. Each affine scaler has a
# constant slope get_scale() = d(scaled)/d(unscaled). For the sqrt composite
# g1 = T*w with T = sqrt(u^2 + K), the second derivatives of T follow
# d2T/dx dy = (u_x u_y + u u_xy)/T - u^2 u_x u_y / T^3.
# We build each Hessian in RESPONSE space, gather into design-variable order,
# then convert element-wise:
# d2g_s/ds_a ds_b = get_scale(constraint) * d2g/dx_a dx_b
# / (get_scale(x_a) * get_scale(x_b))
# Return None instead to let the framework fall back to finite differences.
# ===========================================================================
responses: List[float] = subsystem.get_Responses_Unscaled() # unscaled values
scalers: List[ScalerBasis] = subsystem.get_Scalers()
des_var: List[float] = subsystem.get_DesignVariables_Unscaled() # unscaled values
################################################################
### USER CODE: Hessians of inequality constraints ###
################################################################
dv_to_resp: List[int] = [3, 4, 0, 1, 2]
r: List[float] = list(responses)
n_r: int = len(r)
def mono_hess(coeff: float, p: List[int]) -> List[List[float]]:
M = coeff
for k in range(n_r):
M *= r[k]**p[k]
H = [[0.0 for _ in range(n_r)] for _ in range(n_r)]
for a in range(n_r):
for b in range(n_r):
if a == b:
H[a][b] = p[a] * (p[a] - 1) / r[a]**2 * M
else:
H[a][b] = p[a] * p[b] / (r[a] * r[b]) * M
return H
# g0: monomial
H0 = mono_hess(1.93, [0, -1, -1, 3, -4])
# g1 = T * w, T = sqrt(u^2 + K), u = 745 r1^-1 r2^-1 r3^1 (r1=x2,r2=x3,r3=x5),
# w = (1/85) r4^-3 (r4=x7).
K = 157.5E+6
D = 85.0
u = 745.0 * r[3] / (r[1] * r[2])
T = math.sqrt(u**2 + K)
uvars = [1, 2, 3] # response indices appearing in u
pexp = [-1, -1, 1] # corresponding exponents of u
u_x = {vi: pexp[i] / r[vi] * u for i, vi in enumerate(uvars)}
u_xy = {}
for i, vi in enumerate(uvars):
for j, vj in enumerate(uvars):
if vi == vj:
u_xy[(vi, vj)] = pexp[i] * (pexp[i] - 1) / r[vi]**2 * u
else:
u_xy[(vi, vj)] = pexp[i] * pexp[j] / (r[vi] * r[vj]) * u
w = (1.0 / D) * r[4]**-3
wp = (-3.0 / D) * r[4]**-4 # dw/dx7
wpp = (12.0 / D) * r[4]**-5 # d2w/dx7^2
H1 = [[0.0 for _ in range(n_r)] for _ in range(n_r)]
for vi in uvars:
for vj in uvars:
Txy = (u_x[vi] * u_x[vj] + u * u_xy[(vi, vj)]) / T - u**2 * u_x[vi] * u_x[vj] / T**3
H1[vi][vj] = w * Txy
for vi in uvars:
T_i = (u / T) * u_x[vi]
H1[vi][4] = T_i * wp
H1[4][vi] = T_i * wp
H1[4][4] = T * wpp
# g2: sum of two monomials
H2a = mono_hess(1.1, [0, 0, 0, -1, 1])
H2b = mono_hess(1.9, [0, 0, 0, -1, 0])
H2 = [[H2a[a][b] + H2b[a][b] for b in range(n_r)] for a in range(n_r)]
Hs = [H0, H1, H2]
constraint_scalers: List[ScalerBasis] = scalers[7:10]
n_dv: int = len(des_var)
hessians: List[List[List[float]]] = []
for row in range(3):
scale_c = constraint_scalers[row].get_scale()
hessians.append([
[scale_c * Hs[row][dv_to_resp[a]][dv_to_resp[b]] / (scalers[a].get_scale() * scalers[b].get_scale())
for b in range(n_dv)]
for a in range(n_dv)
])
# hessians must be scaled01 quantities (w.r.t. scaled01 design variables)
################################################################
### END USER CODE ###
################################################################
return hessians