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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyMOTO
3
- Version: 1.1.0
3
+ Version: 1.2.1
4
4
  Summary: A modular approach for topology optimization
5
5
  Home-page: https://github.com/aatmdelissen/pyMOTO
6
6
  Author: Arnoud Delissen
@@ -1,24 +1,24 @@
1
- pymoto/__init__.py,sha256=ax6tQsXkt19zyUhq3aSsHltR1_Yqg0r1pFWhENsRhw4,2119
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=Bo5Qqnben3Ih0IuprnoO2CmG09shEVUWt1hyiVFnB9U,15136
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=j5m-Qq9wKbvKauhWpwtGKHDElStMIbXacZxxapWMj6I,11313
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=99GvUfPAScQxfvcuajWyqWUcOZTtFRNQG8xMxALxPiE,21050
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.0.dist-info/LICENSE,sha256=ZXMC2Txpzs-dBwz9Me4_1rQCSVl4P1B27MomNi43F30,1072
20
- pyMOTO-1.1.0.dist-info/METADATA,sha256=am9fTat6g_-KNqBIQppkJmZgLKQ1MLXfX7MJW2SrNF8,4907
21
- pyMOTO-1.1.0.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
22
- pyMOTO-1.1.0.dist-info/top_level.txt,sha256=EdvAUSmFMaiqhuEZW8jxANMiK-LdPtlmDWL6SfmCdUU,7
23
- pyMOTO-1.1.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
24
- pyMOTO-1.1.0.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: bdist_wheel (0.42.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
pymoto/__init__.py CHANGED
@@ -1,27 +1,30 @@
1
- __version__ = '1.1.0'
1
+ __version__ = '1.2.1'
2
2
 
3
- from .core_objects import Signal, Module, Network, make_signals
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
- # Import from modules
16
- from .modules.generic import MathGeneral, EinSum, ConcatSignal
17
- from .modules.linalg import Inverse, LinSolve, EigenSolve, SystemOfEquations
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.complex import MakeComplex, RealPart, ImagPart, ComplexNorm
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
- ret = np.ones(self.nnodes)/v
187
+ shapefn = np.ones(self.elemnodes)/v
188
188
  for i in range(self.dim):
189
- ret *= np.array([self.element_size[i] + n[i]*pos[i] for n in self.node_numbering])
190
- return ret
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.
@@ -1,9 +1,12 @@
1
1
  """ Assembly modules for finite element analysis """
2
2
  import sys
3
- from pymoto import Module, DyadCarrier, DomainDefinition
3
+ from typing import Union
4
+
4
5
  import numpy as np
5
6
  from scipy.sparse import csc_matrix
6
- from typing import Union
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
- def _prepare(self, domain: DomainDefinition, element_matrix: np.ndarray, bc=None, bcdiagval=None, matrix_type=csc_matrix):
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=float)
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 = dgdx[self.f, ...] + self.Afp * dgdb[self.p, ...]
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
- lam[self.p, ...] = dgdb[self.p, ...]
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
- dgdff = dgdb[self.f, ...] - lam[self.f, ...]
116
- dgdup = dgdx[self.p, ...] + self.App * dgdb[self.p, ...] + self.Afp.T * lam[self.f, ...]
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
- return dgdA, dgdff, dgdup
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):