structsolve 0.3.0__tar.gz → 0.3.1__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.
Files changed (26) hide show
  1. {structsolve-0.3.0 → structsolve-0.3.1}/PKG-INFO +2 -1
  2. {structsolve-0.3.0 → structsolve-0.3.1}/pyproject.toml +2 -1
  3. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve/freq.py +35 -23
  4. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve/linear_buckling.py +31 -24
  5. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve.egg-info/PKG-INFO +2 -1
  6. {structsolve-0.3.0 → structsolve-0.3.1}/LICENSE +0 -0
  7. {structsolve-0.3.0 → structsolve-0.3.1}/README.md +0 -0
  8. {structsolve-0.3.0 → structsolve-0.3.1}/setup.cfg +0 -0
  9. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve/__init__.py +0 -0
  10. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve/analysis.py +0 -0
  11. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve/arc_length_crisfield.py +0 -0
  12. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve/arc_length_riks.py +0 -0
  13. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve/logger.py +0 -0
  14. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve/newton_raphson.py +0 -0
  15. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve/sparseutils.py +0 -0
  16. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve/static.py +0 -0
  17. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve.egg-info/SOURCES.txt +0 -0
  18. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve.egg-info/dependency_links.txt +0 -0
  19. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve.egg-info/requires.txt +0 -0
  20. {structsolve-0.3.0 → structsolve-0.3.1}/structsolve.egg-info/top_level.txt +0 -0
  21. {structsolve-0.3.0 → structsolve-0.3.1}/tests/test_analysis.py +0 -0
  22. {structsolve-0.3.0 → structsolve-0.3.1}/tests/test_freq.py +0 -0
  23. {structsolve-0.3.0 → structsolve-0.3.1}/tests/test_linear_buckling.py +0 -0
  24. {structsolve-0.3.0 → structsolve-0.3.1}/tests/test_sparseutils.py +0 -0
  25. {structsolve-0.3.0 → structsolve-0.3.1}/tests/test_static.py +0 -0
  26. {structsolve-0.3.0 → structsolve-0.3.1}/tests/test_static_deflection.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: structsolve
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Structural analysis solvers
5
5
  Author-email: "Saullo G. P. Castro" <castrosaullo@gmail.com>
6
6
  License: BSD-2-Clause
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
21
22
  Classifier: Operating System :: Microsoft :: Windows
22
23
  Classifier: Operating System :: Unix
23
24
  Requires-Python: >=3.8
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "structsolve"
7
- version = "0.3.0"
7
+ version = "0.3.1"
8
8
  description = "Structural analysis solvers"
9
9
  readme = "README.md"
10
10
  license = {text = "BSD-2-Clause"}
@@ -28,6 +28,7 @@ classifiers = [
28
28
  "Programming Language :: Python :: 3.11",
29
29
  "Programming Language :: Python :: 3.12",
30
30
  "Programming Language :: Python :: 3.13",
31
+ "Programming Language :: Python :: 3.14",
31
32
  "Operating System :: Microsoft :: Windows",
32
33
  "Operating System :: Unix",
33
34
  ]
@@ -1,3 +1,5 @@
1
+ import warnings
2
+
1
3
  import numpy as np
2
4
  import scipy
3
5
  from scipy.sparse.linalg import eigs, spsolve
@@ -9,8 +11,16 @@ from .sparseutils import remove_null_cols
9
11
 
10
12
  def _estimate_sigma(K, M):
11
13
  try:
12
- x = np.random.RandomState(42).randn(K.shape[0])
13
- y = spsolve(K, M @ x)
14
+ rhs = M @ np.random.RandomState(42).randn(K.shape[0])
15
+ with warnings.catch_warnings(record=True) as caught:
16
+ warnings.simplefilter("always")
17
+ y = spsolve(K, rhs)
18
+ if any(issubclass(w.category, (RuntimeWarning, Warning))
19
+ and "singular" in str(w.message).lower() for w in caught):
20
+ return -1.
21
+ residual = np.linalg.norm(K @ y - rhs)
22
+ if residual > 1e-6 * np.linalg.norm(rhs):
23
+ return -1.
14
24
  sigma = -abs((y @ K @ y) / (y @ M @ y))
15
25
  if not np.isfinite(sigma) or sigma == 0:
16
26
  return -1.
@@ -21,7 +31,7 @@ def _estimate_sigma(K, M):
21
31
 
22
32
  def freq(K, M, tol=0, sparse_solver=True,
23
33
  silent=False, sort=True, num_eigvalues=25,
24
- num_eigvalues_print=5):
34
+ num_eigvalues_print=5, skip_null_cols=False):
25
35
  """Frequency Analysis
26
36
 
27
37
  Calculate the eigenvalues (`\lambda^2`) and mass-normalized eigenvectors
@@ -53,6 +63,9 @@ def freq(K, M, tol=0, sparse_solver=True,
53
63
  Number of calculated eigenvalues.
54
64
  num_eigvalues_print : int, optional
55
65
  Number of eigenvalues to print.
66
+ skip_null_cols : bool, optional
67
+ If True, skip the removal of null columns from the matrices.
68
+ Use only when K is known to be non-singular.
56
69
 
57
70
  Returns
58
71
  -------
@@ -67,10 +80,14 @@ def freq(K, M, tol=0, sparse_solver=True,
67
80
  msg('Eigenvalue solver... ', level=2, silent=silent)
68
81
 
69
82
  k = min(num_eigvalues, M.shape[0]-2)
70
- if sparse_solver:
71
- sizebkp = M.shape[0]
83
+ size = M.shape[0]
84
+ if skip_null_cols:
85
+ used_cols = None
86
+ Keff, Meff = K, M
87
+ else:
72
88
  Keff, Meff, used_cols = remove_null_cols(K, M, silent=silent,
73
89
  level=3)
90
+ if sparse_solver:
74
91
  #NOTE Looking for better performance with symmetric matrices, I tried
75
92
  # using sparseutils.sparse.is_symmetric and eigsh, but it seems not
76
93
  # to improve speed (I did not try passing only half of the sparse
@@ -82,35 +99,30 @@ def freq(K, M, tol=0, sparse_solver=True,
82
99
  #NOTE eigs solves: [K] {u} = eigval [M] {u}
83
100
  # therefore we must correct he sign of lambda^2 here:
84
101
  lambda2 = -eigvals
85
- eigvecs = np.zeros((sizebkp, k), dtype=peigvecs.dtype)
86
- eigvecs[used_cols, :] = peigvecs
87
102
  else:
88
- msg('eig() solver...', level=3, silent=silent)
89
- if isinstance(M, scipy.sparse.spmatrix):
90
- Meff = M.toarray()
103
+ if isinstance(Meff, scipy.sparse.spmatrix):
104
+ Meff = Meff.toarray()
91
105
  else:
92
- Meff = np.asarray(M)
93
- if isinstance(K, scipy.sparse.spmatrix):
94
- Keff = K.toarray()
106
+ Meff = np.asarray(Meff)
107
+ if isinstance(Keff, scipy.sparse.spmatrix):
108
+ Keff = Keff.toarray()
95
109
  else:
96
- Keff = np.asarray(K)
97
- sizebkp = Meff.shape[0]
98
- col_sum = Meff.sum(axis=0)
99
- check = col_sum != 0
100
- used_cols = np.arange(Meff.shape[0])[check]
101
- Meff = Meff[:, check][check, :]
102
- Keff = Keff[:, check][check, :]
110
+ Keff = np.asarray(Keff)
103
111
 
104
112
  #TODO did not try using eigh when input is symmetric to see if there
105
113
  # will be speed improvements
106
114
  # for effiency reasons, solving:
107
115
  # [M]{u} = (-1/lambda2)[K]{u}
108
116
  # [M]{u} = eigval [K]{u}
117
+ msg('eig() solver...', level=3, silent=silent)
109
118
  eigvals, peigvecs = eig(a=Meff, b=Keff)
110
119
  lambda2 = -1./eigvals
111
- eigvecs = np.zeros((sizebkp, Keff.shape[0]),
112
- dtype=peigvecs.dtype)
113
- eigvecs[check, :] = peigvecs
120
+
121
+ if used_cols is not None:
122
+ eigvecs = np.zeros((size, peigvecs.shape[1]), dtype=peigvecs.dtype)
123
+ eigvecs[used_cols, :] = peigvecs
124
+ else:
125
+ eigvecs = peigvecs
114
126
 
115
127
  msg('finished!', level=3, silent=silent)
116
128
 
@@ -1,3 +1,5 @@
1
+ import warnings
2
+
1
3
  import numpy as np
2
4
  from scipy.sparse.linalg import eigsh, spsolve
3
5
  from scipy.linalg import eigh
@@ -8,8 +10,16 @@ from .sparseutils import remove_null_cols
8
10
 
9
11
  def _estimate_sigma(K, KG):
10
12
  try:
11
- x = np.random.RandomState(42).randn(K.shape[0])
12
- y = spsolve(K, KG @ x)
13
+ rhs = KG @ np.random.RandomState(42).randn(K.shape[0])
14
+ with warnings.catch_warnings(record=True) as caught:
15
+ warnings.simplefilter("always")
16
+ y = spsolve(K, rhs)
17
+ if any(issubclass(w.category, (RuntimeWarning, Warning))
18
+ and "singular" in str(w.message).lower() for w in caught):
19
+ return 1.
20
+ residual = np.linalg.norm(K @ y - rhs)
21
+ if residual > 1e-6 * np.linalg.norm(rhs):
22
+ return 1.
13
23
  sigma = abs((y @ KG @ y) / (y @ K @ y))
14
24
  if not np.isfinite(sigma) or sigma == 0:
15
25
  return 1.
@@ -18,7 +28,8 @@ def _estimate_sigma(K, KG):
18
28
  return 1.
19
29
 
20
30
  def lb(K, KG, tol=0, sparse_solver=True, silent=False,
21
- num_eigvalues=25, num_eigvalues_print=5):
31
+ num_eigvalues=25, num_eigvalues_print=5,
32
+ skip_null_cols=False):
22
33
  """Linear Buckling Analysis
23
34
 
24
35
  It can also be used for more general eigenvalue analyzes if `K` is the
@@ -44,6 +55,9 @@ def lb(K, KG, tol=0, sparse_solver=True, silent=False,
44
55
  Number of calculated eigenvalues.
45
56
  num_eigvalues_print : int, optional
46
57
  Number of eigenvalues to print.
58
+ skip_null_cols : bool, optional
59
+ If True, skip the removal of null columns from the matrices.
60
+ Use only when K is known to be non-singular.
47
61
 
48
62
  Notes
49
63
  -----
@@ -57,38 +71,31 @@ def lb(K, KG, tol=0, sparse_solver=True, silent=False,
57
71
  msg('Eigenvalue solver... ', level=2, silent=silent)
58
72
 
59
73
  k = min(num_eigvalues, KG.shape[0]-2)
74
+ size = KG.shape[0]
75
+ if skip_null_cols:
76
+ used_cols = None
77
+ else:
78
+ K, KG, used_cols = remove_null_cols(K, KG, silent=silent)
60
79
  if sparse_solver:
61
80
  mode = 'cayley'
62
- try:
63
- sigma = _estimate_sigma(K, KG)
64
- msg('eigsh() solver (sigma={0})...'.format(sigma), level=3, silent=silent)
65
- eigvals, eigvecs = eigsh(A=KG, k=k,
66
- which='SM', M=K, tol=tol, sigma=sigma, mode=mode)
67
- msg('finished!', level=3, silent=silent)
68
- except Exception as e:
69
- warn(str(e), level=4, silent=silent)
70
- msg('aborted!', level=3, silent=silent)
71
- sizebkp = KG.shape[0]
72
- K, KG, used_cols = remove_null_cols(K, KG, silent=silent)
73
- sigma = _estimate_sigma(K, KG)
74
- msg('eigsh() solver (sigma={0})...'.format(sigma), level=3, silent=silent)
75
- eigvals, peigvecs = eigsh(A=KG, k=k,
76
- which='SM', M=K, tol=tol, sigma=sigma, mode=mode)
77
- msg('finished!', level=3, silent=silent)
78
- eigvecs = np.zeros((sizebkp, num_eigvalues),
79
- dtype=peigvecs.dtype)
80
- eigvecs[used_cols, :] = peigvecs
81
+ sigma = _estimate_sigma(K, KG)
82
+ msg('eigsh() solver (sigma={0})...'.format(sigma), level=3, silent=silent)
83
+ eigvals, peigvecs = eigsh(A=KG, k=k,
84
+ which='SM', M=K, tol=tol, sigma=sigma, mode=mode)
85
+ msg('finished!', level=3, silent=silent)
81
86
 
82
87
  else:
83
- size = KG.shape[0]
84
- K, KG, used_cols = remove_null_cols(K, KG, silent=silent)
85
88
  K = K.toarray()
86
89
  KG = KG.toarray()
87
90
  msg('eigh() solver...', level=3, silent=silent)
88
91
  eigvals, peigvecs = eigh(a=KG, b=K)
89
92
  msg('finished!', level=3, silent=silent)
93
+
94
+ if used_cols is not None:
90
95
  eigvecs = np.zeros((size, num_eigvalues), dtype=peigvecs.dtype)
91
96
  eigvecs[used_cols, :] = peigvecs[:, :num_eigvalues]
97
+ else:
98
+ eigvecs = peigvecs[:, :num_eigvalues]
92
99
 
93
100
  eigvals = -1./eigvals
94
101
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: structsolve
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Structural analysis solvers
5
5
  Author-email: "Saullo G. P. Castro" <castrosaullo@gmail.com>
6
6
  License: BSD-2-Clause
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
21
22
  Classifier: Operating System :: Microsoft :: Windows
22
23
  Classifier: Operating System :: Unix
23
24
  Requires-Python: >=3.8
File without changes
File without changes
File without changes