pyMOTO 1.1.0__py3-none-any.whl → 1.2.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.
- {pyMOTO-1.1.0.dist-info → pyMOTO-1.2.1.dist-info}/METADATA +1 -1
- {pyMOTO-1.1.0.dist-info → pyMOTO-1.2.1.dist-info}/RECORD +10 -10
- {pyMOTO-1.1.0.dist-info → pyMOTO-1.2.1.dist-info}/WHEEL +1 -1
- pymoto/__init__.py +15 -12
- pymoto/common/domain.py +3 -3
- pymoto/modules/assembly.py +14 -4
- pymoto/modules/linalg.py +90 -7
- {pyMOTO-1.1.0.dist-info → pyMOTO-1.2.1.dist-info}/LICENSE +0 -0
- {pyMOTO-1.1.0.dist-info → pyMOTO-1.2.1.dist-info}/top_level.txt +0 -0
- {pyMOTO-1.1.0.dist-info → pyMOTO-1.2.1.dist-info}/zip-safe +0 -0
@@ -1,24 +1,24 @@
|
|
1
|
-
pymoto/__init__.py,sha256=
|
1
|
+
pymoto/__init__.py,sha256=V3OzqL_4NDy7lzIv_8tvu6s-8qS4bUwczd6XoH75M3s,2196
|
2
2
|
pymoto/core_objects.py,sha256=9TjGunvGVwa-LqM2tmE1XlWnqHDscZLeqbKFBZ7ZroU,25111
|
3
3
|
pymoto/routines.py,sha256=pMJlEFXa413XbqvbJuw3bZTNGQJ4Al-BRdAy_Es_M2g,14360
|
4
4
|
pymoto/utils.py,sha256=YJ-PNLJLc12Yx6TYCrEechS2aaBRx0o4mTM1soeeyz0,1122
|
5
|
-
pymoto/common/domain.py,sha256=
|
5
|
+
pymoto/common/domain.py,sha256=_VWgm0sjMDsan_GiKnwmpJqZcuksgDU6UTdZYMTSi98,15153
|
6
6
|
pymoto/common/dyadcarrier.py,sha256=VwLJnOq1omfMX2udG6DMHOkD3AsIB05LTpDY7veYXcc,17136
|
7
7
|
pymoto/common/mma.py,sha256=W1Z0h5f3a9BP8nFIlCdahDCIHT4XrcDcDyE6Y1Brq3k,23318
|
8
8
|
pymoto/common/solvers.py,sha256=U7XNMSyHhp0fiZ8ASo1guUb-CHGygik7A4lfLOnh07c,8316
|
9
9
|
pymoto/common/solvers_dense.py,sha256=vuBUp3y4qJLwmsXbFQ_tEb-7LqqCEemFunTn1z_Qu0U,9901
|
10
10
|
pymoto/common/solvers_sparse.py,sha256=QVbGTwGtbhOqRUyg2gHmY-K5haiPbskGA6uj_g-dKz8,15776
|
11
|
-
pymoto/modules/assembly.py,sha256=
|
11
|
+
pymoto/modules/assembly.py,sha256=i0zwigijmsJRR3aHZXSIzlDcYMbCWxKW4fe3NqjW8ew,11540
|
12
12
|
pymoto/modules/autodiff.py,sha256=WAfoAOHBSozf7jbr9gQz9Vw4a_2G9wGJxLMMqUQP0Co,1684
|
13
13
|
pymoto/modules/complex.py,sha256=vwzqRo5W319mVf_RqbB7LpYe7jXruVxa3ZV560Iq39k,4421
|
14
14
|
pymoto/modules/filter.py,sha256=8A-dmWSFEqFyQcutjFv__pfgAwszCVZeZgLxuG9hi0g,18840
|
15
15
|
pymoto/modules/generic.py,sha256=27EuDMfUtWkkwEqkfbHMCRlHkt6wcV40aUQKfhL2xKI,9783
|
16
16
|
pymoto/modules/io.py,sha256=4k5S-YQHKhw_HwmqOoYQWFEzdcL5nMJ5fVD2FJFqpFg,10532
|
17
|
-
pymoto/modules/linalg.py,sha256=
|
17
|
+
pymoto/modules/linalg.py,sha256=j8bZfjo5Il_vbdEHr2BhWB3e7OssYVovieoN54zygx8,24266
|
18
18
|
pymoto/modules/scaling.py,sha256=hK3sfCoAoseabjqdn5VXe6aGA_fV-MRmMtiv4uIg_I4,2252
|
19
|
-
pyMOTO-1.1.
|
20
|
-
pyMOTO-1.1.
|
21
|
-
pyMOTO-1.1.
|
22
|
-
pyMOTO-1.1.
|
23
|
-
pyMOTO-1.1.
|
24
|
-
pyMOTO-1.1.
|
19
|
+
pyMOTO-1.2.1.dist-info/LICENSE,sha256=ZXMC2Txpzs-dBwz9Me4_1rQCSVl4P1B27MomNi43F30,1072
|
20
|
+
pyMOTO-1.2.1.dist-info/METADATA,sha256=YKmcOzqRFlAObECUODa5oZgcIG9r7GRhjn86x12_8fk,4907
|
21
|
+
pyMOTO-1.2.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
22
|
+
pyMOTO-1.2.1.dist-info/top_level.txt,sha256=EdvAUSmFMaiqhuEZW8jxANMiK-LdPtlmDWL6SfmCdUU,7
|
23
|
+
pyMOTO-1.2.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
24
|
+
pyMOTO-1.2.1.dist-info/RECORD,,
|
pymoto/__init__.py
CHANGED
@@ -1,27 +1,30 @@
|
|
1
|
-
__version__ = '1.1
|
1
|
+
__version__ = '1.2.1'
|
2
2
|
|
3
|
-
from .
|
4
|
-
from .routines import finite_difference, minimize_oc, minimize_mma
|
3
|
+
from .common.domain import DomainDefinition
|
5
4
|
|
6
5
|
# Imports from common
|
7
6
|
from .common.dyadcarrier import DyadCarrier
|
8
|
-
from .common.domain import DomainDefinition
|
9
7
|
from .common.mma import MMA
|
10
|
-
from .common.solvers import matrix_is_complex, matrix_is_diagonal, matrix_is_symmetric, matrix_is_hermitian,
|
11
|
-
LinearSolver, LDAWrapper
|
8
|
+
from .common.solvers import matrix_is_complex, matrix_is_diagonal, matrix_is_symmetric, matrix_is_hermitian, LinearSolver, LDAWrapper
|
12
9
|
from .common.solvers_dense import SolverDiagonal, SolverDenseQR, SolverDenseLU, SolverDenseCholesky, SolverDenseLDL
|
13
10
|
from .common.solvers_sparse import SolverSparsePardiso, SolverSparseLU, SolverSparseCholeskyScikit, SolverSparseCholeskyCVXOPT
|
14
11
|
|
15
|
-
#
|
16
|
-
from .
|
17
|
-
|
12
|
+
# Modular inports
|
13
|
+
from .core_objects import Signal, Module, Network, make_signals
|
14
|
+
|
15
|
+
# Import modules
|
18
16
|
from .modules.assembly import AssembleGeneral, AssembleStiffness, AssembleMass
|
17
|
+
from .modules.autodiff import AutoMod
|
18
|
+
from .modules.complex import MakeComplex, RealPart, ImagPart, ComplexNorm
|
19
19
|
from .modules.filter import FilterConv, Filter, DensityFilter, OverhangFilter
|
20
|
+
from .modules.generic import MathGeneral, EinSum, ConcatSignal
|
20
21
|
from .modules.io import PlotDomain, PlotGraph, PlotIter, WriteToVTI
|
21
|
-
from .modules.
|
22
|
-
from .modules.autodiff import AutoMod
|
22
|
+
from .modules.linalg import Inverse, LinSolve, EigenSolve, SystemOfEquations, StaticCondensation
|
23
23
|
from .modules.scaling import Scaling
|
24
24
|
|
25
|
+
# Further helper routines
|
26
|
+
from .routines import finite_difference, minimize_oc, minimize_mma
|
27
|
+
|
25
28
|
__all__ = [
|
26
29
|
'Signal', 'Module', 'Network', 'make_signals',
|
27
30
|
'finite_difference', 'minimize_oc', 'minimize_mma',
|
@@ -35,7 +38,7 @@ __all__ = [
|
|
35
38
|
'SolverSparsePardiso', 'SolverSparseLU', 'SolverSparseCholeskyScikit', 'SolverSparseCholeskyCVXOPT',
|
36
39
|
# Modules
|
37
40
|
"MathGeneral", "EinSum", "ConcatSignal",
|
38
|
-
"Inverse", "LinSolve", "EigenSolve", "SystemOfEquations",
|
41
|
+
"Inverse", "LinSolve", "EigenSolve", "SystemOfEquations", "StaticCondensation",
|
39
42
|
"AssembleGeneral", "AssembleStiffness", "AssembleMass",
|
40
43
|
"FilterConv", "Filter", "DensityFilter", "OverhangFilter",
|
41
44
|
"PlotDomain", "PlotGraph", "PlotIter", "WriteToVTI",
|
pymoto/common/domain.py
CHANGED
@@ -184,10 +184,10 @@ class DomainDefinition:
|
|
184
184
|
"""
|
185
185
|
v = np.prod(self.element_size[:self.dim])
|
186
186
|
assert v > 0.0, 'Element volume needs to be positive'
|
187
|
-
|
187
|
+
shapefn = np.ones(self.elemnodes)/v
|
188
188
|
for i in range(self.dim):
|
189
|
-
|
190
|
-
return
|
189
|
+
shapefn *= np.array([self.element_size[i]/2 + n[i]*pos[i] for n in self.node_numbering])
|
190
|
+
return shapefn
|
191
191
|
|
192
192
|
def eval_shape_fun_der(self, pos: np.ndarray):
|
193
193
|
""" Evaluates the shape function derivatives in x, y, and optionally z-direction.
|
pymoto/modules/assembly.py
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
""" Assembly modules for finite element analysis """
|
2
2
|
import sys
|
3
|
-
from
|
3
|
+
from typing import Union
|
4
|
+
|
4
5
|
import numpy as np
|
5
6
|
from scipy.sparse import csc_matrix
|
6
|
-
|
7
|
+
|
8
|
+
from pymoto import Module, DyadCarrier, DomainDefinition
|
9
|
+
|
7
10
|
try:
|
8
11
|
from opt_einsum import contract as einsum
|
9
12
|
except ModuleNotFoundError:
|
@@ -33,8 +36,11 @@ class AssembleGeneral(Module):
|
|
33
36
|
bcdiagval (optional): Value to put on the diagonal of the matrix at dofs where boundary conditions are active.
|
34
37
|
matrix_type (optional): The matrix type to construct. This is a constructor which must accept the arguments
|
35
38
|
``matrix_type((vals, (row_idx, col_idx)), shape=(n, n))``
|
39
|
+
add_constant (optional): A constant (e.g. matrix) to add.
|
36
40
|
"""
|
37
|
-
|
41
|
+
|
42
|
+
def _prepare(self, domain: DomainDefinition, element_matrix: np.ndarray, bc=None, bcdiagval=None,
|
43
|
+
matrix_type=csc_matrix, add_constant=None):
|
38
44
|
self.elmat = element_matrix
|
39
45
|
self.ndof = self.elmat.shape[-1] // domain.elemnodes # Number of dofs per node
|
40
46
|
self.n = self.ndof * domain.nnodes # Matrix size
|
@@ -59,6 +65,8 @@ class AssembleGeneral(Module):
|
|
59
65
|
else:
|
60
66
|
self.bcselect = None
|
61
67
|
|
68
|
+
self.add_constant = add_constant
|
69
|
+
|
62
70
|
def _response(self, xscale: np.ndarray):
|
63
71
|
scaled_el = ((self.elmat.flatten()[np.newaxis]).T * xscale).flatten(order='F')
|
64
72
|
|
@@ -76,6 +84,8 @@ class AssembleGeneral(Module):
|
|
76
84
|
"scipy.sparse.csrmatrix are supported"
|
77
85
|
.format(self.matrix_type)).with_traceback(sys.exc_info()[2]) from None
|
78
86
|
|
87
|
+
if self.add_constant is not None:
|
88
|
+
mat += self.add_constant
|
79
89
|
return mat
|
80
90
|
|
81
91
|
def _sensitivity(self, dgdmat: Union[DyadCarrier, np.ndarray]):
|
@@ -232,6 +242,7 @@ class AssembleMass(AssembleGeneral):
|
|
232
242
|
bcdiagval: The value to put on the diagonal in case of boundary conditions (bc)
|
233
243
|
**kwargs : Other keyword-arguments are passed to AssembleGeneral
|
234
244
|
"""
|
245
|
+
|
235
246
|
def _prepare(self, domain: DomainDefinition, *args, rho: float = 1.0, bcdiagval=0.0, **kwargs):
|
236
247
|
# Element mass matrix
|
237
248
|
# 1/36 Mass of one element
|
@@ -259,4 +270,3 @@ class AssembleMass(AssembleGeneral):
|
|
259
270
|
else:
|
260
271
|
raise RuntimeError("Only for 2D and 3D")
|
261
272
|
super()._prepare(domain, ME, *args, bcdiagval=bcdiagval, **kwargs)
|
262
|
-
|
pymoto/modules/linalg.py
CHANGED
@@ -13,6 +13,71 @@ from pymoto import SolverSparseLU, SolverSparseCholeskyCVXOPT, SolverSparsePardi
|
|
13
13
|
from pymoto import matrix_is_symmetric, matrix_is_hermitian, matrix_is_diagonal
|
14
14
|
|
15
15
|
|
16
|
+
class StaticCondensation(Module):
|
17
|
+
r"""Static condensation of a linear system of equations
|
18
|
+
|
19
|
+
The partitioned system of equations
|
20
|
+
|
21
|
+
:math:`\begin{bmatrix} \mathbf{A}_\text{mm} & \mathbf{A}_\text{ms} \\ \mathbf{A}_\text{sm} & \mathbf{A}_\text{ss}
|
22
|
+
\end{bmatrix}
|
23
|
+
\begin{bmatrix} \mathbf{x}_\text{m} \\ \mathbf{x}_\text{s} \end{bmatrix} =
|
24
|
+
\begin{bmatrix} \mathbf{b}_\text{m} \\ \mathbf{b}_\text{s} \end{bmatrix}
|
25
|
+
,`
|
26
|
+
with subscripts ``(m)`` and ``(s)`` referring to the main and secondary dofs, respectively.
|
27
|
+
|
28
|
+
The system is solved in two steps:
|
29
|
+
|
30
|
+
:math:`\begin{aligned}
|
31
|
+
\mathbf{A}_\text{ss} \mathbf{x}_\text{sm} &= \mathbf{A}_\text{sm} \\
|
32
|
+
\tilde{\mathbf{A}} &= \mathbf{A}_\text{mm} - \mathbf{A}_\text{ms} \mathbf{x}_\text{sm}.
|
33
|
+
\end{aligned}`
|
34
|
+
|
35
|
+
Assumptions:
|
36
|
+
(i) It is assumed the prescribed DOFs (all dof - main dof - free dof) are prescribed to zero.
|
37
|
+
(ii) It is assumed the applied load on the free DOFs is zero; there is no reduced load.
|
38
|
+
|
39
|
+
Implemented by @artofscience (s.koppen@tudelft.nl).
|
40
|
+
|
41
|
+
References:
|
42
|
+
|
43
|
+
Koppen, S., Langelaar, M., & van Keulen, F. (2022).
|
44
|
+
Efficient multi-partition topology optimization.
|
45
|
+
Computer Methods in Applied Mechanics and Engineering, 393, 114829.
|
46
|
+
DOI: https://doi.org/10.1016/j.cma.2022.114829
|
47
|
+
|
48
|
+
Input Signals:
|
49
|
+
- ``A`` (`dense or sparse matrix`): The system matrix :math:`\mathbf{A}` of size ``(n, n)``
|
50
|
+
|
51
|
+
Output Signal:
|
52
|
+
- ``Ared`` (`dense or sparse matrix`): The reduced system matrix :math:`\tilde{\mathbf{A}}` of size ``(m, m)``
|
53
|
+
|
54
|
+
Args:
|
55
|
+
free: The indices corresponding to the free degrees of freedom
|
56
|
+
main: The indices corresponding to the main degrees of freedom
|
57
|
+
**kwargs: See `pymoto.LinSolve`, as they are directly passed into the `LinSolve` module
|
58
|
+
"""
|
59
|
+
|
60
|
+
def _prepare(self, main, free, **kwargs):
|
61
|
+
self.module_LinSolve = LinSolve([self.sig_in[0], Signal()], **kwargs)
|
62
|
+
self.module_LinSolve.use_lda_solver = False
|
63
|
+
self.m = main
|
64
|
+
self.f = free
|
65
|
+
|
66
|
+
def _response(self, A):
|
67
|
+
self.n = np.shape(A)[0]
|
68
|
+
self.module_LinSolve.sig_in[0].state = A[self.f, ...][..., self.f]
|
69
|
+
self.module_LinSolve.sig_in[1].state = A[self.f, ...][..., self.m].todense()
|
70
|
+
self.module_LinSolve.response()
|
71
|
+
self.X = self.module_LinSolve.sig_out[0].state
|
72
|
+
return A[self.m, ...][..., self.m] - A[self.m, ...][..., self.f] @ self.X
|
73
|
+
|
74
|
+
def _sensitivity(self, dfdB):
|
75
|
+
C = np.zeros((self.n, len(self.m)), dtype=float)
|
76
|
+
C[self.m, ...] = np.eye(len(self.m))
|
77
|
+
C[self.f, ...] = -self.X
|
78
|
+
return C @ dfdB @ C.T if isinstance(dfdB, DyadCarrier) else DyadCarrier(list(C.T), list(np.asarray(dfdB @ C.T)))
|
79
|
+
|
80
|
+
|
16
81
|
class SystemOfEquations(Module):
|
17
82
|
r""" Solve a partitioned linear system of equations
|
18
83
|
|
@@ -63,6 +128,7 @@ class SystemOfEquations(Module):
|
|
63
128
|
assert bf.shape[0] + xp.shape[0] == A.shape[0], "Dimensions of applied force and displacement must match matrix"
|
64
129
|
assert bf.ndim == xp.ndim, "Number of loadcases for applied force and displacement must match"
|
65
130
|
self.n = np.shape(A)[0]
|
131
|
+
self.dim = xp.ndim
|
66
132
|
|
67
133
|
if self.f is None:
|
68
134
|
all_dofs = np.arange(self.n)
|
@@ -73,7 +139,8 @@ class SystemOfEquations(Module):
|
|
73
139
|
assert self.f.size + self.p.size == self.n, "Size of free and prescribed indices must match the matrix size"
|
74
140
|
|
75
141
|
# create empty output
|
76
|
-
self.x = np.zeros((self.n, *bf.shape[1:]), dtype=
|
142
|
+
self.x = np.zeros((self.n, *bf.shape[1:]), dtype=complex) if np.iscomplexobj(A) else np.zeros(
|
143
|
+
(self.n, *bf.shape[1:]), dtype=float)
|
77
144
|
self.x[self.p, ...] = xp
|
78
145
|
|
79
146
|
b = np.zeros_like(self.x)
|
@@ -97,13 +164,19 @@ class SystemOfEquations(Module):
|
|
97
164
|
return self.x, b
|
98
165
|
|
99
166
|
def _sensitivity(self, dgdx, dgdb):
|
100
|
-
adjoint_load =
|
167
|
+
adjoint_load = np.zeros_like(self.x[self.f, ...])
|
168
|
+
|
169
|
+
if dgdx is not None:
|
170
|
+
adjoint_load += dgdx[self.f, ...]
|
171
|
+
if dgdb is not None:
|
172
|
+
adjoint_load += self.Afp * dgdb[self.p, ...]
|
101
173
|
|
102
|
-
# adjoint equation
|
103
174
|
lam = np.zeros_like(self.x)
|
104
175
|
lamf = -1.0 * self.module_LinSolve.solver.adjoint(adjoint_load)
|
105
176
|
lam[self.f, ...] = lamf
|
106
|
-
|
177
|
+
|
178
|
+
if dgdb is not None:
|
179
|
+
lam[self.p, ...] = dgdb[self.p, ...]
|
107
180
|
|
108
181
|
# sensitivities to system matrix
|
109
182
|
if self.x.ndim > 1:
|
@@ -112,10 +185,19 @@ class SystemOfEquations(Module):
|
|
112
185
|
dgdA = DyadCarrier(lam, self.x)
|
113
186
|
|
114
187
|
# sensitivities to applied load and prescribed state
|
115
|
-
|
116
|
-
dgdup =
|
188
|
+
dgdbf = np.zeros_like(adjoint_load)
|
189
|
+
dgdup = np.zeros_like(self.x[self.p, ...])
|
190
|
+
dgdbf -= lam[self.f, ...]
|
191
|
+
dgdup += self.Afp.T * lam[self.f, ...]
|
192
|
+
|
193
|
+
if dgdx is not None:
|
194
|
+
dgdup += dgdx[self.p, ...]
|
117
195
|
|
118
|
-
|
196
|
+
if dgdb is not None:
|
197
|
+
dgdbf += dgdb[self.f, ...]
|
198
|
+
dgdup += self.App * dgdb[self.p, ...]
|
199
|
+
|
200
|
+
return dgdA, dgdbf, dgdup
|
119
201
|
|
120
202
|
|
121
203
|
class Inverse(Module):
|
@@ -257,6 +339,7 @@ class LinSolve(Module):
|
|
257
339
|
Attributes:
|
258
340
|
use_lda_solver: Use the linear-dependency-aware solver :class:`LDAWrapper` to prevent redundant computations
|
259
341
|
"""
|
342
|
+
|
260
343
|
use_lda_solver = True
|
261
344
|
|
262
345
|
def _prepare(self, dep_tol=1e-5, hermitian=None, symmetric=None, solver=None):
|
File without changes
|
File without changes
|
File without changes
|