-
Notifications
You must be signed in to change notification settings - Fork 10
/
PETScNonlinearSolvers.jl
188 lines (151 loc) · 6.61 KB
/
PETScNonlinearSolvers.jl
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
mutable struct PETScNonlinearSolver{F} <: NonlinearSolver
setup::F
end
mutable struct PETScNonlinearSolverCache{A,B,C,D,E}
initialized::Bool
comm::MPI.Comm
snes::Ref{SNES}
op::NonlinearOperator
# The input vector to solve!
x_fe_space_layout::A
# Julia LA data structures
x_sys_layout::B
res_sys_layout::B
jac_mat_A::C
jac_mat_P::C
# Counterpart PETSc data structures
x_petsc::D
res_petsc::D
jac_petsc_mat_A::E
jac_petsc_mat_P::E
function PETScNonlinearSolverCache(comm::MPI.Comm, snes::Ref{SNES}, op::NonlinearOperator,
x_fe_space_layout::A,
x_sys_layout::B, res_sys_layout::B,
jac_mat_A::C, jac_mat_P::C,
x_petsc::D, res_petsc::D,
jac_petsc_mat_A::E, jac_petsc_mat_P::E) where {A,B,C,D,E}
cache=new{A,B,C,D,E}(true, comm,
snes, op,
x_fe_space_layout,
x_sys_layout, res_sys_layout,
jac_mat_A, jac_mat_P,
x_petsc, res_petsc,
jac_petsc_mat_A, jac_petsc_mat_P)
@assert Threads.threadid() == 1
_NREFS[] += 1
finalizer(Finalize,cache)
end
end
function snes_residual(csnes::Ptr{Cvoid},
cx::Ptr{Cvoid},
cfx::Ptr{Cvoid},
ctx::Ptr{Cvoid})::PetscInt
cache = unsafe_pointer_to_objref(ctx)
# 1. Transfer cx to Julia data structures
_copy!(cache.x_sys_layout, Vec(cx))
copy!(cache.x_fe_space_layout,cache.x_sys_layout)
# 2. Evaluate residual into Julia data structures
residual!(cache.res_sys_layout, cache.op, cache.x_fe_space_layout)
# 3. Transfer Julia residual to PETSc residual (cfx)
_copy!(Vec(cfx), cache.res_sys_layout)
return PetscInt(0)
end
function snes_jacobian(csnes:: Ptr{Cvoid},
cx :: Ptr{Cvoid},
cA :: Ptr{Cvoid},
cP :: Ptr{Cvoid},
ctx::Ptr{Cvoid})::PetscInt
cache = unsafe_pointer_to_objref(ctx)
# 1. Transfer cx to Julia data structures
# Extract pointer to array of values out of cx and put it in a PVector
_copy!(cache.x_sys_layout, Vec(cx))
copy!(cache.x_fe_space_layout,cache.x_sys_layout)
# 2. Evaluate Jacobian into Julia data structures
jacobian!(cache.jac_mat_A,cache.op,cache.x_fe_space_layout)
# 3. Transfer nls.jac_mat_A/P to PETSc (cA/cP)
_copy!(Mat(cA), cache.jac_mat_A)
return PetscInt(0)
end
function Finalize(cache::PETScNonlinearSolverCache)
if GridapPETSc.Initialized() && cache.initialized
GridapPETSc.Finalize(cache.x_petsc)
GridapPETSc.Finalize(cache.res_petsc)
GridapPETSc.Finalize(cache.jac_petsc_mat_A)
if !(cache.jac_petsc_mat_P === cache.jac_petsc_mat_A)
GridapPETSc.Finalize(cache.jac_petsc_mat_P)
end
if cache.comm == MPI.COMM_SELF
@check_error_code PETSC.SNESDestroy(cache.snes)
else
@check_error_code PETSC.PetscObjectRegisterDestroy(cache.snes[].ptr)
end
@assert Threads.threadid() == 1
cache.initialized=false
_NREFS[] -= 1
end
nothing
end
snes_from_options(snes) = @check_error_code PETSC.SNESSetFromOptions(snes[])
function PETScNonlinearSolver()
PETScNonlinearSolver(snes_from_options)
end
function _set_petsc_residual_function!(nls::PETScNonlinearSolver, cache)
ctx = pointer_from_objref(cache)
fptr = @cfunction(snes_residual, PetscInt, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}))
PETSC.SNESSetFunction(cache.snes[],cache.res_petsc.vec[],fptr,ctx)
end
function _set_petsc_jacobian_function!(nls::PETScNonlinearSolver, cache)
ctx = pointer_from_objref(cache)
fptr = @cfunction(snes_jacobian, PetscInt, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid},Ptr{Cvoid}))
PETSC.SNESSetJacobian(cache.snes[],cache.jac_petsc_mat_A.mat[],cache.jac_petsc_mat_A.mat[],fptr,ctx)
end
function _setup_cache(x::AbstractVector,nls::PETScNonlinearSolver,op::NonlinearOperator)
res_sys_layout, jac_mat_A = residual_and_jacobian(op,x)
res_petsc = convert(PETScVector,res_sys_layout)
jac_petsc_mat_A = convert(PETScMatrix,jac_mat_A)
# In a parallel MPI context, x is a vector with a data layout typically different from
# the one of res_sys_layout. On the one hand, x holds the free dof values of a FE
# Function, and thus has the data layout of the FE space (i.e., local DOFs
# include all DOFs touched by local cells, i.e., owned and ghost cells).
# On the other hand, res_petsc has the data layout of the rows of the
# distributed linear system (e.g., local DoFs only include those touched from owned
# cells/facets during assembly, assuming the SubAssembledRows strategy).
# The following lines of code generate a version of x, namely, x_sys_layout, with the
# same data layout as the columns of jac_mat_A, but the contents of x
# (for the owned dof values).
x_sys_layout = similar(res_sys_layout,eltype(res_sys_layout),(axes(jac_mat_A)[2],))
copy!(x_sys_layout,x)
x_petsc = convert(PETScVector,x_sys_layout)
snes_ref=Ref{SNES}()
@check_error_code PETSC.SNESCreate(jac_petsc_mat_A.comm,snes_ref)
PETScNonlinearSolverCache(jac_petsc_mat_A.comm, snes_ref, op, x, x_sys_layout, res_sys_layout,
jac_mat_A, jac_mat_A,
x_petsc, res_petsc,
jac_petsc_mat_A, jac_petsc_mat_A)
end
function Algebra.solve!(x::T,
nls::PETScNonlinearSolver,
op::NonlinearOperator,
cache::PETScNonlinearSolverCache{<:T}) where T <: AbstractVector
@assert cache.op === op
@check_error_code PETSC.SNESSolve(cache.snes[],C_NULL,cache.x_petsc.vec[])
copy!(x,cache.x_petsc)
cache
end
function Algebra.solve!(x::AbstractVector,nls::PETScNonlinearSolver,op::NonlinearOperator,::Nothing)
cache=_setup_cache(x,nls,op)
if (cache.comm != MPI.COMM_SELF)
gridap_petsc_gc() # Do garbage collection of PETSc objects
end
# set petsc residual function
ctx = pointer_from_objref(cache)
fptr = @cfunction(snes_residual, PetscInt, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}))
PETSC.SNESSetFunction(cache.snes[],cache.res_petsc.vec[],fptr,ctx)
# set petsc jacobian function
fptr = @cfunction(snes_jacobian, PetscInt, (Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid},Ptr{Cvoid}))
PETSC.SNESSetJacobian(cache.snes[],cache.jac_petsc_mat_A.mat[],cache.jac_petsc_mat_A.mat[],fptr,ctx)
nls.setup(cache.snes)
@check_error_code PETSC.SNESSolve(cache.snes[],C_NULL,cache.x_petsc.vec[])
copy!(x,cache.x_petsc)
cache
end