14
14
"""
15
15
16
16
import functools
17
+ import itertools
17
18
import typing
18
19
19
20
from petsc4py import PETSc
22
23
import numpy .typing as npt
23
24
24
25
import dolfinx
25
- from dolfinx .la import IndexMap , Vector
26
+ from dolfinx .fem .function import FunctionSpace
27
+ from dolfinx .la import Vector
26
28
27
29
assert dolfinx .has_petsc4py
28
30
29
31
__all__ = ["assign" , "create_vector" , "create_vector_wrap" ]
30
32
31
33
32
- def create_vector (index_map : IndexMap , bs : int ) -> PETSc .Vec : # type: ignore[name-defined]
33
- """Create a distributed PETSc vector.
34
-
35
- Args:
36
- index_map: Index map that describes the size and parallel layout of
37
- the vector to create.
38
- bs: Block size of the vector.
39
-
40
- Returns:
41
- PETSc Vec object.
42
- """
43
- ghosts = index_map .ghosts .astype (PETSc .IntType ) # type: ignore[attr-defined]
44
- size = (index_map .size_local * bs , index_map .size_global * bs )
45
- return PETSc .Vec ().createGhost (ghosts , size = size , bsize = bs , comm = index_map .comm ) # type: ignore
46
-
47
-
48
34
def create_vector_wrap (x : Vector ) -> PETSc .Vec : # type: ignore[name-defined]
49
35
"""Wrap a distributed DOLFINx vector as a PETSc vector.
50
36
@@ -63,6 +49,90 @@ def create_vector_wrap(x: Vector) -> PETSc.Vec: # type: ignore[name-defined]
63
49
)
64
50
65
51
52
+ def create_vector (
53
+ V : typing .Union [list [FunctionSpace ]],
54
+ kind : typing .Optional [str ] = None ,
55
+ ) -> PETSc .Vec :
56
+ """Create a PETSc vector that is compatible with a Functionspace
57
+ or a list of Functionspace(s).
58
+
59
+ Three cases are supported:
60
+
61
+ 1. For a single functionspace ``V``, if ``kind`` is ``None`` or is
62
+ ``PETSc.Vec.Type.MPI``, a ghosted PETSc vector which is
63
+ compatible with ``V`` is created.
64
+
65
+ 2. If ``V=[V_0, ..., V_n]`` is a sequence of function spaces and ``kind`` is ``None``
66
+ or is ``PETSc.Vec.Type.MPI``, a ghosted PETSc vector which is
67
+ compatible with ``V`` is created. The created vector ``b`` is
68
+ initialized such that on each MPI process ``b = [b_0, b_1, ...,
69
+ b_n, b_0g, b_1g, ..., b_ng]``, where ``b_i`` are the entries
70
+ associated with the 'owned' degrees-of-freedom for ``V[i]`` and
71
+ ``b_ig`` are the 'unowned' (ghost) entries for ``V[i]``.
72
+
73
+ For this case, the returned vector has an attribute ``_blocks``
74
+ that holds the local offsets into ``b`` for the (i) owned and
75
+ (ii) ghost entries for each ``V[i]``. It can be accessed by
76
+ ``b.getAttr("_blocks")``. The offsets can be used to get views
77
+ into ``b`` for blocks, e.g.::
78
+
79
+ >>> offsets0, offsets1, = b.getAttr("_blocks")
80
+ >>> offsets0
81
+ (0, 12, 28)
82
+ >>> offsets1
83
+ (28, 32, 35)
84
+ >>> b0_owned = b.array[offsets0[0]:offsets0[1]]
85
+ >>> b0_ghost = b.array[offsets1[0]:offsets1[1]]
86
+ >>> b1_owned = b.array[offsets0[1]:offsets0[2]]
87
+ >>> b1_ghost = b.array[offsets1[1]:offsets1[2]]
88
+
89
+ 3. If `V=[V_0, ..., V_n]`` is a sequence of function space and ``kind`` is
90
+ ``PETSc.Vec.Type.NEST``, a PETSc nested vector (a 'nest' of
91
+ ghosted PETSc vectors) which is compatible with ``V`` is created.
92
+
93
+ Args:
94
+ V: Functionspace or a sequence of functionspaces.
95
+ kind: PETSc vector type (``VecType``) to create.
96
+
97
+ Returns:
98
+ A PETSc vector with a layout that is compatible with ````. The
99
+ vector is not initialised to zero.
100
+ """
101
+ if len (V ) == 1 :
102
+ # Single space case
103
+ index_map = V [0 ].dofmap .index_map
104
+ bs = V [0 ].dofmap .index_map_bs
105
+ ghosts = index_map .ghosts .astype (PETSc .IntType ) # type: ignore[attr-defined]
106
+ size = (index_map .size_local * bs , index_map .size_global * bs )
107
+ return PETSc .Vec ().createGhost (ghosts , size = size , bsize = bs , comm = index_map .comm ) # type: ignore
108
+
109
+ maps = [(_V .dofmaps (0 ).index_map , _V .dofmaps (0 ).index_map_bs ) for _V in V ]
110
+
111
+ if kind is None or kind == PETSc .Vec .Type .MPI :
112
+ off_owned = tuple (
113
+ itertools .accumulate (maps , lambda off , m : off + m [0 ].size_local * m [1 ], initial = 0 )
114
+ )
115
+ off_ghost = tuple (
116
+ itertools .accumulate (
117
+ maps , lambda off , m : off + m [0 ].num_ghosts * m [1 ], initial = off_owned [- 1 ]
118
+ )
119
+ )
120
+
121
+ b = dolfinx .cpp .fem .petsc .create_vector_block (maps )
122
+ b .setAttr ("_blocks" , (off_owned , off_ghost ))
123
+ return b
124
+
125
+ elif kind == PETSc .Vec .Type .NEST :
126
+ return dolfinx .cpp .fem .petsc .create_vector_nest (maps )
127
+
128
+ else :
129
+ raise NotImplementedError (
130
+ "Vector type must be specified for blocked/nested assembly."
131
+ f"Vector type '{ kind } ' not supported."
132
+ "Did you mean 'nest' or 'mpi'?"
133
+ )
134
+
135
+
66
136
@functools .singledispatch
67
137
def assign (x0 : typing .Union [npt .NDArray [np .inexact ], list [npt .NDArray [np .inexact ]]], x1 : PETSc .Vec ): # type: ignore
68
138
"""Assign ``x0`` values to a PETSc vector ``x1``.
0 commit comments