Skip to content

Commit 404a61e

Browse files
committed
allow for rotational springs in kernel
1 parent edaadf7 commit 404a61e

File tree

4 files changed

+222
-424
lines changed

4 files changed

+222
-424
lines changed

samples/usage3.ipynb

+16-384
Large diffs are not rendered by default.

src/openpile/analyses.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ def simple_winkler_analysis(model, solver="NR", max_iter: int = 100):
307307
elif solver == "MNR":
308308
pass
309309

310-
# reset displacements in case of displacement-driven analysis
310+
# reset prescribed displacements to converge properly in case
311+
# of displacement-driven analysis
311312
U[:] = 0.0
312313

313314
# Internal forces calculations with dim(nelem,6,6)

src/openpile/construct.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ def create_springs() -> np.ndarray:
753753
shape=(self.element_number, 2, 2, spring_dim), dtype=np.float32
754754
)
755755
mt = np.zeros(
756-
shape=(self.element_number, 2, 2, spring_dim), dtype=np.float32
756+
shape=(self.element_number, 2, 2, spring_dim, spring_dim), dtype=np.float32
757757
)
758758
Hb = np.zeros(shape=(1, 1, 2, spring_dim), dtype=np.float32)
759759
Mb = np.zeros(shape=(1, 1, 2, spring_dim), dtype=np.float32)
@@ -766,6 +766,7 @@ def create_springs() -> np.ndarray:
766766
(self.soil_properties["x_top [m]"] <= layer.top)
767767
& (self.soil_properties["x_bottom [m]"] >= layer.bottom)
768768
].index
769+
769770
# py curve
770771
if layer.lateral_model.spring_signature[
771772
0

src/openpile/core/kernel.py

+202-38
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from typing_extensions import Literal
1616

1717
from openpile.construct import Model, Pile
18-
18+
from openpile.core.validation import UserInputError
1919
from openpile.core import misc
2020

2121

@@ -34,6 +34,34 @@ def reverse_indices(A, B):
3434
return np.array([x for x in A if x not in B])
3535

3636

37+
@njit(f8[:](f8[:]), cache=True)
38+
def double_inner_njit(in_arr):
39+
# d = misc.repeat_inner(u)
40+
out_arr = np.zeros(((len(in_arr) - 1) * 2), dtype=np.float64)
41+
i = 0
42+
while i < len(out_arr) + 1:
43+
if i == 0:
44+
out_arr[i] = in_arr[0]
45+
i += 1
46+
elif i == len(out_arr):
47+
out_arr[i] = in_arr[-1]
48+
i += 1
49+
elif i == len(out_arr) - 1:
50+
out_arr[i] = in_arr[-2]
51+
i += 1
52+
elif i % 2 != 0:
53+
x = int((i + 1) / 2)
54+
out_arr[i] = in_arr[x]
55+
out_arr[i + 1] = in_arr[x]
56+
i += 2
57+
elif i % 2 == 0:
58+
x = int(i / 2)
59+
out_arr[i] = in_arr[x]
60+
out_arr[i + 1] = in_arr[x]
61+
i += 2
62+
63+
return out_arr
64+
3765
@njit(parallel=True, cache=True)
3866
def numba_ix(arr, rows, cols):
3967
"""
@@ -279,7 +307,7 @@ def elem_py_stiffness_matrix(model, u, kind):
279307
L = mesh_to_element_length(model)
280308

281309
# calculate the spring stiffness
282-
ksoil = calculate_springs_stiffness(u=u[1::3], springs=model._py_springs, kind=kind)
310+
ksoil = calculate_py_springs_stiffness(u=u[1::3], springs=model._py_springs, kind=kind)
283311

284312
N = 0 * L
285313
A = 2 * L / 7
@@ -323,6 +351,73 @@ def elem_py_stiffness_matrix(model, u, kind):
323351
return ktop + kbottom
324352

325353

354+
def elem_mt_stiffness_matrix(model, u, kind):
355+
"""creates soil element stiffness matrix based on model info and element number.
356+
357+
The soil stiffness matrix assumes the soil stiffness to vary lineraly along the elements.
358+
359+
#TODO: proof here
360+
361+
Parameters
362+
----------
363+
model : openpile class object `openpile.construct.Model`
364+
includes information on soil/structure, elements, nodes and other model-related data.
365+
u: np.ndarray
366+
Global displacement vector
367+
kind: str
368+
"initial", "secant" or "tangent"
369+
370+
Returns
371+
-------
372+
k: numpy array (3d)
373+
soil consistent stiffness matrix of all elememts related to the p-y soil springs' stiffness
374+
375+
Raises
376+
------
377+
ValueError
378+
ndof per node can be either 2 or 3
379+
380+
"""
381+
382+
# calculate length vector
383+
L = mesh_to_element_length(model)
384+
385+
# calculate the py spring stiffness
386+
kspy = calculate_py_springs_stiffness(u=u[1::3], springs=model._py_springs, kind='secant')
387+
upy = double_inner_njit(u[1::3]).reshape(-1,2,1,1)
388+
p_mobilised = kspy * upy
389+
390+
# calculate the spring stiffness
391+
ksoil = calculate_mt_springs_stiffness(u=u[2::3],
392+
mt_springs=model._mt_springs,
393+
py_springs=model._py_springs,
394+
p_mobilised=p_mobilised,
395+
kind=kind,
396+
)
397+
398+
N = 0 * L
399+
A = 6 / (5*L)
400+
B = N + 1 / 10
401+
C = 2*L / 15
402+
D = -L / 30
403+
404+
km = (
405+
np.block(
406+
[
407+
[N, N, N, N, N, N],
408+
[N, A, B, N,-A, B],
409+
[N, B, C, N,-B, D],
410+
[N, N, N, N, N, N],
411+
[N,-A,-B, N, A,-B],
412+
[N, B, D, N,-B, C],
413+
]
414+
)
415+
* (0.5*ksoil[:,0] + 0.5*ksoil[:,1])
416+
)
417+
418+
return km
419+
420+
326421
@njit(parallel=True, cache=True)
327422
def jit_build(k, ndim, n_elem, node_per_element, ndof_per_node):
328423
# pre-allocate stiffness matrix
@@ -342,7 +437,7 @@ def jit_build(k, ndim, n_elem, node_per_element, ndof_per_node):
342437
return K
343438

344439

345-
def build_stiffness_matrix(model, u=None, kind=None):
440+
def build_stiffness_matrix(model, u=None, kind=None, p_mobilised=None):
346441
"""Builds the stiffness matrix based on the model(element and node) properties
347442
348443
Element stiffness matrices are first computed for each element and then loaded in the global stiffness matrix through summation.
@@ -354,6 +449,8 @@ def build_stiffness_matrix(model, u=None, kind=None):
354449
stores all information about nodes elements, and other model-related items, see #TODO:link to model class
355450
u: np.ndarray, Optional
356451
Global displacement vector. Must be given if soil_flag is not None
452+
p_mobilised: np.ndarray, Optional
453+
Mobilised p-value. Must be given if soil_flag is not None
357454
kind: str, Optional
358455
"initial", "secant" or "tangent". Must be given if soil_flag is not None
359456
@@ -381,12 +478,17 @@ def build_stiffness_matrix(model, u=None, kind=None):
381478
UserWarning("'u' and 'kind' must be stipulated.")
382479
else:
383480
if model.distributed_lateral:
384-
k += elem_py_stiffness_matrix(model, u, kind)
385-
elif model.distributed_moment:
386-
k += 0
387-
elif model.base_shear:
481+
k += elem_py_stiffness_matrix(model, u, kind)
482+
483+
if model.distributed_moment:
484+
if not model.distributed_lateral:
485+
raise UserInputError('Error: Distributed moment cannot be calculated without distributed lateral springs.')
486+
k += elem_mt_stiffness_matrix(model, u, kind)
487+
488+
if model.base_shear:
388489
k += 0
389-
elif model.base_moment:
490+
491+
if model.base_moment:
390492
k += 0
391493

392494
K = jit_build(k, ndim_global, n_elem, node_per_element, ndof_per_node)
@@ -434,7 +536,7 @@ def struct_internal_force(model, u) -> np.ndarray:
434536
if model.distributed_lateral:
435537
k += elem_py_stiffness_matrix(model, u, kind)
436538
elif model.distributed_moment:
437-
k += 0
539+
k += elem_mt_stiffness_matrix(model, u, kind)
438540
elif model.base_shear:
439541
k += 0
440542
elif model.base_moment:
@@ -451,20 +553,19 @@ def struct_internal_force(model, u) -> np.ndarray:
451553

452554

453555
@njit(cache=True)
454-
def calculate_springs_stiffness(
556+
def calculate_py_springs_stiffness(
455557
u: np.ndarray, springs: np.ndarray, kind: Literal["initial", "secant", "tangent"]
456558
):
457-
"""Calculate springs stiffness
559+
"""Calculate springs stiffness for py or t-z springs.
458560
459561
Parameters
460562
----------
461563
u : np.ndarray
462564
displacements to calculate stiffness.
463565
For dofs related to t-z curves, u = U[::3] where U is the global displacement vector.
464566
For dofs related to p-y curves, u = U[1::3] where U is the global displacement vector.
465-
For dofs related to m-t curves, u = U[2::3] where U is the global displacement vector.
466567
springs : np.ndarray
467-
soil-structure interaction springs array of shape (n_elem, 2, 2, spring_dim)
568+
soil-structure interaction py springs array of shape (n_elem, 2, 2, spring_dim)
468569
kind : str
469570
defines whether it is initial, secant of tangent stiffness to define
470571
@@ -474,30 +575,8 @@ def calculate_springs_stiffness(
474575
secant or tangent stiffness for all elements. Array of shape(n_elem,2,1,1)
475576
"""
476577

477-
# double inner values
478-
# d = misc.repeat_inner(u)
479-
d = np.zeros(((len(u) - 1) * 2), dtype=np.float64)
480-
i = 0
481-
while i < len(d) + 1:
482-
if i == 0:
483-
d[i] = u[0]
484-
i += 1
485-
elif i == len(d):
486-
d[i] = u[-1]
487-
i += 1
488-
elif i == len(d) - 1:
489-
d[i] = u[-2]
490-
i += 1
491-
elif i % 2 != 0:
492-
x = int((i + 1) / 2)
493-
d[i] = u[x]
494-
d[i + 1] = u[x]
495-
i += 2
496-
elif i % 2 == 0:
497-
x = int(i / 2)
498-
d[i] = u[x]
499-
d[i + 1] = u[x]
500-
i += 2
578+
# double inner values for u
579+
d = double_inner_njit(u)
501580

502581
# displacemet with same dimension as spring
503582
d = np.abs(d).reshape((-1, 2, 1, 1))
@@ -535,11 +614,96 @@ def calculate_springs_stiffness(
535614
d[i, j, 0, 0], springs[i, j, 1], springs[i, j, 0]
536615
)
537616

538-
k[i, j, 0, 0] = abs((p1 - p0) / dx)
617+
k[i, j, 0, 0] = abs((p1 - p0) / dx)
539618

540619
return k
541620

542621

622+
623+
# @njit(cache=True)
624+
def calculate_mt_springs_stiffness(
625+
u: np.ndarray, mt_springs: np.ndarray, py_springs:np.ndarray, p_mobilised: np.ndarray, kind: Literal["initial", "secant", "tangent"]
626+
):
627+
"""Calculate springs stiffness for rotational springs
628+
629+
.. note::
630+
The difference with the py function is that rotational springs can depend on lateral springs
631+
632+
Parameters
633+
----------
634+
u : np.ndarray
635+
displacements to calculate stiffness.
636+
For dofs related to m-t curves, u = U[2::3] where U is the global displacement vector.
637+
mt_springs : np.ndarray
638+
soil-structure interaction m-t springs array of shape (n_elem, 2, 2, py_spring_dim, mt_spring_dim)
639+
py_springs : np.ndarray
640+
soil-structure interaction p-y springs array of shape (n_elem, 2, 2, py_spring_dim)
641+
p_mobilised : np.ndarray
642+
current p value of p-y springs of shape (nelem, 2, 1, 1)
643+
kind : str
644+
defines whether it is initial, secant of tangent stiffness to define
645+
646+
Returns
647+
-------
648+
k: np.ndarray
649+
secant or tangent stiffness for all elements. Array of shape(n_elem,2,1,1)
650+
"""
651+
652+
# double inner values for u
653+
d = double_inner_njit(u)
654+
655+
# displacemet and p_mobilised with same dimension as spring
656+
d = np.abs(d).reshape((-1, 2, 1, 1))
657+
p = p_mobilised.reshape((-1, 2, 1, 1))
658+
659+
k = np.zeros(d.shape, dtype=np.float64)
660+
661+
# i for each element
662+
for i in range(k.shape[0]):
663+
# j for top and bottom values of spring
664+
for j in range(k.shape[1]):
665+
if np.sum(mt_springs[i, j, 1, 0]) == 0:
666+
#check if first m-vector is not a defined spring
667+
#if that is the case, we do not calculate any stiffness
668+
pass
669+
else:
670+
# get the proper m-t spring
671+
m = np.zeros(k.shape[4])
672+
t = np.zeros(k.shape[4])
673+
674+
for ii in range((k.shape[4])):
675+
if ii == 0:
676+
pass
677+
else:
678+
m[ii] = np.interp(p[i,j,1,1], py_springs[i,j,0,:], mt_springs[i,j,0,:,ii])
679+
t[ii] = np.interp(p[i,j,1,1], py_springs[i,j,0,:], mt_springs[i,j,1,:,ii])
680+
681+
if kind == "initial" or d[i, j, 0, 0] == 0.0:
682+
dt = t[1] - t[0]
683+
m0 = m[0]
684+
m1 = m[1]
685+
elif kind == "secant":
686+
dt = d[i, j, 0, 0]
687+
m0 = m[0]
688+
if d[i, j, 0, 0] > t[-1]:
689+
m1 = m[-1]
690+
else:
691+
m1 = np.interp(dt, t, m)
692+
elif kind == "tangent":
693+
dt = min(0.0005, d[i, j, 0, 0])
694+
if (d[i, j, 0, 0] - dt) > t[-1]:
695+
m0 = m[-1]
696+
else:
697+
m0 = np.interp(d[i, j, 0, 0] - dt, t, m)
698+
if (d[i, j, 0, 0] - dt) > t[-1]:
699+
m1 = m[-1]
700+
else:
701+
m1 = np.interp(d[i, j, 0, 0], t, m)
702+
703+
k[i, j, 0, 0] = abs((m1 - m0) / dt)
704+
705+
return k
706+
543707
def computer():
544708
"""This function is the solver of openpile.
545709

0 commit comments

Comments
 (0)