structsolve 0.9.2__tar.gz → 0.9.3__tar.gz
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.
- {structsolve-0.9.2/structsolve.egg-info → structsolve-0.9.3}/PKG-INFO +7 -4
- {structsolve-0.9.2 → structsolve-0.9.3}/README.md +6 -3
- {structsolve-0.9.2 → structsolve-0.9.3}/pyproject.toml +1 -1
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/analysis.py +34 -30
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/arc_length.py +45 -41
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/arpackutils.py +20 -23
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/callbacks.py +21 -19
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/convergence.py +159 -32
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/freq.py +73 -31
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/linear_buckling.py +228 -119
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/newton_raphson.py +78 -45
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/static.py +1 -1
- {structsolve-0.9.2 → structsolve-0.9.3/structsolve.egg-info}/PKG-INFO +7 -4
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve.egg-info/SOURCES.txt +3 -1
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_analysis.py +55 -5
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_arc_length.py +1 -1
- structsolve-0.9.3/tests/test_arc_length_fung_kaplan.py +261 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_follower_freq_static.py +2 -2
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_follower_loads.py +11 -9
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_freq.py +100 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_linear_buckling.py +32 -32
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_load_factor_dependent_load.py +37 -31
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_newton_raphson.py +6 -3
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_reuse.py +3 -3
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_static.py +6 -6
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_static_deflection.py +19 -16
- structsolve-0.9.3/tests/test_warnings_registry.py +159 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/LICENSE +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/setup.cfg +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/__init__.py +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/arc_length_crisfield.py +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/arc_length_riks.py +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/logger.py +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve/sparseutils.py +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve.egg-info/dependency_links.txt +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve.egg-info/requires.txt +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/structsolve.egg-info/top_level.txt +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_arpackutils.py +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_linear_buckling_unsymmetric.py +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_sparse_first.py +0 -0
- {structsolve-0.9.2 → structsolve-0.9.3}/tests/test_sparseutils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: structsolve
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
4
4
|
Summary: Structural analysis solvers
|
|
5
5
|
Author-email: "Saullo G. P. Castro" <castrosaullo@gmail.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -63,7 +63,7 @@ Currently these solvers are pretty much compatible with my other repository
|
|
|
63
63
|
Citing this library
|
|
64
64
|
===================
|
|
65
65
|
|
|
66
|
-
Saullo G. P. Castro (2026). Structural analysis solvers tailored for semi-analytical models (Version 0.9.
|
|
66
|
+
Saullo G. P. Castro (2026). Structural analysis solvers tailored for semi-analytical models (Version 0.9.3). Zenodo. DOI: https://doi.org/10.5281/zenodo.2581212.
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
Documentation
|
|
@@ -78,12 +78,15 @@ History
|
|
|
78
78
|
|
|
79
79
|
See [CHANGELOG.md](CHANGELOG.md) for the details of each version.
|
|
80
80
|
|
|
81
|
-
* version 0.9.
|
|
81
|
+
* version 0.9.3 (2026-10-26)
|
|
82
82
|
- Crisfield's convergence criterion is the default of the non-linear analyses, `Analysis.convergence = 'crisfield'`, with `relTOL = 1e-9`: the residual and the reference forces are measured with the norm scaled by the diagonal of `calc_kC()` over the free degrees of freedom; the former check is kept with `convergence = 'relative'`
|
|
83
|
+
- With both convergence criteria, an iteration also converges at the round-off floor of its residual, `3 eps ||(|K_C0| |c|)||`: the arc-length methods trace the snap-through of a low arch at `relTOL = 1e-9` through the null load where they stopped
|
|
83
84
|
- `lb` returns the negative load multipliers closest to zero after the positive ones, and a tensile reference load no longer needs the dense fallback
|
|
84
|
-
- `freq(sort=True)` sorts and filters `lambda2` together with the eigenvectors, and rigid-body modes
|
|
85
|
+
- `freq(sort=True)` sorts and filters `lambda2` together with the eigenvectors, and neither rigid-body modes nor eigenpairs with an eigenvalue close to zero whose backward error is at the round-off, e.g. pre-stressed close to buckling, fail the verification of the residuals any more
|
|
85
86
|
- arc-length solvers: a converged step that points back along the path is repeated with half the arc length, instead of being accepted
|
|
86
87
|
- documentation published again: pandoc installed in the documentation workflow
|
|
88
|
+
- `lb` and `freq` no longer reset the registry of the warnings already shown, which made a warning of the caller under the `'default'` action be shown again after each call
|
|
89
|
+
- The residual is `R = Fint - lbd*Fext`, the convention of `pyfe3d.solver` and `panels`, with `K_T dc = -R` in the Newton-Raphson corrections and `dR/dlbd = -Fext`; the solutions are bitwise identical
|
|
87
90
|
* version 0.8.0 (2026-10-14)
|
|
88
91
|
- Fewer evaluations and factorizations, same solutions: `dR/dlbd` is evaluated together with the tangent stiffness matrix only, Newton-Raphson reuses the factorization of the tangent while it is not updated, the follower linear static analysis factorizes `K0` once, and `lb` estimates its shift and the inertia of `K` once for all its attempts
|
|
89
92
|
* version 0.7.0 (2026-10-13)
|
|
@@ -28,7 +28,7 @@ Currently these solvers are pretty much compatible with my other repository
|
|
|
28
28
|
Citing this library
|
|
29
29
|
===================
|
|
30
30
|
|
|
31
|
-
Saullo G. P. Castro (2026). Structural analysis solvers tailored for semi-analytical models (Version 0.9.
|
|
31
|
+
Saullo G. P. Castro (2026). Structural analysis solvers tailored for semi-analytical models (Version 0.9.3). Zenodo. DOI: https://doi.org/10.5281/zenodo.2581212.
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
Documentation
|
|
@@ -43,12 +43,15 @@ History
|
|
|
43
43
|
|
|
44
44
|
See [CHANGELOG.md](CHANGELOG.md) for the details of each version.
|
|
45
45
|
|
|
46
|
-
* version 0.9.
|
|
46
|
+
* version 0.9.3 (2026-10-26)
|
|
47
47
|
- Crisfield's convergence criterion is the default of the non-linear analyses, `Analysis.convergence = 'crisfield'`, with `relTOL = 1e-9`: the residual and the reference forces are measured with the norm scaled by the diagonal of `calc_kC()` over the free degrees of freedom; the former check is kept with `convergence = 'relative'`
|
|
48
|
+
- With both convergence criteria, an iteration also converges at the round-off floor of its residual, `3 eps ||(|K_C0| |c|)||`: the arc-length methods trace the snap-through of a low arch at `relTOL = 1e-9` through the null load where they stopped
|
|
48
49
|
- `lb` returns the negative load multipliers closest to zero after the positive ones, and a tensile reference load no longer needs the dense fallback
|
|
49
|
-
- `freq(sort=True)` sorts and filters `lambda2` together with the eigenvectors, and rigid-body modes
|
|
50
|
+
- `freq(sort=True)` sorts and filters `lambda2` together with the eigenvectors, and neither rigid-body modes nor eigenpairs with an eigenvalue close to zero whose backward error is at the round-off, e.g. pre-stressed close to buckling, fail the verification of the residuals any more
|
|
50
51
|
- arc-length solvers: a converged step that points back along the path is repeated with half the arc length, instead of being accepted
|
|
51
52
|
- documentation published again: pandoc installed in the documentation workflow
|
|
53
|
+
- `lb` and `freq` no longer reset the registry of the warnings already shown, which made a warning of the caller under the `'default'` action be shown again after each call
|
|
54
|
+
- The residual is `R = Fint - lbd*Fext`, the convention of `pyfe3d.solver` and `panels`, with `K_T dc = -R` in the Newton-Raphson corrections and `dR/dlbd = -Fext`; the solutions are bitwise identical
|
|
52
55
|
* version 0.8.0 (2026-10-14)
|
|
53
56
|
- Fewer evaluations and factorizations, same solutions: `dR/dlbd` is evaluated together with the tangent stiffness matrix only, Newton-Raphson reuses the factorization of the tangent while it is not updated, the follower linear static analysis factorizes `K0` once, and `lb` estimates its shift and the inertia of `K` once for all its attempts
|
|
54
57
|
* version 0.7.0 (2026-10-13)
|
|
@@ -15,7 +15,7 @@ import numpy as np
|
|
|
15
15
|
from .static import solve, _factorize
|
|
16
16
|
from .logger import msg, warn
|
|
17
17
|
from . import callbacks
|
|
18
|
-
from .newton_raphson import _check_convergence, _weights
|
|
18
|
+
from .newton_raphson import _check_convergence, _weights, _abs_matrix
|
|
19
19
|
from .newton_raphson import _solver_NR
|
|
20
20
|
from .arc_length_riks import _solver_arc_length_riks
|
|
21
21
|
from .arc_length_crisfield import _solver_arc_length_crisfield
|
|
@@ -172,19 +172,20 @@ class Analysis(object):
|
|
|
172
172
|
Notes
|
|
173
173
|
-----
|
|
174
174
|
For non-linear analyses, the internal force vector
|
|
175
|
-
`\{
|
|
175
|
+
`\boldsymbol{F}_{int}(\boldsymbol{c})` must be in equilibrium with the external force
|
|
176
176
|
vector scaled by the load factor `\lambda`:
|
|
177
177
|
|
|
178
178
|
.. math::
|
|
179
179
|
|
|
180
|
-
\{R
|
|
180
|
+
\boldsymbol{R} = \boldsymbol{F}_{int}(\boldsymbol{c}) - \lambda \boldsymbol{F}_{ext} = \boldsymbol{0}
|
|
181
181
|
|
|
182
|
-
and the tangent stiffness matrix
|
|
182
|
+
and the tangent stiffness matrix `\boldsymbol{K}_T = \boldsymbol{K}_C + \boldsymbol{K}_G`, given by the
|
|
183
183
|
callables ``calc_kC`` and ``calc_kG``, should be the exact derivative of
|
|
184
|
-
`\{
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
184
|
+
`\boldsymbol{F}_{int}` with respect to `\boldsymbol{c}`, `\boldsymbol{K}_T = \partial
|
|
185
|
+
\boldsymbol{R}/\partial \boldsymbol{c}`, such that the Newton-Raphson
|
|
186
|
+
iterations, `\boldsymbol{K}_T \delta \boldsymbol{c} = -\boldsymbol{R}`, converge
|
|
187
|
+
quadratically. The convergence is checked by default with Crisfield's
|
|
188
|
+
criterion, the residual and the reference force vectors scaled with the diagonal of the linear stiffness matrix
|
|
188
189
|
``calc_kC()`` over the free degrees of freedom, such that it does not
|
|
189
190
|
depend on the units of each degree of freedom, e.g. translations and
|
|
190
191
|
rotations, see :mod:`.convergence`; ``convergence = 'relative'`` keeps
|
|
@@ -209,15 +210,15 @@ class Analysis(object):
|
|
|
209
210
|
such that ``calc_fint`` may contain the configuration-dependent part of
|
|
210
211
|
the load, scaled by ``inc``, and ``calc_kC + calc_kG`` its derivative.
|
|
211
212
|
- ``calc_fext(inc=1., c=c)`` must return the load vector of the
|
|
212
|
-
configuration ``c`` for a unit load factor, the derivative of
|
|
213
|
-
residual with respect to the load factor,
|
|
214
|
-
solvers.
|
|
213
|
+
configuration ``c`` for a unit load factor, minus the derivative of
|
|
214
|
+
the residual with respect to the load factor, `-\partial
|
|
215
|
+
\boldsymbol{R}/\partial \lambda`, used by the arc-length solvers.
|
|
215
216
|
- ``calc_fext(inc=1., c=c, lbd=lbd)`` also receives the current load
|
|
216
217
|
factor when it declares ``lbd``, for a derivative `\partial
|
|
217
|
-
R/\partial \lambda` that depends on the load factor, e.g. prescribed
|
|
218
|
+
\boldsymbol{R}/\partial \lambda` that depends on the load factor, e.g. prescribed
|
|
218
219
|
displacements imposed exactly as linear constraints, where the
|
|
219
220
|
reference vector ``calc_fext(inc=1.)`` may be null. The solvers then
|
|
220
|
-
use
|
|
221
|
+
use `-\partial \boldsymbol{R}/\partial \lambda` for the displacement scale of the
|
|
221
222
|
arc-length methods, for the predictor of the first Newton-Raphson
|
|
222
223
|
increment and as a reference of the convergence criterion, see
|
|
223
224
|
:mod:`.callbacks`. The linear static analysis never passes
|
|
@@ -313,29 +314,29 @@ class Analysis(object):
|
|
|
313
314
|
|
|
314
315
|
Notes
|
|
315
316
|
-----
|
|
316
|
-
The linear analysis solves
|
|
317
|
+
The linear analysis solves `\boldsymbol{K}_C \boldsymbol{c} = \boldsymbol{F}_{ext}`. When the load
|
|
317
318
|
depends on the configuration, the structure is still geometrically
|
|
318
319
|
linear but the load is evaluated in the current configuration,
|
|
319
|
-
|
|
320
|
+
`\boldsymbol{K}_C \boldsymbol{c} = \boldsymbol{F}_{ext}(\boldsymbol{c})`, which is solved by the load
|
|
320
321
|
correction iterations
|
|
321
322
|
|
|
322
323
|
.. math::
|
|
323
324
|
|
|
324
|
-
\{
|
|
325
|
-
-
|
|
325
|
+
\boldsymbol{c}_{k+1} = \boldsymbol{c}_k + \boldsymbol{K}_C^{-1}(\boldsymbol{F}_{ext}(\boldsymbol{c}_k)
|
|
326
|
+
- \boldsymbol{K}_C \boldsymbol{c}_k)
|
|
326
327
|
|
|
327
|
-
starting from `\{
|
|
328
|
-
reference load ``calc_fext()``, with `\{
|
|
328
|
+
starting from `\boldsymbol{c}_0 = \boldsymbol{K}_C^{-1} \boldsymbol{F}_{ext}`, the solution of the
|
|
329
|
+
reference load ``calc_fext()``, with `\boldsymbol{F}_{ext}(\boldsymbol{c}_k)` given by
|
|
329
330
|
``calc_fext(inc=1., c=c_k)``. This is a fixed-point iteration
|
|
330
331
|
without the load stiffness in the iteration matrix. For a follower
|
|
331
|
-
load whose force vector is affine in `c`, `\{F(c)
|
|
332
|
-
|
|
333
|
-
\{
|
|
334
|
-
|
|
332
|
+
load whose force vector is affine in `c`, `\boldsymbol{F}(\boldsymbol{c}) = \boldsymbol{F}_0 -
|
|
333
|
+
\boldsymbol{K}_f \boldsymbol{c}`, this is the solution of `(\boldsymbol{K}_C + \boldsymbol{K}_f) \boldsymbol{c} =
|
|
334
|
+
\boldsymbol{F}_0`, with the convergence rate of the spectral radius of
|
|
335
|
+
`\boldsymbol{K}_C^{-1} \boldsymbol{K}_f`, i.e. of the ratio of the load to the lowest
|
|
335
336
|
critical load of the linearized problem; beyond it the iterations
|
|
336
|
-
diverge. The error is multiplied by
|
|
337
|
+
diverge. The error is multiplied by `-\boldsymbol{K}_C^{-1} \boldsymbol{K}_f` at each
|
|
337
338
|
iteration, whose spectral radius is `1/\min|\lambda|` over the load
|
|
338
|
-
multipliers `\lambda` of `(
|
|
339
|
+
multipliers `\lambda` of `(\boldsymbol{K}_C + \lambda \boldsymbol{K}_f) \boldsymbol{u} = \boldsymbol{0}`,
|
|
339
340
|
including the negative and complex ones.
|
|
340
341
|
|
|
341
342
|
"""
|
|
@@ -378,15 +379,18 @@ class Analysis(object):
|
|
|
378
379
|
msg('Configuration-dependent load: load correction iterations',
|
|
379
380
|
level=2, silent=silent)
|
|
380
381
|
w = _weights(self, k0)
|
|
382
|
+
k0_abs = _abs_matrix(k0)
|
|
381
383
|
# k0 is factorized once for all the corrections
|
|
382
384
|
solve_k0 = None
|
|
383
385
|
for iteration in range(1, self.maxNumIter + 1):
|
|
384
386
|
fext = self.calc_fext(inc=1., c=c, silent=True)
|
|
385
387
|
fint = k0 @ c
|
|
386
|
-
R =
|
|
387
|
-
conv, Rnorm, Rrel = _check_convergence(self, R, fint,
|
|
388
|
-
|
|
389
|
-
|
|
388
|
+
R = fint - fext
|
|
389
|
+
conv, Rnorm, Rrel, roundoff = _check_convergence(self, R, fint,
|
|
390
|
+
fext, w=w, Kc_abs=k0_abs @ np.abs(c), full_output=True)
|
|
391
|
+
msg('Iteration: %d, %s = %1.3e%s' % (iteration, 'zeta' if
|
|
392
|
+
self.convergence == 'crisfield' else 'relative ||R||', Rrel,
|
|
393
|
+
', converged at the round-off floor' if roundoff else ''),
|
|
390
394
|
level=3, silent=silent)
|
|
391
395
|
if conv:
|
|
392
396
|
return c
|
|
@@ -397,7 +401,7 @@ class Analysis(object):
|
|
|
397
401
|
solve_k0 = _factorize(k0)
|
|
398
402
|
except RuntimeError:
|
|
399
403
|
break
|
|
400
|
-
c = c + solve_k0(R)
|
|
404
|
+
c = c + solve_k0(-R)
|
|
401
405
|
warn('The load correction iterations did not converge, the load may '
|
|
402
406
|
'exceed the critical load of the linearized problem', level=1,
|
|
403
407
|
silent=silent)
|
|
@@ -14,7 +14,7 @@ from .static import solve, _factorize
|
|
|
14
14
|
from . import callbacks
|
|
15
15
|
from .newton_raphson import (_check_convergence, _check_divergence,
|
|
16
16
|
_NR_iterations, _log_iteration, _weights,
|
|
17
|
-
INC_CUT_FACTOR)
|
|
17
|
+
_abs_matrix, INC_CUT_FACTOR)
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
#: Scaling factor of the load-factor term in the arc-length constraint
|
|
@@ -75,65 +75,67 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
75
75
|
|
|
76
76
|
Notes
|
|
77
77
|
-----
|
|
78
|
-
The equilibrium path `\{
|
|
78
|
+
The equilibrium path `\boldsymbol{F}_{int}(\boldsymbol{c}) = \lambda \boldsymbol{F}_{ext}` is traced with
|
|
79
79
|
the arc-length constraint
|
|
80
80
|
|
|
81
81
|
.. math::
|
|
82
82
|
|
|
83
|
-
\frac{\
|
|
83
|
+
\frac{\Delta \boldsymbol{c}^\top \Delta \boldsymbol{c}}{\boldsymbol{u}_{ref}^\top \boldsymbol{u}_{ref}}
|
|
84
84
|
+ \psi^2 \Delta\lambda^2 = \Delta s^2
|
|
85
85
|
|
|
86
|
-
where `\
|
|
87
|
-
step and `\{
|
|
86
|
+
where `\Delta \boldsymbol{c}` and `\Delta\lambda` are the increments of the current
|
|
87
|
+
step and `\boldsymbol{u}_{ref}` is the linear solution for `\lambda=1`, which makes
|
|
88
88
|
the arc length `\Delta s` independent of the units of the model. In the
|
|
89
89
|
linear regime `\Delta s \approx \sqrt{1 + \psi^2} \Delta\lambda`. It is
|
|
90
90
|
the spherical arc-length constraint of [4]_ (Section 9.3.2, Eq. (9.19),
|
|
91
91
|
p. 271), where the displacement term is not scaled and the load term is
|
|
92
|
-
`\psi^2 \Delta\lambda^2 \{
|
|
92
|
+
`\psi^2 \Delta\lambda^2 \boldsymbol{F}_{ext}^\top \boldsymbol{F}_{ext}`. `\psi` is the
|
|
93
93
|
constant ``PSI``,
|
|
94
|
-
and `\{
|
|
94
|
+
and `\boldsymbol{u}_{ref}^\top \boldsymbol{u}_{ref}` is
|
|
95
95
|
replaced by 1 when it is null. With the inner product
|
|
96
96
|
|
|
97
97
|
.. math::
|
|
98
98
|
|
|
99
99
|
\langle (a, \alpha), (b, \beta) \rangle =
|
|
100
|
-
\frac{\{a
|
|
100
|
+
\frac{\boldsymbol{a}^\top \boldsymbol{b}}{\boldsymbol{u}_{ref}^\top \boldsymbol{u}_{ref}} + \psi^2 \alpha \beta
|
|
101
101
|
|
|
102
102
|
the constraint reads `g = \frac{1}{2}(\langle (\Delta c, \Delta\lambda),
|
|
103
103
|
(\Delta c, \Delta\lambda) \rangle - \Delta s^2) = 0`.
|
|
104
104
|
|
|
105
105
|
Each step starts from a tangent predictor, whose direction follows the
|
|
106
106
|
previous increment, allowing limit points to be passed. With
|
|
107
|
-
`\
|
|
107
|
+
`\delta \boldsymbol{c}_q = \boldsymbol{K}_T^{-1} \boldsymbol{F}_{ext}` at the last converged state,
|
|
108
108
|
the predictor
|
|
109
109
|
|
|
110
110
|
.. math::
|
|
111
111
|
|
|
112
112
|
\Delta\lambda = \pm \frac{\Delta s}{\sqrt{\langle (\delta c_q, 1),
|
|
113
113
|
(\delta c_q, 1) \rangle}}, \qquad
|
|
114
|
-
\
|
|
114
|
+
\Delta \boldsymbol{c} = \Delta\lambda \delta \boldsymbol{c}_q
|
|
115
115
|
|
|
116
116
|
satisfies the constraint, with the sign of `\langle (\delta c_q, 1),
|
|
117
117
|
(\Delta c_{prev}, \Delta\lambda_{prev}) \rangle`, where
|
|
118
118
|
`(\Delta c_{prev}, \Delta\lambda_{prev})` is the increment of the
|
|
119
119
|
previous converged step, and a positive sign at the first step. The
|
|
120
|
-
initial arc length, `\Delta s_0 = initialInc \sqrt{\langle (\delta
|
|
121
|
-
1), (\delta
|
|
120
|
+
initial arc length, `\Delta s_0 = initialInc \sqrt{\langle (\delta \boldsymbol{c}_q,
|
|
121
|
+
1), (\delta \boldsymbol{c}_q, 1) \rangle}` at the origin, corresponds to a load factor
|
|
122
122
|
increment of ``an.initialInc`` along the initial tangent.
|
|
123
123
|
|
|
124
|
-
At each iteration, the residual is `\{R
|
|
125
|
-
\{
|
|
124
|
+
At each iteration, the residual is `\boldsymbol{R} = \boldsymbol{F}_{int}(\boldsymbol{c} +
|
|
125
|
+
\Delta \boldsymbol{c}) - (\lambda + \Delta\lambda) \boldsymbol{F}_{ext}`, with `\partial
|
|
126
|
+
\boldsymbol{R}/\partial \lambda = -\boldsymbol{F}_{ext}`, and the bordered system
|
|
127
|
+
`\boldsymbol{K}_T \delta \boldsymbol{c} - \delta\lambda \, \boldsymbol{F}_{ext} = -\boldsymbol{R}` is
|
|
126
128
|
solved with two solutions of the tangent stiffness matrix,
|
|
127
|
-
`\
|
|
128
|
-
|
|
129
|
-
\delta
|
|
129
|
+
`\delta \boldsymbol{c}_R = -\boldsymbol{K}_T^{-1} \boldsymbol{R}` and `\delta \boldsymbol{c}_q =
|
|
130
|
+
\boldsymbol{K}_T^{-1} \boldsymbol{F}_{ext}`; the increment is updated to `(\Delta \boldsymbol{c} +
|
|
131
|
+
\delta \boldsymbol{c}_R + \delta\lambda \, \delta \boldsymbol{c}_q, \Delta\lambda +
|
|
130
132
|
\delta\lambda)`, where the correction of the load factor
|
|
131
133
|
`\delta\lambda` comes from the constraint:
|
|
132
134
|
|
|
133
135
|
- ``method='riks'``: the constraint is linearized at the current
|
|
134
136
|
increment (updated normal plane), which is Newton-Raphson applied to
|
|
135
137
|
the augmented system of the equilibrium equations and the constraint
|
|
136
|
-
([4]_, Section 9.3.2, p. 272), with quadratic convergence when `
|
|
138
|
+
([4]_, Section 9.3.2, p. 272), with quadratic convergence when `\boldsymbol{K}_T`
|
|
137
139
|
is updated at every iteration and is the exact Jacobian. It follows
|
|
138
140
|
Riks [1]_, who applies Newton's method to the equilibrium equations
|
|
139
141
|
augmented with an equation that controls the arc length, and states
|
|
@@ -158,7 +160,7 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
158
160
|
- ``method='crisfield'``: the quadratic (spherical) constraint is solved
|
|
159
161
|
exactly, choosing the root with the smallest angle to the current
|
|
160
162
|
increment. When the roots are complex, the linearized constraint is
|
|
161
|
-
used, see [2]_. With `\{u
|
|
163
|
+
used, see [2]_. With `\boldsymbol{u} = \Delta \boldsymbol{c} + \delta \boldsymbol{c}_R`, the
|
|
162
164
|
constraint
|
|
163
165
|
`\langle (u + \delta\lambda \, \delta c_q, \Delta\lambda +
|
|
164
166
|
\delta\lambda), (u + \delta\lambda \, \delta c_q, \Delta\lambda +
|
|
@@ -185,7 +187,7 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
185
187
|
tangent predictor of the hyperplane normal to it, in the inner product
|
|
186
188
|
above, through the last converged point, as the points of the plane of
|
|
187
189
|
Riks [1]_, normal to the tangent at the distance `\Delta s` from the
|
|
188
|
-
last converged point. With full Newton-Raphson, `
|
|
190
|
+
last converged point. With full Newton-Raphson, `\boldsymbol{K}_T` is updated at
|
|
189
191
|
every iteration at the current iterate `(c + \Delta c, \lambda +
|
|
190
192
|
\Delta\lambda)`; with
|
|
191
193
|
``an.modified_NR = True`` the factorization at the last converged state
|
|
@@ -210,27 +212,27 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
210
212
|
`\lambda < 1` to `\lambda + \Delta\lambda > 1`, the step is replaced by
|
|
211
213
|
Newton-Raphson iterations at exactly `\lambda = 1`, see
|
|
212
214
|
:func:`.newton_raphson._NR_iterations`, from the predictor
|
|
213
|
-
`t \
|
|
215
|
+
`t \Delta \boldsymbol{c}` with `t = (1 - \lambda)/\Delta\lambda`. When these
|
|
214
216
|
iterations do not converge, the step is repeated with a shorter arc
|
|
215
217
|
length.
|
|
216
218
|
|
|
217
219
|
When the load depends on the configuration, i.e. ``calc_fext`` accepts
|
|
218
|
-
the keyword argument ``c``, `\{
|
|
220
|
+
the keyword argument ``c``, `\boldsymbol{F}_{ext}` in `\delta \boldsymbol{c}_q` and in the
|
|
219
221
|
tangent predictor is the load vector of the current configuration
|
|
220
|
-
``calc_fext(inc=1., c=c)``, the derivative `\partial R/\partial
|
|
221
|
-
\lambda` of the residual, whereas `\{
|
|
222
|
+
``calc_fext(inc=1., c=c)``, minus the derivative `\partial \boldsymbol{R}/\partial
|
|
223
|
+
\lambda` of the residual, whereas `\boldsymbol{u}_{ref}` remains the linear
|
|
222
224
|
solution of the reference vector. The callables that accept ``inc``
|
|
223
225
|
receive the current load factor, see :mod:`.callbacks`.
|
|
224
226
|
|
|
225
227
|
When ``calc_fext`` also accepts the keyword argument ``lbd``, e.g. for
|
|
226
228
|
prescribed displacements imposed exactly, it receives the load factor
|
|
227
|
-
together with the configuration, `\{
|
|
228
|
-
|
|
229
|
-
`\{
|
|
229
|
+
together with the configuration, `\boldsymbol{F}_{ext}` above being then
|
|
230
|
+
`-\partial \boldsymbol{R}/\partial \lambda` at the current `c` and `\lambda`;
|
|
231
|
+
`\boldsymbol{u}_{ref}` is the linear solution of `-\partial \boldsymbol{R}/\partial \lambda`
|
|
230
232
|
at `c = 0`, `\lambda = 0`, since the reference vector may be null, e.g.
|
|
231
|
-
under a prescribed displacement alone; and
|
|
232
|
-
R/\partial \lambda` is an additional reference of the relative
|
|
233
|
-
convergence criterion.
|
|
233
|
+
under a prescribed displacement alone; and `-\lambda \, \partial
|
|
234
|
+
\boldsymbol{R}/\partial \lambda` is an additional reference of the relative
|
|
235
|
+
convergence criterion. `-\partial \boldsymbol{R}/\partial \lambda` is evaluated
|
|
234
236
|
together with the tangent stiffness matrix, at the same iterate, i.e.
|
|
235
237
|
once per update of the tangent stiffness matrix, and used in the
|
|
236
238
|
bordered solution; the last one evaluated is the reference of the next
|
|
@@ -261,9 +263,10 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
261
263
|
fext = an.calc_fext(inc=1., silent=True)
|
|
262
264
|
kC0 = an.calc_kC(silent=silent)
|
|
263
265
|
w = _weights(an, kC0)
|
|
266
|
+
K0_abs = _abs_matrix(kC0)
|
|
264
267
|
lbd_dependent = callbacks.is_load_factor_dependent(an)
|
|
265
268
|
if lbd_dependent:
|
|
266
|
-
# dR/dlbd at c = 0 and lbd = 0
|
|
269
|
+
# -dR/dlbd at c = 0 and lbd = 0
|
|
267
270
|
q0 = callbacks.calc_load_vector(an, np.zeros_like(fext), fext, 0.)
|
|
268
271
|
u_ref = solve(kC0, q0, silent=True)
|
|
269
272
|
else:
|
|
@@ -305,7 +308,7 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
305
308
|
warn('Singular tangent stiffness matrix at the last converged state! Analysis stopped',
|
|
306
309
|
level=1, silent=silent)
|
|
307
310
|
break
|
|
308
|
-
# load vector of the converged configuration, dR/dlbd, kept with
|
|
311
|
+
# load vector of the converged configuration, -dR/dlbd, kept with
|
|
309
312
|
# the tangent stiffness matrix when the step is repeated
|
|
310
313
|
if step_num == 1 and lbd_dependent:
|
|
311
314
|
q_conv = q0
|
|
@@ -336,7 +339,7 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
336
339
|
Rnorms = []
|
|
337
340
|
solve_kT = solve_kT_conv
|
|
338
341
|
dc_q = dc_q_conv
|
|
339
|
-
# the last dR/dlbd evaluated, with the tangent stiffness matrix
|
|
342
|
+
# the last -dR/dlbd evaluated, with the tangent stiffness matrix
|
|
340
343
|
q_iter = q_conv
|
|
341
344
|
iter_kT = 0
|
|
342
345
|
iteration = 0
|
|
@@ -344,14 +347,15 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
344
347
|
iteration += 1
|
|
345
348
|
fint = callbacks.calc_fint(an, c + dc, lbd + dlbd)
|
|
346
349
|
fext_total = (lbd + dlbd)*fext
|
|
347
|
-
R =
|
|
350
|
+
R = fint - fext_total
|
|
348
351
|
q_ref = None
|
|
349
352
|
if lbd_dependent:
|
|
350
353
|
q_ref = (lbd + dlbd)*q_iter
|
|
351
|
-
conv, Rnorm, Rrel = _check_convergence(an, R, fint,
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
_log_iteration(an, iteration, R, Rrel, silent, lbd=lbd + dlbd
|
|
354
|
+
conv, Rnorm, Rrel, roundoff = _check_convergence(an, R, fint,
|
|
355
|
+
fext_total, fext_ref=dlbd*fext, q_ref=q_ref, w=w,
|
|
356
|
+
Kc_abs=K0_abs @ np.abs(c + dc), full_output=True)
|
|
357
|
+
_log_iteration(an, iteration, R, Rrel, silent, lbd=lbd + dlbd,
|
|
358
|
+
roundoff=roundoff)
|
|
355
359
|
if conv:
|
|
356
360
|
converged = True
|
|
357
361
|
break
|
|
@@ -370,7 +374,7 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
370
374
|
warn('Singular tangent stiffness matrix', level=2, silent=silent)
|
|
371
375
|
break
|
|
372
376
|
if lbd_dependent:
|
|
373
|
-
# dR/dlbd at the iterate of the tangent stiffness matrix
|
|
377
|
+
# -dR/dlbd at the iterate of the tangent stiffness matrix
|
|
374
378
|
q_iter = callbacks.calc_load_vector(an, c + dc, fext,
|
|
375
379
|
lbd + dlbd)
|
|
376
380
|
dc_q = solve_kT(q_iter)
|
|
@@ -378,7 +382,7 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
378
382
|
dc_q = solve_kT(callbacks.calc_load_vector(an, c + dc,
|
|
379
383
|
fext))
|
|
380
384
|
iter_kT = 1
|
|
381
|
-
dc_R = solve_kT(R)
|
|
385
|
+
dc_R = solve_kT(-R)
|
|
382
386
|
|
|
383
387
|
# linearized constraint
|
|
384
388
|
den = inner(dc, dlbd, dc_q, 1.)
|
|
@@ -421,7 +425,7 @@ def _solver_arc_length(an, method, silent=False):
|
|
|
421
425
|
t = (1. - lbd)/dlbd
|
|
422
426
|
converged, dc1, _ = _NR_iterations(an, c, t*dc, 1., fext,
|
|
423
427
|
kT0=None, silent=silent, lbd_dependent=lbd_dependent,
|
|
424
|
-
q=q_iter, w=w)
|
|
428
|
+
q=q_iter, w=w, K0_abs=K0_abs)
|
|
425
429
|
if converged:
|
|
426
430
|
msg('Converged at load factor 1.00000', level=2, silent=silent)
|
|
427
431
|
an.cs.append((c + dc1).copy())
|
|
@@ -37,7 +37,6 @@ import io
|
|
|
37
37
|
import os
|
|
38
38
|
import re
|
|
39
39
|
import sys
|
|
40
|
-
import warnings
|
|
41
40
|
|
|
42
41
|
import numpy as np
|
|
43
42
|
from scipy.linalg import cholesky, eigh, solve_triangular
|
|
@@ -407,27 +406,27 @@ def _rayleigh_ritz(A, M, V):
|
|
|
407
406
|
Notes
|
|
408
407
|
-----
|
|
409
408
|
The eigenvectors of different passes of :func:`capped_eigsh` are only
|
|
410
|
-
|
|
411
|
-
close eigenvalues of different passes.
|
|
412
|
-
with the Cholesky factor
|
|
413
|
-
problem
|
|
409
|
+
`\boldsymbol{M}`-orthogonal to the accuracy of ARPACK, e.g. `10^{-6}` between
|
|
410
|
+
close eigenvalues of different passes. `\boldsymbol{V}` is `\boldsymbol{M}`-orthonormalized
|
|
411
|
+
with the Cholesky factor `\boldsymbol{L}` of `\boldsymbol{V}^\top \boldsymbol{M} \boldsymbol{V}`, and the projected
|
|
412
|
+
problem `\boldsymbol{W}^\top \boldsymbol{A} \boldsymbol{W} \boldsymbol{y} = w \boldsymbol{y}`, with `\boldsymbol{W} = \boldsymbol{V} \boldsymbol{L}^{-\top}`, is
|
|
414
413
|
solved with the LAPACK driver ``dsyevr`` (``driver='evr'``), which does
|
|
415
414
|
not use the ``dsteqr`` of Intel MKL broken above 32 x 32 matrices, see
|
|
416
|
-
:func:`dsteqr_is_faulty`. The returned eigenvectors
|
|
417
|
-
|
|
418
|
-
least as accurate as the eigenvalues of the passes.
|
|
419
|
-
|
|
415
|
+
:func:`dsteqr_is_faulty`. The returned eigenvectors `\boldsymbol{W} \boldsymbol{y}` are
|
|
416
|
+
`\boldsymbol{M}`-orthonormal to the rounding errors, and the Ritz values are at
|
|
417
|
+
least as accurate as the eigenvalues of the passes. `\boldsymbol{V}^\top \boldsymbol{M} \boldsymbol{V}` and
|
|
418
|
+
`\boldsymbol{W}^\top \boldsymbol{A} \boldsymbol{W}` are symmetrized, `\frac{1}{2}(\boldsymbol{B} + \boldsymbol{B}^\top)`, before the
|
|
420
419
|
Cholesky factorization and the eigenvalue solution.
|
|
421
420
|
|
|
422
421
|
The Ritz values are the optimal approximations of the eigenvalues that
|
|
423
|
-
can be obtained from the subspace spanned by the columns of
|
|
422
|
+
can be obtained from the subspace spanned by the columns of `\boldsymbol{V}`
|
|
424
423
|
([1]_, Chapter 11, "Approximations from a Subspace", Section 11.4,
|
|
425
424
|
"Optimality", Eq. (11.5) and Theorem 11.4.1, p. 236): the `j`-th
|
|
426
425
|
Ritz value is the minimum over the `j`-dimensional subspaces of the span
|
|
427
426
|
of the maximum of the Rayleigh quotient, so it can only improve when the
|
|
428
427
|
span grows, and the span contains the eigenvectors of every pass. The
|
|
429
428
|
theorem is stated for the standard problem; it holds for the pencil in
|
|
430
|
-
the
|
|
429
|
+
the `\boldsymbol{M}` inner product, through the Cholesky factor of `\boldsymbol{V}^\top \boldsymbol{M} \boldsymbol{V}`.
|
|
431
430
|
|
|
432
431
|
References
|
|
433
432
|
----------
|
|
@@ -446,7 +445,7 @@ def _rayleigh_ritz(A, M, V):
|
|
|
446
445
|
def capped_eigsh(A, k, M, sigma, which, mode='normal', tol=0):
|
|
447
446
|
r""":func:`scipy.sparse.linalg.eigsh` with ``ncv`` from :func:`arpack_ncv`
|
|
448
447
|
|
|
449
|
-
Solves
|
|
448
|
+
Solves `\boldsymbol{A} \boldsymbol{x} = w \boldsymbol{M} \boldsymbol{x}` with a positive definite ``M`` about the
|
|
450
449
|
shift ``sigma``, in the shift-invert (``mode='normal'``) or Cayley
|
|
451
450
|
(``mode='cayley'``) mode, selecting the eigenvalues by increasing `w`,
|
|
452
451
|
e.g. the Cayley mode with ``which='SM'`` and ``sigma`` larger than all
|
|
@@ -490,7 +489,7 @@ def capped_eigsh(A, k, M, sigma, which, mode='normal', tol=0):
|
|
|
490
489
|
Returns
|
|
491
490
|
-------
|
|
492
491
|
w, x : ndarray
|
|
493
|
-
The eigenvalues and the
|
|
492
|
+
The eigenvalues and the `\boldsymbol{M}`-orthonormal eigenvectors, by
|
|
494
493
|
increasing `w` when computed in passes. ``w`` is a 1-D array of size
|
|
495
494
|
``k`` and ``x`` an array of shape ``(n, k)``.
|
|
496
495
|
|
|
@@ -502,7 +501,7 @@ def capped_eigsh(A, k, M, sigma, which, mode='normal', tol=0):
|
|
|
502
501
|
-----
|
|
503
502
|
In the shift-invert mode ARPACK computes the eigenvalues
|
|
504
503
|
`\nu = 1/(w - \sigma)` of largest modulus of the operator
|
|
505
|
-
`(
|
|
504
|
+
`(\boldsymbol{A} - \sigma \boldsymbol{M})^{-1} \boldsymbol{M}`, i.e. the `w` nearest `\sigma` ([1]_,
|
|
506
505
|
Chapter 3, Section 3.2, Eq. (3.2.2)).
|
|
507
506
|
The passes keep ``k <= (max_ncv - 1)//2``, i.e. ``ncv >= 2 k + 1``.
|
|
508
507
|
With ``k`` close to ``ncv`` ARPACK converges very slowly, not at all, or
|
|
@@ -510,22 +509,22 @@ def capped_eigsh(A, k, M, sigma, which, mode='normal', tol=0):
|
|
|
510
509
|
|
|
511
510
|
The passes after the first one use the shift-invert mode about
|
|
512
511
|
`\sigma_d`, the middle of the largest gap between the last five
|
|
513
|
-
eigenvalues found, with the eigenvectors
|
|
512
|
+
eigenvalues found, with the eigenvectors `\boldsymbol{V}` of the previous passes
|
|
514
513
|
deflated from the operator:
|
|
515
514
|
|
|
516
515
|
.. math::
|
|
517
516
|
|
|
518
|
-
\{y
|
|
517
|
+
\boldsymbol{y} = (\boldsymbol{I} - \boldsymbol{V} \boldsymbol{V}^\top \boldsymbol{M})(\boldsymbol{A} - \sigma_d \boldsymbol{M})^{-1} \boldsymbol{x}
|
|
519
518
|
|
|
520
|
-
such that the eigenvalues of
|
|
519
|
+
such that the eigenvalues of `\boldsymbol{V}`, already found, become null and are
|
|
521
520
|
not found again. Each pass therefore returns the eigenvalues nearest
|
|
522
521
|
`\sigma_d` among those not found yet, i.e. the next ones by increasing
|
|
523
522
|
`w`. A shift close to them, instead of ``sigma``, separates them better:
|
|
524
523
|
in the study of the panels cylinders it was 30 to 45% faster and gave
|
|
525
524
|
eigenvalues 5 to 10 times more accurate. ARPACK applies the operator
|
|
526
|
-
to
|
|
527
|
-
`(
|
|
528
|
-
|
|
525
|
+
to `\boldsymbol{M} \boldsymbol{x}`; for an eigenvector `\boldsymbol{v}_i` of `\boldsymbol{V}`,
|
|
526
|
+
`(\boldsymbol{A} - \sigma_d \boldsymbol{M})^{-1} \boldsymbol{M} \boldsymbol{v}_i = \boldsymbol{v}_i/(w_i - \sigma_d)` and,
|
|
527
|
+
`\boldsymbol{V}` being `\boldsymbol{M}`-orthonormal, `(\boldsymbol{I} - \boldsymbol{V} \boldsymbol{V}^\top \boldsymbol{M}) \boldsymbol{v}_i = \boldsymbol{0}`.
|
|
529
528
|
|
|
530
529
|
References
|
|
531
530
|
----------
|
|
@@ -557,9 +556,7 @@ def capped_eigsh(A, k, M, sigma, which, mode='normal', tol=0):
|
|
|
557
556
|
order = np.argsort(w, kind='stable')
|
|
558
557
|
w, V = w[order], V[:, order]
|
|
559
558
|
sigma_d = _deflation_shift(w, far_shift)
|
|
560
|
-
|
|
561
|
-
warnings.simplefilter("ignore")
|
|
562
|
-
lu = splu(csc_matrix(A - sigma_d*M))
|
|
559
|
+
lu = splu(csc_matrix(A - sigma_d*M))
|
|
563
560
|
MV = M @ V
|
|
564
561
|
|
|
565
562
|
def matvec(x, V=V, MV=MV, lu=lu):
|