import underworld3 as uw
import numpy as np
import sympy
Notebook 7: Unsteady Flow

We’ll look at tracking an unsteady flow using a swarm of particle flow-tracers.
To begin with, the set up follows the same path as all previous notebooks: - Create a mesh - Add some variables - Create the solver we need (NavierStokes
this time) - Add boundary conditions and constitutive properties.
We also add a projection solver to compute the vorticity of the flow (see 4-
To track the time evolution of the flow, we introduce a “passive” particle swarm. Passive, here, refers to the fact that the flow is not changed by the presence of the marker particles.
In the time-loop we have to update the particle locations and we keep this as an explicity operation, in general, because it provide the opportunity for you to make changes or perform analyses. In this case, we are adding new particles near the inflow to track the flow.
= 8
res = 4
width
= 1000
reynolds_number
= uw.meshing.UnstructuredSimplexBox(
mesh =1/res,
cellSize=(0.0,0.0),
minCoords=(width, 1.0),
maxCoords=3,
qdegree
)
# Coordinate directions etc
= mesh.CoordinateSystem.X
x, y
# Mesh variables for the unknowns
= uw.discretisation.MeshVariable("V0", mesh, 2, degree=2, varsymbol=r"{v_0}")
v_soln = uw.discretisation.MeshVariable("p", mesh, 1, degree=1, continuous=True)
p_soln = uw.discretisation.MeshVariable("omega", mesh, 1, degree=1, continuous=True, varsymbol=r"\omega") vorticity
= uw.systems.NavierStokes(
navier_stokes
mesh, =v_soln,
velocityField=p_soln,
pressureField=reynolds_number,
rho=1,
order
)
= uw.constitutive_models.ViscousFlowModel
navier_stokes.constitutive_model = 1
navier_stokes.constitutive_model.Parameters.shear_viscosity_0 = 1.0e-3
navier_stokes.tolerance
"fieldsplit_velocity_mg_coarse_pc_type"] = "svd"
navier_stokes.petsc_options[
= sympy.Matrix((0,0))
navier_stokes.bodyforce
# Inflow boundary - incoming jet
4*y*(1-y))**8, 0), "Left")
navier_stokes.add_essential_bc(((0, 0), "Bottom")
navier_stokes.add_essential_bc((0, 0), "Top") navier_stokes.add_essential_bc((
= uw.systems.Projection(mesh, vorticity)
vorticity_from_v = mesh.vector.curl(v_soln.sym)
vorticity_from_v.uw_function = 1.0e-3
vorticity_from_v.smoothing # nodal_vorticity_from_v.petsc_options.delValue("ksp_monitor")
= uw.swarm.Swarm(mesh=mesh)
passive_swarm
passive_swarm.populate(=0,
fill_param
)
# add new points at the inflow
= 100
new_points = np.array([0.0, 0.25] + 0.5 * np.random.random((new_points, 2)))
new_coords passive_swarm.add_particles_with_coordinates(new_coords)
=0.01)
navier_stokes.solve(timestep vorticity_from_v.solve()
# Keep the initialisation separate
# so we can run the loop below again without resetting
# the timer.
= 50
max_steps = 0
timestep = 0.0
elapsed_time = 0.05 delta_t
for step in range(0, max_steps):
=False, timestep=delta_t)
navier_stokes.solve(zero_init_guess
=2, corrector=False, evalf=True)
passive_swarm.advection(v_soln.sym, delta_t, order
= 100
new_points = np.array([0.0, 0.25] + 0.5 * np.random.random((new_points, 2)))
new_coords
passive_swarm.add_particles_with_coordinates(new_coords)
# Save the data at every 10th step
if timestep % 10 == 0:
vorticity_from_v.solve()
mesh.write_timestep("Example_7",
=True,
meshUpdates=[p_soln, v_soln, vorticity],
meshVars="Example_output",
outputPath=timestep,
index
)
passive_swarm.write_timestep("Example_7",
"passive_swarm",
=None,
swarmVars="Example_output",
outputPath=timestep,
index=True,
force_sequential
)
+= 1
timestep += delta_t
elapsed_time
print(f"Timestep: {timestep}, time {elapsed_time:.4f}")
Timestep: 1, time 0.0500
Timestep: 2, time 0.1000
Timestep: 3, time 0.1500
Timestep: 4, time 0.2000
Timestep: 5, time 0.2500
Timestep: 6, time 0.3000
Timestep: 7, time 0.3500
Timestep: 8, time 0.4000
Timestep: 9, time 0.4500
Timestep: 10, time 0.5000
Timestep: 11, time 0.5500
Timestep: 12, time 0.6000
Timestep: 13, time 0.6500
Timestep: 14, time 0.7000
Timestep: 15, time 0.7500
Timestep: 16, time 0.8000
Timestep: 17, time 0.8500
Timestep: 18, time 0.9000
Timestep: 19, time 0.9500
Timestep: 20, time 1.0000
Timestep: 21, time 1.0500
Timestep: 22, time 1.1000
Timestep: 23, time 1.1500
Timestep: 24, time 1.2000
Timestep: 25, time 1.2500
Timestep: 26, time 1.3000
Timestep: 27, time 1.3500
Timestep: 28, time 1.4000
Timestep: 29, time 1.4500
Timestep: 30, time 1.5000
Timestep: 31, time 1.5500
Timestep: 32, time 1.6000
Timestep: 33, time 1.6500
Timestep: 34, time 1.7000
Timestep: 35, time 1.7500
Timestep: 36, time 1.8000
Timestep: 37, time 1.8500
Timestep: 38, time 1.9000
Timestep: 39, time 1.9500
Timestep: 40, time 2.0000
Timestep: 41, time 2.0500
Timestep: 42, time 2.1000
Timestep: 43, time 2.1500
Timestep: 44, time 2.2000
Timestep: 45, time 2.2500
Timestep: 46, time 2.3000
Timestep: 47, time 2.3500
Timestep: 48, time 2.4000
Timestep: 49, time 2.4500
Timestep: 50, time 2.5000
# visualise it
if uw.mpi.size == 1:
import pyvista as pv
import underworld3.visualisation as vis
= vis.mesh_to_pv_mesh(mesh)
pvmesh "P"] = vis.scalar_fn_to_pv_points(pvmesh, p_soln.sym)
pvmesh.point_data["Omega"] = vis.scalar_fn_to_pv_points(pvmesh, vorticity.sym)
pvmesh.point_data["V"] = vis.vector_fn_to_pv_points(pvmesh, v_soln.sym)
pvmesh.point_data[
= vis.meshVariable_to_pv_mesh_object(v_soln, alpha=None)
pvmesh_v "V"] = vis.vector_fn_to_pv_points(pvmesh_v, v_soln.sym)
pvmesh_v.point_data[
= 1
skip = np.zeros((mesh._centroids[::skip].shape[0], 3))
points 0] = mesh._centroids[::skip, 0]
points[:, 1] = mesh._centroids[::skip, 1]
points[:, = pv.PolyData(points)
point_cloud
= pvmesh.streamlines_from_source(
pvstream ="V",
point_cloud, vectors="both",
integration_direction=45,
integrator_type=True,
surface_streamlines=0.01,
initial_step_length=1.0,
max_time=500,
max_steps
)
= uw.visualisation.swarm_to_pv_cloud(passive_swarm)
passive_swarm_points
= pv.Plotter(window_size=(750, 750))
pl
pl.add_mesh(
pvmesh,="RdBu_r",
cmap="Grey",
edge_color=0.33,
edge_opacity="Omega",
scalars=True,
show_edges=False,
use_transparency=0.75,
opacity=False,
show_scalar_bar
)
# Optional: plot streamlines
# pl.add_mesh(pvstream, opacity=0.3, show_scalar_bar=False, cmap="Greys_r", render_lines_as_tubes=False)
pl.add_points(
passive_swarm_points,="Black",
color=False,
render_points_as_spheres=4,
point_size=0.33,
opacity
)
# pl.add_arrows(pvmesh_v.points, pvmesh_v.point_data["V"], mag=0.1)
#pl.camera_position = 'xy'
= (2.0, 0.5, 4)
pl.camera.position
f"html5/ns_flow_plot_{timestep}.html")
pl.export_html(
f"ns_flow_at_{timestep}.png", window_size=(4000,1000), return_img=False) pl.screenshot(
from IPython.display import IFrame
=f"html5/ns_flow_plot_{timestep}.html", width=1000, height=400) IFrame(src
Interactive Image: Convection model output
Exercise - 7.1
Now that you have seen the structure of the flow in this example, can you adjust the addition of particles to capture the structure more clearly ?
This is a very low-resolution example. You can try increasing the resolution but you will see that this would benefit from running in parallel. In parallel we should remove all the visualisation and visualise the h5 output files when we are done. The rest of the code is parallel-safe.