|
| 1 | +# --- |
| 2 | +# jupyter: |
| 3 | +# jupytext: |
| 4 | +# formats: py:light |
| 5 | +# text_representation: |
| 6 | +# extension: .py |
| 7 | +# format_name: light |
| 8 | +# format_version: '1.5' |
| 9 | +# jupytext_version: 1.16.1 |
| 10 | +# kernelspec: |
| 11 | +# display_name: Python 3 (ipykernel) |
| 12 | +# language: python |
| 13 | +# name: python3 |
| 14 | +# --- |
| 15 | + |
| 16 | +# # Darcy flow (1d) using xy coordinates to define permeability distribution |
| 17 | + |
| 18 | +# to fix trame issue |
| 19 | +import nest_asyncio |
| 20 | +nest_asyncio.apply() |
| 21 | + |
| 22 | +# + editable=true slideshow={"slide_type": ""} |
| 23 | +from petsc4py import PETSc |
| 24 | +import underworld3 as uw |
| 25 | +import numpy as np |
| 26 | +import sympy |
| 27 | +from sympy import Piecewise, ceiling, Abs |
| 28 | +import matplotlib.pyplot as plt |
| 29 | +# %matplotlib inline |
| 30 | + |
| 31 | +options = PETSc.Options() |
| 32 | + |
| 33 | +# + editable=true slideshow={"slide_type": ""} |
| 34 | +# vis tools |
| 35 | +if uw.mpi.size == 1: |
| 36 | + import pyvista as pv |
| 37 | + import underworld3.visualisation as vis |
| 38 | + |
| 39 | +# + editable=true slideshow={"slide_type": ""} |
| 40 | +minX, maxX = -1.0, 0.0 |
| 41 | +minY, maxY = -1.0, 0.0 |
| 42 | + |
| 43 | +mesh = uw.meshing.UnstructuredSimplexBox(minCoords=(minX, minY), maxCoords=(maxX, maxY), |
| 44 | + cellSize=1/25, qdegree=3) |
| 45 | + |
| 46 | +# x and y coordinates |
| 47 | +x = mesh.N.x |
| 48 | +y = mesh.N.y |
| 49 | + |
| 50 | +# + |
| 51 | +# Create Darcy Solver |
| 52 | +darcy = uw.systems.SteadyStateDarcy(mesh) |
| 53 | + |
| 54 | +p_soln = darcy.Unknowns.u |
| 55 | +v_soln = darcy.v |
| 56 | + |
| 57 | +# Needs to be smaller than the contrast in properties |
| 58 | +darcy.petsc_options["snes_rtol"] = 1.0e-6 |
| 59 | + |
| 60 | +darcy.constitutive_model = uw.constitutive_models.DarcyFlowModel |
| 61 | +# - |
| 62 | + |
| 63 | + |
| 64 | +p_soln_0 = p_soln.clone("P_no_g", r"{p_\textrm{(no g)}}") |
| 65 | +v_soln_0 = v_soln.clone("V_no_g", r"{v_\textrm{(no g)}}") |
| 66 | + |
| 67 | + |
| 68 | +# + editable=true slideshow={"slide_type": ""} |
| 69 | +def plot_P_V(_mesh, _p_soln, _v_soln): |
| 70 | + ''' |
| 71 | + Plot pressure and velcity streamlines |
| 72 | + ''' |
| 73 | + pvmesh = vis.mesh_to_pv_mesh(_mesh) |
| 74 | + pvmesh.point_data["P"] = vis.scalar_fn_to_pv_points(pvmesh, _p_soln.sym) |
| 75 | + pvmesh.point_data["V"] = vis.vector_fn_to_pv_points(pvmesh, _v_soln.sym) |
| 76 | + |
| 77 | + velocity_points = vis.meshVariable_to_pv_cloud(_v_soln) |
| 78 | + velocity_points.point_data["V"] = vis.vector_fn_to_pv_points(velocity_points, _v_soln.sym) |
| 79 | + |
| 80 | + # point sources at cell centres |
| 81 | + points = np.zeros((_mesh._centroids.shape[0], 3)) |
| 82 | + points[:, 0] = _mesh._centroids[:, 0] |
| 83 | + points[:, 1] = _mesh._centroids[:, 1] |
| 84 | + point_cloud = pv.PolyData(points[::3]) |
| 85 | + |
| 86 | + pvstream = pvmesh.streamlines_from_source(point_cloud, vectors="V", integrator_type=45, |
| 87 | + integration_direction="both", max_steps=1000, |
| 88 | + max_time=0.1, initial_step_length=0.001, |
| 89 | + max_step_length=0.01,) |
| 90 | + |
| 91 | + pl = pv.Plotter(window_size=(750, 750)) |
| 92 | + pl.add_mesh(pvmesh, cmap="coolwarm", edge_color="Black", show_edges=True, scalars="P", |
| 93 | + use_transparency=False, opacity=1.0,) |
| 94 | + pl.add_mesh(pvstream, line_width=1.0) |
| 95 | + # pl.add_arrows(velocity_points.points, velocity_points.point_data["V"], mag=0.005, opacity=0.75) |
| 96 | + |
| 97 | + pl.show(cpos="xy") |
| 98 | + |
| 99 | + |
| 100 | +# + |
| 101 | +# set up two materials |
| 102 | +interfaceY = -0.25 |
| 103 | +k1 = 1.0 |
| 104 | +k2 = 1.0e-4 |
| 105 | + |
| 106 | +# Groundwater pressure boundary condition on the bottom wall |
| 107 | +max_pressure = 0.5 |
| 108 | + |
| 109 | +# The piecewise version |
| 110 | +kFunc = Piecewise((k1, y >= interfaceY), (k2, y < interfaceY), (1.0, True)) |
| 111 | + |
| 112 | +# A smooth version |
| 113 | +darcy.constitutive_model.Parameters.permeability = kFunc |
| 114 | +darcy.constitutive_model.Parameters.s = sympy.Matrix([0, 0]).T |
| 115 | +darcy.f = 0.0 |
| 116 | + |
| 117 | +# set up boundary conditions |
| 118 | +darcy.add_dirichlet_bc(0.0, "Top") |
| 119 | +darcy.add_dirichlet_bc(-1.0 * minY * max_pressure, "Bottom") |
| 120 | +# + editable=true slideshow={"slide_type": ""} |
| 121 | +# darcy solve without gravity |
| 122 | +darcy.solve() |
| 123 | + |
| 124 | + |
| 125 | +# + editable=true slideshow={"slide_type": ""} |
| 126 | +# plotting soln without gravity |
| 127 | +plot_P_V(mesh, p_soln, v_soln) |
| 128 | + |
| 129 | +# + editable=true slideshow={"slide_type": ""} |
| 130 | +# copying solution |
| 131 | +with mesh.access(p_soln_0, v_soln_0): |
| 132 | + p_soln_0.data[...] = p_soln.data[...] |
| 133 | + v_soln_0.data[...] = v_soln.data[...] |
| 134 | +# - |
| 135 | + |
| 136 | +# now switch on gravity |
| 137 | +darcy.constitutive_model.Parameters.s = sympy.Matrix([0, -1]).T |
| 138 | +darcy.solve() |
| 139 | +# + editable=true slideshow={"slide_type": ""} |
| 140 | +# plotting soln with gravity |
| 141 | +plot_P_V(mesh, p_soln, v_soln) |
| 142 | + |
| 143 | +# + |
| 144 | +# set up interpolation coordinates |
| 145 | +ycoords = np.linspace(minY + 0.001 * (maxY - minY), maxY - 0.001 * (maxY - minY), 100) |
| 146 | +xcoords = np.full_like(ycoords, -0.5) |
| 147 | +xy_coords = np.column_stack([xcoords, ycoords]) |
| 148 | + |
| 149 | +pressure_interp = uw.function.evaluate(p_soln.sym[0], xy_coords) |
| 150 | +pressure_interp_0 = uw.function.evaluate(p_soln_0.sym[0], xy_coords) |
| 151 | + |
| 152 | + |
| 153 | +# + editable=true slideshow={"slide_type": ""} |
| 154 | +La = -1.0 * interfaceY |
| 155 | +Lb = 1.0 + interfaceY |
| 156 | +dP = max_pressure |
| 157 | + |
| 158 | +S = 1 |
| 159 | +Pa = (dP / Lb - S + k1 / k2 * S) / (1.0 / Lb + k1 / k2 / La) |
| 160 | +pressure_analytic = np.piecewise( |
| 161 | + ycoords, |
| 162 | + [ycoords >= -La, ycoords < -La], |
| 163 | + [ |
| 164 | + lambda ycoords: -Pa * ycoords / La, |
| 165 | + lambda ycoords: Pa + (dP - Pa) * (-ycoords - La) / Lb, |
| 166 | + ], |
| 167 | +) |
| 168 | + |
| 169 | +y = sympy.symbols('y') |
| 170 | +P1 = -Pa * y / La |
| 171 | +P2 = Pa + (dP - Pa) * (-y - La) / Lb |
| 172 | +print(P1) |
| 173 | +print(P2) |
| 174 | + |
| 175 | +S = 0 |
| 176 | +Pa = (dP / Lb - S + k1 / k2 * S) / (1.0 / Lb + k1 / k2 / La) |
| 177 | +pressure_analytic_noG = np.piecewise( |
| 178 | + ycoords, |
| 179 | + [ycoords >= -La, ycoords < -La], |
| 180 | + [ |
| 181 | + lambda ycoords: -Pa * ycoords / La, |
| 182 | + lambda ycoords: Pa + (dP - Pa) * (-ycoords - La) / Lb, |
| 183 | + ], |
| 184 | +) |
| 185 | + |
| 186 | +y = sympy.symbols('y') |
| 187 | +P1 = -Pa * y / La |
| 188 | +P2 = Pa + (dP - Pa) * (-y - La) / Lb |
| 189 | +print(P1) |
| 190 | +print(P2) |
| 191 | + |
| 192 | +# + editable=true slideshow={"slide_type": ""} |
| 193 | +# plotting numerical and analytical solution |
| 194 | +fig = plt.figure() |
| 195 | +ax1 = fig.add_subplot(111, xlabel="Pressure", ylabel="Depth") |
| 196 | +ax1.plot(pressure_interp, ycoords, linewidth=3, label="Numerical solution") |
| 197 | +ax1.plot(pressure_interp_0, ycoords, linewidth=3, label="Numerical solution (no G)") |
| 198 | +ax1.plot(pressure_analytic, ycoords, linewidth=3, linestyle="--", label="Analytic solution") |
| 199 | +ax1.plot(pressure_analytic_noG, ycoords, linewidth=3, linestyle="--", label="Analytic (no gravity)") |
| 200 | +ax1.grid("on") |
| 201 | +ax1.legend() |
| 202 | +# + editable=true slideshow={"slide_type": ""} |
| 203 | + |
0 commit comments