PyCBA 0.3__tar.gz → 0.4.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.
- {PyCBA-0.3/src/PyCBA.egg-info → PyCBA-0.4.1}/PKG-INFO +2 -1
- {PyCBA-0.3 → PyCBA-0.4.1}/README.md +1 -0
- {PyCBA-0.3 → PyCBA-0.4.1/src/PyCBA.egg-info}/PKG-INFO +2 -1
- {PyCBA-0.3 → PyCBA-0.4.1}/src/PyCBA.egg-info/SOURCES.txt +1 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/src/pycba/__init__.py +2 -1
- {PyCBA-0.3 → PyCBA-0.4.1}/src/pycba/analysis.py +2 -1
- {PyCBA-0.3 → PyCBA-0.4.1}/src/pycba/load.py +33 -1
- PyCBA-0.4.1/src/pycba/pattern.py +164 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/src/pycba/results.py +48 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/tests/test_basic.py +39 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/LICENSE +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/pyproject.toml +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/setup.cfg +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/setup.py +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/src/PyCBA.egg-info/dependency_links.txt +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/src/PyCBA.egg-info/requires.txt +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/src/PyCBA.egg-info/top_level.txt +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/src/pycba/beam.py +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/src/pycba/bridge.py +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/src/pycba/inf_lines.py +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/src/pycba/utils.py +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/src/pycba/vehicle.py +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/tests/test_bridge.py +0 -0
- {PyCBA-0.3 → PyCBA-0.4.1}/tests/test_inf_lines.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyCBA
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Python Continuous Beam Analysis
|
|
5
5
|
Author-email: Colin Caprani <colin.caprani@monash.edu>
|
|
6
6
|
License: Apache 2.0
|
|
@@ -49,3 +49,4 @@ One of the main functions of `PyCBA` is that the basic analysis engine forms the
|
|
|
49
49
|
|
|
50
50
|
- Influence line generation
|
|
51
51
|
- Moving load analysis for bridges, targeted at bridge access assessments
|
|
52
|
+
- Load patterning and enveloping
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyCBA
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Python Continuous Beam Analysis
|
|
5
5
|
Author-email: Colin Caprani <colin.caprani@monash.edu>
|
|
6
6
|
License: Apache 2.0
|
|
@@ -49,3 +49,4 @@ One of the main functions of `PyCBA` is that the basic analysis engine forms the
|
|
|
49
49
|
|
|
50
50
|
- Influence line generation
|
|
51
51
|
- Moving load analysis for bridges, targeted at bridge access assessments
|
|
52
|
+
- Load patterning and enveloping
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
PyCBA - Continuous Beam Analysis in Python
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
__version__ = "0.
|
|
5
|
+
__version__ = "0.4.1"
|
|
6
6
|
|
|
7
7
|
from .analysis import *
|
|
8
8
|
from .beam import *
|
|
@@ -12,3 +12,4 @@ from .inf_lines import *
|
|
|
12
12
|
from .utils import *
|
|
13
13
|
from .bridge import *
|
|
14
14
|
from .vehicle import *
|
|
15
|
+
from .pattern import *
|
|
@@ -799,7 +799,7 @@ def parse_LM(LM: LoadMatrix) -> List[Load]:
|
|
|
799
799
|
return loads
|
|
800
800
|
|
|
801
801
|
|
|
802
|
-
def add_LM(LM1: LoadMatrix, LM2: LoadMatrix):
|
|
802
|
+
def add_LM(LM1: LoadMatrix, LM2: LoadMatrix) -> LoadMatrix:
|
|
803
803
|
"""
|
|
804
804
|
Adds two load matrices and returns the sum; this enables superposition
|
|
805
805
|
|
|
@@ -824,3 +824,35 @@ def add_LM(LM1: LoadMatrix, LM2: LoadMatrix):
|
|
|
824
824
|
LM.append(load)
|
|
825
825
|
|
|
826
826
|
return LM
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
def factor_LM(LM: LoadMatrix, gamma: float) -> LoadMatrix:
|
|
830
|
+
"""
|
|
831
|
+
Applies a factor to the loads in a `LoadMatrix` object
|
|
832
|
+
|
|
833
|
+
Parameters
|
|
834
|
+
----------
|
|
835
|
+
LM : LoadMatrix
|
|
836
|
+
The `LoadMatrix` object
|
|
837
|
+
|
|
838
|
+
gamma : float
|
|
839
|
+
A factor to apply to the load magnitudes
|
|
840
|
+
|
|
841
|
+
Returns
|
|
842
|
+
-------
|
|
843
|
+
LM : LoadMatrix
|
|
844
|
+
The factored `LoadMatrix` object
|
|
845
|
+
"""
|
|
846
|
+
LMnew = []
|
|
847
|
+
for load in LM:
|
|
848
|
+
i_span = load[0]
|
|
849
|
+
l_type = load[1]
|
|
850
|
+
mag = gamma * load[2]
|
|
851
|
+
if l_type == 1: # UDL
|
|
852
|
+
LMnew.append([i_span, l_type, mag])
|
|
853
|
+
elif l_type == 2 or l_type == 4: # PL or ML
|
|
854
|
+
LMnew.append([i_span, l_type, mag, load[3]])
|
|
855
|
+
else: # PUDL
|
|
856
|
+
LMnew.append([i_span, l_type, mag, load[3], load[4]])
|
|
857
|
+
|
|
858
|
+
return LMnew
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyCBA - Continuous Beam Analysis - Load Patterning Module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations # https://bit.ly/3KYiL2o
|
|
6
|
+
from typing import Optional, Union, Dict, List
|
|
7
|
+
import numpy as np
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
from .analysis import BeamAnalysis
|
|
10
|
+
from .results import Envelopes, BeamResults
|
|
11
|
+
from .load import LoadMatrix
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LoadPattern:
|
|
15
|
+
"""
|
|
16
|
+
Automatically patterns dead and live loads to achieve critical load effects.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, ba: BeamAnalysis):
|
|
20
|
+
"""
|
|
21
|
+
Initialize the LoadPattern class with the beam.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
ba : BeamAnalysis, optional
|
|
26
|
+
A :class:`pycba.analysis.BeamAnalysis` object. The default is None.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
None.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
self.ba = ba
|
|
34
|
+
self.LMg = None
|
|
35
|
+
self.LMq = None
|
|
36
|
+
self.gamma_g_min = 0
|
|
37
|
+
self.gamma_g_max = 0
|
|
38
|
+
self.gamma_q_min = 0
|
|
39
|
+
self.gamma_q_max = 0
|
|
40
|
+
|
|
41
|
+
def set_dead_loads(self, LM: LoadMatrix, gamma_max: float, gamma_min: float):
|
|
42
|
+
"""
|
|
43
|
+
Set the nominal dead loads acting on the beam, and the maximum and
|
|
44
|
+
minimum load factor.
|
|
45
|
+
|
|
46
|
+
Parameters
|
|
47
|
+
----------
|
|
48
|
+
LM : List[List[Union[int, float]]]
|
|
49
|
+
The load matrix for the beam, for this loadcase.
|
|
50
|
+
gamma_max : float
|
|
51
|
+
The maximum load factor.
|
|
52
|
+
gamma_min : float
|
|
53
|
+
The minimum load factor.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
None.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
self.LMg = LM
|
|
61
|
+
self.gamma_g_max = gamma_max
|
|
62
|
+
self.gamma_g_min = gamma_min
|
|
63
|
+
|
|
64
|
+
def set_live_loads(self, LM: LoadMatrix, gamma_max: float, gamma_min: float):
|
|
65
|
+
"""
|
|
66
|
+
Set the nominal live loads acting on the beam, and the maximum and
|
|
67
|
+
minimum load factor.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
LM : List[List[Union[int, float]]]
|
|
72
|
+
The load matrix for the beam, for this loadcase.
|
|
73
|
+
gamma_max : float
|
|
74
|
+
The maximum load factor.
|
|
75
|
+
gamma_min : float
|
|
76
|
+
The minimum load factor.
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
None.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
self.LMq = LM
|
|
84
|
+
self.gamma_q_max = gamma_max
|
|
85
|
+
self.gamma_q_min = gamma_min
|
|
86
|
+
|
|
87
|
+
def analyze(self, npts: Optional[int] = None) -> Envelopes:
|
|
88
|
+
"""
|
|
89
|
+
Conduct the load patterning analysis.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
npts : Optional[int]
|
|
94
|
+
The number of evaluation points along a member for load effects.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
Envelopes : `pycba.Envelopes`
|
|
99
|
+
The load effect envelopes from the patterning.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
# Helper function to get the BeamResults object easily
|
|
103
|
+
def analyze_loadcase(w):
|
|
104
|
+
self.ba.set_loads(w.tolist())
|
|
105
|
+
self.ba.analyze(npts)
|
|
106
|
+
return self.ba.beam_results
|
|
107
|
+
|
|
108
|
+
# Basic copies of the load matrices
|
|
109
|
+
wg = np.array(self.LMg)
|
|
110
|
+
wq = np.array(self.LMq)
|
|
111
|
+
gr, gc = wg.shape
|
|
112
|
+
qr, qc = wq.shape
|
|
113
|
+
w = np.zeros((gr + qr, max(gc, qc)))
|
|
114
|
+
wmax = w.copy()
|
|
115
|
+
wmin = w.copy()
|
|
116
|
+
|
|
117
|
+
# Maximum load matrix
|
|
118
|
+
wmax[:gr, :] = wg
|
|
119
|
+
wmax[:gr, 2] = self.gamma_g_max * wmax[:gr, 2]
|
|
120
|
+
wmax[gr : gr + qr, :] = wq
|
|
121
|
+
wmax[gr : gr + qr, 2] = self.gamma_q_max * wmax[gr : gr + qr, 2]
|
|
122
|
+
|
|
123
|
+
# Minimum load matrix
|
|
124
|
+
wmin[:gr, :] = wg
|
|
125
|
+
wmin[:gr, 2] = self.gamma_g_min * wmin[:gr, 2]
|
|
126
|
+
wmin[gr : gr + qr, :] = wq
|
|
127
|
+
wmin[gr : gr + qr, 2] = self.gamma_q_min * wmin[gr : gr + qr, 2]
|
|
128
|
+
|
|
129
|
+
# Parameters for looping over loadcases
|
|
130
|
+
N = self.ba.beam.no_spans
|
|
131
|
+
vResults = []
|
|
132
|
+
|
|
133
|
+
# Maximum support hogging and reaction -
|
|
134
|
+
# adjacent spans fully loaded with MAX, other spans loaded with MIN
|
|
135
|
+
n_max_hog = max(N - 1, 0)
|
|
136
|
+
for i in range(1, n_max_hog + 1):
|
|
137
|
+
w = wmin.copy()
|
|
138
|
+
adjacent_spans = np.array([i, i + 1])
|
|
139
|
+
mask = np.isin(wmax[:, 0], adjacent_spans)
|
|
140
|
+
w[mask] = wmax[mask]
|
|
141
|
+
res = analyze_loadcase(w)
|
|
142
|
+
vResults.append(res)
|
|
143
|
+
|
|
144
|
+
# Odd numbered spans loaded with MAX for maximum sagging moments
|
|
145
|
+
w = wmax.copy() # set all loading to maximum
|
|
146
|
+
odd_spans = np.array([i for i in range(2, N + 1, 2)])
|
|
147
|
+
mask = np.isin(wmax[:, 0], odd_spans)
|
|
148
|
+
w[mask] = wmin[mask]
|
|
149
|
+
res = analyze_loadcase(w)
|
|
150
|
+
vResults.append(res)
|
|
151
|
+
|
|
152
|
+
# Even numbered spans loaded with MAX for maximum sagging moments
|
|
153
|
+
w = wmin.copy() # set all loading to minimum
|
|
154
|
+
even_spans = np.array([i for i in range(2, N + 1, 2)])
|
|
155
|
+
mask = np.isin(wmin[:, 0], even_spans)
|
|
156
|
+
w[mask] = wmax[mask] # make the load a max
|
|
157
|
+
res = analyze_loadcase(w)
|
|
158
|
+
vResults.append(res)
|
|
159
|
+
|
|
160
|
+
# All spans loaded with MAX
|
|
161
|
+
res = analyze_loadcase(wmax)
|
|
162
|
+
vResults.append(res)
|
|
163
|
+
|
|
164
|
+
return Envelopes(vResults)
|
|
@@ -5,6 +5,7 @@ PyCBA - Beam Results module
|
|
|
5
5
|
from __future__ import annotations # https://bit.ly/3KYiL2o
|
|
6
6
|
from typing import List, Tuple
|
|
7
7
|
import numpy as np
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
8
9
|
from scipy import integrate
|
|
9
10
|
from .beam import Beam
|
|
10
11
|
from .load import MemberResults, LoadMaMb
|
|
@@ -348,3 +349,50 @@ class Envelopes:
|
|
|
348
349
|
# Ensure no misleading results returned
|
|
349
350
|
self.Rmax = np.zeros((self.nsup, self.nres))
|
|
350
351
|
self.Rmin = np.zeros((self.nsup, self.nres))
|
|
352
|
+
|
|
353
|
+
def plot(self, each=False, **kwargs):
|
|
354
|
+
"""
|
|
355
|
+
Plots the envelopes of bending and shear.
|
|
356
|
+
|
|
357
|
+
Parameters
|
|
358
|
+
----------
|
|
359
|
+
each : Boolean
|
|
360
|
+
Wether or not to show each BMD and SFD in the enveloping. The default is False
|
|
361
|
+
**kwargs : Dict
|
|
362
|
+
Matplotlib keyword arguments for plotting.
|
|
363
|
+
|
|
364
|
+
Returns
|
|
365
|
+
-------
|
|
366
|
+
None.
|
|
367
|
+
|
|
368
|
+
"""
|
|
369
|
+
|
|
370
|
+
if self.nres < 1:
|
|
371
|
+
raise ValueError("No results to display")
|
|
372
|
+
|
|
373
|
+
L = self.x[-1]
|
|
374
|
+
|
|
375
|
+
fig, axs = plt.subplots(2, 1, sharex=True, **kwargs)
|
|
376
|
+
|
|
377
|
+
ax = axs[0]
|
|
378
|
+
ax.plot([0, L], [0, 0], "k", lw=2)
|
|
379
|
+
ax.plot(self.x, self.Mmax, "r")
|
|
380
|
+
ax.plot(self.x, self.Mmin, "b")
|
|
381
|
+
ax.grid()
|
|
382
|
+
ax.invert_yaxis()
|
|
383
|
+
ax.set_ylabel("Bending Moment (kNm)")
|
|
384
|
+
|
|
385
|
+
ax = axs[1]
|
|
386
|
+
ax.plot([0, L], [0, 0], "k", lw=2)
|
|
387
|
+
ax.plot(self.x, self.Vmax, "r")
|
|
388
|
+
ax.plot(self.x, self.Vmin, "b")
|
|
389
|
+
ax.grid()
|
|
390
|
+
ax.set_ylabel("Shear Force (kN)")
|
|
391
|
+
ax.set_xlabel("Distance along beam (m)")
|
|
392
|
+
|
|
393
|
+
if each:
|
|
394
|
+
for res in self.vResults:
|
|
395
|
+
axs[0].plot(self.x, res.results.M, "r", lw=0.5)
|
|
396
|
+
axs[1].plot(self.x, res.results.V, "b", lw=0.5)
|
|
397
|
+
|
|
398
|
+
return fig, ax
|
|
@@ -226,3 +226,42 @@ def test_moment_load():
|
|
|
226
226
|
# Check deflection closes
|
|
227
227
|
d = beam_analysis.beam_results.D[[0, 2]]
|
|
228
228
|
assert d == pytest.approx([0.0, 0.0])
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def test_envelopes():
|
|
232
|
+
L = [6, 4, 6]
|
|
233
|
+
EI = 30 * 10e9 * 1e-6
|
|
234
|
+
R = [-1, 0, -1, 0, -1, 0, -1, 0]
|
|
235
|
+
beam_analysis = cba.BeamAnalysis(L, EI, R)
|
|
236
|
+
|
|
237
|
+
LMg = [[1, 1, 25, 0, 0], [2, 1, 25, 0, 0], [3, 1, 25, 0, 0]]
|
|
238
|
+
γg_max = 1.4
|
|
239
|
+
γg_min = 1.0
|
|
240
|
+
LMq = [[1, 1, 10, 0, 0], [2, 1, 10, 0, 0], [3, 1, 10, 0, 0]]
|
|
241
|
+
γq_max = 1.6
|
|
242
|
+
γq_min = 0
|
|
243
|
+
|
|
244
|
+
lp = cba.LoadPattern(beam_analysis)
|
|
245
|
+
lp.set_dead_loads(LMg, γg_max, γg_min)
|
|
246
|
+
lp.set_live_loads(LMq, γq_max, γq_min)
|
|
247
|
+
env = lp.analyze()
|
|
248
|
+
|
|
249
|
+
m_locs = np.array([3, 6, 8, 10, 13])
|
|
250
|
+
idx = [(np.abs(env.x - x)).argmin() for x in m_locs]
|
|
251
|
+
assert np.allclose(
|
|
252
|
+
env.Mmax[idx], np.array([163.79, 0, 11.75, 0, 163.79]), atol=1e-2
|
|
253
|
+
)
|
|
254
|
+
assert np.allclose(
|
|
255
|
+
env.Mmin[idx], np.array([0, -163.38, -81.42, -163.38, 0]), atol=1e-2
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
n = beam_analysis.beam_results.npts
|
|
259
|
+
nspans = beam_analysis.beam.no_spans
|
|
260
|
+
Vmax = np.array(
|
|
261
|
+
[np.max(env.Vmax[i * (n + 3) : (i + 1) * (n + 3)]) for i in range(nspans)]
|
|
262
|
+
)
|
|
263
|
+
assert np.allclose(Vmax, np.array([131.1, 123.94, 180.23]), atol=1e-2)
|
|
264
|
+
Vmin = np.array(
|
|
265
|
+
[np.min(env.Vmin[i * (n + 3) : (i + 1) * (n + 3)]) for i in range(nspans)]
|
|
266
|
+
)
|
|
267
|
+
assert np.allclose(Vmin, np.array([-180.23, -123.94, -131.10]), atol=1e-2)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|