Coverage for /usr/share/miniconda3/envs/dolfin/lib/python3.8/site-packages/block/algebraic/hazmath/precond.py: 53%
276 statements
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-20 13:03 +0000
« prev ^ index » next coverage.py v7.2.1, created at 2023-03-20 13:03 +0000
1from block.algebraic import petsc # NOTE: Not used, import initializes petsc from cmdline
2from block.block_base import block_base
3from block import block_mat, supports_mpi
4from builtins import str
5from petsc4py import PETSc
6from scipy.sparse import csr_matrix
7import dolfin as df
8import numpy as np
9import haznics
11# ------------------------------------------------------------------- #
12# -------------- auxiliary functions --------------- #
13# ------------------------------------------------------------------- #
16def PETSc_to_dCSRmat(A):
17 """
18 Change data type for matrix
19 (dolfin PETScMatrix or GenericMatrix to dCSRmat pointer)
20 """
21 if not isinstance(A, df.PETScMatrix): 21 ↛ 23line 21 didn't jump to line 23, because the condition on line 21 was never false
22 A = df.as_backend_type(A)
23 petsc_mat = A.mat()
25 # NB! store copies for now
26 csr = petsc_mat.getValuesCSR()
28 return haznics.create_matrix(csr[2], csr[1], csr[0], A.size(1))
31def block_mat_to_block_dCSRmat(A):
32 """
33 Change data type for matrix
34 (block.block_mat to block_dCSRmat pointer)
35 """
36 # check type
37 assert isinstance(A, block_mat)
39 # allocate block_dCSRmat and its blocks too
40 brow, bcol = A.blocks.shape
41 Abdcsr = haznics.block_dCSRmat()
42 Abdcsr.init(brow, bcol)
44 for i in range(brow):
45 for j in range(bcol):
46 if isinstance(A[i][j], df.Matrix):
47 A[i][j] = df.as_backend_type(A[i][j])
48 if isinstance(A[i][j], df.PETScMatrix):
49 csr = A[i][j].mat().getValuesCSR() # todo: eliminate zeros!
50 mat = haznics.create_matrix(csr[2], csr[1], csr[0], A[i][j].size(1))
51 Abdcsr.set(i, j, mat)
52 elif not A[i][j]:
53 mat = haznics.dCSRmat()
54 haznics.dcsr_null(mat)
55 Abdcsr.set(i, j, mat)
56 else:
57 return NotImplemented
59 return Abdcsr
61# copied from block/algebraic/petsc/
62def discrete_gradient(mesh):
63 """ P1 to Ned1 map """
64 Ned = df.FunctionSpace(mesh, 'Nedelec 1st kind H(curl)', 1)
65 P1 = df.FunctionSpace(mesh, 'CG', 1)
67 return df.as_backend_type(df.DiscreteOperators.build_gradient(Ned, P1))
70def discrete_curl(mesh):
71 """ Ned1 to RT1 map """
72 assert mesh.geometry().dim() == 3
73 assert mesh.topology().dim() == 3
75 Ned = df.FunctionSpace(mesh, 'Nedelec 1st kind H(curl)', 1)
76 RT = df.FunctionSpace(mesh, 'RT', 1)
78 RT_f2dof = np.array(RT.dofmap().entity_dofs(mesh, 2))
79 Ned_e2dof = np.array(Ned.dofmap().entity_dofs(mesh, 1))
81 # Facets in terms of edges
82 mesh.init(2, 1)
83 f2e = mesh.topology()(2, 1)
85 rows = np.repeat(RT_f2dof, 3)
86 cols = Ned_e2dof[f2e()]
87 vals = np.tile(np.array([-2, 2, -2]), RT.dim())
89 Ccsr = csr_matrix((vals, (rows, cols)), shape=(RT.dim(), Ned.dim()))
91 C = PETSc.Mat().createAIJ(comm=df.MPI.comm_world,
92 size=Ccsr.shape,
93 csr=(Ccsr.indptr, Ccsr.indices, Ccsr.data))
95 return df.PETScMatrix(C)
98# NB: this is only 3d!
99def Pdiv(mesh):
100 """ nodes to faces map """
101 RT = df.FunctionSpace(mesh, 'Raviart-Thomas', 1)
102 P1 = df.VectorFunctionSpace(mesh, 'CG', 1)
104 RT_f2dof = np.array(RT.dofmap().entity_dofs(mesh, 2))
105 P1_n2dof_x = np.array(P1.sub(0).dofmap().entity_dofs(mesh, 0))
106 P1_n2dof_y = np.array(P1.sub(1).dofmap().entity_dofs(mesh, 0))
107 P1_n2dof_z = np.array(P1.sub(2).dofmap().entity_dofs(mesh, 0))
109 # Facets in terms of nodes
110 mesh.init(2, 0)
111 f2n = mesh.topology()(2, 0)
112 coordinates = mesh.coordinates()
114 rows_x, rows_y, rows_z = np.repeat(RT_f2dof, 3), np.repeat(RT_f2dof, 3), np.repeat(RT_f2dof, 3)
115 cols_x, cols_y, cols_z = P1_n2dof_x[f2n()], P1_n2dof_y[f2n()], P1_n2dof_z[f2n()]
116 vals_x, vals_y, vals_z = np.zeros(3 * RT.dim()), np.zeros(3 * RT.dim()), np.zeros(3 * RT.dim())
118 for facet, row in enumerate(RT_f2dof):
119 vertices = f2n(facet)
120 n1, n2, n3 = coordinates[vertices]
122 facet_normal = np.cross(n3 - n1, n2 - n1)
124 indices = np.arange(3) + 3 * facet
125 vals_x[indices] = facet_normal[0] / 3
126 vals_y[indices] = facet_normal[1] / 3
127 vals_z[indices] = facet_normal[2] / 3
129 rows = np.concatenate((rows_x, rows_y, rows_z))
130 cols = np.concatenate((cols_x, cols_y, cols_z))
131 vals = np.concatenate((vals_x, vals_y, vals_z))
133 # assemble Pdiv as from xyz components
134 Pdivcsr = csr_matrix((vals, (rows, cols)), shape=(RT.dim(), P1.dim()))
135 Pdivcsr.eliminate_zeros()
137 Pdiv = PETSc.Mat().createAIJ(comm=df.MPI.comm_world,
138 size=Pdivcsr.shape,
139 csr=(Pdivcsr.indptr, Pdivcsr.indices, Pdivcsr.data))
141 return df.PETScMatrix(Pdiv)
144def Pcurl(mesh):
145 """ nodes to edges map """
146 assert mesh.geometry().dim() == 3
147 assert mesh.topology().dim() == 3
149 Ned = df.FunctionSpace(mesh, 'Nedelec 1st kind H(curl)', 1)
150 P1 = df.VectorFunctionSpace(mesh, 'CG', 1)
152 Ned_e2dof = np.array(Ned.dofmap().entity_dofs(mesh, 1))
153 P1_n2dof_x = np.array(P1.sub(0).dofmap().entity_dofs(mesh, 0))
154 P1_n2dof_y = np.array(P1.sub(1).dofmap().entity_dofs(mesh, 0))
155 P1_n2dof_z = np.array(P1.sub(2).dofmap().entity_dofs(mesh, 0))
157 # Facets in terms of edges
158 mesh.init(1, 0)
159 e2n = mesh.topology()(1, 0)
160 coordinates = mesh.coordinates()
162 rows_x, rows_y, rows_z = np.repeat(Ned_e2dof, 2), np.repeat(Ned_e2dof, 2), np.repeat(Ned_e2dof, 2)
163 cols_x, cols_y, cols_z = P1_n2dof_x[e2n()], P1_n2dof_y[e2n()], P1_n2dof_z[e2n()]
164 vals_x, vals_y, vals_z = np.zeros(2 * Ned.dim()), np.zeros(2 * Ned.dim()), np.zeros(2 * Ned.dim())
166 for edge, row in enumerate(Ned_e2dof):
167 vertices = e2n(edge)
168 edge_tangent = coordinates[vertices[1]] - coordinates[vertices[0]]
170 indices = np.arange(2) + 2 * edge
171 vals_x[indices] = 0.5 * edge_tangent[0]
172 vals_y[indices] = 0.5 * edge_tangent[1]
173 vals_z[indices] = 0.5 * edge_tangent[2]
175 rows = np.concatenate((rows_x, rows_y, rows_z))
176 cols = np.concatenate((cols_x, cols_y, cols_z))
177 vals = np.concatenate((vals_x, vals_y, vals_z))
179 # assemble Pcurl from xyz components
180 Pcurlcsr = csr_matrix((vals, (rows, cols)), shape=(Ned.dim(), P1.dim()))
181 Pcurlcsr.eliminate_zeros()
183 Pcurl = PETSc.Mat().createAIJ(comm=df.MPI.comm_world,
184 size=Pcurlcsr.shape,
185 csr=(Pcurlcsr.indptr, Pcurlcsr.indices, Pcurlcsr.data))
187 return df.PETScMatrix(Pcurl)
190# ------------------------------------------------------------------- #
191# -------------- preconditioners --------------- #
192# ------------------------------------------------------------------- #
195class Precond(block_base):
196 """
197 Class of general preconditioners from HAZmath using SWIG
199 Note: Parallel execution (MPI) not supported.
200 """
202 def __init__(self, A, prectype=None, parameters=None, amg_parameters=None, precond=None):
203 supports_mpi(False, 'HAZMath does not work in parallel')
205 # haznics.dCSRmat* type (assert?)
206 self.A = A
207 # python dictionary of parameters
208 self.__parameters = parameters if parameters else {}
209 self.__amg_parameters = amg_parameters
211 # init and set preconditioner (precond *)
212 if precond: 212 ↛ 215line 212 didn't jump to line 215, because the condition on line 212 was never false
213 self.precond = precond
214 else:
215 import warnings
216 warnings.warn(
217 "!! Preconditioner not specified !! Creating default UA-AMG "
218 "precond... ",
219 RuntimeWarning)
220 # change data type for the matrix (to dCSRmat pointer)
221 A_ptr = PETSc_to_dCSRmat(A)
223 # initialize amg parameters (AMG_param pointer)
224 amgparam = haznics.AMG_param()
226 # set extra amg parameters
227 if parameters:
228 haznics.param_amg_set_dict(parameters, amgparam)
230 # print (relevant) amg parameters
231 haznics.param_amg_print(amgparam)
232 self.__amg_parameters = amgparam
234 self.precond = haznics.create_precond(A_ptr, amgparam)
236 # if fail, setup returns null
237 if not precond:
238 raise RuntimeError(
239 "AMG levels failed to set up (null pointer returned) ")
241 # setup time
242 self.setup_time = precond.setup_time if precond.setup_time else 0.
244 # preconditioner type (string)
245 self.prectype = prectype if prectype else "AMG"
247 def matvec(self, b):
248 if not isinstance(b, df.GenericVector): 248 ↛ 249line 248 didn't jump to line 249, because the condition on line 248 was never true
249 return NotImplemented
251 x = self.A.create_vec(dim=1)
252 x = df.Vector(df.MPI.comm_self, x.size())
254 if len(x) != len(b): 254 ↛ 255line 254 didn't jump to line 255, because the condition on line 254 was never true
255 raise RuntimeError('incompatible dimensions for matvec, %d != %d'
256 % (len(x), len(b)))
258 # convert rhs and dx to numpy arrays
259 b_np = b[:]
260 x_np = x[:]
262 # apply the preconditioner (solution dx saved in x_np)
263 haznics.apply_precond(b_np, x_np, self.precond)
264 # convert dx to GenericVector
265 x.set_local(x_np)
267 return x
269 # noinspection PyMethodMayBeStatic
270 def down_cast(self):
271 return NotImplemented
273 def __str__(self):
274 return '<%s prec of %s>' % (self.__class__.__name__, str(self.A))
276 def create_vec(self, dim=1):
277 return self.A.create_vec(dim)
279 def print_amg_parameters(self):
280 haznics.param_amg_print(self.__amg_parameters)
282 def print_all_parameters(self):
283 if self.prectype == "AMG": 283 ↛ 284line 283 didn't jump to line 284, because the condition on line 283 was never true
284 self.print_amg_parameters()
285 elif self.prectype == "FAMG": 285 ↛ 286line 285 didn't jump to line 286, because the condition on line 285 was never true
286 self.print_amg_parameters()
287 print(" Other parameters:")
288 print("-----------------------------------------------")
289 print("Fractional exponent: ", self.__parameters['fpwr']) if self.__parameters['fpwr'] \
290 else print()
291 print("-----------------------------------------------")
292 elif self.prectype == "RA": 292 ↛ 300line 292 didn't jump to line 300, because the condition on line 292 was never false
293 print(" Other parameters:")
294 print("-----------------------------------------------")
295 print("Fractional exponents: ", self.__parameters['pwrs']) if self.__parameters['pwrs'] \
296 else print()
297 print("Fractional weights: ", self.__parameters['coefs']) if self.__parameters['coefs'] \
298 else print()
299 print("-----------------------------------------------")
300 elif self.prectype == "HXCurl_add":
301 print(" Other parameters:")
302 print("-----------------------------------------------")
303 print("HXCurl type: ", "Additive") if self.__parameters['prectype'] else print()
304 print("-----------------------------------------------")
305 elif self.prectype == "HXDiv_add":
306 print(" Other parameters:")
307 print("-----------------------------------------------")
308 print("HXDiv type: ", "Additive") if self.__parameters['prectype'] else print()
309 print("-----------------------------------------------")
310 else:
311 NotImplemented
314class AMG(Precond):
315 """
316 AMG preconditioner from the HAZmath Library with SWIG
318 """
320 def __init__(self, A, parameters=None):
321 supports_mpi(False, 'HAZMath does not work in parallel')
323 # change data type for the matrix (to dCSRmat pointer)
324 A_ptr = PETSc_to_dCSRmat(A)
326 # initialize amg parameters (AMG_param pointer)
327 amgparam = haznics.AMG_param()
329 # set extra amg parameters
330 if parameters:
331 haznics.param_amg_set_dict(parameters, amgparam)
333 # print (relevant) amg parameters
334 haznics.param_amg_print(amgparam)
336 # set AMG preconditioner
337 precond = haznics.create_precond_amg(A_ptr, amgparam)
339 # if fail, setup returns null
340 if not precond: 340 ↛ 341line 340 didn't jump to line 341, because the condition on line 340 was never true
341 raise RuntimeError(
342 "AMG levels failed to set up (null pointer returned) ")
344 Precond.__init__(self, A, "AMG", parameters, amgparam, precond)
347class FAMG(Precond):
348 """
349 Fractional AMG preconditioner from the HAZmath Library
351 """
353 def __init__(self, A, M, parameters=None):
354 supports_mpi(False, 'HAZMath does not work in parallel')
356 # change data type for the matrices (to dCSRmat pointer)
357 A_ptr = PETSc_to_dCSRmat(A)
358 M_ptr = PETSc_to_dCSRmat(M)
360 # initialize amg parameters (AMG_param pointer)
361 amgparam = haznics.AMG_param()
363 # set extra amg parameters
364 parameters = parameters if (parameters and isinstance(parameters, dict)) \
365 else {'fpwr': 0.5, 'smoother': haznics.SMOOTHER_FJACOBI}
366 haznics.param_amg_set_dict(parameters, amgparam)
368 # print (relevant) amg parameters
369 haznics.param_amg_print(amgparam)
371 # set FAMG preconditioner
372 precond = haznics.create_precond_famg(A_ptr, M_ptr, amgparam)
374 # if fail, setup returns null
375 if not precond:
376 raise RuntimeError(
377 "FAMG levels failed to set up (null pointer returned) ")
379 Precond.__init__(self, A, "FAMG", parameters, amgparam, precond)
382class RA(Precond):
383 """
384 Rational approximation preconditioner from the HAZmath library
386 """
388 def __init__(self, A, M, parameters=None):
389 supports_mpi(False, 'HAZMath does not work in parallel')
391 # change data type for the matrices (to dCSRmat pointer)
392 A_ptr = PETSc_to_dCSRmat(A)
393 M_ptr = PETSc_to_dCSRmat(M)
395 # initialize amg parameters (AMG_param pointer)
396 amgparam = haznics.AMG_param()
398 # set extra amg parameters
399 parameters = parameters if (parameters and isinstance(parameters, dict)) \
400 else {'coefs': [1.0, 0.0], 'pwrs': [0.5, 0.0]}
401 haznics.param_amg_set_dict(parameters, amgparam)
403 # print (relevant) amg parameters
404 haznics.param_amg_print(amgparam)
406 # get scalings
407 scaling_a = 1. / A.norm("linf")
408 scaling_m = 1. / df.as_backend_type(M).mat().getDiagonal().min()[1]
410 # get coefs and powers
411 alpha, beta = parameters['coefs']
412 s_power, t_power = parameters['pwrs']
414 # set RA preconditioner #
415 precond = haznics.create_precond_ra(A_ptr, M_ptr, s_power, t_power,
416 alpha, beta, scaling_a, scaling_m,
417 ra_tol=1e-6, amgparam=amgparam)
419 # if fail, setup returns null
420 if not precond: 420 ↛ 421line 420 didn't jump to line 421, because the condition on line 420 was never true
421 raise RuntimeError(
422 "Rational Approximation data failed to set up (null pointer "
423 "returned) ")
425 Precond.__init__(self, A, "RA", parameters, amgparam, precond)
428class HXCurl(Precond):
429 """
430 HX preconditioner from the HAZmath library for the curl-curl inner product
431 NB! only for 3D problems
432 """
434 def __init__(self, Acurl, V, parameters=None):
435 supports_mpi(False, 'HAZMath does not work in parallel')
437 # get auxiliary operators
438 mesh = V.mesh()
439 Pc = Pcurl(mesh)
440 Grad = discrete_gradient(mesh)
442 # change data type for the matrices (to dCSRmat pointer)
443 Acurl_ptr = PETSc_to_dCSRmat(Acurl)
444 Pcurl_ptr = PETSc_to_dCSRmat(Pc)
445 Grad_ptr = PETSc_to_dCSRmat(Grad)
447 # initialize amg parameters (AMG_param pointer)
448 amgparam = haznics.AMG_param()
450 # set extra amg parameters
451 if parameters and isinstance(parameters, dict):
452 haznics.param_amg_set_dict(parameters, amgparam)
453 else:
454 parameters = {'prectype': haznics.PREC_HX_CURL_A}
456 # make sure coarse solver is always iterative
457 amgparam.coarse_solver = 0
458 # print (relevant) amg parameters
459 haznics.param_amg_print(amgparam)
461 # add or multi
462 try:
463 prectype = parameters['prectype']
464 except KeyError:
465 prectype = haznics.PREC_HX_CURL_A
467 # set HX CURL preconditioner (NB: this sets up both data and fct)
468 precond = haznics.create_precond_hxcurl(Acurl_ptr, Pcurl_ptr, Grad_ptr,
469 prectype, amgparam)
471 # if fail, setup returns null
472 if not precond:
473 raise RuntimeError(
474 "HXcurl data failed to set up (null pointer returned) ")
475 """
476 try:
477 prectype = parameters['prectype']
478 except KeyError:
479 prectype = ''
481 if prectype in ["add", "Add", "ADD", "additive", "ADDITIVE"]:
482 precond.fct = haznics.precond_hx_curl_additive
483 elif prectype in ["multi", "MULTI", "Multi", "multiplicative",
484 "MULTIPLICATIVE"]:
485 precond.fct = haznics.precond_hx_curl_multiplicative
486 else: # default is additive
487 precond.fct = haznics.precond_hx_curl_additive
488 """
489 Precond.__init__(self, Acurl, "HXCurl_add", parameters, amgparam, precond)
492class HXDiv(Precond):
493 """
494 HX preconditioner from the HAZmath library for the div-div inner product
495 NB! Pdiv only works for 3D problems
496 """
498 def __init__(self, Adiv, V, parameters=None):
499 supports_mpi(False, 'HAZMath does not work in parallel')
501 # get auxiliary operators
502 mesh = V.mesh()
503 Pd = Pdiv(mesh)
504 Curl = discrete_curl(mesh)
506 # change data type for the matrices (to dCSRmat pointer)
507 Adiv_ptr = PETSc_to_dCSRmat(Adiv)
508 Pdiv_ptr = PETSc_to_dCSRmat(Pd)
509 Curl_ptr = PETSc_to_dCSRmat(Curl)
511 # initialize amg parameters (AMG_param pointer)
512 amgparam = haznics.AMG_param()
514 # set extra amg parameters
515 if parameters and isinstance(parameters, dict):
516 haznics.param_amg_set_dict(parameters, amgparam)
517 else:
518 parameters = {'dim': mesh.topology().dim(),
519 'prectype': haznics.PREC_HX_DIV_A}
521 # make sure coarse solver is always iterative
522 amgparam.coarse_solver = 0
523 # print (relevant) amg parameters
524 haznics.param_amg_print(amgparam)
526 # get dimension and type of HX precond application
527 try:
528 dim = parameters['dim']
529 except KeyError:
530 dim = 3
532 # add or multi
533 try:
534 prectype = parameters['prectype']
535 except KeyError:
536 prectype = haznics.PREC_HX_DIV_A
538 if dim == 3:
539 Pc = Pcurl(mesh)
541 # change data type for the Pcurl matrix (to dCSRmat pointer)
542 Pcurl_ptr = PETSc_to_dCSRmat(Pc)
544 # set HX DIV preconditioner (NB: this sets up both data and fct)
545 precond = haznics.create_precond_hxdiv_3D(Adiv_ptr, Pdiv_ptr,
546 Curl_ptr, Pcurl_ptr,
547 prectype, amgparam)
549 # if fail, setup returns null
550 if not precond:
551 raise RuntimeError(
552 "HXdiv data failed to set up (null pointer returned) ")
553 """
554 if prectype in ["add", "Add", "ADD", "additive", "ADDITIVE"]:
555 precond.fct = haznics.precond_hx_div_additive
557 elif prectype in ["multi", "MULTI", "Multi", "multiplicative",
558 "MULTIPLICATIVE"]:
559 precond.fct = haznics.precond_hx_div_multiplicative
561 else:
562 # default is additive
563 precond.fct = haznics.precond_hx_div_additive
564 """
565 Precond.__init__(self, Adiv, "HXDiv_add", parameters, amgparam, precond)
567 else:
568 # set HX DIV preconditioner (NB: this sets up both data and fct)
569 precond = haznics.create_precond_hxdiv_2D(Adiv_ptr, Pdiv_ptr,
570 Curl_ptr, prectype,
571 amgparam)
573 # if fail, setup returns null
574 if not precond:
575 raise RuntimeError(
576 "HXdiv data failed to set up (null pointer returned) ")
577 """
578 if prectype in ["add", "Add", "ADD", "additive", "ADDITIVE"]:
579 precond.fct = haznics.precond_hx_div_additive_2D
581 elif prectype in ["multi", "MULTI", "Multi", "multiplicative", "MULTIPLICATIVE"]:
582 precond.fct = haznics.precond_hx_div_multiplicative_2D
584 else:
585 # default is additive
586 precond.fct = haznics.precond_hx_div_additive_2D
587 """
588 Precond.__init__(self, Adiv, "HXDiv_add", parameters, amgparam, precond)
591# ----------------------------------- EOF ----------------------------------- #