Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linear stability analysis functionality #176

Merged
merged 44 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2282177
Helmholtz eigenvalue problem example
jcallaham Mar 7, 2024
8747dff
[WIP] testing direct stability on cylinder
jcallaham Mar 7, 2024
dfd4f3b
Link to old code with working SLEPc call
jcallaham Mar 7, 2024
3f348a2
Derive linear control term
jcallaham Mar 7, 2024
773e56f
Simple test problems for KS algorithm
jcallaham Mar 7, 2024
d531af5
Arnoldi example in Firedrake with heat equation
jcallaham Mar 8, 2024
a7b97ca
Debugging KS in Firedrake
jcallaham Mar 11, 2024
a59214a
Working Krylov-Schur on heat equation
jcallaham Mar 11, 2024
616ba0a
Merge branch 'main' into jc/stability-analysis
jcallaham Mar 12, 2024
ee67933
Merge branch 'main' into jc/stability-analysis
jcallaham Mar 12, 2024
6446c4c
Add linearized NS solver
jcallaham Mar 12, 2024
cfdbd17
Add base flow forcing terms
jcallaham Mar 12, 2024
cf1b8e9
Fixed Arnoldi iteration with Navier-Stokes mass matrix
jcallaham Mar 12, 2024
a4a62e1
Tweaking Arnoldi config
jcallaham Mar 12, 2024
98f51b4
Testing shift-invert on heat eqution
jcallaham Mar 14, 2024
50ffe33
Working cylinder stability with spectral transformation
jcallaham Mar 14, 2024
0382362
Add spectral transformation script
jcallaham Mar 14, 2024
d6e1f8d
Sorting ritz vals
jcallaham Mar 14, 2024
29f32c1
Partial cleanup of cyl_st script
jcallaham Mar 14, 2024
200854b
Cleaned up cyl_st. Save before making the analysis generic
jcallaham Mar 14, 2024
91ad43e
Output formatting
jcallaham Mar 14, 2024
81b0440
Non-crashing stability analysis for cavity
jcallaham Mar 14, 2024
be26256
Working shift-invert
jcallaham Mar 16, 2024
b6ac668
Add general-purpose 'residual' method
jcallaham Mar 16, 2024
fc606f5
Filtering duplicate eigenvalues
jcallaham Mar 16, 2024
411b4f5
Working general functions with filtered eigenvalues
jcallaham Mar 16, 2024
114ae06
Shift-invert Arnoldi test on cavity
jcallaham Mar 16, 2024
172ac05
[WIP] adding CLI args to cyl script
jcallaham Mar 17, 2024
87fc267
Clean up examples/cylinder/stability/
jcallaham Mar 18, 2024
510371e
Combined Arnoldi and K-S into one function
jcallaham Mar 18, 2024
0b0f8ea
Move operatorto __matmul__
jcallaham Mar 18, 2024
a6881f0
Pre-assemble linear operator
jcallaham Mar 18, 2024
537b2f8
Separate code for linear stability and linear operators
jcallaham Mar 18, 2024
6ac58e3
Cavity stability analysis
jcallaham Mar 19, 2024
2e105c0
Fix render method for cavity and step
jcallaham Mar 20, 2024
8379510
Move common functionality to utils.stability
jcallaham Mar 20, 2024
3c814de
Black
jcallaham Mar 20, 2024
840d9a6
yapf
jcallaham Mar 20, 2024
892d844
Merge branch 'main' into jc/stability-analysis
jcallaham Mar 20, 2024
b75c40f
Fix codespell
jcallaham Mar 20, 2024
bb5b103
Undo re-running gmsh:
jcallaham Mar 20, 2024
94503bf
Remove examples/cavity/stability/
jcallaham Mar 20, 2024
76a0f7a
Final cleanup of examples/
jcallaham Mar 20, 2024
909d857
codespell complaint
jcallaham Mar 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 179 additions & 0 deletions examples/cavity/stability.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
"""
To get the leading mode, run with:

```
mpiexec -np 20 python stability.py --output-dir eig_output --sigma 1.0+11j
```
"""

import argparse
import os

# from cyl_common import base_checkpoint, evec_checkpoint, flow
from typing import NamedTuple

import firedrake as fd
import numpy as np

import hydrogym.firedrake as hgym

parser = argparse.ArgumentParser(
description="Stability analysis of the open cavity flow.")
parser.add_argument(
"--mesh",
default="fine",
type=str,
help="Identifier for the mesh resolution",
)
parser.add_argument(
"--reynolds",
default=7500.0,
type=float,
help="Reynolds number of the flow",
)
parser.add_argument(
"--krylov-dim",
default=100,
type=int,
help="Dimension of the Krylov subspace (number of Arnoldi vectors)",
)
parser.add_argument(
"--tol",
default=1e-10,
type=float,
help="Tolerance to use for determining converged eigenvalues.",
)
parser.add_argument(
"--schur",
action="store_true",
dest="schur",
default=False,
help="Use Krylov-Schur iteration to restart the Arnoldi process.",
)
parser.add_argument(
"--no-adjoint",
action="store_true",
default=False,
help="Skip computing the adjoint modes.",
)
parser.add_argument(
"--sigma",
default=0.0,
type=complex,
help="Shift for the shift-invert Arnoldi method.",
)
parser.add_argument(
"--base-flow",
type=str,
help="Path to the HDF5 checkpoint containing the base flow.",
)
parser.add_argument(
"--output-dir",
type=str,
default="eig_output",
help="Directory in which output files will be stored.",
)
if __name__ == "__main__":
args = parser.parse_args()

mesh = args.mesh
m = args.krylov_dim
sigma = args.sigma
tol = args.tol
Re = args.reynolds

output_dir = args.output_dir
if not os.path.exists(output_dir):
os.makedirs(output_dir)

velocity_order = 2
stabilization = "none"

flow = hgym.Cavity(
Re=Re,
mesh=mesh,
velocity_order=velocity_order,
)

dof = flow.mixed_space.dim()

hgym.print("|--------------------------------------------------|")
hgym.print("| Linear stability analysis of the open cavity flow |")
hgym.print("|--------------------------------------------------|")
hgym.print(f"Reynolds number: {Re}")
hgym.print(f"Krylov dimension: {m}")
hgym.print(f"Spectral shift: {sigma}")
hgym.print(f"Include adjoint modes: {not args.no_adjoint}")
hgym.print(f"Krylov-Schur restart: {args.schur}")
hgym.print(f"Total dof: {dof} --- dof/rank: {int(dof/fd.COMM_WORLD.size)}")
hgym.print("")

if args.base_flow:
hgym.print(f"Loading base flow from checkpoint {args.base_flow}...")
flow.load_checkpoint(args.base_flow)

else:

# Since this flow is at high Reynolds number we have to
# ramp to get the steady state
hgym.print("Solving the steady-state problem...")

steady_solver = hgym.NewtonSolver(
flow,
stabilization=stabilization,
solver_parameters={"snes_monitor": None})
Re_init = [500, 1000, 2000, 4000, Re]

for i, Re in enumerate(Re_init):
flow.Re.assign(Re)
hgym.print(f"Steady solve at Re={Re_init[i]}")
qB = steady_solver.solve()

flow.save_checkpoint(f"{args.output_dir}/base.h5")

hgym.print("Computing direct modes...")
dir_results = hgym.utils.stability_analysis(
flow, sigma, m, tol, schur_restart=args.schur, adjoint=False)
np.save(f"{output_dir}/raw_evals", dir_results.raw_evals)

# Save checkpoints
evals = dir_results.evals
eval_data = np.zeros((len(evals), 3), dtype=np.float64)
eval_data[:, 0] = evals.real
eval_data[:, 1] = evals.imag
eval_data[:, 2] = dir_results.residuals
np.save(f"{output_dir}/evals", eval_data)

with fd.CheckpointFile(f"{output_dir}/evecs.h5", "w") as chk:
for i in range(len(evals)):
chk.save_mesh(flow.mesh)
dir_results.evecs_real[i].rename(f"evec_{i}_re")
chk.save_function(dir_results.evecs_real[i])
dir_results.evecs_imag[i].rename(f"evec_{i}_im")
chk.save_function(dir_results.evecs_imag[i])

if not args.no_adjoint:
hgym.print("Computing adjoint modes...")
adj_results = hgym.utils.stability_analysis(
flow, sigma, m, tol, adjoint=True)

# Save checkpoints
evals = adj_results.evals
eval_data = np.zeros((len(evals), 3), dtype=np.float64)
eval_data[:, 0] = evals.real
eval_data[:, 1] = evals.imag
eval_data[:, 2] = adj_results.residuals
np.save(f"{output_dir}/adj_evals", eval_data)

with fd.CheckpointFile(f"{output_dir}/adj_evecs.h5", "w") as chk:
for i in range(len(evals)):
chk.save_mesh(flow.mesh)
adj_results.evecs_real[i].rename(f"evec_{i}_re")
chk.save_function(adj_results.evecs_real[i])
adj_results.evecs_imag[i].rename(f"evec_{i}_im")
chk.save_function(adj_results.evecs_imag[i])

hgym.print(
"NOTE: If there is a warning following this, ignore it. It is raised by PETSc "
"CLI argument handling and not argparse. It does not indicate that any CLI "
"arguments are ignored by this script.")
65 changes: 65 additions & 0 deletions examples/cylinder/lti_system.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""Illustrate constructing an LTI system from a flow configuration.

This is a work in progress - so far it's just the base flow and the control
vector. Ultimately it will need LinearOperator functionality to have the form

````
x' = A*x + B*u
y = C*x + D*u
```

where `x` is a firedrake.Function and `u` and `y` are numpy arrays.
"""
import os

import firedrake as fd
import matplotlib.pyplot as plt
from firedrake.pyplot import tripcolor
from ufl import dx, inner

import hydrogym.firedrake as hgym

show_plots = False
output_dir = "output"
if not os.path.exists(output_dir):
os.makedirs(output_dir)

flow = hgym.RotaryCylinder(Re=100, mesh="medium")

# 1. Compute base flow
steady_solver = hgym.NewtonSolver(flow)
qB = steady_solver.solve()
qB.rename("qB")

# Check lift/drag
print(flow.compute_forces(qB))

if show_plots:
vort = flow.vorticity(qB.subfunctions[0])
fig, ax = plt.subplots(1, 1, figsize=(6, 3))
tripcolor(vort, axes=ax, cmap="RdBu", vmin=-2, vmax=2)

# 2. Derive flow field associated with actuation BC
# See Barbagallo et al. (2009) for details on the "lifting" procedure
F = steady_solver.steady_form(qB) # Nonlinear variational form
J = fd.derivative(F, qB) # Jacobian with automatic differentiation

flow.linearize_bcs(mixed=True)
flow.set_control([1.0])
bcs = flow.collect_bcs()

# Solve steady, inhomogeneous problem
qC = fd.Function(flow.mixed_space, name="qC")
v, s = fd.TestFunctions(flow.mixed_space)
zero = inner(fd.Constant((0.0, 0.0)), v) * dx
fd.solve(J == zero, qC, bcs=bcs)

if show_plots:
vort = flow.vorticity(qC.subfunctions[0])
fig, ax = plt.subplots(1, 1, figsize=(6, 3))
tripcolor(vort, axes=ax, cmap="RdBu", vmin=-2, vmax=2)

with fd.CheckpointFile(f"{output_dir}/lin_fields.h5", "w") as chk:
chk.save_mesh(flow.mesh)
chk.save_function(qB)
chk.save_function(qC)
1 change: 1 addition & 0 deletions examples/cylinder/pressure-probes.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
Simulate the flow around the cylinder with evenly spaced surface pressure probes.
"""

import os
import matplotlib.pyplot as plt
import numpy as np
Expand Down
Loading
Loading