-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodel_convergence_ABC.py
262 lines (194 loc) · 8.58 KB
/
model_convergence_ABC.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
"""Model class setup for the convergence analysis of MPSA-Newmark with absorbing
boundaries.
All off-diagonal/shear components of the stiffness tensor are discarded.
"""
import sys
import numpy as np
import porepy as pp
import sympy as sym
sys.path.append("../../")
from models import DynamicMomentumBalanceABCLinear
from utils import u_v_a_wrap
class BoundaryConditionsUnitTest:
def bc_type_mechanics(self, sd: pp.Grid) -> pp.BoundaryConditionVectorial:
"""Method for assigning boundary condition type.
Assigns the following boundary condition types:
* North and south: Roller boundary (Neumann in x-direction and Dirichlet in
y-direction)
* West: Dirichlet
* East: Robin
Parameters:
sd: The subdomain whose bc type is assigned.
Return:
The boundary condition object.
"""
# Fetch boundary sides and assign type of boundary condition for the different
# sides
bounds = self.domain_boundary_sides(sd)
bc = pp.BoundaryConditionVectorial(sd, bounds.all_bf, "dir")
# East side: Absorbing
bc.is_dir[:, bounds.east] = False
bc.is_rob[:, bounds.east] = True
# North side: Roller
bc.is_dir[0, bounds.north + bounds.south] = False
bc.is_neu[0, bounds.north + bounds.south] = True
# Calling helper function for assigning the Robin weight
self.assign_robin_weight(sd=sd, bc=bc)
return bc
def bc_values_stress(self, boundary_grid: pp.BoundaryGrid) -> np.ndarray:
"""Method for assigning Neumann and Robin boundary condition values.
Specifically, for the Robin values, this method assigns the values corresponding
to absorbing boundary conditions with a second order approximation of u_t in:
sigma * n + alpha * u_t = G
Robin/Absorbing boundaries are employed for the east and west boundary. Zero
Neumann conditions are assigned for the north and south boundary.
Parameters:
boundary_grid: The boundary grids on which to define boundary conditions.
Returns:
Array of boundary values.
"""
if boundary_grid.dim != (self.nd - 1):
return np.array([])
data = self.mdg.boundary_grid_data(boundary_grid)
sd = boundary_grid.parent
boundary_faces = sd.get_boundary_faces()
name = "boundary_displacement_values"
if self.time_manager.time_index == 0:
return self.initial_condition_bc(boundary_grid)
displacement_values_0 = pp.get_solution_values(
name=name, data=data, time_step_index=0
)
displacement_values_1 = pp.get_solution_values(
name=name, data=data, time_step_index=1
)
# According to the expression for the absorbing boundaries we have a coefficient
# 2 in front of the values u_(n-1) and -0.5 in front of u_(n-2):
displacement_values = 2 * displacement_values_0 - 0.5 * displacement_values_1
# Transposing and reshaping displacement values to prepare for broadcasting
displacement_values = displacement_values.reshape(
boundary_grid.num_cells, self.nd, 1
)
# Assembling the vector representing the RHS of the Robin conditions
total_coefficient_matrix = self.total_coefficient_matrix(sd=sd)
robin_rhs = np.matmul(total_coefficient_matrix, displacement_values).squeeze(-1)
robin_rhs *= sd.face_areas[boundary_faces][:, None]
robin_rhs = robin_rhs.T
boundary_sides = self.domain_boundary_sides(sd)
inds_north = np.where(boundary_sides.north)[0]
inds_south = np.where(boundary_sides.south)[0]
inds_north = np.where(np.isin(boundary_faces, inds_north))[0]
inds_south = np.where(np.isin(boundary_faces, inds_south))[0]
robin_rhs[:, inds_north] *= 0
robin_rhs[:, inds_south] *= 0
return robin_rhs.ravel("F")
def bc_values_displacement(self, bg: pp.BoundaryGrid) -> np.ndarray:
"""Method for setting Dirichlet boundary values.
Sets a time dependent sine condition in the x-direction of the western boundary.
Zero elsewhere.
Parameters:
bg: Boundary grid whose boundary displacement value is to be set.
Returns:
An array with the displacement boundary values at time t.
"""
values = np.zeros((self.nd, bg.num_cells))
bounds = self.domain_boundary_sides(bg)
t = self.time_manager.time
# Time dependent sine Dirichlet condition
values[0][bounds.west] += np.ones(len(values[0][bounds.west])) * np.sin(t)
return values.ravel("F")
def initial_condition_bc(self, bg: pp.BoundaryGrid) -> np.ndarray:
"""Method for setting initial boundary values for 0th and -1st time step.
Parameters:
bg: Boundary grid whose boundary displacement value is to be set.
Returns:
An array with the initial displacement boundary values.
"""
dt = self.time_manager.dt
vals_0 = self.initial_condition_value_function(bg=bg, t=0)
vals_1 = self.initial_condition_value_function(bg=bg, t=0 - dt)
data = self.mdg.boundary_grid_data(bg)
# The values for the 0th and -1th time step are to be stored
pp.set_solution_values(
name="boundary_displacement_values",
values=vals_0,
data=data,
time_step_index=0,
)
pp.set_solution_values(
name="boundary_displacement_values",
values=vals_1,
data=data,
time_step_index=1,
)
return vals_0
def initial_condition_value_function(
self, bg: pp.BoundaryGrid, t: float
) -> np.ndarray:
"""Initial values for the absorbing boundaries.
In the quasi-1d test we have to assign initial values to the east and west
boundaries (absorbing boundaries).
"""
sd = bg.parent
x = sd.face_centers[0, :]
y = sd.face_centers[1, :]
boundary_sides = self.domain_boundary_sides(sd)
inds_east = np.where(boundary_sides.east)[0]
inds_west = np.where(boundary_sides.west)[0]
bc_vals = np.zeros((sd.dim, sd.num_faces))
displacement_function = u_v_a_wrap(model=self)
# East
bc_vals[0, :][inds_east] = displacement_function[0](
x[inds_east], y[inds_east], t
)
# West
bc_vals[0, :][inds_west] = displacement_function[0](
x[inds_west], y[inds_west], t
)
bc_vals = bg.projection(self.nd) @ bc_vals.ravel("F")
return bc_vals
class ConstitutiveLawsAndSource:
def elastic_force(self, sd, sigma_total, time: float) -> np.ndarray:
"""Evaluate exact elastic force [N] at the face centers for a quasi-1D setting.
Parameters:
sd: Subdomain grid.
time: Time in seconds.
Returns:
Array of ``shape=(2 * sd.num_faces, )`` containing the exact elastic force
at the face centers for the given ``time``.
"""
# Symbolic variables
x, y, t = sym.symbols("x y t")
# Get cell centers and face normals
fc = sd.face_centers
fn = sd.face_normals
# Lambdify expression
sigma_total_fun = [
[
sym.lambdify((x, y, t), sigma_total[0][0], "numpy"),
sym.lambdify((x, y, t), sigma_total[0][1], "numpy"),
],
[
sym.lambdify((x, y, t), sigma_total[1][0], "numpy"),
sym.lambdify((x, y, t), sigma_total[1][1], "numpy"),
],
]
# Face-centered elastic force, but in a quasi 1d setting (y-components are zero)
force_total_fc: list[np.ndarray] = [
# (sigma_xx * n_x + sigma_xy * n_y) * face_area
sigma_total_fun[0][0](fc[0], fc[1], time) * fn[0]
+ sigma_total_fun[0][1](fc[0], fc[1], time) * fn[1],
# (sigma_yx * n_x + sigma_yy * n_y) * face_area
sigma_total_fun[1][0](fc[0], fc[1], time) * fn[0]
+ sigma_total_fun[1][1](fc[0], fc[1], time) * fn[1],
]
# Flatten array
force_total_flat: np.ndarray = np.asarray(force_total_fc).ravel("F")
return force_total_flat
def evaluate_mechanics_source(self, f: list, sd: pp.Grid, t: float) -> np.ndarray:
vals = np.zeros((self.nd, sd.num_cells))
return vals.ravel("F")
class ABCModel(
BoundaryConditionsUnitTest,
ConstitutiveLawsAndSource,
DynamicMomentumBalanceABCLinear,
): ...