loadpath 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- loadpath/README.md +294 -0
- loadpath/SKILL.md +584 -0
- loadpath/__init__.py +17 -0
- loadpath/__main__.py +1202 -0
- loadpath/am.py +316 -0
- loadpath/amgfast.py +326 -0
- loadpath/assembly.py +985 -0
- loadpath/benchmarks/__init__.py +0 -0
- loadpath/benchmarks/beams.py +266 -0
- loadpath/benchmarks/cases/ge-bracket.json +380 -0
- loadpath/benchmarks/evaluate.py +172 -0
- loadpath/benchmarks/mechanisms.py +416 -0
- loadpath/benchmarks/problems.py +140 -0
- loadpath/benchmarks/reference/bridge-3d.top3d.npz +0 -0
- loadpath/benchmarks/reference/cantilever-3d.top3d.npz +0 -0
- loadpath/benchmarks/reference/lbracket-2d.top88-ft2.npz +0 -0
- loadpath/benchmarks/reference/mbb-2d.top88-ft1.npz +0 -0
- loadpath/benchmarks/reference/mbb-2d.top88-ft2.npz +0 -0
- loadpath/benchmarks/reference.py +138 -0
- loadpath/benchmarks/run.py +301 -0
- loadpath/brep.py +1470 -0
- loadpath/bucklingopt.py +300 -0
- loadpath/buildsim.py +1490 -0
- loadpath/cadimport.py +904 -0
- loadpath/calibration.py +885 -0
- loadpath/cavities.py +211 -0
- loadpath/checks/__init__.py +0 -0
- loadpath/checks/dfm.py +354 -0
- loadpath/checks/exchanger.py +7 -0
- loadpath/checks/fea.py +212 -0
- loadpath/checks/fit.py +475 -0
- loadpath/checks/networks.py +260 -0
- loadpath/checks/opening.py +67 -0
- loadpath/checks/sdf_kit.py +7 -0
- loadpath/conjugate.py +267 -0
- loadpath/constrained.py +329 -0
- loadpath/contact.py +691 -0
- loadpath/coopt.py +342 -0
- loadpath/cores.py +163 -0
- loadpath/crease.py +1138 -0
- loadpath/critic.py +212 -0
- loadpath/crosscheck.py +1107 -0
- loadpath/cutcell.py +510 -0
- loadpath/data/lattice_buckling.json +439 -0
- loadpath/data/lattice_tensors.json +27449 -0
- loadpath/data/published_experiments.json +71 -0
- loadpath/decimate.py +177 -0
- loadpath/delivery.py +190 -0
- loadpath/directions.py +262 -0
- loadpath/domain.py +630 -0
- loadpath/drains.py +615 -0
- loadpath/durability.py +413 -0
- loadpath/editor.html +3135 -0
- loadpath/editor.py +610 -0
- loadpath/escape.py +354 -0
- loadpath/evals/__init__.py +0 -0
- loadpath/evals/baselines/coarse.json +1543 -0
- loadpath/evals/baselines/full.json +1497 -0
- loadpath/evals/brief_kinds/__init__.py +1 -0
- loadpath/evals/brief_kinds/heatx.py +387 -0
- loadpath/evals/brief_kinds/lattice.py +571 -0
- loadpath/evals/brief_kinds/multimat.py +593 -0
- loadpath/evals/briefs/assets/bracket.step +1201 -0
- loadpath/evals/briefs/assets/desk_arm.urdf +22 -0
- loadpath/evals/briefs/assets/elbow-gearbox.stl +0 -0
- loadpath/evals/briefs/assets/motor-body.stl +0 -0
- loadpath/evals/briefs/assets/sketch.png +0 -0
- loadpath/evals/briefs/base-adapter-fdm.json +492 -0
- loadpath/evals/briefs/bell-crank-lpbf.json +180 -0
- loadpath/evals/briefs/belt-tensioner-fdm.json +151 -0
- loadpath/evals/briefs/camera-boom-mjf.json +339 -0
- loadpath/evals/briefs/camera-mount-pla.json +203 -0
- loadpath/evals/briefs/coolant-isolator-petg.json +100 -0
- loadpath/evals/briefs/elbow-bearing-bracket-sls.json +300 -0
- loadpath/evals/briefs/elbow-gearbox-forearm-mjf.json +558 -0
- loadpath/evals/briefs/elbow-link-fatigue-fdm.json +251 -0
- loadpath/evals/briefs/elbow-stop-pad-tpu.json +39 -0
- loadpath/evals/briefs/forearm-fdm.json +146 -0
- loadpath/evals/briefs/forearm-servo-link-fdm.json +327 -0
- loadpath/evals/briefs/grip-pad-finger-mm.json +354 -0
- loadpath/evals/briefs/gripper-bracket-creep-fdm.json +340 -0
- loadpath/evals/briefs/gripper-finger-fdm.json +155 -0
- loadpath/evals/briefs/gripper-jaw-fatigue-mjf.json +328 -0
- loadpath/evals/briefs/gripper-palm-fdm.json +715 -0
- loadpath/evals/briefs/hot-stepper-mount-asa-fdm.json +471 -0
- loadpath/evals/briefs/hpu-oil-cooler-lpbf.json +121 -0
- loadpath/evals/briefs/isolated-camera-mount-mm.json +675 -0
- loadpath/evals/briefs/kart-charge-cooler-lpbf.json +93 -0
- loadpath/evals/briefs/lighten-step-bracket.json +220 -0
- loadpath/evals/briefs/nema17-mount-stl.json +429 -0
- loadpath/evals/briefs/pillow-block-sls.json +220 -0
- loadpath/evals/briefs/polyjet-fingertip-mm.json +305 -0
- loadpath/evals/briefs/rod-hook-pla.json +154 -0
- loadpath/evals/briefs/sensor-arm-resin.json +150 -0
- loadpath/evals/briefs/shelf-bracket-pla.json +158 -0
- loadpath/evals/briefs/shoulder-riser-mjf.json +487 -0
- loadpath/evals/briefs/sketch-clevis.json +229 -0
- loadpath/evals/briefs/spring-bracket-pacf.json +294 -0
- loadpath/evals/briefs/tool-flange-ti64.json +280 -0
- loadpath/evals/briefs/tray-arm-lattice-fdm.json +260 -0
- loadpath/evals/briefs/turret-fdm.json +281 -0
- loadpath/evals/briefs/upper-arm-sls.json +179 -0
- loadpath/evals/briefs/urdf-forearm.json +233 -0
- loadpath/evals/briefs/wrist-link-mjf.json +278 -0
- loadpath/evals/briefs/wrist-yoke-mjf.json +245 -0
- loadpath/evals/briefs.py +393 -0
- loadpath/evals/cases/arm-link-cast.json +157 -0
- loadpath/evals/cases/arm-link-driven.json +171 -0
- loadpath/evals/cases/arm-link-sandwich.json +315 -0
- loadpath/evals/cases/arm-link.json +111 -0
- loadpath/evals/cases/bell-crank.json +116 -0
- loadpath/evals/cases/grip-turret.json +479 -0
- loadpath/evals/cases/grip-upper-arm.json +253 -0
- loadpath/evals/cases/grip-wrist-link-mjf.json +929 -0
- loadpath/evals/cases/grip-wrist-yoke.json +831 -0
- loadpath/evals/cases/lpbf-intercooler-core.json +59 -0
- loadpath/evals/cases/mbb-beam.json +61 -0
- loadpath/evals/cases/motor-body.stl +0 -0
- loadpath/evals/cases/motor-mount.json +265 -0
- loadpath/evals/cases/printed-hook.json +77 -0
- loadpath/evals/cases/wall-bracket-fasteners.json +125 -0
- loadpath/evals/cases/wall-bracket.json +126 -0
- loadpath/evals/corpus.py +179 -0
- loadpath/evals/pareto.py +90 -0
- loadpath/evals/run.py +860 -0
- loadpath/examples/arm-link.json +25 -0
- loadpath/examples/bumpered-bracket-compression.json +225 -0
- loadpath/examples/bumpered-bracket-lattice.json +204 -0
- loadpath/examples/bumpered-bracket.json +34 -0
- loadpath/examples/gripper-finger-mm.json +185 -0
- loadpath/examples/heat-exchanger-counterflow.json +21 -0
- loadpath/examples/heat-exchanger-crossflow-tuned.json +48 -0
- loadpath/examples/heat-exchanger-crossflow.json +20 -0
- loadpath/examples/pedal-lattice.json +23 -0
- loadpath/examples/pressure-plenum.json +12 -0
- loadpath/examples/shelf-bracket.json +27 -0
- loadpath/exchanger.py +831 -0
- loadpath/fasteners.py +199 -0
- loadpath/fem.py +668 -0
- loadpath/femops.py +70 -0
- loadpath/fieldjit.py +981 -0
- loadpath/fieldops.py +119 -0
- loadpath/fields.py +3697 -0
- loadpath/fieldtorch.py +464 -0
- loadpath/flow.py +984 -0
- loadpath/flowtopo.py +300 -0
- loadpath/gpu.py +521 -0
- loadpath/heatx.py +1067 -0
- loadpath/heatx_thermal.py +432 -0
- loadpath/homogenized.py +886 -0
- loadpath/ibrep.py +552 -0
- loadpath/impact.py +693 -0
- loadpath/infill.py +376 -0
- loadpath/interface_library.py +340 -0
- loadpath/interlock.py +186 -0
- loadpath/isolator.py +149 -0
- loadpath/knn.py +204 -0
- loadpath/lattice.py +1065 -0
- loadpath/lattice3mf.py +760 -0
- loadpath/lattice_buckling.py +245 -0
- loadpath/lattice_grade.py +325 -0
- loadpath/lattice_orient.py +595 -0
- loadpath/lattice_print.py +159 -0
- loadpath/lattice_tensors.py +558 -0
- loadpath/latticestep.py +1210 -0
- loadpath/manufacture.py +446 -0
- loadpath/material_library.py +489 -0
- loadpath/materials.py +144 -0
- loadpath/mcp_server.py +786 -0
- loadpath/mechanism.py +218 -0
- loadpath/mechanism_nl.py +811 -0
- loadpath/meshops.py +452 -0
- loadpath/mg.py +853 -0
- loadpath/mma.py +214 -0
- loadpath/modal.py +210 -0
- loadpath/multimat.py +1788 -0
- loadpath/narrowband.py +418 -0
- loadpath/nlkernel.py +241 -0
- loadpath/nonlinear.py +1095 -0
- loadpath/orient.py +334 -0
- loadpath/porous.py +994 -0
- loadpath/pressure.py +171 -0
- loadpath/preview.py +335 -0
- loadpath/printcheck.py +263 -0
- loadpath/printsetup.py +207 -0
- loadpath/processcheck.py +376 -0
- loadpath/quick.py +113 -0
- loadpath/recall.py +406 -0
- loadpath/reconstruct.py +797 -0
- loadpath/render.py +436 -0
- loadpath/resolved.py +189 -0
- loadpath/schema.py +1701 -0
- loadpath/sdf_kit.py +843 -0
- loadpath/selfx.py +292 -0
- loadpath/shapes.py +584 -0
- loadpath/sharp.py +717 -0
- loadpath/simp.py +1170 -0
- loadpath/sizing.py +312 -0
- loadpath/slicer.py +321 -0
- loadpath/spec.py +1072 -0
- loadpath/step.py +806 -0
- loadpath/struts.py +639 -0
- loadpath/study.py +1937 -0
- loadpath/tasks.py +822 -0
- loadpath/thermal.py +1691 -0
- loadpath/threemf.py +487 -0
- loadpath/tune.py +437 -0
- loadpath/validation.py +145 -0
- loadpath/verify.py +1215 -0
- loadpath/viewer.html +694 -0
- loadpath/viewer.py +446 -0
- loadpath/viscoelastic.py +106 -0
- loadpath/voxelprint.py +582 -0
- loadpath/voxels.py +402 -0
- loadpath-0.1.1.dist-info/METADATA +319 -0
- loadpath-0.1.1.dist-info/RECORD +220 -0
- loadpath-0.1.1.dist-info/WHEEL +5 -0
- loadpath-0.1.1.dist-info/entry_points.txt +2 -0
- loadpath-0.1.1.dist-info/licenses/LICENSE +21 -0
- loadpath-0.1.1.dist-info/top_level.txt +1 -0
loadpath/mechanism.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Compliant mechanisms: parts that are meant to bend.
|
|
3
|
+
|
|
4
|
+
Everything else in loadpath makes a part as stiff as possible for its mass.
|
|
5
|
+
A gripper finger, a flexure hinge, a snap fit or a clip needs the opposite:
|
|
6
|
+
a push at one place (the input) should move another place (the output) as
|
|
7
|
+
far as possible in a chosen direction, against something that pushes back
|
|
8
|
+
(the workpiece). That is the classic compliant-mechanism problem (Sigmund
|
|
9
|
+
1997; Bendsøe & Sigmund, "Topology Optimization", ch. 2.6):
|
|
10
|
+
|
|
11
|
+
maximize u_out = l . u
|
|
12
|
+
subject to (K(x) + springs) u = f_in, sum(x) <= V
|
|
13
|
+
|
|
14
|
+
with a spring k_in at the input (the actuator is not infinitely stiff) and
|
|
15
|
+
k_out at the output (the object being gripped). Without the springs the
|
|
16
|
+
optimum is a mechanism of one-node hinges with no strength at all; the
|
|
17
|
+
output spring is what makes the result carry a grip force.
|
|
18
|
+
|
|
19
|
+
The sensitivity needs one adjoint solve, K lambda = l, and is
|
|
20
|
+
lambda_e . k_e u_e scaled by the SIMP derivative: unlike compliance, it can
|
|
21
|
+
take either sign. Sigmund's 104-line code uses damped optimality criteria
|
|
22
|
+
with the sensitivities clipped positive; here it is MMA (mma.py), which
|
|
23
|
+
takes the gradient whole.
|
|
24
|
+
|
|
25
|
+
The density filter, Heaviside projection, extrusion (2.5D) and
|
|
26
|
+
reconstruction are the same as for stiffness designs, so the result comes
|
|
27
|
+
back as a smooth, printable part. The optimization is linear: a TPU finger
|
|
28
|
+
that curls 60 degrees is well outside small deflections, so treat the output
|
|
29
|
+
as the layout. `verify_nonlinear` re-solves the solid cells of the result
|
|
30
|
+
with large deflections and a buckling check (nonlinear.verify_mechanism):
|
|
31
|
+
how far the output really travels, and whether the input force buckles a
|
|
32
|
+
thin member first.
|
|
33
|
+
"""
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import time
|
|
37
|
+
from dataclasses import dataclass, field
|
|
38
|
+
|
|
39
|
+
import numpy as np
|
|
40
|
+
import scipy.sparse as sp
|
|
41
|
+
|
|
42
|
+
from .domain import Domain
|
|
43
|
+
from .fem import Solver, Stiffness
|
|
44
|
+
from .simp import E_MIN, PENALTY, _dproject, _extrude, _project, density_filter
|
|
45
|
+
|
|
46
|
+
__all__ = ["MechanismOptions", "MechanismResult", "optimize_mechanism"]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class MechanismOptions:
|
|
51
|
+
volume_fraction: float
|
|
52
|
+
filter_radius: float
|
|
53
|
+
input_case: int = 0 # domain load case that is the actuator force
|
|
54
|
+
output_case: int = 1 # domain load case whose force direction is the output (any magnitude)
|
|
55
|
+
# Several (input, output) pairs at once, e.g. contact anywhere along a
|
|
56
|
+
# finger: the objective is the sum of the pairs' output displacements.
|
|
57
|
+
pairs: list | None = None
|
|
58
|
+
k_in: float = 0.0 # N/mm, spread over the input's loaded dofs
|
|
59
|
+
k_out: float = 0.0 # N/mm, spread over the output's dofs
|
|
60
|
+
modulus: float = 1.0 # MPa: the material's E (springs are in real units)
|
|
61
|
+
max_iterations: int = 120
|
|
62
|
+
beta_max: float = 16.0
|
|
63
|
+
beta_every: int = 20
|
|
64
|
+
move: float = 0.1
|
|
65
|
+
extrusion: int | None = None
|
|
66
|
+
tol: float = 0.005
|
|
67
|
+
verify_nonlinear: bool = False # large-deflection + buckling check of the result (first pair)
|
|
68
|
+
# Robust formulation (Wang, Lazarov & Sigmund 2011): also project the
|
|
69
|
+
# filtered field at 0.5 + robust (eroded) and 0.5 - robust (dilated) and
|
|
70
|
+
# maximize the worst of the three outputs. A one-node hinge disconnects
|
|
71
|
+
# in the eroded design, so the optimum keeps hinges at least about a
|
|
72
|
+
# filter radius wide: printable flexures instead of corner contacts.
|
|
73
|
+
robust: float | None = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass
|
|
77
|
+
class MechanismResult:
|
|
78
|
+
x: np.ndarray
|
|
79
|
+
xphys: np.ndarray
|
|
80
|
+
output_displacement: float # mm along the output direction, under the input force
|
|
81
|
+
iterations: int
|
|
82
|
+
seconds: float
|
|
83
|
+
history: list = field(default_factory=list)
|
|
84
|
+
nonlinear: dict | None = None # nonlinear.verify_mechanism's report, with verify_nonlinear
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _spring_diag(f, k, n_dof):
|
|
88
|
+
"""A grounded spring of k N/mm in all, shared evenly by the nodes the
|
|
89
|
+
vector f loads, on the dofs it loads (so an x push gets an x spring of
|
|
90
|
+
k, whatever the number of nodes)."""
|
|
91
|
+
dofs = np.nonzero(np.abs(f) > 0)[0]
|
|
92
|
+
d = np.zeros(n_dof)
|
|
93
|
+
if len(dofs) and k > 0:
|
|
94
|
+
d[dofs] = k / len(np.unique(dofs // 3))
|
|
95
|
+
return d
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def optimize_mechanism(domain: Domain, nu: float, opts: MechanismOptions, on_iter=None) -> MechanismResult:
|
|
99
|
+
t0 = time.time()
|
|
100
|
+
mesh = domain.mesh
|
|
101
|
+
n = mesh.n_elem
|
|
102
|
+
pres = domain.preserve
|
|
103
|
+
design = ~pres
|
|
104
|
+
pairs = opts.pairs or [(opts.input_case, opts.output_case)]
|
|
105
|
+
ins = sorted({i for i, _ in pairs})
|
|
106
|
+
outs = sorted({o for _, o in pairs})
|
|
107
|
+
# l . u = the output region's mean displacement along the output direction
|
|
108
|
+
ls = {o: domain.loads[o] / max(np.linalg.norm(domain.loads[o].reshape(-1, 3).sum(axis=0)), 1e-30) for o in outs}
|
|
109
|
+
sd = sum(_spring_diag(domain.loads[i], opts.k_in, mesh.n_dof) for i in ins) + \
|
|
110
|
+
sum(_spring_diag(domain.loads[o], opts.k_out, mesh.n_dof) for o in outs)
|
|
111
|
+
sd[domain.fixed] = 0.0
|
|
112
|
+
springs = sp.diags(sd).tocsr()
|
|
113
|
+
|
|
114
|
+
stiff = Stiffness(mesh, nu, domain.fixed)
|
|
115
|
+
solver = Solver(mesh, rtol=1e-5)
|
|
116
|
+
columns = None
|
|
117
|
+
if opts.extrusion is not None:
|
|
118
|
+
a = opts.extrusion
|
|
119
|
+
others = [i for i in range(3) if i != a]
|
|
120
|
+
ij = mesh.elem_ijk[:, others]
|
|
121
|
+
_, columns = np.unique(ij[:, 0] * (domain.dims[others[1]] + 1) + ij[:, 1], return_inverse=True)
|
|
122
|
+
h, hs = density_filter(domain, opts.filter_radius, pad=opts.extrusion is None)
|
|
123
|
+
ht = h.T.tocsr()
|
|
124
|
+
from .fem import hex8_ke
|
|
125
|
+
ke = hex8_ke(nu) * mesh.pitch # as Stiffness assembles it
|
|
126
|
+
edof = mesh.edof
|
|
127
|
+
|
|
128
|
+
x = np.full(n, opts.volume_fraction)
|
|
129
|
+
x[pres] = 1.0
|
|
130
|
+
target = opts.volume_fraction * design.sum()
|
|
131
|
+
beta, since = 1.0, 0
|
|
132
|
+
prev = {}
|
|
133
|
+
u0 = None
|
|
134
|
+
etas = [0.5] if not opts.robust else [0.5 + opts.robust, 0.5, 0.5 - opts.robust]
|
|
135
|
+
from .mma import MMA
|
|
136
|
+
mma = MMA(int(design.sum()), 1, xmin=0.0, xmax=1.0, move=opts.move)
|
|
137
|
+
history = []
|
|
138
|
+
it = 0
|
|
139
|
+
for it in range(1, opts.max_iterations + 1):
|
|
140
|
+
xt = _extrude((h @ x) / hs, columns, design)
|
|
141
|
+
dE_scale = opts.modulus * PENALTY * (1 - E_MIN)
|
|
142
|
+
real = [] # per realization: (raw outputs, d sum / d xt)
|
|
143
|
+
for eta in etas:
|
|
144
|
+
xp = _project(xt, beta, eta)
|
|
145
|
+
xp[pres] = 1.0
|
|
146
|
+
e = opts.modulus * (E_MIN + xp ** PENALTY * (1 - E_MIN))
|
|
147
|
+
k = (stiff.assemble(e) + springs).tocsr()
|
|
148
|
+
sol = solver.solve_many(k, [domain.loads[i] for i in ins] + [ls[o] for o in outs], prev.get(eta))
|
|
149
|
+
prev[eta] = sol
|
|
150
|
+
U = dict(zip(ins, sol[:len(ins)]))
|
|
151
|
+
LAM = dict(zip(outs, sol[len(ins):]))
|
|
152
|
+
raw = [float(ls[o] @ U[i]) for i, o in pairs]
|
|
153
|
+
# d(u_out)/d(xp_e) = -lambda_e . k_e u_e * dE/dxp (K lambda = l, K symmetric)
|
|
154
|
+
g = np.zeros(n)
|
|
155
|
+
for i, o in pairs:
|
|
156
|
+
g -= np.einsum("ij,jk,ik->i", LAM[o][edof], ke, U[i][edof])
|
|
157
|
+
g = g * dE_scale * xp ** (PENALTY - 1) * _dproject(xt, beta, eta)
|
|
158
|
+
real.append((raw, g))
|
|
159
|
+
raw = real[len(etas) // 2][0] # the blueprint (eta 0.5) is what is delivered
|
|
160
|
+
# Sum the outputs in mm, with one overall scale for MMA. (Scaling each
|
|
161
|
+
# pair by its own starting value let a pair that started near zero
|
|
162
|
+
# dominate; the fin-ray finger traded one contact's wrap for another's.)
|
|
163
|
+
if u0 is None:
|
|
164
|
+
u0 = max(abs(sum(raw)), max(abs(r) for r in raw), 1e-12)
|
|
165
|
+
vals = np.array([sum(r) / u0 for r, _ in real])
|
|
166
|
+
if len(etas) == 1:
|
|
167
|
+
uout, g = vals[0], real[0][1] / u0
|
|
168
|
+
else:
|
|
169
|
+
# smooth minimum (KS) of the realizations' outputs
|
|
170
|
+
q = 30.0
|
|
171
|
+
w = np.exp(-q * (vals - vals.min()))
|
|
172
|
+
uout = float(vals.min() - np.log(w.sum()) / q)
|
|
173
|
+
w /= w.sum()
|
|
174
|
+
g = sum(wk * gk for wk, (_, gk) in zip(w, real)) / u0
|
|
175
|
+
g = _extrude(g, columns, design)
|
|
176
|
+
g = ht @ (g / hs)
|
|
177
|
+
dv = ht @ (_extrude(_dproject(xt, beta, 0.5), columns, design) / hs)
|
|
178
|
+
# MMA on (-u_out, volume): the output sensitivity takes both signs,
|
|
179
|
+
# which optimality criteria can only handle by clipping (the first
|
|
180
|
+
# version did, and stalled on the fin-ray problem with half its
|
|
181
|
+
# gradient thrown away)
|
|
182
|
+
xp = _project(xt, beta, 0.5)
|
|
183
|
+
xp[pres] = 1.0
|
|
184
|
+
vol = float(xp[design].sum())
|
|
185
|
+
xn = x.copy()
|
|
186
|
+
xn[design] = mma.update(x[design], -uout * 10.0, (-g * 10.0)[design],
|
|
187
|
+
np.array([vol / target - 1.0]), np.array([(dv / target)[design]]))
|
|
188
|
+
change = float(np.abs(xn - x).max())
|
|
189
|
+
x = xn
|
|
190
|
+
rec = {"iteration": it, "outputDisplacement": raw[0], "objective": uout, "outputs": raw, "beta": beta, "change": change,
|
|
191
|
+
"volume": float(xp[design].mean())}
|
|
192
|
+
if len(etas) > 1:
|
|
193
|
+
rec["robustOutputs"] = [float(v * u0) for v in vals] # eroded, blueprint, dilated
|
|
194
|
+
history.append(rec)
|
|
195
|
+
if on_iter is not None:
|
|
196
|
+
on_iter(rec, xp)
|
|
197
|
+
since += 1
|
|
198
|
+
if beta < opts.beta_max and (since >= opts.beta_every or change < opts.tol * 2):
|
|
199
|
+
beta, since = min(beta * 2, opts.beta_max), 0
|
|
200
|
+
elif beta >= opts.beta_max:
|
|
201
|
+
if change < opts.tol:
|
|
202
|
+
break
|
|
203
|
+
# mechanisms chatter at the move limit on a few hinge cells while
|
|
204
|
+
# the output has stopped moving: stop on the output instead
|
|
205
|
+
if since >= 10 and len(history) > 10:
|
|
206
|
+
old = history[-11]["objective"]
|
|
207
|
+
if abs(uout - old) <= 2e-3 * abs(old):
|
|
208
|
+
break
|
|
209
|
+
xt = _extrude((h @ x) / hs, columns, design)
|
|
210
|
+
xp = _project(xt, beta, 0.5)
|
|
211
|
+
xp[pres] = 1.0
|
|
212
|
+
nonlinear = None
|
|
213
|
+
if opts.verify_nonlinear:
|
|
214
|
+
from .nonlinear import verify_mechanism
|
|
215
|
+
nonlinear = verify_mechanism(domain, xp, opts.modulus, nu, input_case=pairs[0][0], output_case=pairs[0][1],
|
|
216
|
+
k_in=opts.k_in, k_out=opts.k_out)
|
|
217
|
+
return MechanismResult(x=x, xphys=xp, output_displacement=float(history[-1]["outputDisplacement"]),
|
|
218
|
+
iterations=it, seconds=time.time() - t0, history=history, nonlinear=nonlinear)
|