wawi 0.0.16__tar.gz → 0.0.18__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.
- {wawi-0.0.16 → wawi-0.0.18}/PKG-INFO +42 -4
- {wawi-0.0.16 → wawi-0.0.18}/README.md +41 -3
- {wawi-0.0.16 → wawi-0.0.18}/wawi/__init__.py +1 -1
- wawi-0.0.18/wawi/fe.py +381 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/general.py +538 -70
- {wawi-0.0.16 → wawi-0.0.18}/wawi/identification.py +33 -11
- {wawi-0.0.16 → wawi-0.0.18}/wawi/io.py +206 -3
- {wawi-0.0.16 → wawi-0.0.18}/wawi/modal.py +384 -6
- {wawi-0.0.16 → wawi-0.0.18}/wawi/model/_model.py +1 -1
- wawi-0.0.18/wawi/plot.py +868 -0
- wawi-0.0.18/wawi/prob.py +37 -0
- wawi-0.0.18/wawi/random.py +254 -0
- wawi-0.0.18/wawi/signal.py +153 -0
- wawi-0.0.18/wawi/structural.py +536 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/time_domain.py +122 -1
- wawi-0.0.18/wawi/tools.py +30 -0
- wawi-0.0.18/wawi/wave.py +1035 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/wind.py +775 -32
- wawi-0.0.18/wawi/wind_code.py +40 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi.egg-info/PKG-INFO +42 -4
- wawi-0.0.16/wawi/fe.py +0 -134
- wawi-0.0.16/wawi/plot.py +0 -572
- wawi-0.0.16/wawi/prob.py +0 -9
- wawi-0.0.16/wawi/random.py +0 -38
- wawi-0.0.16/wawi/signal.py +0 -45
- wawi-0.0.16/wawi/structural.py +0 -278
- wawi-0.0.16/wawi/tools.py +0 -7
- wawi-0.0.16/wawi/wave.py +0 -490
- wawi-0.0.16/wawi/wind_code.py +0 -14
- {wawi-0.0.16 → wawi-0.0.18}/LICENSE +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/examples/3 Software interfacing/Abaqus model export/bergsoysund-export.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/examples/3 Software interfacing/Abaqus model export/hardanger-export.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/pyproject.toml +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/setup.cfg +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/tests/test_IABSE_step11a.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/tests/test_IABSE_step11c.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/tests/test_IABSE_step2a.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/tests/test_wind.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/ext/__init__.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/ext/abq.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/ext/ansys.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/ext/orcaflex.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/ext/sofistik.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/model/__init__.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/model/_aero.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/model/_dry.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/model/_hydro.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi/model/_screening.py +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi.egg-info/SOURCES.txt +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi.egg-info/dependency_links.txt +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi.egg-info/requires.txt +0 -0
- {wawi-0.0.16 → wawi-0.0.18}/wawi.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: wawi
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.18
|
4
4
|
Summary: WAve and WInd response prediction
|
5
5
|
Author-email: "Knut A. Kvåle" <knut.a.kvale@ntnu.no>, Ole Øiseth <ole.oiseth@ntnu.no>, Aksel Fenerci <aksel.fenerci@ntnu.no>, Øivind Wiig Petersen <oyvind.w.petersen@ntnu.no>
|
6
6
|
License: MIT License
|
@@ -92,17 +92,51 @@ pip install git+https://www.github.com/knutankv/wawi.git@main
|
|
92
92
|
How does WAWI work?
|
93
93
|
======================
|
94
94
|
By representing both aerodynamic and hydrodynamic motion-induced forces and excitation using a coordinate basis defined by the dry in-vacuum mode shapes of the structure, WAWI is able to versatily predict response based on input from any commercial FE software. The main structure used for response prediction is given in this figure:
|
95
|
+
|
95
96
|

|
96
97
|
|
97
98
|
The object structure of a WAWI model is given here:
|
98
|
-
|
99
|
+
|
100
|
+

|
99
101
|
|
100
102
|
Further details regarding hydrodynamic definitions initiated by the `Hydro` class is given below:
|
103
|
+
|
101
104
|

|
102
105
|
|
103
106
|
Further details regarding aerodynamic definitions initiated by the `Aero` class is given below:
|
107
|
+
|
104
108
|

|
105
109
|
|
110
|
+
Wave conditions
|
111
|
+
----------------------
|
112
|
+
The wave field definition is based on the assumption that the two-dimensional wave spectral density can be decomposed into a directional distribution and a one-dimensional wave spectral density, i.e., $S_\eta(\omega,\theta) = D(\theta) S(\omega)$. The two factors are defined using these well-known formulations:
|
113
|
+
|
114
|
+
* $S(\omega)$: JONSWAP spectrum (see [Hasselmann et al., 1973](https://pure.mpg.de/pubman/faces/ViewItemOverviewPage.jsp?itemId=item_3262854))
|
115
|
+
* $D(\theta)$: cos-2s directional distribution (see Longuet-Higgins et al., 1963)
|
116
|
+
|
117
|
+
Currents are defined by a homogeneous current speed `U` and corresponding direction `thetaU`.
|
118
|
+
|
119
|
+
An example of a two-dimensional wave spectral density based on (arbitrarily chosen) parameters $H_s = 2.1$ m, $T_p = 2.1$ s, $\gamma = 4.0$, $s = 10$ and $\theta_0 = 75^\circ$ is shown in this plot:
|
120
|
+
|
121
|
+

|
122
|
+
|
123
|
+
It is noted that you can easily assign custom functions of the `S` and `D` of the seastate (or customly on all pontoons for full control) instead of relying on the built in JONSWAP and cos-2s definitions.
|
124
|
+
|
125
|
+
Wind conditions
|
126
|
+
----------------------
|
127
|
+
The wind field is defined by single-point turbulence wind spectra (for all turbulence components $u$, $v$ and $w$) and coherence definitions.
|
128
|
+
|
129
|
+
By default, wind spectra can be defined using these two definitions:
|
130
|
+
|
131
|
+
* Kaimal spectrum defined by length scale parameters ($L^x_u$, $L^x_v$, $L^x_w$), spectral shape parameters ($A_u$, $A_v$ and $A_w$) and turbulence intensities ($I_u$, $I_v$ and $I_w$); see [Kaimal et al., 1972](https://www.climatexchange.nl/projects/alteddy/papers/Kaimal-1972.pdf)
|
132
|
+
* von Karmán spectrum defined by only length scale parameters ($L^x_u$, $L^x_v$, $L^x_w$) and turbulence intensities ($I_u=\sigma_u/U$, $I_v=\sigma_v/U$ and $I_w=\sigma_w/U$); see [von Kármán, 1948](http://dx.doi.org/10.1073/pnas.34.11.530)
|
133
|
+
|
134
|
+
Furthermore, the coherence of the wind field is defined by the nine decay parameters $C_{ux}$, $C_{vx}$, $C_{wx}$, $C_{uy}$, $C_{vy}$, $C_{wy}$, $C_{uz}$ $C_{vz}$, $C_{wz}$; see e.g. [Simiu and Scanlan, 1996](https://library.wur.nl/WebQuery/titel/1606468) for details.
|
135
|
+
|
136
|
+
An example of turbulence spectral densities based on (arbitrarily chosen) parameters $I_u=0.136$, $I_v=0.0$, $I_w=0.072$, $L^x_u=115$, $L^x_w=9.58$, $A_u=6.8$ (only relevant for Kaimal-type) and $A_w=9.4$ (only relevant for Kaimal-type) is given below:
|
137
|
+
|
138
|
+

|
139
|
+
|
106
140
|
|
107
141
|
Quick start
|
108
142
|
=======================
|
@@ -181,16 +215,20 @@ References
|
|
181
215
|
=======================
|
182
216
|
The following papers provide background for the implementation:
|
183
217
|
|
184
|
-
* Beam (FE) description of aerodynamic forces: [Øiseth et al. (2012)](https://www.sciencedirect.com/science/article/abs/pii/S0168874X11001880)
|
185
218
|
* Wave modelling and response prediction: [Kvåle et al. (2016)](https://www.sciencedirect.com/science/article/abs/pii/S004579491500334X)
|
186
219
|
* Inhomogeneous wave modelling: [Kvåle et al. (2024)](https://www.sciencedirect.com/science/article/pii/S0141118723003437)
|
187
220
|
* Hydrodynamic interaction effects: [Fenerci et al. (2022)](https://www.sciencedirect.com/science/article/pii/S095183392200017X)
|
221
|
+
* Beam (FE) description of aerodynamic forces: [Øiseth et al. (2012)](https://www.sciencedirect.com/science/article/abs/pii/S0168874X11001880)
|
188
222
|
* Wave-current interaction: [Fredriksen et al. (2024)](https://www.researchgate.net/profile/Arnt-Fredriksen/publication/386453916_On_the_wave-current_interaction_effect_on_linear_motion_for_floating_bridges/links/6751a40fabddbb448c65cbef/On-the-wave-current-interaction-effect-on-linear-motion-for-floating-bridges.pdf)
|
189
223
|
|
190
224
|
|
191
225
|
Citation
|
192
226
|
=======================
|
193
|
-
|
227
|
+
Please cite the use of this software as follows:
|
228
|
+
|
229
|
+
Kvåle, K. A., Fenerci, A., Petersen, Ø. W., & Øiseth, O. A. (2025). WAWI. Zenodo. https://doi.org/10.5281/zenodo.15482552
|
230
|
+
[](https://doi.org/10.5281/zenodo.14895014)
|
231
|
+
|
194
232
|
|
195
233
|
Support
|
196
234
|
=======================
|
@@ -45,17 +45,51 @@ pip install git+https://www.github.com/knutankv/wawi.git@main
|
|
45
45
|
How does WAWI work?
|
46
46
|
======================
|
47
47
|
By representing both aerodynamic and hydrodynamic motion-induced forces and excitation using a coordinate basis defined by the dry in-vacuum mode shapes of the structure, WAWI is able to versatily predict response based on input from any commercial FE software. The main structure used for response prediction is given in this figure:
|
48
|
+
|
48
49
|

|
49
50
|
|
50
51
|
The object structure of a WAWI model is given here:
|
51
|
-
|
52
|
+
|
53
|
+

|
52
54
|
|
53
55
|
Further details regarding hydrodynamic definitions initiated by the `Hydro` class is given below:
|
56
|
+
|
54
57
|

|
55
58
|
|
56
59
|
Further details regarding aerodynamic definitions initiated by the `Aero` class is given below:
|
60
|
+
|
57
61
|

|
58
62
|
|
63
|
+
Wave conditions
|
64
|
+
----------------------
|
65
|
+
The wave field definition is based on the assumption that the two-dimensional wave spectral density can be decomposed into a directional distribution and a one-dimensional wave spectral density, i.e., $S_\eta(\omega,\theta) = D(\theta) S(\omega)$. The two factors are defined using these well-known formulations:
|
66
|
+
|
67
|
+
* $S(\omega)$: JONSWAP spectrum (see [Hasselmann et al., 1973](https://pure.mpg.de/pubman/faces/ViewItemOverviewPage.jsp?itemId=item_3262854))
|
68
|
+
* $D(\theta)$: cos-2s directional distribution (see Longuet-Higgins et al., 1963)
|
69
|
+
|
70
|
+
Currents are defined by a homogeneous current speed `U` and corresponding direction `thetaU`.
|
71
|
+
|
72
|
+
An example of a two-dimensional wave spectral density based on (arbitrarily chosen) parameters $H_s = 2.1$ m, $T_p = 2.1$ s, $\gamma = 4.0$, $s = 10$ and $\theta_0 = 75^\circ$ is shown in this plot:
|
73
|
+
|
74
|
+

|
75
|
+
|
76
|
+
It is noted that you can easily assign custom functions of the `S` and `D` of the seastate (or customly on all pontoons for full control) instead of relying on the built in JONSWAP and cos-2s definitions.
|
77
|
+
|
78
|
+
Wind conditions
|
79
|
+
----------------------
|
80
|
+
The wind field is defined by single-point turbulence wind spectra (for all turbulence components $u$, $v$ and $w$) and coherence definitions.
|
81
|
+
|
82
|
+
By default, wind spectra can be defined using these two definitions:
|
83
|
+
|
84
|
+
* Kaimal spectrum defined by length scale parameters ($L^x_u$, $L^x_v$, $L^x_w$), spectral shape parameters ($A_u$, $A_v$ and $A_w$) and turbulence intensities ($I_u$, $I_v$ and $I_w$); see [Kaimal et al., 1972](https://www.climatexchange.nl/projects/alteddy/papers/Kaimal-1972.pdf)
|
85
|
+
* von Karmán spectrum defined by only length scale parameters ($L^x_u$, $L^x_v$, $L^x_w$) and turbulence intensities ($I_u=\sigma_u/U$, $I_v=\sigma_v/U$ and $I_w=\sigma_w/U$); see [von Kármán, 1948](http://dx.doi.org/10.1073/pnas.34.11.530)
|
86
|
+
|
87
|
+
Furthermore, the coherence of the wind field is defined by the nine decay parameters $C_{ux}$, $C_{vx}$, $C_{wx}$, $C_{uy}$, $C_{vy}$, $C_{wy}$, $C_{uz}$ $C_{vz}$, $C_{wz}$; see e.g. [Simiu and Scanlan, 1996](https://library.wur.nl/WebQuery/titel/1606468) for details.
|
88
|
+
|
89
|
+
An example of turbulence spectral densities based on (arbitrarily chosen) parameters $I_u=0.136$, $I_v=0.0$, $I_w=0.072$, $L^x_u=115$, $L^x_w=9.58$, $A_u=6.8$ (only relevant for Kaimal-type) and $A_w=9.4$ (only relevant for Kaimal-type) is given below:
|
90
|
+
|
91
|
+

|
92
|
+
|
59
93
|
|
60
94
|
Quick start
|
61
95
|
=======================
|
@@ -134,16 +168,20 @@ References
|
|
134
168
|
=======================
|
135
169
|
The following papers provide background for the implementation:
|
136
170
|
|
137
|
-
* Beam (FE) description of aerodynamic forces: [Øiseth et al. (2012)](https://www.sciencedirect.com/science/article/abs/pii/S0168874X11001880)
|
138
171
|
* Wave modelling and response prediction: [Kvåle et al. (2016)](https://www.sciencedirect.com/science/article/abs/pii/S004579491500334X)
|
139
172
|
* Inhomogeneous wave modelling: [Kvåle et al. (2024)](https://www.sciencedirect.com/science/article/pii/S0141118723003437)
|
140
173
|
* Hydrodynamic interaction effects: [Fenerci et al. (2022)](https://www.sciencedirect.com/science/article/pii/S095183392200017X)
|
174
|
+
* Beam (FE) description of aerodynamic forces: [Øiseth et al. (2012)](https://www.sciencedirect.com/science/article/abs/pii/S0168874X11001880)
|
141
175
|
* Wave-current interaction: [Fredriksen et al. (2024)](https://www.researchgate.net/profile/Arnt-Fredriksen/publication/386453916_On_the_wave-current_interaction_effect_on_linear_motion_for_floating_bridges/links/6751a40fabddbb448c65cbef/On-the-wave-current-interaction-effect-on-linear-motion-for-floating-bridges.pdf)
|
142
176
|
|
143
177
|
|
144
178
|
Citation
|
145
179
|
=======================
|
146
|
-
|
180
|
+
Please cite the use of this software as follows:
|
181
|
+
|
182
|
+
Kvåle, K. A., Fenerci, A., Petersen, Ø. W., & Øiseth, O. A. (2025). WAWI. Zenodo. https://doi.org/10.5281/zenodo.15482552
|
183
|
+
[](https://doi.org/10.5281/zenodo.14895014)
|
184
|
+
|
147
185
|
|
148
186
|
Support
|
149
187
|
=======================
|
wawi-0.0.18/wawi/fe.py
ADDED
@@ -0,0 +1,381 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
import numpy as np
|
3
|
+
from .general import blkdiag, transform_unit
|
4
|
+
|
5
|
+
'''
|
6
|
+
FE-related tools.
|
7
|
+
'''
|
8
|
+
|
9
|
+
def intpoints_from_elements(nodes, elements, sort_axis=0):
|
10
|
+
"""
|
11
|
+
Calculates the integration points (midpoints) for each element based on node coordinates.
|
12
|
+
|
13
|
+
Parameters
|
14
|
+
----------
|
15
|
+
nodes : np.ndarray
|
16
|
+
Array of node coordinates with shape (n_nodes, 4), where columns represent node index and x, y, z coordinates.
|
17
|
+
elements : np.ndarray
|
18
|
+
Array of element connectivity with shape (n_elements, 2), where each row contains indices of the two nodes forming an element.
|
19
|
+
sort_axis : int, optional
|
20
|
+
Axis (0 for x, 1 for y, 2 for z) to sort the integration points by. Default is 0 (x-axis).
|
21
|
+
|
22
|
+
Returns
|
23
|
+
-------
|
24
|
+
x : np.ndarray
|
25
|
+
Array of x-coordinates of the integration points, sorted by the specified axis.
|
26
|
+
y : np.ndarray
|
27
|
+
Array of y-coordinates of the integration points, sorted by the specified axis.
|
28
|
+
z : np.ndarray
|
29
|
+
Array of z-coordinates of the integration points, sorted by the specified axis.
|
30
|
+
|
31
|
+
Notes
|
32
|
+
-----
|
33
|
+
Assumes that `nodeix_from_elements` is a function that returns the indices of the nodes for each element. Docstring is generated by Github Copilot.
|
34
|
+
"""
|
35
|
+
|
36
|
+
nodeix = nodeix_from_elements(elements, nodes).astype('int')
|
37
|
+
|
38
|
+
intpoints = (nodes[nodeix[:,0], 1:4]+nodes[nodeix[:,1], 1:4])/2
|
39
|
+
sortix = np.argsort(intpoints[:,sort_axis])
|
40
|
+
intpoints = intpoints[sortix, :]
|
41
|
+
|
42
|
+
x = intpoints[:, 0]
|
43
|
+
y = intpoints[:, 1]
|
44
|
+
z = intpoints[:, 2]
|
45
|
+
|
46
|
+
return x, y, z
|
47
|
+
|
48
|
+
|
49
|
+
def nodeix_from_elements(element_matrix, node_matrix, return_as_flat=False):
|
50
|
+
"""
|
51
|
+
Map element node labels to their corresponding indices in the node matrix.
|
52
|
+
|
53
|
+
Parameters
|
54
|
+
----------
|
55
|
+
element_matrix : np.ndarray
|
56
|
+
Array of elements with shape (n_elements, m), where columns 1 and 2 contain node IDs for each element.
|
57
|
+
node_matrix : np.ndarray
|
58
|
+
Array of nodes with shape (n_nodes, k), where column 0 contains node IDs.
|
59
|
+
return_as_flat : bool, optional
|
60
|
+
If True, returns a 1D array of unique node indices used by all elements.
|
61
|
+
If False, returns a 2D array of shape (n_elements, 2) with node indices for each element.
|
62
|
+
Default is False.
|
63
|
+
|
64
|
+
Returns
|
65
|
+
-------
|
66
|
+
np.ndarray
|
67
|
+
If return_as_flat is False, returns a 2D array of node indices for each element (shape: n_elements, 2).
|
68
|
+
If return_as_flat is True, returns a 1D array of unique node indices.
|
69
|
+
|
70
|
+
Notes
|
71
|
+
-----
|
72
|
+
Each element is assumed to be defined by two node labels in columns 1 and 2 of element_matrix. Docstring is generated by GitHub Copilot.
|
73
|
+
"""
|
74
|
+
nodeix = [None] * len(element_matrix[:, 0])
|
75
|
+
for element_ix, __ in enumerate(element_matrix[:, 0]):
|
76
|
+
node1 = element_matrix[element_ix, 1]
|
77
|
+
node2 = element_matrix[element_ix, 2]
|
78
|
+
|
79
|
+
nodeix1 = np.where(node_matrix[:, 0] == node1)[0][0]
|
80
|
+
nodeix2 = np.where(node_matrix[:, 0] == node2)[0][0]
|
81
|
+
nodeix[element_ix] = [nodeix1, nodeix2]
|
82
|
+
|
83
|
+
nodeix = np.array(nodeix)
|
84
|
+
|
85
|
+
if return_as_flat:
|
86
|
+
nodeix = np.unique(nodeix.flatten())
|
87
|
+
|
88
|
+
return nodeix
|
89
|
+
|
90
|
+
|
91
|
+
def create_node_dict(element_dict, node_labels, x_nodes, y_nodes, z_nodes):
|
92
|
+
"""
|
93
|
+
Creates a dictionary mapping element keys to their corresponding node data.
|
94
|
+
|
95
|
+
Parameters
|
96
|
+
----------
|
97
|
+
element_dict : dict
|
98
|
+
A dictionary where each key corresponds to an element and each value contains information
|
99
|
+
about the nodes associated with that element.
|
100
|
+
node_labels : array-like
|
101
|
+
An array of node labels/IDs.
|
102
|
+
x_nodes : array-like
|
103
|
+
An array of x-coordinates for each node.
|
104
|
+
y_nodes : array-like
|
105
|
+
An array of y-coordinates for each node.
|
106
|
+
z_nodes : array-like
|
107
|
+
An array of z-coordinates for each node.
|
108
|
+
|
109
|
+
Returns
|
110
|
+
-------
|
111
|
+
node_dict : dict
|
112
|
+
A dictionary where each key matches an element key from `element_dict`, and each value is
|
113
|
+
an array containing the node label and coordinates (label, x, y, z) for the nodes
|
114
|
+
associated with that element.
|
115
|
+
|
116
|
+
Notes
|
117
|
+
-----
|
118
|
+
This function relies on the helper function `nodeix_from_elements` to determine the indices
|
119
|
+
of nodes associated with each element. Docstring is generated by GitHub Copilot.
|
120
|
+
"""
|
121
|
+
|
122
|
+
node_dict = dict()
|
123
|
+
node_matrix = np.vstack([node_labels, x_nodes, y_nodes, z_nodes]).T
|
124
|
+
|
125
|
+
for key in element_dict.keys():
|
126
|
+
node_ix = nodeix_from_elements(element_dict[key], node_matrix, return_as_flat=True)
|
127
|
+
node_dict[key] = node_matrix[node_ix, :]
|
128
|
+
|
129
|
+
return node_dict
|
130
|
+
|
131
|
+
|
132
|
+
def elements_with_node(element_matrix, node_label):
|
133
|
+
"""
|
134
|
+
Finds elements containing a specific node and returns their labels, indices, and local node indices.
|
135
|
+
|
136
|
+
Parameters
|
137
|
+
----------
|
138
|
+
element_matrix : np.ndarray
|
139
|
+
A 2D array where each row represents an element. The first column contains element labels,
|
140
|
+
and the second and third columns contain node labels associated with each element.
|
141
|
+
node_label : int or float
|
142
|
+
The node label to search for within the element matrix.
|
143
|
+
|
144
|
+
Returns
|
145
|
+
-------
|
146
|
+
element_labels : np.ndarray
|
147
|
+
Array of element labels that contain the specified node.
|
148
|
+
element_ixs : np.ndarray
|
149
|
+
Array of indices in `element_matrix` where the specified node is found.
|
150
|
+
local_node_ix : np.ndarray
|
151
|
+
Array indicating the local node index (0 or 1) within each element where the node is found.
|
152
|
+
|
153
|
+
Examples
|
154
|
+
--------
|
155
|
+
>>> element_matrix = np.array([[1, 10, 20],
|
156
|
+
... [2, 20, 30],
|
157
|
+
... [3, 10, 30]])
|
158
|
+
>>> elements_with_node(element_matrix, 10)
|
159
|
+
(array([1, 3]), array([0, 2]), array([0., 0.]))
|
160
|
+
|
161
|
+
Notes
|
162
|
+
---------
|
163
|
+
Docstring is generated by GitHub Copilot.
|
164
|
+
"""
|
165
|
+
element_ixs1 = np.array(np.where(element_matrix[:,1]==node_label)).flatten()
|
166
|
+
element_ixs2 = np.array(np.where(element_matrix[:,2]==node_label)).flatten()
|
167
|
+
|
168
|
+
element_ixs = np.hstack([element_ixs1, element_ixs2])
|
169
|
+
element_labels = element_matrix[element_ixs, 0]
|
170
|
+
|
171
|
+
local_node_ix = np.zeros(np.shape(element_ixs))
|
172
|
+
local_node_ix[np.arange(0,len(element_ixs1))] = 0
|
173
|
+
local_node_ix[np.arange(len(element_ixs1), len(element_ixs1) + len(element_ixs2))] = 1
|
174
|
+
|
175
|
+
return element_labels, element_ixs, local_node_ix
|
176
|
+
|
177
|
+
|
178
|
+
def nodes_to_beam_element_matrix(node_labels, first_element_label=1):
|
179
|
+
"""
|
180
|
+
Generates an element connectivity matrix for beam elements from a sequence of node labels.
|
181
|
+
|
182
|
+
Parameters
|
183
|
+
----------
|
184
|
+
node_labels : array_like
|
185
|
+
Sequence of node labels (integers or floats) defining the order of nodes along the beam.
|
186
|
+
first_element_label : int, optional
|
187
|
+
The label to assign to the first element. Default is 1.
|
188
|
+
|
189
|
+
Returns
|
190
|
+
-------
|
191
|
+
element_matrix : ndarray of shape (n_elements, 3)
|
192
|
+
Array where each row represents a beam element. The columns are:
|
193
|
+
[element_label, start_node_label, end_node_label].
|
194
|
+
|
195
|
+
Examples
|
196
|
+
--------
|
197
|
+
>>> nodes = [10, 20, 30, 40]
|
198
|
+
>>> nodes_to_beam_element_matrix(nodes)
|
199
|
+
array([[ 1., 10., 20.],
|
200
|
+
[ 2., 20., 30.],
|
201
|
+
[ 3., 30., 40.]])
|
202
|
+
|
203
|
+
Notes
|
204
|
+
---------
|
205
|
+
Docstring is generated by GitHub Copilot.
|
206
|
+
"""
|
207
|
+
|
208
|
+
n_nodes = len(node_labels)
|
209
|
+
n_elements = n_nodes-1
|
210
|
+
|
211
|
+
element_matrix = np.empty([n_elements, 3])
|
212
|
+
element_matrix[:, 0] = np.arange(first_element_label,first_element_label+n_elements)
|
213
|
+
element_matrix[:, 1] = node_labels[0:-1]
|
214
|
+
element_matrix[:, 2] = node_labels[1:]
|
215
|
+
|
216
|
+
return element_matrix
|
217
|
+
|
218
|
+
|
219
|
+
def node_ix_to_dof_ix(node_ix, n_dofs=6):
|
220
|
+
"""
|
221
|
+
Converts a node index to a degree of freedom (DOF) index.
|
222
|
+
Each node has n_dofs degrees of freedom, and this function returns the corresponding DOF indices.
|
223
|
+
|
224
|
+
Parameters
|
225
|
+
----------
|
226
|
+
node_ix : int
|
227
|
+
Index of the node for which to find the DOF indices.
|
228
|
+
n_dofs : int, optional
|
229
|
+
Number of degrees of freedom per node. Default is 6.
|
230
|
+
|
231
|
+
Returns
|
232
|
+
-------
|
233
|
+
dof_ix : np.ndarray
|
234
|
+
Array of DOF indices corresponding to the given node index.
|
235
|
+
|
236
|
+
Examples
|
237
|
+
--------
|
238
|
+
>>> node_ix = 2
|
239
|
+
>>> n_dofs = 6
|
240
|
+
>>> dof_ix = node_ix_to_dof_ix(node_ix, n_dofs)
|
241
|
+
>>> print(dof_ix)
|
242
|
+
[12 13 14 15 16 17]
|
243
|
+
|
244
|
+
Notes
|
245
|
+
--------
|
246
|
+
Docstring is generated by GitHub Copilot.
|
247
|
+
|
248
|
+
"""
|
249
|
+
start = node_ix*n_dofs
|
250
|
+
stop = node_ix*n_dofs+n_dofs
|
251
|
+
dof_ix = []
|
252
|
+
for (i,j) in zip(start,stop):
|
253
|
+
dof_ix.append(np.arange(i,j))
|
254
|
+
|
255
|
+
dof_ix = np.array(dof_ix).flatten()
|
256
|
+
|
257
|
+
return dof_ix
|
258
|
+
|
259
|
+
|
260
|
+
def dof_sel(arr, dof_sel, n_dofs=6, axis=0):
|
261
|
+
"""
|
262
|
+
Selects degrees of freedom (DOFs) from an array along a specified axis.
|
263
|
+
|
264
|
+
Parameters
|
265
|
+
----------
|
266
|
+
arr : np.ndarray
|
267
|
+
Input array from which to select DOFs.
|
268
|
+
dof_sel : array-like
|
269
|
+
Indices of the DOFs to select (relative to each block of n_dofs).
|
270
|
+
n_dofs : int, optional
|
271
|
+
Number of DOFs per node or block. Default is 6.
|
272
|
+
axis : int, optional
|
273
|
+
Axis along which to select DOFs. Default is 0.
|
274
|
+
|
275
|
+
Returns
|
276
|
+
-------
|
277
|
+
arr_sel : np.ndarray
|
278
|
+
Array containing only the selected DOFs along the specified axis.
|
279
|
+
|
280
|
+
Examples
|
281
|
+
--------
|
282
|
+
>>> arr = np.arange(18).reshape(3, 6)
|
283
|
+
>>> dof_sel(arr, [0, 2], n_dofs=6, axis=1)
|
284
|
+
array([[ 0, 2],
|
285
|
+
[ 6, 8],
|
286
|
+
[12, 14]])
|
287
|
+
|
288
|
+
Notes
|
289
|
+
-------
|
290
|
+
Docstring is generated by GitHub Copilot.
|
291
|
+
"""
|
292
|
+
|
293
|
+
N = np.shape(arr)[axis]
|
294
|
+
all_ix = [range(dof_sel_i, N, n_dofs) for dof_sel_i in dof_sel]
|
295
|
+
sel_ix = np.array(all_ix).T.flatten()
|
296
|
+
|
297
|
+
# Select the elements
|
298
|
+
arr_sel = np.take(arr, sel_ix, axis=axis)
|
299
|
+
|
300
|
+
return arr_sel
|
301
|
+
|
302
|
+
|
303
|
+
|
304
|
+
def elements_from_common_nodes(element_matrix, selected_nodes):
|
305
|
+
"""
|
306
|
+
Find elements that have both nodes in `selected_nodes`.
|
307
|
+
|
308
|
+
Parameters
|
309
|
+
----------
|
310
|
+
element_matrix : np.ndarray
|
311
|
+
Array of elements with shape (n_elements, 3), where columns are [element_id, node1, node2].
|
312
|
+
selected_nodes : array-like
|
313
|
+
List or array of node labels to search for.
|
314
|
+
|
315
|
+
Returns
|
316
|
+
-------
|
317
|
+
selected_element_matrix : np.ndarray
|
318
|
+
Subset of `element_matrix` where both nodes are in `selected_nodes`.
|
319
|
+
sel_ix : np.ndarray
|
320
|
+
Indices of the selected elements in the original `element_matrix`.
|
321
|
+
|
322
|
+
Notes
|
323
|
+
-----
|
324
|
+
Only elements where both node1 and node2 are in `selected_nodes` are selected. Docstring is generated by GitHub Copilot.
|
325
|
+
"""
|
326
|
+
|
327
|
+
mask = np.isin(element_matrix[:, 1], selected_nodes) & np.isin(element_matrix[:, 2], selected_nodes)
|
328
|
+
sel_ix = np.where(mask)[0]
|
329
|
+
selected_element_matrix = element_matrix[sel_ix, :]
|
330
|
+
|
331
|
+
return selected_element_matrix, sel_ix
|
332
|
+
|
333
|
+
|
334
|
+
def transform_elements(node_matrix, element_matrix, e2p, repeats=1):
|
335
|
+
"""
|
336
|
+
Transforms elements from global to local coordinates.
|
337
|
+
Given a node matrix and an element matrix, this function computes the transformation matrices
|
338
|
+
from global to local coordinates for each element. The transformation is based on the direction
|
339
|
+
vector between the nodes of each element. The transformation matrices are constructed using
|
340
|
+
the `transform_unit` function and are repeated as specified.
|
341
|
+
|
342
|
+
Parameters
|
343
|
+
----------
|
344
|
+
node_matrix : np.ndarray
|
345
|
+
Array of node data. The first column should contain node IDs, and the remaining columns
|
346
|
+
should contain node coordinates.
|
347
|
+
element_matrix : np.ndarray
|
348
|
+
Array of element data. Each row corresponds to an element, with the first column as the
|
349
|
+
element ID and the next columns as node IDs defining the element.
|
350
|
+
e2p : np.ndarray or similar
|
351
|
+
Additional parameter passed to `transform_unit` for transformation construction.
|
352
|
+
repeats : int, optional
|
353
|
+
Number of times to repeat the transformation block (default is 1).
|
354
|
+
|
355
|
+
Returns
|
356
|
+
-------
|
357
|
+
list of np.ndarray
|
358
|
+
List of transformation matrices, one for each element, mapping global to local coordinates.
|
359
|
+
|
360
|
+
Notes
|
361
|
+
-----
|
362
|
+
- Assumes that `transform_unit` and `blkdiag` functions are defined elsewhere.
|
363
|
+
- The function matches node IDs between `element_matrix` and `node_matrix` to determine node positions.
|
364
|
+
Docstring is generated by GitHub Copilot.
|
365
|
+
"""
|
366
|
+
|
367
|
+
n_elements = np.shape(element_matrix)[0]
|
368
|
+
T_g2el = [None]*n_elements
|
369
|
+
|
370
|
+
for el in range(0, n_elements):
|
371
|
+
n1_ix = np.where(node_matrix[:,0]==element_matrix[el, 1])
|
372
|
+
n2_ix = np.where(node_matrix[:,0]==element_matrix[el, 2])
|
373
|
+
|
374
|
+
X1 = node_matrix[n1_ix, 1:]
|
375
|
+
X2 = node_matrix[n2_ix, 1:]
|
376
|
+
dx = X2-X1
|
377
|
+
e1 = dx/np.linalg.norm(dx)
|
378
|
+
|
379
|
+
T_g2el[el] = blkdiag(transform_unit(e1, e2p), repeats) # Transform from global to local coordinates VLocal = T*VGlobal
|
380
|
+
|
381
|
+
return T_g2el
|