structsolve 0.9.4__tar.gz → 0.10.0__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.4/structsolve.egg-info → structsolve-0.10.0}/PKG-INFO +13 -2
- {structsolve-0.9.4 → structsolve-0.10.0}/README.md +10 -1
- {structsolve-0.9.4 → structsolve-0.10.0}/pyproject.toml +4 -1
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/__init__.py +2 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/analysis.py +257 -44
- structsolve-0.10.0/structsolve/arc_length.py +1190 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/arpackutils.py +152 -8
- structsolve-0.10.0/structsolve/displacement_control.py +32 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/freq.py +45 -13
- structsolve-0.10.0/structsolve/generalized_displacement_control.py +33 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/linear_buckling.py +153 -38
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/newton_raphson.py +478 -24
- structsolve-0.10.0/structsolve/pardiso.py +236 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/sparseutils.py +25 -9
- structsolve-0.10.0/structsolve/static.py +970 -0
- structsolve-0.10.0/structsolve/stopping.py +41 -0
- {structsolve-0.9.4 → structsolve-0.10.0/structsolve.egg-info}/PKG-INFO +13 -2
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve.egg-info/SOURCES.txt +20 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve.egg-info/requires.txt +3 -0
- structsolve-0.10.0/tests/test_arc_length_robustness.py +167 -0
- structsolve-0.10.0/tests/test_arc_length_weights.py +87 -0
- structsolve-0.10.0/tests/test_branch_switching.py +305 -0
- structsolve-0.10.0/tests/test_constraint_values.py +269 -0
- structsolve-0.10.0/tests/test_constraint_values_load_factor.py +121 -0
- structsolve-0.10.0/tests/test_constraints.py +192 -0
- structsolve-0.10.0/tests/test_displacement_control.py +159 -0
- structsolve-0.10.0/tests/test_eigen_pardiso.py +228 -0
- structsolve-0.10.0/tests/test_factorization_cache.py +291 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_follower_freq_static.py +18 -0
- structsolve-0.10.0/tests/test_generalized_displacement_control.py +90 -0
- structsolve-0.10.0/tests/test_inertia.py +401 -0
- structsolve-0.10.0/tests/test_inertia_unsymmetric.py +380 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_linear_buckling.py +2 -1
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_newton_raphson.py +24 -0
- structsolve-0.10.0/tests/test_pardiso.py +139 -0
- structsolve-0.10.0/tests/test_restart.py +95 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_reuse.py +2 -1
- structsolve-0.10.0/tests/test_simo1990_cylinder.py +124 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_sparse_first.py +1 -1
- structsolve-0.10.0/tests/test_stopping.py +85 -0
- structsolve-0.9.4/structsolve/arc_length.py +0 -475
- structsolve-0.9.4/structsolve/static.py +0 -147
- {structsolve-0.9.4 → structsolve-0.10.0}/LICENSE +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/setup.cfg +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/arc_length_crisfield.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/arc_length_riks.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/callbacks.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/convergence.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve/logger.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve.egg-info/dependency_links.txt +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/structsolve.egg-info/top_level.txt +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_analysis.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_arc_length.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_arc_length_fung_kaplan.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_arpackutils.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_follower_loads.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_freq.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_linear_buckling_unsymmetric.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_load_factor_dependent_load.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_sparseutils.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_static.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_static_deflection.py +0 -0
- {structsolve-0.9.4 → structsolve-0.10.0}/tests/test_warnings_registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: structsolve
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Structural analysis solvers
|
|
5
5
|
Author-email: "Saullo G. P. Castro" <castrosaullo@gmail.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -26,6 +26,8 @@ Description-Content-Type: text/markdown
|
|
|
26
26
|
License-File: LICENSE
|
|
27
27
|
Requires-Dist: numpy
|
|
28
28
|
Requires-Dist: scipy
|
|
29
|
+
Provides-Extra: pardiso
|
|
30
|
+
Requires-Dist: mkl; extra == "pardiso"
|
|
29
31
|
Provides-Extra: test
|
|
30
32
|
Requires-Dist: pytest; extra == "test"
|
|
31
33
|
Requires-Dist: pytest-cov; extra == "test"
|
|
@@ -59,11 +61,14 @@ Structural analysis solvers tailored for semi-analytical models
|
|
|
59
61
|
Currently these solvers are pretty much compatible with my other repository
|
|
60
62
|
[panels](https://github.com/saullocastro/panels).
|
|
61
63
|
|
|
64
|
+
The folder `notebooks/` holds validation notebooks against results of the
|
|
65
|
+
literature, run with models of `panels`, see `notebooks/README.md`.
|
|
66
|
+
|
|
62
67
|
|
|
63
68
|
Citing this library
|
|
64
69
|
===================
|
|
65
70
|
|
|
66
|
-
Saullo G. P. Castro (2026). Structural analysis solvers tailored for semi-analytical models (Version 0.
|
|
71
|
+
Saullo G. P. Castro (2026). Structural analysis solvers tailored for semi-analytical models (Version 0.10.0). Zenodo. DOI: https://doi.org/10.5281/zenodo.2581212.
|
|
67
72
|
|
|
68
73
|
|
|
69
74
|
Documentation
|
|
@@ -78,6 +83,12 @@ History
|
|
|
78
83
|
|
|
79
84
|
See [CHANGELOG.md](CHANGELOG.md) for the details of each version.
|
|
80
85
|
|
|
86
|
+
* version 0.10.0 (2026-11-03)
|
|
87
|
+
- Arc-length corrections: line search and early divergence check; indirect displacement control and generalized displacement control; weights of the degrees of freedom in the arc-length norm
|
|
88
|
+
- Stability along the path: the number of negative eigenvalues of the tangent with PARDISO (`linear_solver='pardiso'`, `inertia=True`), increments cut when it jumps (`max_inertia_jump`) and switching to the bifurcated branch along the critical eigenvector (`branch_switching`)
|
|
89
|
+
- Linear constraints of the solution, `C^T c = 0`, by a border of the factorizations; stop criteria and `final_load_factor`; restart of the non-linear analyses from `path_state`
|
|
90
|
+
- Cheaper factorizations: the linear solutions use `linear_solver`, and the null rows and columns, the symmetry check and the scaling of PARDISO cost less
|
|
91
|
+
|
|
81
92
|
* version 0.9.4 (2026-10-28)
|
|
82
93
|
- 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
94
|
- 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
|
|
@@ -24,11 +24,14 @@ Structural analysis solvers tailored for semi-analytical models
|
|
|
24
24
|
Currently these solvers are pretty much compatible with my other repository
|
|
25
25
|
[panels](https://github.com/saullocastro/panels).
|
|
26
26
|
|
|
27
|
+
The folder `notebooks/` holds validation notebooks against results of the
|
|
28
|
+
literature, run with models of `panels`, see `notebooks/README.md`.
|
|
29
|
+
|
|
27
30
|
|
|
28
31
|
Citing this library
|
|
29
32
|
===================
|
|
30
33
|
|
|
31
|
-
Saullo G. P. Castro (2026). Structural analysis solvers tailored for semi-analytical models (Version 0.
|
|
34
|
+
Saullo G. P. Castro (2026). Structural analysis solvers tailored for semi-analytical models (Version 0.10.0). Zenodo. DOI: https://doi.org/10.5281/zenodo.2581212.
|
|
32
35
|
|
|
33
36
|
|
|
34
37
|
Documentation
|
|
@@ -43,6 +46,12 @@ History
|
|
|
43
46
|
|
|
44
47
|
See [CHANGELOG.md](CHANGELOG.md) for the details of each version.
|
|
45
48
|
|
|
49
|
+
* version 0.10.0 (2026-11-03)
|
|
50
|
+
- Arc-length corrections: line search and early divergence check; indirect displacement control and generalized displacement control; weights of the degrees of freedom in the arc-length norm
|
|
51
|
+
- Stability along the path: the number of negative eigenvalues of the tangent with PARDISO (`linear_solver='pardiso'`, `inertia=True`), increments cut when it jumps (`max_inertia_jump`) and switching to the bifurcated branch along the critical eigenvector (`branch_switching`)
|
|
52
|
+
- Linear constraints of the solution, `C^T c = 0`, by a border of the factorizations; stop criteria and `final_load_factor`; restart of the non-linear analyses from `path_state`
|
|
53
|
+
- Cheaper factorizations: the linear solutions use `linear_solver`, and the null rows and columns, the symmetry check and the scaling of PARDISO cost less
|
|
54
|
+
|
|
46
55
|
* version 0.9.4 (2026-10-28)
|
|
47
56
|
- 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
57
|
- 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
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "structsolve"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.10.0"
|
|
8
8
|
description = "Structural analysis solvers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "BSD-3-Clause"
|
|
@@ -38,6 +38,9 @@ dependencies = [
|
|
|
38
38
|
]
|
|
39
39
|
|
|
40
40
|
[project.optional-dependencies]
|
|
41
|
+
pardiso = [
|
|
42
|
+
"mkl",
|
|
43
|
+
]
|
|
41
44
|
test = [
|
|
42
45
|
"pytest",
|
|
43
46
|
"pytest-cov",
|
|
@@ -17,6 +17,8 @@ directly from the ``structsolve`` namespace:
|
|
|
17
17
|
- :class:`.DenseFallbackWarning`: warning of :func:`.lb` and :func:`.freq`
|
|
18
18
|
when a dense solver was used as a last resort
|
|
19
19
|
|
|
20
|
+
and the criteria to stop a non-linear analysis in :mod:`structsolve.stopping`.
|
|
21
|
+
|
|
20
22
|
"""
|
|
21
23
|
from __future__ import absolute_import
|
|
22
24
|
|
|
@@ -12,13 +12,18 @@ from __future__ import absolute_import
|
|
|
12
12
|
|
|
13
13
|
import numpy as np
|
|
14
14
|
|
|
15
|
-
from .static import solve, _factorize
|
|
15
|
+
from .static import solve, _factorize, _FactorizationCache
|
|
16
16
|
from .logger import msg, warn
|
|
17
17
|
from . import callbacks
|
|
18
|
-
from .newton_raphson import _check_convergence, _weights, _abs_matrix
|
|
18
|
+
from .newton_raphson import (_check_convergence, _weights, _abs_matrix,
|
|
19
|
+
_constraint_matrix, _constraint_values,
|
|
20
|
+
_residual_projector)
|
|
19
21
|
from .newton_raphson import _solver_NR
|
|
20
22
|
from .arc_length_riks import _solver_arc_length_riks
|
|
21
23
|
from .arc_length_crisfield import _solver_arc_length_crisfield
|
|
24
|
+
from .displacement_control import _solver_displacement_control
|
|
25
|
+
from .generalized_displacement_control import (
|
|
26
|
+
_solver_generalized_displacement_control)
|
|
22
27
|
|
|
23
28
|
|
|
24
29
|
class Analysis(object):
|
|
@@ -38,37 +43,158 @@ class Analysis(object):
|
|
|
38
43
|
The analysis parameters are attributes of this class, described in the
|
|
39
44
|
following tables together with their default values:
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
Non-Linear Algorithm
|
|
43
|
-
|
|
44
|
-
``NL_method``
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
46
|
+
============================ =============================================
|
|
47
|
+
Non-Linear Algorithm Description
|
|
48
|
+
============================ =============================================
|
|
49
|
+
``NL_method`` ``str``, ``'NR'`` (default) for the
|
|
50
|
+
Newton-Raphson method, see
|
|
51
|
+
:func:`.newton_raphson._solver_NR`, or
|
|
52
|
+
``'arc_length_riks'`` and
|
|
53
|
+
``'arc_length_crisfield'`` for the arc-length
|
|
54
|
+
methods, or ``'displacement_control'`` for
|
|
55
|
+
the indirect displacement control, or
|
|
56
|
+
``'generalized_displacement_control'``,
|
|
57
|
+
see
|
|
58
|
+
:func:`.arc_length._solver_arc_length`
|
|
59
|
+
``control_vector`` ``array``, the vector
|
|
60
|
+
`\boldsymbol{\psi}` of the controlled
|
|
61
|
+
quantity `\boldsymbol{\psi}^\top
|
|
62
|
+
\boldsymbol{c}` of
|
|
63
|
+
``NL_method='displacement_control'``, e.g.
|
|
64
|
+
a unit vector that picks one degree of
|
|
65
|
+
freedom, or a buckling mode. Default is
|
|
66
|
+
``None``
|
|
67
|
+
``control_scale`` ``float``, the reference value of the
|
|
68
|
+
controlled quantity, which makes the step
|
|
69
|
+
dimensionless. Default is ``None``, i.e.
|
|
70
|
+
`|\boldsymbol{\psi}^\top
|
|
71
|
+
\boldsymbol{u}_{ref}|`, its value in the linear solution for a
|
|
72
|
+
load factor of 1
|
|
73
|
+
``line_search`` ``bool``, activates a safeguarding
|
|
74
|
+
line-search, for the Newton-Raphson method
|
|
75
|
+
only, see
|
|
76
|
+
:func:`.newton_raphson._NR_iterations`.
|
|
77
|
+
Default is ``False``
|
|
78
|
+
``max_iter_line_search`` ``int``, maximum number of iterations of the
|
|
79
|
+
line-search. Default is ``20``
|
|
80
|
+
``arc_length_line_search`` ``bool``, activates a backtracking line
|
|
81
|
+
search on the corrections of the arc-length
|
|
82
|
+
methods, see
|
|
83
|
+
:func:`.arc_length._solver_arc_length`.
|
|
84
|
+
Default is ``False``
|
|
85
|
+
``branch_switching`` ``int``, number of bifurcation points at
|
|
86
|
+
which the arc-length and the displacement
|
|
87
|
+
control methods switch to the bifurcated
|
|
88
|
+
branch, along the critical eigenvector,
|
|
89
|
+
where the number of negative eigenvalues
|
|
90
|
+
increases or decreases, which requires
|
|
91
|
+
``inertia``, see
|
|
92
|
+
:func:`.arc_length._solver_arc_length`.
|
|
93
|
+
Default is ``0``
|
|
94
|
+
``arc_length_weights`` ``array``, non-negative weights of the
|
|
95
|
+
degrees of freedom in the norm of the
|
|
96
|
+
arc-length constraint, e.g. 1 for the
|
|
97
|
+
translations and 0 for their derivatives
|
|
98
|
+
or the rotations, see
|
|
99
|
+
:func:`.arc_length._solver_arc_length`.
|
|
100
|
+
Default is ``None``, all equal to 1
|
|
101
|
+
``early_divergence`` ``bool``, stops a diverging step earlier,
|
|
102
|
+
such that its increment is cut before the
|
|
103
|
+
iterations are spent, see
|
|
104
|
+
:func:`.newton_raphson._check_divergence`.
|
|
105
|
+
Default is ``False``
|
|
106
|
+
``modified_NR`` ``bool``, activates the modified
|
|
107
|
+
Newton-Raphson method, where the tangent
|
|
108
|
+
stiffness matrix is not updated at every
|
|
109
|
+
iteration. Default is ``False``, i.e. full
|
|
110
|
+
Newton-Raphson
|
|
111
|
+
``compute_every_n`` ``int``, if ``modified_NR=True``, the tangent
|
|
112
|
+
stiffness matrix is updated at every `n`
|
|
113
|
+
iterations. Default is ``6``
|
|
114
|
+
``kT_initial_state`` ``bool``, if ``modified_NR=True``, tells if
|
|
115
|
+
the tangent stiffness matrix should be
|
|
116
|
+
calculated already at the first iteration of
|
|
117
|
+
the analysis, which is required for example
|
|
118
|
+
when initial imperfections take place.
|
|
119
|
+
Otherwise the linear constitutive stiffness
|
|
120
|
+
matrix is used. Default is ``True``
|
|
121
|
+
``linear_solver`` ``str``, factorization of the tangent
|
|
122
|
+
stiffness matrix of the non-linear solvers,
|
|
123
|
+
``'splu'`` (default), the sparse LU of
|
|
124
|
+
SuperLU, or ``'pardiso'``, the symmetric
|
|
125
|
+
indefinite factorization of PARDISO, which
|
|
126
|
+
requires oneMKL, see :mod:`.pardiso`, and
|
|
127
|
+
gives the number
|
|
128
|
+
of negative eigenvalues, see
|
|
129
|
+
:func:`.static._factorize`
|
|
130
|
+
``inertia`` ``bool``, the tangent stiffness matrix is
|
|
131
|
+
factorized at every converged increment of
|
|
132
|
+
the non-linear analyses, and its number of
|
|
133
|
+
negative eigenvalues (``'pardiso'``) and the
|
|
134
|
+
sign of its determinant are recorded, such
|
|
135
|
+
that limit points, bifurcations crossed and
|
|
136
|
+
unstable branches are visible. Default is
|
|
137
|
+
``False``
|
|
138
|
+
``inertia_num_eigvals`` ``int`` `k`, with ``inertia``: when the
|
|
139
|
+
count of PARDISO is not available, e.g.
|
|
140
|
+
for an unsymmetric tangent stiffness
|
|
141
|
+
matrix, the number of its negative real
|
|
142
|
+
eigenvalues among the `k` of smallest
|
|
143
|
+
magnitude, scaled by the diagonal of the
|
|
144
|
+
linear stiffness matrix, by shift-invert
|
|
145
|
+
with ARPACK and the LU factorization of
|
|
146
|
+
the converged state, see
|
|
147
|
+
:func:`.newton_raphson._eigvals_inertia`.
|
|
148
|
+
Default is ``None``, not computed
|
|
149
|
+
``max_inertia_jump`` ``int``, a converged increment whose number
|
|
150
|
+
of negative eigenvalues changed by more
|
|
151
|
+
than this value is repeated with a smaller
|
|
152
|
+
increment, which requires ``inertia`` and
|
|
153
|
+
``linear_solver='pardiso'`` or
|
|
154
|
+
``inertia_num_eigvals``. Default is
|
|
155
|
+
``None``
|
|
156
|
+
``constraints`` ``array`` or sparse matrix `\boldsymbol{C}`,
|
|
157
|
+
of shape ``(n, m)``, a 1-D array for one
|
|
158
|
+
constraint: the linear constraints
|
|
159
|
+
`\boldsymbol{C}^\top \boldsymbol{c} =
|
|
160
|
+
\boldsymbol{0}` of the solution, e.g. an
|
|
161
|
+
inertia relief or multipoint constraints,
|
|
162
|
+
imposed by bordering every factorization
|
|
163
|
+
of the stiffness matrices, see
|
|
164
|
+
:func:`.static._factorize`, in the linear
|
|
165
|
+
and the non-linear analyses. Default is
|
|
166
|
+
``None``
|
|
167
|
+
``constraint_values`` ``array`` `\boldsymbol{d}`, of shape
|
|
168
|
+
``(m,)``, the values of the constraints,
|
|
169
|
+
`\boldsymbol{C}^\top \boldsymbol{c} =
|
|
170
|
+
\lambda \boldsymbol{d}` along the
|
|
171
|
+
non-linear path and `\boldsymbol{C}^\top
|
|
172
|
+
\boldsymbol{c} = \boldsymbol{d}` in the
|
|
173
|
+
linear analysis, e.g. a displacement or a
|
|
174
|
+
rotation prescribed in proportion to the
|
|
175
|
+
load factor, with or without external
|
|
176
|
+
load, which requires ``constraints``.
|
|
177
|
+
Default is ``None``, all null
|
|
178
|
+
``stop_criterion`` ``callable``, ``stop_criterion(an)`` is
|
|
179
|
+
called after every converged increment of
|
|
180
|
+
the non-linear analyses, which stop when it
|
|
181
|
+
returns ``True``, e.g. a number of
|
|
182
|
+
increments beyond the first limit point or
|
|
183
|
+
a displacement measure, see
|
|
184
|
+
:mod:`.stopping`. Default is ``None``
|
|
185
|
+
``final_load_factor`` ``float``, the load factor at which the
|
|
186
|
+
non-linear analyses finish, exactly, or,
|
|
187
|
+
for the arc-length and displacement control
|
|
188
|
+
methods, ``None``: the path is traced
|
|
189
|
+
beyond any load factor until another stop,
|
|
190
|
+
e.g. ``stop_criterion`` or
|
|
191
|
+
``maxArcLength``. Default is ``1.``
|
|
192
|
+
``restart_state`` ``dict``, the ``path_state`` of a previous
|
|
193
|
+
analysis with the same ``NL_method``, from
|
|
194
|
+
whose last converged increment the
|
|
195
|
+
non-linear analysis continues. Default is
|
|
196
|
+
``None``
|
|
197
|
+
============================ =============================================
|
|
72
198
|
|
|
73
199
|
================ =================================================
|
|
74
200
|
Incrementation Description
|
|
@@ -166,6 +292,33 @@ class Analysis(object):
|
|
|
166
292
|
cs : list
|
|
167
293
|
Solution vectors of the converged increments, filled by
|
|
168
294
|
:meth:`.static`.
|
|
295
|
+
num_negative_eigvals : list
|
|
296
|
+
With ``inertia=True``, for each converged increment of a non-linear
|
|
297
|
+
analysis, the number of negative eigenvalues of the tangent
|
|
298
|
+
stiffness matrix on the degrees of freedom whose row or column is
|
|
299
|
+
not null, ``None`` with ``linear_solver='splu'``, when PARDISO fell
|
|
300
|
+
back to SuperLU for an unsymmetric matrix, or for a singular matrix,
|
|
301
|
+
unless ``inertia_num_eigvals`` is given: the number of negative real
|
|
302
|
+
eigenvalues among the ``inertia_num_eigvals`` of smallest magnitude
|
|
303
|
+
is then recorded for the SuperLU factorizations. Empty otherwise.
|
|
304
|
+
det_signs : list
|
|
305
|
+
With ``inertia=True``, for each converged increment of a non-linear
|
|
306
|
+
analysis, the sign of the determinant of the same matrix, ``1`` or
|
|
307
|
+
``-1``, ``0`` when it is singular. Empty otherwise.
|
|
308
|
+
num_perturbed_pivots : list
|
|
309
|
+
With ``inertia=True`` and ``linear_solver='pardiso'``, the number of
|
|
310
|
+
pivots perturbed by PARDISO at each converged increment, which makes
|
|
311
|
+
the count of negative eigenvalues uncertain when not zero; ``None``
|
|
312
|
+
otherwise.
|
|
313
|
+
branch_points : list
|
|
314
|
+
With ``branch_switching``, the indices in ``increments`` of the
|
|
315
|
+
converged increments from which the path was switched to a
|
|
316
|
+
bifurcated branch.
|
|
317
|
+
path_state : dict or None
|
|
318
|
+
After each converged increment of a non-linear analysis, the state
|
|
319
|
+
from which it can be continued, see ``restart_state``: the solution,
|
|
320
|
+
the load factor, the next increment and the directions of the
|
|
321
|
+
predictors.
|
|
169
322
|
last_analysis : str
|
|
170
323
|
Type of the last analysis run, ``'static'`` after :meth:`.static`.
|
|
171
324
|
|
|
@@ -229,12 +382,21 @@ class Analysis(object):
|
|
|
229
382
|
|
|
230
383
|
"""
|
|
231
384
|
__slots__ = ['NL_method', 'line_search', 'max_iter_line_search',
|
|
385
|
+
'arc_length_line_search',
|
|
386
|
+
'early_divergence', 'arc_length_weights', 'control_vector',
|
|
387
|
+
'control_scale', 'linear_solver', 'inertia',
|
|
388
|
+
'constraints', 'constraint_values', 'stop_criterion',
|
|
389
|
+
'final_load_factor',
|
|
390
|
+
'branch_switching', 'max_inertia_jump', 'inertia_num_eigvals',
|
|
391
|
+
'restart_state', 'path_state', 'branch_points',
|
|
392
|
+
'num_negative_eigvals', 'det_signs', 'num_perturbed_pivots',
|
|
232
393
|
'modified_NR', 'compute_every_n',
|
|
233
394
|
'kT_initial_state', 'initialInc', 'minInc', 'maxInc',
|
|
234
395
|
'maxArcLength', 'convergence', 'absTOL', 'relTOL', 'maxNumIter',
|
|
235
396
|
'too_slow_TOL',
|
|
236
397
|
'increments', 'cs', 'last_analysis', 'calc_fext', 'calc_kC',
|
|
237
|
-
'calc_fint', 'calc_kG'
|
|
398
|
+
'calc_fint', 'calc_kG', '_factorization_cache',
|
|
399
|
+
'_inertia_scaling']
|
|
238
400
|
|
|
239
401
|
|
|
240
402
|
def __init__(self, calc_fext=None, calc_fint=None, calc_kC=None,
|
|
@@ -243,6 +405,21 @@ class Analysis(object):
|
|
|
243
405
|
self.NL_method = 'NR'
|
|
244
406
|
self.line_search = False
|
|
245
407
|
self.max_iter_line_search = 20
|
|
408
|
+
self.arc_length_line_search = False
|
|
409
|
+
self.early_divergence = False
|
|
410
|
+
self.arc_length_weights = None
|
|
411
|
+
self.control_vector = None
|
|
412
|
+
self.control_scale = None
|
|
413
|
+
self.linear_solver = 'splu'
|
|
414
|
+
self.inertia = False
|
|
415
|
+
self.constraints = None
|
|
416
|
+
self.constraint_values = None
|
|
417
|
+
self.stop_criterion = None
|
|
418
|
+
self.final_load_factor = 1.
|
|
419
|
+
self.branch_switching = 0
|
|
420
|
+
self.max_inertia_jump = None
|
|
421
|
+
self.inertia_num_eigvals = None
|
|
422
|
+
self.restart_state = None
|
|
246
423
|
self.modified_NR = False
|
|
247
424
|
self.compute_every_n = 6
|
|
248
425
|
self.kT_initial_state = True
|
|
@@ -267,9 +444,18 @@ class Analysis(object):
|
|
|
267
444
|
# outputs to be filled
|
|
268
445
|
self.increments = None
|
|
269
446
|
self.cs = None
|
|
447
|
+
self.num_negative_eigvals = None
|
|
448
|
+
self.det_signs = None
|
|
449
|
+
self.num_perturbed_pivots = None
|
|
450
|
+
self.branch_points = None
|
|
451
|
+
self.path_state = None
|
|
270
452
|
|
|
271
453
|
# flag telling the last analysis
|
|
272
454
|
self.last_analysis = ''
|
|
455
|
+
# pattern-dependent work of the factorizations of a running analysis
|
|
456
|
+
self._factorization_cache = None
|
|
457
|
+
# scaling of the counts of inertia_num_eigvals of a running analysis
|
|
458
|
+
self._inertia_scaling = None
|
|
273
459
|
|
|
274
460
|
|
|
275
461
|
def static(self, NLgeom=False, silent=False):
|
|
@@ -310,7 +496,9 @@ class Analysis(object):
|
|
|
310
496
|
------
|
|
311
497
|
ValueError
|
|
312
498
|
When ``NLgeom=True`` and the ``NL_method`` attribute is not
|
|
313
|
-
``'NR'``, ``'arc_length_riks'
|
|
499
|
+
``'NR'``, ``'arc_length_riks'``, ``'arc_length_crisfield'``,
|
|
500
|
+
``'displacement_control'`` or
|
|
501
|
+
``'generalized_displacement_control'``.
|
|
314
502
|
|
|
315
503
|
Notes
|
|
316
504
|
-----
|
|
@@ -342,25 +530,46 @@ class Analysis(object):
|
|
|
342
530
|
"""
|
|
343
531
|
self.increments = []
|
|
344
532
|
self.cs = []
|
|
533
|
+
self.num_negative_eigvals = []
|
|
534
|
+
self.det_signs = []
|
|
535
|
+
self.num_perturbed_pivots = []
|
|
536
|
+
self.branch_points = []
|
|
537
|
+
self.path_state = None
|
|
345
538
|
|
|
346
539
|
if NLgeom:
|
|
347
540
|
self.maxInc = max(self.initialInc, self.maxInc)
|
|
348
541
|
msg('Started Non-Linear Static Analysis', silent=silent)
|
|
349
|
-
|
|
350
|
-
_solver_NR
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
542
|
+
solvers = {
|
|
543
|
+
'NR': _solver_NR,
|
|
544
|
+
'arc_length_riks': _solver_arc_length_riks,
|
|
545
|
+
'arc_length_crisfield': _solver_arc_length_crisfield,
|
|
546
|
+
'displacement_control': _solver_displacement_control,
|
|
547
|
+
'generalized_displacement_control':
|
|
548
|
+
_solver_generalized_displacement_control}
|
|
549
|
+
if self.NL_method not in solvers:
|
|
356
550
|
raise ValueError('{0} is an invalid NL_method'.format(self.NL_method))
|
|
551
|
+
# the work of the factorizations that depends only on the
|
|
552
|
+
# pattern of the tangent stiffness matrix is reused
|
|
553
|
+
self._factorization_cache = _FactorizationCache()
|
|
554
|
+
self._inertia_scaling = None
|
|
555
|
+
try:
|
|
556
|
+
solvers[self.NL_method](self, silent=silent)
|
|
557
|
+
finally:
|
|
558
|
+
self._factorization_cache.close()
|
|
559
|
+
self._factorization_cache = None
|
|
560
|
+
self._inertia_scaling = None
|
|
357
561
|
|
|
358
562
|
else:
|
|
359
563
|
msg('Started Linear Static Analysis', silent=silent)
|
|
360
564
|
fext = self.calc_fext(silent=silent)
|
|
361
565
|
k0 = self.calc_kC(silent=silent)
|
|
362
566
|
|
|
363
|
-
|
|
567
|
+
d = _constraint_values(self)
|
|
568
|
+
if self.constraints is None:
|
|
569
|
+
c = solve(k0, fext, silent=silent)
|
|
570
|
+
else:
|
|
571
|
+
c = _factorize(k0, constraints=_constraint_matrix(self))(fext,
|
|
572
|
+
d)
|
|
364
573
|
if callbacks.is_load_configuration_dependent(self):
|
|
365
574
|
c = self._linear_static_follower(k0, c, silent)
|
|
366
575
|
|
|
@@ -380,12 +589,15 @@ class Analysis(object):
|
|
|
380
589
|
level=2, silent=silent)
|
|
381
590
|
w = _weights(self, k0)
|
|
382
591
|
k0_abs = _abs_matrix(k0)
|
|
592
|
+
proj = _residual_projector(self, w)
|
|
383
593
|
# k0 is factorized once for all the corrections
|
|
384
594
|
solve_k0 = None
|
|
385
595
|
for iteration in range(1, self.maxNumIter + 1):
|
|
386
596
|
fext = self.calc_fext(inc=1., c=c, silent=True)
|
|
387
597
|
fint = k0 @ c
|
|
388
598
|
R = fint - fext
|
|
599
|
+
if proj is not None:
|
|
600
|
+
R = proj(R)
|
|
389
601
|
conv, Rnorm, Rrel, roundoff = _check_convergence(self, R, fint,
|
|
390
602
|
fext, w=w, Kc_abs=k0_abs @ np.abs(c), full_output=True)
|
|
391
603
|
msg('Iteration: %d, %s = %1.3e%s' % (iteration, 'zeta' if
|
|
@@ -398,7 +610,8 @@ class Analysis(object):
|
|
|
398
610
|
break
|
|
399
611
|
if solve_k0 is None:
|
|
400
612
|
try:
|
|
401
|
-
solve_k0 = _factorize(k0
|
|
613
|
+
solve_k0 = _factorize(k0,
|
|
614
|
+
constraints=_constraint_matrix(self))
|
|
402
615
|
except RuntimeError:
|
|
403
616
|
break
|
|
404
617
|
c = c + solve_k0(-R)
|