# 5.2 Parallel dofs and Vector-types¶

In this tutorial we learn how NGSolve represents distributed finite element spaces.

[1]:

from ipyparallel import Client
c = Client(profile='mpi')

[2]:

%%px
from ngsolve import *
from netgen.geom2d import unit_square
comm = MPI.COMM_WORLD
if comm.rank == 0:
mesh = Mesh(unit_square.GenerateMesh(maxh=0.1).Distribute(comm))
else:


Create the space on the distributed mesh. All processes agree on the global number of dofs. Locally, each rank has access only to the subset of dofs associated with its elements. Some dofs are shared by several ranks:

[3]:

%%px
fes = H1(mesh, order=1)
# fes = L2(mesh, order=0)
print ("global dofs =", fes.ndofglobal, ", local dofs =", fes.ndof, \
", sum of local dofs =", comm.allreduce(fes.ndof))

[stdout:0] global dofs = 137 , local dofs = 0 , sum of local dofs = 156
[stdout:1] global dofs = 137 , local dofs = 53 , sum of local dofs = 156
[stdout:2] global dofs = 137 , local dofs = 53 , sum of local dofs = 156
[stdout:3] global dofs = 137 , local dofs = 50 , sum of local dofs = 156


## Parallel Dofs¶

A ParallelDofs object maintains information how dofs are connected across the cluster. The ParallelDofs object is generated by the FESpace, which has access to the connectivity of the mesh.

[4]:

%%px
pardofs = fes.ParallelDofs()
for k in range(pardofs.ndoflocal):
print ("dof", k, "is shard with ranks", list(pardofs.Dof2Proc(k)))

[stdout:1]
dof 0 is shard with ranks []
dof 1 is shard with ranks []
dof 2 is shard with ranks []
dof 3 is shard with ranks []
dof 4 is shard with ranks [3]
dof 5 is shard with ranks [2]
dof 6 is shard with ranks []
dof 7 is shard with ranks []
dof 8 is shard with ranks []
dof 9 is shard with ranks []
dof 10 is shard with ranks []
dof 11 is shard with ranks []
dof 12 is shard with ranks []
dof 13 is shard with ranks []
dof 14 is shard with ranks []
dof 15 is shard with ranks []
dof 16 is shard with ranks []
dof 17 is shard with ranks []
dof 18 is shard with ranks []
dof 19 is shard with ranks [3]
dof 20 is shard with ranks [2]
dof 21 is shard with ranks []
dof 22 is shard with ranks []
dof 23 is shard with ranks []
dof 24 is shard with ranks []
dof 25 is shard with ranks []
dof 26 is shard with ranks []
dof 27 is shard with ranks []
dof 28 is shard with ranks []
dof 29 is shard with ranks []
dof 30 is shard with ranks []
dof 31 is shard with ranks [3]
dof 32 is shard with ranks []
dof 33 is shard with ranks [2]
dof 34 is shard with ranks []
dof 35 is shard with ranks []
dof 36 is shard with ranks []
dof 37 is shard with ranks []
dof 38 is shard with ranks []
dof 39 is shard with ranks []
dof 40 is shard with ranks []
dof 41 is shard with ranks []
dof 42 is shard with ranks []
dof 43 is shard with ranks [2]
dof 44 is shard with ranks []
dof 45 is shard with ranks []
dof 46 is shard with ranks [2]
dof 47 is shard with ranks []
dof 48 is shard with ranks [3]
dof 49 is shard with ranks []
dof 50 is shard with ranks [2]
dof 51 is shard with ranks [3]
dof 52 is shard with ranks [2, 3]
[stdout:2]
dof 0 is shard with ranks []
dof 1 is shard with ranks [3]
dof 2 is shard with ranks []
dof 3 is shard with ranks []
dof 4 is shard with ranks []
dof 5 is shard with ranks []
dof 6 is shard with ranks []
dof 7 is shard with ranks []
dof 8 is shard with ranks []
dof 9 is shard with ranks []
dof 10 is shard with ranks []
dof 11 is shard with ranks [1]
dof 12 is shard with ranks [3]
dof 13 is shard with ranks []
dof 14 is shard with ranks []
dof 15 is shard with ranks []
dof 16 is shard with ranks []
dof 17 is shard with ranks []
dof 18 is shard with ranks []
dof 19 is shard with ranks []
dof 20 is shard with ranks []
dof 21 is shard with ranks []
dof 22 is shard with ranks [1]
dof 23 is shard with ranks [3]
dof 24 is shard with ranks []
dof 25 is shard with ranks []
dof 26 is shard with ranks []
dof 27 is shard with ranks []
dof 28 is shard with ranks []
dof 29 is shard with ranks []
dof 30 is shard with ranks []
dof 31 is shard with ranks [1]
dof 32 is shard with ranks [3]
dof 33 is shard with ranks []
dof 34 is shard with ranks []
dof 35 is shard with ranks []
dof 36 is shard with ranks []
dof 37 is shard with ranks []
dof 38 is shard with ranks []
dof 39 is shard with ranks [1]
dof 40 is shard with ranks [1]
dof 41 is shard with ranks []
dof 42 is shard with ranks [3]
dof 43 is shard with ranks []
dof 44 is shard with ranks []
dof 45 is shard with ranks []
dof 46 is shard with ranks []
dof 47 is shard with ranks [1]
dof 48 is shard with ranks []
dof 49 is shard with ranks [3]
dof 50 is shard with ranks []
dof 51 is shard with ranks []
dof 52 is shard with ranks [1, 3]
[stdout:3]
dof 0 is shard with ranks []
dof 1 is shard with ranks [1]
dof 2 is shard with ranks []
dof 3 is shard with ranks []
dof 4 is shard with ranks []
dof 5 is shard with ranks []
dof 6 is shard with ranks []
dof 7 is shard with ranks []
dof 8 is shard with ranks []
dof 9 is shard with ranks []
dof 10 is shard with ranks []
dof 11 is shard with ranks []
dof 12 is shard with ranks [2]
dof 13 is shard with ranks [1]
dof 14 is shard with ranks []
dof 15 is shard with ranks []
dof 16 is shard with ranks []
dof 17 is shard with ranks []
dof 18 is shard with ranks []
dof 19 is shard with ranks []
dof 20 is shard with ranks []
dof 21 is shard with ranks []
dof 22 is shard with ranks []
dof 23 is shard with ranks []
dof 24 is shard with ranks [2]
dof 25 is shard with ranks [1]
dof 26 is shard with ranks []
dof 27 is shard with ranks []
dof 28 is shard with ranks []
dof 29 is shard with ranks []
dof 30 is shard with ranks []
dof 31 is shard with ranks []
dof 32 is shard with ranks []
dof 33 is shard with ranks []
dof 34 is shard with ranks [2]
dof 35 is shard with ranks []
dof 36 is shard with ranks []
dof 37 is shard with ranks []
dof 38 is shard with ranks []
dof 39 is shard with ranks [2]
dof 40 is shard with ranks []
dof 41 is shard with ranks []
dof 42 is shard with ranks [1]
dof 43 is shard with ranks [2]
dof 44 is shard with ranks []
dof 45 is shard with ranks []
dof 46 is shard with ranks []
dof 47 is shard with ranks [1]
dof 48 is shard with ranks [2]
dof 49 is shard with ranks [1, 2]

[5]:

%%px
print ("I share dofs with ranks:", list(pardofs.ExchangeProcs()))
for k in range(MPI.COMM_WORLD.size):
print ("with rank", k, "I share dofs", list(pardofs.Proc2Dof(k)))

[stdout:0]
I share dofs with ranks: []
with rank 0 I share dofs []
with rank 1 I share dofs []
with rank 2 I share dofs []
with rank 3 I share dofs []
[stdout:1]
I share dofs with ranks: [2, 3]
with rank 0 I share dofs []
with rank 1 I share dofs []
with rank 2 I share dofs [5, 20, 33, 43, 46, 50, 52]
with rank 3 I share dofs [4, 19, 31, 48, 51, 52]
[stdout:2]
I share dofs with ranks: [1, 3]
with rank 0 I share dofs []
with rank 1 I share dofs [11, 22, 31, 39, 40, 47, 52]
with rank 2 I share dofs []
with rank 3 I share dofs [1, 12, 23, 32, 42, 49, 52]
[stdout:3]
I share dofs with ranks: [1, 2]
with rank 0 I share dofs []
with rank 1 I share dofs [1, 13, 25, 42, 47, 49]
with rank 2 I share dofs [12, 24, 34, 39, 43, 48, 49]
with rank 3 I share dofs []

[6]:

%%px
u,v = fes.TnT()
M = BilinearForm(u*v*dx).Assemble().mat
gfu = GridFunction(fes)
gfu.Set (1)
print (gfu.vec)

[stdout:0]
CUMULATED

[stdout:1]
CUMULATED
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1

[stdout:2]
CUMULATED
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1

[stdout:3]
CUMULATED
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1



We see that all values are set to 1, i.e. joint dofs have the same value. We call such a vector ‘cumulated’. The matrix M is stored locally assembled, i.e. every rank has the contributions from its elements. When we multiply this matrix with a cumulated vector, every rank performs a local matrix vector product. The resulting vector is stored ‘distributed’, i.e. the true values are obtained by adding up rank-local contributions for joint dofs:

[7]:

%%px
r = M.CreateColVector()
r.data = M*gfu.vec
print (r)

[stdout:0]
DISTRIBUTED

[stdout:1]
DISTRIBUTED
0.00166667
0.00268739
0.00543538
0.00468192
0.00144669
0.00144006
0.00416828
0.00386831
0.00383791
0.00374104
0.00370187
0.00400439
0.00412487
0.00416843
0.0042514
0.00469718
0.0029978
0.00785051
0.011179
0.00609326
0.0042006
0.00796541
0.0070218
0.00710992
0.00683636
0.00774519
0.00819502
0.00829034
0.0084408
0.00921096
0.012325
0.00383967
0.0087203
0.00428803
0.00773
0.00508265
0.00868291
0.00766309
0.00830502
0.00826294
0.00843237
0.0086137
0.0059988
0.00624854
0.00788378
0.00839573
0.00579947
0.00857215
0.00411724
0.00698451
0.00400253
0.00647131
0.00315203

[stdout:2]
DISTRIBUTED
0.00166667
0.00286129
0.00432333
0.00438304
0.00431312
0.00498657
0.00642514
0.00407647
0.00460771
0.00458069
0.00438155
0.00285724
0.0028588
0.00862993
0.00887179
0.00894042
0.00912449
0.00666367
0.00907802
0.00970813
0.00891071
0.00857388
0.00423494
0.00574418
0.0088786
0.0096309
0.00909324
0.00936286
0.012369
0.00834598
0.00828961
0.00441117
0.0028415
0.00853147
0.00944794
0.00744527
0.00762891
0.00901091
0.0085904
0.00479992
0.00291898
0.00818569
0.00533323
0.0078581
0.00568907
0.00740453
0.00731584
0.00390896
0.00658572
0.00524651
0.0103868
0.00651562
0.00521917

[stdout:3]
DISTRIBUTED
0.00278656
0.002926
0.00438904
0.0044449
0.00432611
0.00394281
0.0037244
0.0038837
0.00379821
0.00357516
0.00379677
0.00414303
0.0014176
0.00288554
0.00869532
0.00894111
0.00889695
0.00831332
0.00716847
0.00686877
0.00751464
0.00667972
0.00698517
0.00805739
0.00567371
0.00428121
0.00940631
0.00916239
0.00814812
0.00548054
0.00654637
0.0085296
0.00543747
0.00912942
0.00292712
0.00903824
0.0103686
0.0102254
0.0102033
0.00594029
0.0122284
0.0104388
0.00478448
0.00275777
0.00745187
0.010634
0.0122638
0.00612087
0.00742165
0.00456138



This cumulated/distributed pair of vectors is prefect for computing inner products. We can compute inner products of local vectors, and sum up (i.e. reduce in MPI terms) across all ranks:

[8]:

%%px
print ("global ip =", InnerProduct(gfu.vec, r))
localip = InnerProduct(r.local_vec, gfu.vec.local_vec)
print ("local contribution:", localip)
print ("cummulated:", comm.allreduce(localip, MPI.SUM))

[stdout:0]
global ip = 1.0
local contribution: 0.0
cummulated: 1.0
[stdout:1]
global ip = 1.0
local contribution: 0.32063053722171725
cummulated: 1.0
[stdout:2]
global ip = 1.0
local contribution: 0.3520477143398174
cummulated: 1.0
[stdout:3]
global ip = 1.0
local contribution: 0.32732174843846523
cummulated: 1.0

[ ]: