15
15
from typing_extensions import Literal
16
16
17
17
from openpile .construct import Model , Pile
18
-
18
+ from openpile . core . validation import UserInputError
19
19
from openpile .core import misc
20
20
21
21
@@ -34,6 +34,34 @@ def reverse_indices(A, B):
34
34
return np .array ([x for x in A if x not in B ])
35
35
36
36
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
+
37
65
@njit (parallel = True , cache = True )
38
66
def numba_ix (arr , rows , cols ):
39
67
"""
@@ -279,7 +307,7 @@ def elem_py_stiffness_matrix(model, u, kind):
279
307
L = mesh_to_element_length (model )
280
308
281
309
# 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 )
283
311
284
312
N = 0 * L
285
313
A = 2 * L / 7
@@ -323,6 +351,73 @@ def elem_py_stiffness_matrix(model, u, kind):
323
351
return ktop + kbottom
324
352
325
353
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
+
326
421
@njit (parallel = True , cache = True )
327
422
def jit_build (k , ndim , n_elem , node_per_element , ndof_per_node ):
328
423
# pre-allocate stiffness matrix
@@ -342,7 +437,7 @@ def jit_build(k, ndim, n_elem, node_per_element, ndof_per_node):
342
437
return K
343
438
344
439
345
- def build_stiffness_matrix (model , u = None , kind = None ):
440
+ def build_stiffness_matrix (model , u = None , kind = None , p_mobilised = None ):
346
441
"""Builds the stiffness matrix based on the model(element and node) properties
347
442
348
443
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):
354
449
stores all information about nodes elements, and other model-related items, see #TODO:link to model class
355
450
u: np.ndarray, Optional
356
451
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
357
454
kind: str, Optional
358
455
"initial", "secant" or "tangent". Must be given if soil_flag is not None
359
456
@@ -381,12 +478,17 @@ def build_stiffness_matrix(model, u=None, kind=None):
381
478
UserWarning ("'u' and 'kind' must be stipulated." )
382
479
else :
383
480
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 :
388
489
k += 0
389
- elif model .base_moment :
490
+
491
+ if model .base_moment :
390
492
k += 0
391
493
392
494
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:
434
536
if model .distributed_lateral :
435
537
k += elem_py_stiffness_matrix (model , u , kind )
436
538
elif model .distributed_moment :
437
- k += 0
539
+ k += elem_mt_stiffness_matrix ( model , u , kind )
438
540
elif model .base_shear :
439
541
k += 0
440
542
elif model .base_moment :
@@ -451,20 +553,19 @@ def struct_internal_force(model, u) -> np.ndarray:
451
553
452
554
453
555
@njit (cache = True )
454
- def calculate_springs_stiffness (
556
+ def calculate_py_springs_stiffness (
455
557
u : np .ndarray , springs : np .ndarray , kind : Literal ["initial" , "secant" , "tangent" ]
456
558
):
457
- """Calculate springs stiffness
559
+ """Calculate springs stiffness for py or t-z springs.
458
560
459
561
Parameters
460
562
----------
461
563
u : np.ndarray
462
564
displacements to calculate stiffness.
463
565
For dofs related to t-z curves, u = U[::3] where U is the global displacement vector.
464
566
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.
466
567
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)
468
569
kind : str
469
570
defines whether it is initial, secant of tangent stiffness to define
470
571
@@ -474,30 +575,8 @@ def calculate_springs_stiffness(
474
575
secant or tangent stiffness for all elements. Array of shape(n_elem,2,1,1)
475
576
"""
476
577
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 )
501
580
502
581
# displacemet with same dimension as spring
503
582
d = np .abs (d ).reshape ((- 1 , 2 , 1 , 1 ))
@@ -535,11 +614,96 @@ def calculate_springs_stiffness(
535
614
d [i , j , 0 , 0 ], springs [i , j , 1 ], springs [i , j , 0 ]
536
615
)
537
616
538
- k [i , j , 0 , 0 ] = abs ((p1 - p0 ) / dx )
617
+ k [i , j , 0 , 0 ] = abs ((p1 - p0 ) / dx )
539
618
540
619
return k
541
620
542
621
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
+
543
707
def computer ():
544
708
"""This function is the solver of openpile.
545
709
0 commit comments