MeshDistributor.cc 75.7 KB
Newer Older
1
//
2
3
4
5
6
7
8
9
10
11
12
// Software License for AMDiS
//
// Copyright (c) 2010 Dresden University of Technology 
// All rights reserved.
// Authors: Simon Vey, Thomas Witkowski et al.
//
// This file is part of AMDiS
//
// See also license.opensource.txt in the distribution.


Thomas Witkowski's avatar
Thomas Witkowski committed
13
#include <algorithm>
14
15
#include <iostream>
#include <fstream>
16
17
#include <limits>
#include <stdint.h>
Thomas Witkowski's avatar
Thomas Witkowski committed
18
#include <boost/lexical_cast.hpp>
19
20
#include <boost/filesystem.hpp>

21
#include "parallel/MeshDistributor.h"
22
#include "parallel/MeshManipulation.h"
23
#include "parallel/ParallelDebug.h"
24
#include "parallel/StdMpi.h"
25
#include "parallel/MeshPartitioner.h"
26
#include "parallel/ParMetisPartitioner.h"
27
#include "parallel/ZoltanPartitioner.h"
Thomas Witkowski's avatar
Thomas Witkowski committed
28
#include "parallel/SimplePartitioner.h"
29
#include "parallel/CheckerPartitioner.h"
30
#include "parallel/MpiHelper.h"
31
#include "parallel/DofComm.h"
32
33
34
#include "io/ElementFileWriter.h"
#include "io/MacroInfo.h"
#include "io/VtkWriter.h"
35
36
37
38
39
#include "Mesh.h"
#include "Traverse.h"
#include "ElInfo.h"
#include "Element.h"
#include "MacroElement.h"
40
41
#include "DOFMatrix.h"
#include "DOFVector.h"
42
#include "SystemVector.h"
43
#include "ElementDofIterator.h"
44
45
#include "ProblemStatBase.h"
#include "StandardProblemIteration.h"
46
#include "VertexVector.h"
47
#include "MeshStructure.h"
48
#include "ProblemStat.h"
Thomas Witkowski's avatar
Thomas Witkowski committed
49
#include "ProblemInstat.h"
50
#include "RefinementManager3d.h"
51
#include "Debug.h"
52

53
54
namespace AMDiS {

Thomas Witkowski's avatar
Thomas Witkowski committed
55
  using boost::lexical_cast;
56
  using namespace boost::filesystem;
57
  using namespace std;
Thomas Witkowski's avatar
Thomas Witkowski committed
58

59
60
  MeshDistributor* MeshDistributor::globalMeshDistributor = NULL;

61
62
63
  const Flag MeshDistributor::BOUNDARY_SUBOBJ_SORTED              = 0X01L;
  const Flag MeshDistributor::BOUNDARY_FILL_INFO_SEND_DOFS        = 0X02L;
  const Flag MeshDistributor::BOUNDARY_FILL_INFO_RECV_DOFS        = 0X04L;
Thomas Witkowski's avatar
Thomas Witkowski committed
64

65
66
67
68
69
  inline bool cmpDofsByValue(const DegreeOfFreedom* dof1, const DegreeOfFreedom* dof2)
  {
    return (*dof1 < *dof2);
  }

70

71
  MeshDistributor::MeshDistributor()
72
    : problemStat(0),
73
      initialized(false),
74
      name("parallel"),
75
      feSpaces(0),
76
77
78
79
      mesh(NULL),
      refineManager(NULL),
      info(10),
      partitioner(NULL),
80
      deserialized(false),
81
      writeSerializationFile(false),
82
      repartitioningAllowed(false),
83
      repartitionIthChange(20),
84
85
      nMeshChangesAfterLastRepartitioning(0),
      repartitioningCounter(0),
86
      debugOutputDir(""),
Thomas Witkowski's avatar
Thomas Witkowski committed
87
      lastMeshChangeIndex(0),
88
89
      createBoundaryDofFlag(0),
      sebastianMode(false)
90
  {
91
    FUNCNAME("MeshDistributor::ParalleDomainBase()");
Thomas Witkowski's avatar
Thomas Witkowski committed
92

93
94
95
    mpiRank = MPI::COMM_WORLD.Get_rank();
    mpiSize = MPI::COMM_WORLD.Get_size();
    mpiComm = MPI::COMM_WORLD;
96

97
    Parameters::get(name + "->repartitioning", repartitioningAllowed);
98
99
    Parameters::get(name + "->debug output dir", debugOutputDir);
    Parameters::get(name + "->repartition ith change", repartitionIthChange);
100
    Parameters::get(name + "->log main rank", Msg::outputMainRank);
101

102
    string partStr = "parmetis";
103
    Parameters::get(name + "->partitioner", partStr);
104
105
106
107

    if (partStr == "parmetis") 
      partitioner = new ParMetisPartitioner(&mpiComm);

Thomas Witkowski's avatar
Thomas Witkowski committed
108
109
    if (partStr == "zoltan") {
#ifdef HAVE_ZOLTAN
110
      partitioner = new ZoltanPartitioner(&mpiComm);
Thomas Witkowski's avatar
Thomas Witkowski committed
111
#else
Thomas Witkowski's avatar
Thomas Witkowski committed
112
      ERROR_EXIT("AMDiS was compiled without Zoltan support. Therefore you cannot make use of it!\n");
Thomas Witkowski's avatar
Thomas Witkowski committed
113
114
#endif
    }
115

116
117
118
    if (partStr == "checker")
      partitioner = new CheckerPartitioner(&mpiComm);

Thomas Witkowski's avatar
Thomas Witkowski committed
119
120
121
    if (partStr == "simple")
      partitioner = new SimplePartitioner(&mpiComm);

122
    int tmp = 0;
123
    Parameters::get(name + "->box partitioning", tmp);
Thomas Witkowski's avatar
Thomas Witkowski committed
124
125
    partitioner->setBoxPartitioning(static_cast<bool>(tmp));

126
    TEST_EXIT(partitioner)("Could not create partitioner \"%s\"!\n", partStr.c_str());
127
128
  }

Thomas Witkowski's avatar
Thomas Witkowski committed
129

130
  void MeshDistributor::initParallelization()
131
  {
132
    FUNCNAME("MeshDistributor::initParallelization()");
133

134
135
136
    if (initialized)
      return;

137
138
139
    TEST_EXIT(mpiSize > 1)
      ("Parallelization does not work with only one process!\n");

140
141
    TEST_EXIT(feSpaces.size() > 0)
      ("No FE space has been defined for the mesh distributor!\n");
142
    TEST_EXIT(mesh)("No mesh has been defined for the mesh distributor!\n");
143

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
    // === Sort FE spaces with respect to the degree of the basis ===
    // === functions. Use stuiped bubble sort for this.           ===

    bool doNext = false;
    do {
      for (unsigned int i = 0; i < feSpaces.size() - 1; i++)
	if (feSpaces[i]->getBasisFcts()->getDegree() >
	    feSpaces[i + 1]->getBasisFcts()->getDegree()) {
	  const FiniteElemSpace *tmp = feSpaces[i + 1];
	  feSpaces[i + 1] = feSpaces[i];
	  feSpaces[i] = tmp;
	  doNext = true;
	}
    } while (doNext);

159
    elObjDb.setMesh(feSpaces[0]->getMesh());
160

161
162
163
    // If the problem has been already read from a file, we need only to set
    // isRankDofs to all matrices and rhs vector and to remove periodic 
    // boundary conditions (if there are some).
164
    if (deserialized) {
165
166
      updateMacroElementInfo();

167
      setRankDofs();
168

169
      removePeriodicBoundaryConditions();
170

171
172
      macroElIndexMap.clear();
      macroElIndexTypeMap.clear();
173

174
      for (vector<MacroElement*>::iterator it = allMacroElements.begin();
175
	   it != allMacroElements.end(); ++it) {
176
177
	macroElIndexMap.insert(make_pair((*it)->getIndex(), (*it)->getElement()));
	macroElIndexTypeMap.insert(make_pair((*it)->getIndex(), (*it)->getElType()));
178
179
      }

180
181
      createBoundaryDofs();

Thomas Witkowski's avatar
Thomas Witkowski committed
182
#if (DEBUG != 0)
183
      ParallelDebug::writeDebugFile(*this, debugOutputDir + "mpi-dbg", "dat");
Thomas Witkowski's avatar
Thomas Witkowski committed
184
#endif    
185
186

      initialized = true;
187
      return;
188
    }
189

190

191
192
193
194
195
    // Test, if the mesh is the macro mesh only! Paritioning of the mesh is supported
    // only for macro meshes, so it will not work yet if the mesh is already refined
    // in some way.
    testForMacroMesh();

196
197
198
199
    // For later mesh repartitioning, we need to store some information about the
    // macro mesh.
    createMacroElementInfo();

200
    // create an initial partitioning of the mesh
201
    partitioner->createInitialPartitioning();
202

203
    // set the element weights, which are 1 at the very first begin
204
    setInitialElementWeights();
205
206

    // and now partition the mesh    
207
208
    bool partitioningSucceed = partitioner->partition(elemWeights, INITIAL);
    TEST_EXIT(partitioningSucceed)("Initial partitioning does not work!\n");
209
    partitioner->createPartitionMap(partitionMap);
210

211

212
#if (DEBUG != 0)    
213
214
    debug::ElementIdxToDofs elMap;
    debug::createSortedDofs(mesh, elMap);
215
216
#endif

217
    if (mpiRank == 0) {
218
#if (DEBUG != 0)    
219
      int writePartMesh = 1;
220
221
222
#else
      int writePartMesh = 0;
#endif
223
      Parameters::get("dbg->write part mesh", writePartMesh);
224

225
      if (writePartMesh > 0) {
226
	debug::writeElementIndexMesh(mesh, debugOutputDir + "elementIndex.vtu");
227
	ParallelDebug::writePartitioning(*this, debugOutputDir + "part.vtu");
228
      }
229
    }
230

231

232
    // === Create interior boundary information. ===
Thomas Witkowski's avatar
Thomas Witkowski committed
233

234
    createInteriorBoundaryInfo();
Thomas Witkowski's avatar
Thomas Witkowski committed
235

236
#if (DEBUG != 0)    
237
    ParallelDebug::printBoundaryInfo(*this);
238
#endif
239
240
241
    
    // === Remove neighbourhood relations due to periodic bounday conditions. ===

242
243
244
245
246
    for (deque<MacroElement*>::iterator it = mesh->firstMacroElement();
	 it != mesh->endOfMacroElements(); ++it) {
      for (int i = 0; i < mesh->getGeo(NEIGH); i++) {
	if ((*it)->getNeighbour(i) && 
	    mesh->isPeriodicAssociation((*it)->getBoundary(i))) {
247
248
249

	  int neighIndex = (*it)->getNeighbour(i)->getIndex();

250
251
252
253
254
	  (*it)->getNeighbour(i)->setNeighbour((*it)->getOppVertex(i), NULL);
	  (*it)->setNeighbour(i, NULL);
	  (*it)->setBoundary(i, 0);

	  macroElementNeighbours[(*it)->getIndex()][i] = -1;
255
	  macroElementNeighbours[neighIndex][(*it)->getOppVertex(i)] = -1;
256
257
258
259
260
261
262
263
264
	}
      }
    }

    for (vector<MacroElement*>::iterator it = allMacroElements.begin();
	 it != allMacroElements.end(); ++it) {
      for (int i = 0; i < mesh->getGeo(NEIGH); i++) {
	if ((*it)->getNeighbour(i) && 
	    mesh->isPeriodicAssociation((*it)->getBoundary(i))) {
265
266
267

	  int neighIndex = (*it)->getNeighbour(i)->getIndex();

268
269
270
271
272
	  (*it)->getNeighbour(i)->setNeighbour((*it)->getOppVertex(i), NULL);
	  (*it)->setNeighbour(i, NULL);
	  (*it)->setBoundary(i, 0);

	  macroElementNeighbours[(*it)->getIndex()][i] = -1;
273
	  macroElementNeighbours[neighIndex][(*it)->getOppVertex(i)] = -1;
274

275
276
277
	}
      }
    }
278

Thomas Witkowski's avatar
n    
Thomas Witkowski committed
279
280
281
    // === Remove all macro elements that are not part of the rank partition. ===

    removeMacroElements();
282

283
284
    // === Create new global and local DOF numbering. ===

Thomas Witkowski's avatar
Thomas Witkowski committed
285

286
    // We have to remove the VertexVectors, which contain periodic assoiciations, 
287
288
    // because they are not valid anymore after some macro elements have been
    // removed and the corresponding DOFs were deleted.
289
    for (map<BoundaryType, VertexVector*>::iterator it = mesh->getPeriodicAssociations().begin();
290
291
	 it != mesh->getPeriodicAssociations().end(); ++it)
      const_cast<DOFAdmin&>(mesh->getDofAdmin(0)).removeDOFContainer(dynamic_cast<DOFContainer*>(it->second));
292

293
    updateLocalGlobalNumbering();
294

295
296
297
    // === In 3D we have to make some test, if the resulting mesh is valid.  ===
    // === If it is not valid, there is no possiblity yet to fix this        ===
    // === problem, just exit with an error message.                         ===
298
299
300
    
    check3dValidMesh();

301
302
    // === If in debug mode, make some tests. ===

Thomas Witkowski's avatar
Thomas Witkowski committed
303
#if (DEBUG != 0)
304
    MSG("AMDiS runs in debug mode, so make some test ...\n");
305

306
    ParallelDebug::testAllElements(*this);
307
    debug::testSortedDofs(mesh, elMap);
308
    ParallelDebug::testInteriorBoundary(*this);
Thomas Witkowski's avatar
Thomas Witkowski committed
309
    ParallelDebug::followBoundary(*this);
Thomas Witkowski's avatar
Thomas Witkowski committed
310

311
    debug::writeMesh(feSpaces[0], -1, debugOutputDir + "macro_mesh");   
312
313

    MSG("Debug mode tests finished!\n");
314
#endif
315

316
    // === Create periodic DOF mapping, if there are periodic boundaries. ===
317

318
    createPeriodicMap();
319

320
321
322
#if (DEBUG != 0)
    ParallelDebug::testPeriodicBoundary(*this);
#endif
323

324
    // === Global refinements. ===
325
    
Thomas Witkowski's avatar
Thomas Witkowski committed
326
    int globalRefinement = 0;
327
    Parameters::get(mesh->getName() + "->global refinements", globalRefinement);
328
    
Thomas Witkowski's avatar
Thomas Witkowski committed
329
    if (globalRefinement > 0) {
330
      refineManager->globalRefine(mesh, globalRefinement);
331

332
      updateLocalGlobalNumbering();
333
     
334
335
      // === Update periodic mapping, if there are periodic boundaries. ===     

336
      createPeriodicMap();
337
338
339
340

#if (DEBUG != 0)
    ParallelDebug::testPeriodicBoundary(*this);
#endif
Thomas Witkowski's avatar
Thomas Witkowski committed
341
    }
Thomas Witkowski's avatar
Thomas Witkowski committed
342

343
    // Set DOF rank information to all matrices and vectors.
344
    setRankDofs();
Thomas Witkowski's avatar
Thomas Witkowski committed
345

346
    // Remove periodic boundary conditions in sequential problem definition.
347
    removePeriodicBoundaryConditions();
348
349

    initialized = true;
350
351
  }

352

353
  void MeshDistributor::addProblemStat(ProblemStatSeq *probStat)
354
  {
355
    FUNCNAME("MeshDistributor::addProblemStat()");
356

357
358
359
    TEST_EXIT_DBG(probStat->getFeSpaces().size())
      ("No FE spaces in stationary problem!\n");

360
361
362
363
364
365
366
367
368
369
    // === Add FE spaces from stationary problem to mesh distributor. ===

    for (unsigned int i = 0; i < probStat->getFeSpaces().size(); i++) {
      bool foundFeSpace = false;
      for (unsigned int j = 0; j < feSpaces.size(); j++) {
	if (feSpaces[j] == probStat->getFeSpaces()[i])
	  foundFeSpace = true;

	TEST_EXIT(feSpaces[j]->getMesh() == probStat->getFeSpaces()[i]->getMesh())
	  ("MeshDistributor does not yet support usage of different meshes!\n");
370
371
      }

372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
      if (!foundFeSpace)
	feSpaces.push_back(probStat->getFeSpaces()[i]);
    }

    TEST_EXIT(feSpaces.size() > 0)("Should not happen!\n");

    mesh = feSpaces[0]->getMesh();
    info = probStat->getInfo();
    
    switch (mesh->getDim()) {
    case 2:
      refineManager = new RefinementManager2d();
      break;
    case 3:
      refineManager = new RefinementManager3d();
      break;
    default:
      ERROR_EXIT("This should not happen for dim = %d!\n", mesh->getDim());
390
    }
391
392
393
    
    partitioner->setMesh(mesh);
    
394
395
396

    // Create parallel serialization file writer, if needed.
    int writeSerialization = 0;
397
    Parameters::get(probStat->getName() + "->output->write serialization",  
398
		    writeSerialization);
399
    if (writeSerialization && !writeSerializationFile) {
400
      string filename = "";
401
      Parameters::get(name + "->output->serialization filename", filename);
402
403
404
      
      TEST_EXIT(filename != "")
	("No filename defined for parallel serialization file!\n");
405
406

      int tsModulo = -1;
407
      Parameters::get(probStat->getName() + "->output->write every i-th timestep", 
408
		      tsModulo);
409
      
410
      probStat->getFileWriterList().push_back(new Serializer<MeshDistributor>(this, filename, tsModulo));
411
412
      writeSerializationFile = true;
    }    
413
414

    int readSerialization = 0;
415
    Parameters::get(probStat->getName() + "->input->read serialization", 
416
		    readSerialization);
417
    if (readSerialization) {
418
      string filename = "";
419
      Parameters::get(probStat->getName() + "->input->serialization filename", 
420
		      filename);
421
      filename += ".p" + lexical_cast<string>(mpiRank);
422
      MSG("Start deserialization with %s\n", filename.c_str());
423
      ifstream in(filename.c_str());
424
425
426
427

      TEST_EXIT(!in.fail())("Could not open deserialization file: %s\n",
			    filename.c_str());

428
429
430
431
432
      // Read the revision number of the AMDiS version which was used to create 
      // the serialization file.
      int revNumber = -1;
      SerUtil::deserialize(in, revNumber);

433
      probStat->deserialize(in);
434
      in.close();
435
436
      MSG("Deserialization from file: %s\n", filename.c_str());

437
      filename = "";
438
      Parameters::get(name + "->input->serialization filename", filename);
439
440
441
442
      
      TEST_EXIT(filename != "")
	("No filename defined for parallel deserialization file!\n");
      
443
      string rankFilename = filename + ".p" + lexical_cast<string>(mpiRank);
444
445
446
447
      in.open(rankFilename.c_str());
      
      TEST_EXIT(!in.fail())("Could not open parallel deserialization file: %s\n",
			    filename.c_str());
448
449
450
451
452

      // Read the revision number of the AMDiS version which was used to create 
      // the serialization file.
      revNumber = -1;
      SerUtil::deserialize(in, revNumber);
453
454
455
456
457
      
      deserialize(in);
      in.close();
      MSG("Deserializtion of mesh distributor from file: %s\n", rankFilename.c_str());
      deserialized = true;
458
459
    }

460
    MSG("PUSH BACK THAT SHITT!\n");
461
    problemStat.push_back(probStat);
462

463
464
465
    // If the mesh distributor is already initialized, don't forget to set rank
    // DOFs object to the matrices and vectors of the added stationary problem,
    // and to remove the periodic boundary conditions on these objects.
466

467
    if (initialized) {
468
      setRankDofs(probStat);
469
470
      removePeriodicBoundaryConditions(probStat);
    }
471
472
473
  }


474
475
476
477
478
479
480
481
482
483
484
  void MeshDistributor::addProblemStatGlobal(ProblemStatSeq *probStat)
  {
    FUNCNAME("MeshDistributor::addProblemStatGlobal()");

    if (globalMeshDistributor == NULL)
      globalMeshDistributor = new MeshDistributor();

    globalMeshDistributor->addProblemStat(probStat);
  }


485
  void MeshDistributor::exitParallelization()
486
  {}
487

488
  
489
  void MeshDistributor::testForMacroMesh()
490
  {
491
    FUNCNAME("MeshDistributor::testForMacroMesh()");
492
493
494
495
496
497
498

    int nMacroElements = 0;

    TraverseStack stack;
    ElInfo *elInfo = stack.traverseFirst(mesh, -1, Mesh::CALL_LEAF_EL);
    while (elInfo) {
      TEST_EXIT(elInfo->getLevel() == 0)
499
	("Mesh is already refined! This does not work with parallelization!\n");
500
501
502

      TEST_EXIT(elInfo->getType() == 0)
	("Only macro elements with level 0 are supported!\n");
503
504
505
506
507
508
509
510
511
512
      
      nMacroElements++;

      elInfo = stack.traverseNext(elInfo);
    }

    TEST_EXIT(nMacroElements >= mpiSize)
      ("The mesh has less macro elements than number of mpi processes!\n");
  }

513

514
  void MeshDistributor::synchVector(SystemVector &vec)
Thomas Witkowski's avatar
Thomas Witkowski committed
515
  {
516
517
    FUNCNAME("MeshDistributor::synchVector()");

518
    StdMpi<vector<double> > stdMpi(mpiComm);
Thomas Witkowski's avatar
Thomas Witkowski committed
519

520
    for (DofComm::Iterator it(sendDofs); !it.end(); it.nextRank()) {
521
      vector<double> dofs;
522

523
524
      for (int i = 0; i < vec.getSize(); i++) {
	DOFVector<double> &dofVec = *(vec.getDOFVector(i));
525

526
527
528
	for (it.beginDofIter(vec.getFeSpace(i)); !it.endDofIter(); it.nextDof())
	  dofs.push_back(dofVec[it.getDofIndex()]);
      }
529

530
      stdMpi.send(it.getRank(), dofs);
531
    }
532
533
534
	   
    for (DofComm::Iterator it(recvDofs); !it.end(); it.nextRank())
      stdMpi.recv(it.getRank());
535

536
    stdMpi.startCommunication();
537

538
    for (DofComm::Iterator it(recvDofs); !it.end(); it.nextRank()) {
539
      int counter = 0;
540
541
542
543
544
545

      for (int i = 0; i < vec.getSize(); i++) {
	DOFVector<double> &dofVec = *(vec.getDOFVector(i));

	for (it.beginDofIter(vec.getFeSpace(i)); !it.endDofIter(); it.nextDof())
	  dofVec[it.getDofIndex()] = stdMpi.getRecvData(it.getRank())[counter++];
546
547
548
549
      }
    }
  }

550

551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
  void MeshDistributor::check3dValidMesh()
  {
    FUNCNAME("MeshDistributor::check3dValidMesh()");

    if (mesh->getDim() != 3)
      return;

    std::set<DofEdge> allEdges;
    std::set<int> rankMacroEls;

    TraverseStack stack;
    ElInfo *elInfo = stack.traverseFirst(mesh, 0, Mesh::CALL_EL_LEVEL);
    while (elInfo) {
      for (int i = 0; i < 4; i++) {
	ElementObjectData elData(elInfo->getElement()->getIndex(), i);
566
	allEdges.insert(elObjDb.getEdgeLocalMap(elData));
567
568
569
570
571
572
573
      }

      rankMacroEls.insert(elInfo->getElement()->getIndex());

      elInfo = stack.traverseNext(elInfo);
    }

574
575

    FixRefinementPatch::connectedEdges.clear();
576
    bool valid3dMesh = true;
577
578

    for (std::set<DofEdge>::iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
579
      vector<ElementObjectData>& edgeEls = elObjDb.getElements(*it);
580
581
582
583
584

      TEST_EXIT_DBG(edgeEls.size() > 0)
	("No edge %d/%d in elObjDB!\n", it->first, it->second);

      std::set<int> el0, el1;
585
      map<int, int> edgeNoInEl;
586
587
588

      for (unsigned int i = 0; i < edgeEls.size(); i++) {
	if (rankMacroEls.count(edgeEls[i].elIndex)) {
589
590
591
592
	  if (el0.empty())
	    el0.insert(edgeEls[i].elIndex);
	  else
	    el1.insert(edgeEls[i].elIndex);
593
594
595
596

	  edgeNoInEl[edgeEls[i].elIndex] = edgeEls[i].ithObject;
	}
      }
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
            
      TEST_EXIT_DBG(!el0.empty())("Should not happen!\n");

      if (el1.empty())
	continue;
      
      bool found = false;      
      do {
	found = false;
	for (std::set<int>::iterator elIt = el0.begin(); elIt != el0.end(); ++elIt) {
	  for (int i = 0; i < 4; i++) {
	    int neighEl = macroElementNeighbours[*elIt][i];
	    if (neighEl != -1 && el1.count(neighEl)) {
	      el0.insert(neighEl);
	      el1.erase(neighEl);
	      found = true;
	    }
	  }
	  
	  if (found)
	    break;
	}
      } while (found);
      
      if (!el1.empty()) {
622
623
624
625
	valid3dMesh = false;

	MSG_DBG("Unvalid 3D mesh with %d (%d - %d) elements on this edge!\n", 
		edgeEls.size(), el0.size(), el1.size());
626

627
628
629
630
631
632
633
634
635
636
637
	for (std::set<int>::iterator elIt0 = el0.begin(); elIt0 != el0.end(); ++elIt0) {
	  for (std::set<int>::iterator elIt1 = el1.begin(); elIt1 != el1.end(); ++elIt1) {
	    pair<Element*, int> edge0 = 
	      make_pair(macroElIndexMap[*elIt0], edgeNoInEl[*elIt0]);
	    pair<Element*, int> edge1 = 
	      make_pair(macroElIndexMap[*elIt1], edgeNoInEl[*elIt1]);

	    DofEdge dofEdge0 = edge0.first->getEdge(edge0.second);
	    DofEdge dofEdge1 = edge1.first->getEdge(edge1.second);

	    WorldVector<double> c0, c1;
638
639
	    mesh->getDofIndexCoords(dofEdge0.first, feSpaces[0], c0);
	    mesh->getDofIndexCoords(dofEdge0.second, feSpaces[0], c1);
640

641
	    /*
642
643
644
645
	    MSG_DBG("FOUND EDGE: %d %d with coords %f %f %f   %f %f %f\n",
		    dofEdge0.first, dofEdge0.second, 
		    c0[0], c0[1], c0[2],
		    c1[0], c1[1], c1[2]);
646
	    */
647
648
649
650
651
652
653
654
655

	    TEST_EXIT_DBG(dofEdge0 == dofEdge1)("Should noth happen!\n");

	    FixRefinementPatch::connectedEdges.push_back(make_pair(edge0, edge1));
	    FixRefinementPatch::connectedEdges.push_back(make_pair(edge1, edge0));
	  }
	}
      }
    }
656
657

    MSG_DBG("Mesh is 3d valid mesh: %d\n", valid3dMesh);
658
659
660
  }


661
662
  void MeshDistributor::getAllBoundaryDofs(const FiniteElemSpace *feSpace,
					   DofContainer& dofs)
663
664
665
666
  {
    FUNCNAME("MeshDistributor::getAllBoundaryDofs()");

    DofContainerSet dofSet;
667
668
669
670
    for (DofComm::Iterator it(sendDofs, feSpace); !it.end(); it.nextRank())
      dofSet.insert(it.getDofs().begin(), it.getDofs().end());
    for (DofComm::Iterator it(recvDofs, feSpace); !it.end(); it.nextRank())
      dofSet.insert(it.getDofs().begin(), it.getDofs().end());
671
672
673
674
675
676

    dofs.clear();
    dofs.insert(dofs.begin(), dofSet.begin(), dofSet.end());
  }


677
  void MeshDistributor::setRankDofs(ProblemStatSeq *probStat)
678
  {
679
680
    FUNCNAME("MeshDistributor::setRankDofs()");

681
    int nComponents = probStat->getNumComponents();
682
683
    for (int i = 0; i < nComponents; i++) {
      for (int j = 0; j < nComponents; j++)
684
685
686
687
688
689
690
691
692
693
	if (probStat->getSystemMatrix(i, j)) {
	  const FiniteElemSpace *rowFeSpace = 
	    probStat->getSystemMatrix(i, j)->getRowFeSpace();

	  TEST_EXIT(find(feSpaces.begin(), feSpaces.end(), rowFeSpace) != feSpaces.end())
	    ("Should not happen!\n");

	  probStat->getSystemMatrix(i, j)->setRankDofs(dofFeData[rowFeSpace].isRankDof);
	}
    
694

695
      TEST_EXIT_DBG(probStat->getRhs()->getDOFVector(i))("No RHS vector!\n");
696
697
      TEST_EXIT_DBG(probStat->getSolution()->getDOFVector(i))
	("No solution vector!\n");
698
699
700
      TEST_EXIT_DBG(probStat->getRhsVector(i)->getFeSpace() ==
		    probStat->getSolution(i)->getFeSpace())
	("Should not happen!\n");
701
      
702
703
704
705
706
707
      const FiniteElemSpace *feSpace = probStat->getRhsVector(i)->getFeSpace();
      TEST_EXIT(find(feSpaces.begin(), feSpaces.end(), feSpace) != feSpaces.end())
	("Should not happen!\n");

      probStat->getRhsVector(i)->setRankDofs(dofFeData[feSpace].isRankDof);
      probStat->getSolution(i)->setRankDofs(dofFeData[feSpace].isRankDof);
708
709
710
711
    }    
  }


712
713
  void MeshDistributor::setRankDofs()
  {
714
715
    for (unsigned int i = 0; i < problemStat.size(); i++) 
      setRankDofs(problemStat[i]);
716
717
718
  }


719
720
  void MeshDistributor::removePeriodicBoundaryConditions()
  {
721
722
    FUNCNAME("MeshDistributor::removePeriodicBoundaryConditions()");

723
    // Remove periodic boundaries in boundary manager on matrices and vectors.
724
725
    for (unsigned int i = 0; i < problemStat.size(); i++)
      removePeriodicBoundaryConditions(problemStat[i]);
726
727
728
729
730
731
732
733

    // Remove periodic boundaries on elements in mesh.
    TraverseStack stack;
    ElInfo *elInfo = stack.traverseFirst(mesh,  -1, Mesh::CALL_EVERY_EL_PREORDER);
    while (elInfo) {
      elInfo->getElement()->deleteElementData(PERIODIC);
      elInfo = stack.traverseNext(elInfo);
    }    
734
735
736

    // Remove periodic vertex associations
    mesh->getPeriodicAssociations().clear();
737
738
739
  }


740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
  void MeshDistributor::removePeriodicBoundaryConditions(ProblemStatSeq *probStat)
  {
    FUNCNAME("MeshDistributor::removePeriodicBoundaryConditions()");

    int nComponents = probStat->getNumComponents();

    for (int j = 0; j < nComponents; j++) {
      for (int k = 0; k < nComponents; k++) {
	DOFMatrix* mat = probStat->getSystemMatrix(j, k);
	if (mat && mat->getBoundaryManager())
	  removePeriodicBoundaryConditions(const_cast<BoundaryIndexMap&>(mat->getBoundaryManager()->getBoundaryConditionMap()));
      }
      
      if (probStat->getSolution()->getDOFVector(j)->getBoundaryManager())
	removePeriodicBoundaryConditions(const_cast<BoundaryIndexMap&>(probStat->getSolution()->getDOFVector(j)->getBoundaryManager()->getBoundaryConditionMap()));
      
      if (probStat->getRhs()->getDOFVector(j)->getBoundaryManager())
	removePeriodicBoundaryConditions(const_cast<BoundaryIndexMap&>(probStat->getRhs()->getDOFVector(j)->getBoundaryManager()->getBoundaryConditionMap()));
    }
  }


762
  void MeshDistributor::removePeriodicBoundaryConditions(BoundaryIndexMap& boundaryMap)
Thomas Witkowski's avatar
Thomas Witkowski committed
763
  {
764
765
    FUNCNAME("MeshDistributor::removePeriodicBoundaryConditions()");

Thomas Witkowski's avatar
Thomas Witkowski committed
766
767
    BoundaryIndexMap::iterator it = boundaryMap.begin();
    while (it != boundaryMap.end()) {
768
      if (it->second->isPeriodic())
Thomas Witkowski's avatar
Thomas Witkowski committed
769
	boundaryMap.erase(it++);
770
      else
Thomas Witkowski's avatar
Thomas Witkowski committed
771
772
773
774
775
	++it;      
    }    
  }


776
  void MeshDistributor::checkMeshChange(bool tryRepartition)
777
  {
778
    FUNCNAME("MeshDistributor::checkMeshChange()");
779

780
781
    double first = MPI::Wtime();

782
783
784
    int skip = 0;
    Parameters::get("parallel->debug->skip check mesh change", skip);

785
786
787
788
789
    // === If mesh has not been changed on all ranks, return. ===

    int recvAllValues = 0;
    int sendValue = static_cast<int>(mesh->getChangeIndex() != lastMeshChangeIndex);
    mpiComm.Allreduce(&sendValue, &recvAllValues, 1, MPI_INT, MPI_SUM);
790

791
    if (recvAllValues == 0)
792
793
      return;

794
795
    // === At least one rank mesh has been changed, so the boundaries must be ===
    // === adapted to the new mesh structure.                                 ===
796
797

    if (skip == 0)
798
    do {
799
800
      bool meshChanged = false;

801
802
      // To check the interior boundaries, the ownership of the boundaries is not 
      // important. Therefore, we add all boundaries to one boundary container.
803
      RankToBoundMap allBound;
Thomas Witkowski's avatar
Thomas Witkowski committed
804

805
      for (InteriorBoundary::iterator it(rankIntBoundary); !it.end(); ++it)
806
807
	if ((mesh->getDim() == 2 && it->rankObj.subObj == EDGE) || 
	    (mesh->getDim() == 3 && it->rankObj.subObj == FACE))
808
 	  allBound[it.getRank()].push_back(*it);
Thomas Witkowski's avatar
Thomas Witkowski committed
809
810

      for (InteriorBoundary::iterator it(otherIntBoundary); !it.end(); ++it)
811
812
	if ((mesh->getDim() == 2 && it->rankObj.subObj == EDGE) || 
	    (mesh->getDim() == 3 && it->rankObj.subObj == FACE))
813
	  allBound[it.getRank()].push_back(*it);
Thomas Witkowski's avatar
Thomas Witkowski committed
814

815
816
      for (InteriorBoundary::iterator it(periodicBoundary); !it.end(); ++it) {
	if (it.getRank() == mpiRank) {
817
818
819
820
821
	  if ((mesh->getDim() == 2 && it->rankObj.subObj == EDGE) || 
	      (mesh->getDim() == 3 && it->rankObj.subObj == FACE)) {
	    MeshStructure elCode;
	    elCode.init(it->rankObj);
	    
822
	    MeshManipulation mm(mesh);
823
	    meshChanged |= mm.fitElementToMeshCode(elCode, it->neighObj);
824
	  }
825
826
827
828
829
830
	} else {
	  if ((mesh->getDim() == 2 && it->rankObj.subObj == EDGE) || 
	      (mesh->getDim() == 3 && it->rankObj.subObj == FACE))
	    allBound[it.getRank()].push_back(*it);	
	}
      }
831

832

833
      // === Check the boundaries and adapt mesh if necessary. ===
834
      MSG_DBG("Run checkAndAdaptBoundary ...\n");
835

836
      meshChanged |= checkAndAdaptBoundary(allBound);
837
838
839

      // === Check on all ranks if at least one rank's mesh has changed. ===

840
      int sendValue = static_cast<int>(meshChanged);
841
842
      recvAllValues = 0;
      mpiComm.Allreduce(&sendValue, &recvAllValues, 1, MPI_INT, MPI_SUM);
843
844

      MSG("Mesh changed on %d ranks!\n", recvAllValues);
845
    } while (recvAllValues != 0);
846

847
#if (DEBUG != 0)
848
    debug::writeMesh(feSpaces[0], -1, debugOutputDir + "mesh");
849
850
#endif

851

852
    // Because the mesh has been changed, update the DOF numbering and mappings.
853
854
    updateLocalGlobalNumbering();

855
856
    // Update periodic mapping, if there are periodic boundaries.
    createPeriodicMap();
857

858

859
860
861
#if (DEBUG != 0)
    ParallelDebug::testPeriodicBoundary(*this);
#endif
862
863


864
865
    // === The mesh has changed, so check if it is required to repartition ===
    // === the mesh.                                                       ===
866

867
    nMeshChangesAfterLastRepartitioning++;
868

869
870
871
872

    INFO(info, 8)("Parallel mesh adaption needed %.5f seconds\n", 
		  MPI::Wtime() - first);

873
874
    if (tryRepartition &&
	repartitioningAllowed && 
875
876
877
	nMeshChangesAfterLastRepartitioning >= repartitionIthChange) {
      repartitionMesh();
      nMeshChangesAfterLastRepartitioning = 0;
878
    } else {
879
      MSG_DBG("Repartitioning not tried because tryRepartitioning = %d   repartitioningAllowed = %d   nMeshChange = %d   repartitionIthChange = %d\n",
880
881
	      tryRepartition, repartitioningAllowed, 
	      nMeshChangesAfterLastRepartitioning, repartitionIthChange);
882
    }
883
884
885

    // === Print imbalance factor. ===

886
887
888
889
890
891
892
893
    printImbalanceFactor();
  }

  
  void MeshDistributor::printImbalanceFactor()
  {
    FUNCNAME("MeshDistributor::printImbalanceFactor()");

894
895
896
897
898
899
900
    vector<int> nDofsInRank(mpiSize);
    int nDofs = mesh->getDofAdmin(0).getUsedDofs();
    mpiComm.Gather(&nDofs, 1, MPI_INT, &(nDofsInRank[0]), 1, MPI_INT, 0);

    if (mpiRank == 0) {
      int nOverallDofs = 0;
      int maxDofs = numeric_limits<int>::min();
901
      int minDofs = numeric_limits<int>::max();
902
903
904
      for (int i = 0; i < mpiSize; i++) {
	nOverallDofs += nDofsInRank[i];
	maxDofs = std::max(maxDofs, nDofsInRank[i]);
905
	minDofs = std::min(minDofs, nDofsInRank[i]);
906
      }      
907
908
909
910
//       int avrgDofs = nOverallDofs / mpiSize;
//       double imbalance0 = 
// 	(static_cast<double>(maxDofs - avrgDofs) /  avrgDofs) * 100.0;
      double imbalance1 = (static_cast<double>(maxDofs) / minDofs - 1.0) * 100.0;
911

912
      MSG("Imbalancing factor: %.1f\%\n", imbalance1);
913
    }
914
915
916
  }

  
917
  bool MeshDistributor::checkAndAdaptBoundary(RankToBoundMap &allBound)