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

Hybridisation using firedrake.SCPC #182

Merged
merged 27 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2daa0d3
move serial swe scripts to directory
JHopeCollins Mar 25, 2024
9af754c
broken hdiv projector
JHopeCollins Mar 25, 2024
22ffcb0
lswe serial case with hybrid_scpc
JHopeCollins Mar 25, 2024
0c0b1f4
generalising hybridisation_scpc to any hdiv x l2 form
JHopeCollins Mar 25, 2024
f256edd
factor out HybridisationPC and let it take any form
JHopeCollins Mar 26, 2024
a8cd450
start of merging complex and real hybridscpc
JHopeCollins Mar 26, 2024
a0b8ac3
combine complex-proxy hybridpc into utils HybridisationSCPC
JHopeCollins Mar 26, 2024
6b66930
remove old lswe_rblock script
JHopeCollins Mar 26, 2024
ed64b61
rename lswe complex block script
JHopeCollins Mar 26, 2024
5172f43
starting hybridisation for serial miniapp
JHopeCollins Mar 26, 2024
8f63074
Merge remote-tracking branch 'origin/master' into hybrid_scpc
JHopeCollins Mar 27, 2024
ca96edc
update HybridisationSCPC to use form_mass and form_function
JHopeCollins Mar 27, 2024
f2da3fd
serial miniapp fills the appctx with uref, tref, etc
JHopeCollins Mar 27, 2024
05b83c4
update shallow water scripts using HybridisationSCPC
JHopeCollins Mar 27, 2024
ce54d37
flake8
JHopeCollins Mar 27, 2024
7488bd8
VTKFile in swe miniapp
JHopeCollins Mar 28, 2024
d8422f8
remove duplicate serial lswe script
JHopeCollins Mar 28, 2024
8fee13d
use HybridisedSCPC for lswe paradiag script
JHopeCollins Mar 28, 2024
2ea6f9e
move utils tests to seperate folder
JHopeCollins Apr 4, 2024
16cdfe2
tests for hybridisationSCPC
JHopeCollins Apr 4, 2024
7fca31b
tidy up swe hybridscpc examples
JHopeCollins Apr 8, 2024
2d891a9
Only rebuild the trace solver for HybridisedSCPC when the outer preco…
JHopeCollins Apr 22, 2024
1ee70f6
Invalidate hybridisationSCPC jacobian properly
JHopeCollins Apr 22, 2024
de56cf7
add cmdline option for preconditioning method in serial swe cases
JHopeCollins Apr 23, 2024
f58cb39
run multiple eigenvalues for lswe_cpx
JHopeCollins Apr 23, 2024
0c53401
run multiple eigenvalues for lswe_cpx
JHopeCollins Apr 23, 2024
5b32e36
Merge branch 'master' into hybrid_scpc
JHopeCollins Apr 23, 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
168 changes: 168 additions & 0 deletions case_studies/shallow_water/blockpc/lswe_cpx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import firedrake as fd
from firedrake.petsc import PETSc
from asQ.complex_proxy import vector as cpx

import utils.shallow_water.gravity_bumps as lcase
from utils import shallow_water as swe
from utils.planets import earth
from utils import units
from utils.hybridisation import HybridisedSCPC # noqa: F401

import numpy as np
from scipy.fft import fft, fftfreq

PETSc.Sys.popErrorHandler()
Print = PETSc.Sys.Print

# get command arguments
import argparse
parser = argparse.ArgumentParser(
description='Test preconditioners for the complex linear shallow water equations.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)

parser.add_argument('--dt', type=float, default=1.0, help='Timestep in hours (used to calculate the circulant eigenvalues).')
parser.add_argument('--nt', type=int, default=16, help='Number of timesteps (used to calculate the circulant eigenvalues).')
parser.add_argument('--theta', type=float, default=0.5, help='Parameter for implicit theta method. 0.5 for trapezium rule, 1 for backwards Euler (used to calculate the circulant eigenvalues).')
parser.add_argument('--alpha', type=float, default=1e-3, help='Circulant parameter (used to calculate the circulant eigenvalues).')
parser.add_argument('--eigenvalue', type=int, nargs='+', default=-1, help='Index of the circulant eigenvalues to use for the complex coefficients. -1 for all.')
parser.add_argument('--seed', type=int, default=12345, help='Seed for the random right hand side.')
parser.add_argument('--ref_level', type=int, default=3, help='Icosahedral sphere mesh refinement level with mesh hierarchy. 0 for no mesh hierarchy.')
parser.add_argument('--show_args', action='store_true', help='Output all the arguments.')

args = parser.parse_known_args()
args = args[0]

if args.show_args:
Print(args)

# eigenvalues
nt, theta, alpha = args.nt, args.theta, args.alpha
dt = args.dt*units.hour

eigenvalues = args.eigenvalue if type(args.eigenvalue) is list else [args.eigenvalue]

if len(args.eigenvalue) > 1:
eigenvalues = tuple(eigenvalues)
elif args.eigenvalue[0] < 0:
eigenvalues = tuple(i for i in range(nt//2+1))

gamma = args.alpha**(np.arange(nt)/nt)
C1 = np.zeros(nt)
C2 = np.zeros(nt)

C1[:2] = [1/dt, -1/dt]
C2[:2] = [theta, 1-theta]

D1 = np.sqrt(nt)*fft(gamma*C1)
D2 = np.sqrt(nt)*fft(gamma*C2)
freqs = fftfreq(nt, dt)

d1 = D1[eigenvalues[0]]
d2 = D2[eigenvalues[0]]

d1c = cpx.ComplexConstant(d1)
d2c = cpx.ComplexConstant(d2)

dhat = (d1/d2) / (1/(theta*dt))

mesh = swe.create_mg_globe_mesh(ref_level=args.ref_level,
coords_degree=1)
x = fd.SpatialCoordinate(mesh)

# case parameters
g = earth.Gravity
f = lcase.coriolis_expression(*x)
H = lcase.H

# function spaces
V = swe.default_function_space(mesh)
W = cpx.FunctionSpace(V)


# shallow water equation forms
def form_mass(u, h, v, q):
return swe.linear.form_mass(mesh, u, h, v, q)


def form_function(u, h, v, q, t=None):
return swe.linear.form_function(mesh, g, H, f,
u, h, v, q, t)


# random rhs
L = fd.Cofunction(W.dual())

# PETSc solver parameters
lu_params = {
'ksp_type': 'preonly',
'pc_type': 'lu',
'pc_factor_mat_solver_type': 'mumps'
}

condensed_params = lu_params

scpc_params = {
"ksp_type": 'preonly',
"mat_type": "matfree",
"pc_type": "python",
"pc_python_type": f"{__name__}.HybridisedSCPC",
"hybridscpc_condensed_field": lu_params
}

rtol = 1e-3
params = {
'ksp': {
'monitor': None,
'converged_rate': None,
'rtol': rtol,
},
}
params.update(scpc_params)

# trace component should have zero rhs
np.random.seed(args.seed)
L.assign(0)
for dat in L.dat:
dat.data[:] = np.random.rand(*(dat.data.shape))

# block forms
M = cpx.BilinearForm(W, d1c, form_mass)
K = cpx.BilinearForm(W, d2c, form_function)

A = M + K

appctx = {
'cpx': cpx,
'uref': fd.Function(W),
'bcs': None,
'tref': None,
'form_mass': form_mass,
'form_function': form_function,
'd1': d1c,
'd2': d2c,
}

wout = fd.Function(W).assign(0)
problem = fd.LinearVariationalProblem(A, L, wout)
solver = fd.LinearVariationalSolver(problem, appctx=appctx,
solver_parameters=params)

for j in eigenvalues:
d1, d2 = D1[j], D2[j]

dhat = (d1/d2) / (1/(theta*dt))
Print(f"dhat = {np.round(dhat, 4)}")

d1c.real.assign(d1.real)
d1c.imag.assign(d1.imag)

d2c.real.assign(d2.real)
d2c.imag.assign(d2.imag)

L.assign(0)
for dat in L.dat:
dat.data[:] = np.random.rand(*(dat.data.shape))
wout.assign(0)

solver.solve()
119 changes: 119 additions & 0 deletions case_studies/shallow_water/blockpc/lswe_rblock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import firedrake as fd
from firedrake.petsc import PETSc

import utils.shallow_water.gravity_bumps as lcase
from utils import shallow_water as swe
from utils.planets import earth
from utils import units
from utils.hybridisation import HybridisedSCPC # noqa: F401

import numpy as np

PETSc.Sys.popErrorHandler()
Print = PETSc.Sys.Print


# get command arguments
import argparse
parser = argparse.ArgumentParser(
description='Test preconditioners for the real-valued linear shallow water equations.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)

parser.add_argument('--dt', type=float, default=1.0, help='Timestep in hours (used to calculate the circulant eigenvalues).')
parser.add_argument('--theta', type=float, default=0.5, help='Parameter for implicit theta method. 0.5 for trapezium rule, 1 for backwards Euler (used to calculate the circulant eigenvalues).')
parser.add_argument('--seed', type=int, default=12345, help='Seed for the random right hand side.')
parser.add_argument('--ref_level', type=int, default=3, help='Icosahedral sphere mesh refinement level with mesh hierarchy. 0 for no mesh hierarchy.')
parser.add_argument('--show_args', action='store_true', help='Output all the arguments.')

args = parser.parse_known_args()
args = args[0]

if args.show_args:
Print(args)

# eigenvalues
theta = args.theta
dt = args.dt*units.hour

d1 = fd.Constant(1/dt)
d2 = fd.Constant(theta)

mesh = swe.create_mg_globe_mesh(ref_level=args.ref_level,
coords_degree=1)
x = fd.SpatialCoordinate(mesh)

# case parameters
g = earth.Gravity
f = lcase.coriolis_expression(*x)
H = lcase.H


# shallow water equation forms
def form_mass(u, h, v, q):
return swe.linear.form_mass(mesh, u, h, v, q)


def form_function(u, h, v, q, t=None):
return swe.linear.form_function(mesh, g, H, f,
u, h, v, q, t)


V = swe.default_function_space(mesh, degree=0)

# random rhs
L = fd.Cofunction(V.dual())

# PETSc solver parameters
lu_params = {
'ksp_type': 'preonly',
'pc_type': 'lu',
'pc_factor_mat_solver_type': 'petsc'
}

hybrid_params = {
'mat_type': 'matfree',
'ksp_type': 'preonly',
'pc_type': 'python',
'pc_python_type': f'{__name__}.HybridisedSCPC',
'hybridscpc_condensed_field': lu_params
}

sparams = {
'ksp': {
'monitor': None,
'converged_rate': None,
},
}
sparams.update(hybrid_params)

# trace component should have zero rhs
np.random.seed(args.seed)
L.assign(0)
for ldat in L.dat:
ldat.data[:] = np.random.rand(*(ldat.data.shape))

# block forms
us = fd.TrialFunctions(V)
vs = fd.TestFunctions(V)

M = form_mass(*us, *vs)
K = form_function(*us, *vs)

A = d1*M + d2*K

appctx = {
'uref': fd.Function(V),
'bcs': None,
'tref': None,
'form_mass': form_mass,
'form_function': form_function,
'dt': dt,
'theta': theta,
}

wout = fd.Function(V)
problem = fd.LinearVariationalProblem(A, L, wout)
solver = fd.LinearVariationalSolver(problem, appctx=appctx,
solver_parameters=sparams)
solver.solve()
74 changes: 18 additions & 56 deletions case_studies/shallow_water/linear_gravity_bumps.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,84 +47,46 @@

# parameters for the implicit diagonal solve in step-(b)

patch_parameters = {
'pc_patch': {
'save_operators': True,
'partition_of_unity': True,
'sub_mat_type': 'seqdense',
'construct_dim': 0,
'construct_type': 'vanka',
'local_type': 'additive',
'precompute_element_tensors': True,
'symmetrise_sweep': False
},
'sub': {
from utils.hybridisation import HybridisedSCPC # noqa: F401
hybridscpc_parameters = {
"ksp_type": 'preonly',
"mat_type": "matfree",
"pc_type": "python",
"pc_python_type": f"{__name__}.HybridisedSCPC",
"hybridscpc_condensed_field": {
'ksp_type': 'preonly',
'pc_type': 'lu',
'pc_factor_shift_type': 'nonzero',
}
}

from utils.mg import ManifoldTransferManager # noqa: F401
mg_parameters = {
'transfer_manager': f'{__name__}.ManifoldTransferManager',
'levels': {
'ksp_type': 'gmres',
'ksp_max_it': 5,
'pc_type': 'python',
'pc_python_type': 'firedrake.PatchPC',
'patch': patch_parameters
},
'coarse': {
'pc_type': 'python',
'pc_python_type': 'firedrake.AssembledPC',
'assembled_pc_type': 'lu',
'assembled_pc_factor_mat_solver_type': 'mumps'
'pc_factor_mat_solver_type': 'mumps',
'snes': { # reuse factorisation
'lag_jacobian': -2,
'lag_jacobian_persists': None,
'lag_preconditioner': -2,
'lag_preconditioner_persists': None,
}
}
}

sparameters = {
'mat_type': 'matfree',
'ksp_type': 'fgmres',
'ksp': {
'atol': 1e-5,
'rtol': 1e-5,
'max_it': 60
},
'pc_type': 'mg',
'pc_mg_cycle_type': 'w',
'pc_mg_type': 'multiplicative',
'mg': mg_parameters
}

rtol = 1e-10
atol = 1e0
sparameters_diag = {
'snes_type': 'ksponly',
'snes': {
'linesearch_type': 'basic',
'monitor': None,
'converged_reason': None,
'rtol': rtol,
'atol': atol,
},
'mat_type': 'matfree',
'ksp_type': 'fgmres',
'ksp': {
'monitor': None,
'converged_reason': None,
'converged_rate': None,
'rtol': rtol,
'atol': atol,
},
'pc_type': 'python',
'pc_python_type': 'asQ.CirculantPC',
'diagfft_alpha': args.alpha,
'diagfft_state': 'linear',
'circulant_alpha': args.alpha,
'circulant_state': 'linear',
'aaos_jacobian_state': 'linear',
}

for i in range(window_length):
sparameters_diag['diagfft_block_'+str(i)+'_'] = sparameters
sparameters_diag['circulant_block_'+str(i)+'_'] = hybridscpc_parameters

create_mesh = partial(
swe.create_mg_globe_mesh,
Expand Down
Loading
Loading