MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
cartesiangridcontroller.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The m-AIA AUTHORS
2//
3// This file is part of m-AIA (https://git.rwth-aachen.de/aia/m-AIA/m-AIA)
4//
5// SPDX-License-Identifier: LGPL-3.0-only
6
8#include "UTIL/maiamath.h"
9#ifdef MAIA_GCC_COMPILER
10#pragma GCC diagnostic push
11#pragma GCC diagnostic ignored "-Wduplicated-branches"
12#pragma GCC diagnostic ignored "-Wfloat-equal"
13#pragma GCC diagnostic ignored "-Wuseless-cast"
14#pragma GCC diagnostic ignored "-Wdeprecated-copy-dtor"
15#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
16#pragma GCC diagnostic ignored "-Wunused-result"
17#endif
18#ifdef MAIA_CLANG_COMPILER
19#pragma clang diagnostic push
20#pragma clang diagnostic ignored "-Wc99-extensions"
21#pragma clang diagnostic ignored "-Wextra-semi-stmt"
22#pragma clang diagnostic ignored "-Wfloat-equal"
23#endif
24#ifdef MAIA_GCC_COMPILER
25#pragma GCC diagnostic pop
26#endif
27#ifdef MAIA_CLANG_COMPILER
28#pragma clang diagnostic pop
29#endif
30
39template <MInt nDim>
40void maia::grid::Controller<nDim>::computeWeights(const MFloat* loads, const MFloat domainWeight,
41 std::vector<MFloat>& weights) {
42 TRACE();
43 using namespace maia;
44
45 // Number of load quantities (should be the same for every rank)
46 const MInt noLoadTypes = globalNoLoadTypes();
47 if(noLoadTypes < 1) {
48 TERMM(1,
49 "There are no load quantities present in the solver(s), but needed to determine a new "
50 "partition for dynamic load balancing!");
51 }
52 weights.resize(noLoadTypes);
53 std::fill(weights.begin(), weights.end(), 1.0);
54
55 MIntScratchSpace loadQuantities(noLoadTypes, AT_, "loadQuantities");
56 MFloatScratchSpace loadQuantitiesScaled(noLoadTypes, AT_, "loadQuantitiesScaled");
57 MFloatScratchSpace globalLoadQuantities(noDomains(), noLoadTypes, AT_, "globalLoadQuantities");
58
59 // Get the solver specific load quantities
60 getLoadQuantities(&loadQuantities[0]);
61
62 // Scale the load quantities with the domain weight
63 for(MInt i = 0; i < noLoadTypes; i++) {
64 loadQuantitiesScaled[i] = domainWeight * loadQuantities[i];
65 }
66
67 // Gather (scaled) load quantities on rank 0
68 MPI_Gather(&loadQuantitiesScaled[0], noLoadTypes, type_traits<MFloat>::mpiType(), &globalLoadQuantities[0],
69 noLoadTypes, type_traits<MFloat>::mpiType(), 0, gridb().mpiComm(), AT_, "loadQuantitiesScaled[0]",
70 "globalLoadQuantities[0]");
71
72 if(domainId() == 0) {
73 // Estimate the parameters, i.e. the new weights
74 estimateParameters(noDomains(), noLoadTypes, &globalLoadQuantities[0], &loads[0], &weights[0]);
75 // limit weights for certain types
76 if(!m_testDynamicLoadBalancing) {
77 MInt offset = 0;
78 for(MInt i = 0; i < noSolvers(); i++) {
79 solver(i).limitWeights(&weights[offset]);
80 offset += solver(i).noLoadTypes();
81 }
82 // scale weights
83 const MFloat norm = std::accumulate(&weights[0], &weights[0] + noLoadTypes, 0.0);
84 for(MInt i = 0; i < noLoadTypes; i++) {
85 weights[i] /= norm;
86 }
87 }
88 }
89
90 // Broadcast the estimated parameters/weights
91 MPI_Bcast(&weights[0], noLoadTypes, type_traits<MFloat>::mpiType(), 0, gridb().mpiComm(), AT_, "weights[0]");
92
93 /* storeLoadsAndWeights(&loads[0], noLoadTypes, &loadQuantities[0], domainWeight, &weights[0]); */
94
95 { // Output to m_log
96 m_log << " * computed weights:";
97 MInt count = 0;
98 for(MInt i = 0; i < noSolvers(); i++) {
99 const MInt solverCount = solver(i).noLoadTypes();
100 std::vector<MString> names(solverCount);
101 std::vector<MFloat> weightsTmp(solverCount);
102
103 solver(i).getDefaultWeights(&weightsTmp[0], names);
104 for(MInt j = 0; j < solverCount; j++) {
105 m_log << " s" << i << "_" << names[j] << ":" << count;
106 count++;
107 }
108 }
109
110 m_log << std::endl << " * computed weights:";
111 for(MInt i = 0; i < noLoadTypes; i++) {
112 TERMM_IF_COND(std::isnan(weights[i]), "computed weight is NaN");
113 m_log << " " << std::scientific << weights[i];
114 }
115 m_log << std::endl;
116 }
117
118 // use static pre-defined weights instead
119 if(m_dlbStaticWeights != nullptr && m_dlbStaticWeightMode > -1) {
120 if(m_dlbStaticWeightMode == 0
121 || (m_dlbStaticWeightMode > 0 && m_dlbStep < m_dlbStaticWeightMode && m_dlbStaticWeightMode != 98)
122 || (m_dlbStaticWeightMode == 98 && m_dlbStep % 2 == 0)) {
123 m_log << "Using static weights at dlb-step " << m_dlbStep << " :" << std::endl;
124 for(MInt i = 0; i < noLoadTypes; i++) {
125 weights[i] = m_dlbStaticWeights[i];
126 m_log << " " << std::scientific << weights[i];
127 }
128 m_log << std::endl;
129 }
130 if(m_dlbStaticWeightMode > 0 && m_dlbStaticWeightMode < 90 && m_dlbStep == m_dlbStaticWeightMode) {
131 m_dlbStaticWeightMode = -1;
132 m_log << "Switching to computed weights from now on!" << std::endl;
133 }
134 }
135
136
137 // store weights and use last weights at restart
138 if(m_dlbLastWeights == nullptr) {
139 mAlloc(m_dlbLastWeights, noLoadTypes, "dlbLastWeights", AT_);
140 }
141 for(MInt i = 0; i < noLoadTypes; i++) {
142 m_dlbLastWeights[i] = weights[i];
143 }
144}
158template <MInt nDim>
160 MFloat* const x) {
161 TRACE();
162
163 // Always return the same parameters for testing
164 if(m_testDynamicLoadBalancing) {
165 // For testing: switch weights in consecutive steps to force a partitioning change
166 std::cerr << "estimateParameters: n = " << n << ", setting weights to 1.0" << std::endl;
167 std::fill_n(x, n, 1.0);
168 if(n > 1 && m_dlbStep % 2 == 0) {
169 if(Context::propertyExists("solverWeights_0")) {
170 std::vector<MFloat> weights;
171 getSpecifiedSolverWeights(weights);
172 std::copy(weights.begin(), weights.end(), &x[0]);
173 std::cerr << "estimateParameters: using specified solver weights" << std::endl;
174 } else {
175 std::cerr << "estimateParameters: n = " << n << ", setting w[n-1] = 5.0" << std::endl;
176 x[n - 1] = 5.0;
177 }
178 }
179 return;
180 }
181
182 if(n == 1) {
183 // Single parameter, return 1
184 x[0] = 1.0;
185 } else {
186 // Solve the (overdetermined) system of linear equations (linear least squares)
187
188 // Regularization parameter
189 MFloat alpha = 0.01;
190 // Maximum number of iterations
191 const MInt maxNoIt = 30;
192
193 // Determine column sums
194 MFloatScratchSpace sum_j(n, AT_, "sum_j");
195 for(MInt j = 0; j < n; j++) {
196 sum_j[j] = 0.0;
197 for(MInt i = 0; i < m; i++) {
198 sum_j[j] += A[i * n + j];
199 }
200 if(m_debugBalance) {
201 m_log << " * estimateParameters regularize " << j << " proportional to " << sum_j[j] << std::endl;
202 }
203 }
204
205 MFloat oldResidual = 0.0;
206 MFloatScratchSpace oldParam(n, AT_, "oldParam");
207
208 MInt it = 0;
209 while(it < maxNoIt) {
210 // Work on copies of A and b since they get overwritten and additional
211 // rows are added to allow a regularization of the overdetermined system
212 MFloatScratchSpace A_work((m + n) * n, AT_, "A_work");
213 MFloatScratchSpace b_work(m + n, AT_, "b_work");
214
215 // Copy matrix A and fill additional rows with zeros
216 std::copy_n(&A[0], m * n, &A_work[0]);
217 std::fill_n(&A_work[m * n], n * n, 0.0);
218
219 // Set 'alpha' on diagonal of additional (n x n) solver (Tikhonov regular.)
220 for(MInt i = 0; i < n; i++) {
221 // Set proportional to average column value or to 1 if column sum is zero
222 const MFloat reg = (sum_j[i] > 0) ? alpha * sum_j[i] / noDomains() : 1.0;
223 A_work[m * n + i * (n + 1)] = reg;
224 }
225
226 // Copy vector b and add zeros for regularization
227 std::copy_n(&b[0], m, &b_work[0]);
228 std::fill_n(&b_work[m], n, 0.0);
229
230 // Compute the minimum-norm solution to the linear least squares problem:
231 // minimize 2-norm(|b - A*x|) using singular value decomposition (SVD) of
232 // A. A is an M-by-N matrix which can be rank deficient.
233
234 // TODO labels:CONTROLLER,totest check if this works properly!
235
236 std::vector<MFloat> singularValues = maia::math::svd(A_work.data(), b_work.data(), m + n, n, x);
237
238 // Compute condition number of A (cond_2-norm = S(0)/S(min(m,n)-1))
239 // TODO labels:CONTROLLER this is not correct it should be max(S)/min(S)
240 const MFloat cond_2 = singularValues[0] / singularValues[std::min(m, n) - 1];
241
242 // Compute residual
243 // TODO labels:CONTROLLER could be replaced by
244 // maia::math::MFloatVectorXd residual = A_eigen*x_eigen - b_eigen
245 MFloat residual = 0.0;
246 MFloat maxDiff = 0.0;
247 MFloatScratchSpace mxv(m, AT_, "mxv");
248 for(MInt i = 0; i < m; i++) {
249 mxv[i] = 0.0;
250 for(MInt j = 0; j < n; j++) {
251 mxv[i] += A[i * n + j] * x[j];
252 }
253 residual += POW2(mxv[i] - b[i]);
254 maxDiff = std::max(maxDiff, mxv[i] - b[i]);
255 }
256
257 if(m_debugBalance) {
258 m_log << " * estimateParameters iteration " << it << ":";
259 for(MInt i = 0; i < n; i++) {
260 m_log << std::scientific << " " << x[i];
261 }
262 m_log << "; residual " << residual << "; maxDiff " << maxDiff << std::endl;
263 }
264
265 /* if (std::any_of(&x[0], &x[0] + n, [](MFloat p) { return p < 0.0; }) || cond_2 > maxCond)
266 * { */
267 /* const MBool hasNegativeParam = std::any_of(&x[0], &x[0] + n, [](MFloat p) { return p <
268 * 0.0; }); */
269
270 // If the residual decreased increase regularization constant and repeat
271 if(it == 0 || residual <= oldResidual) {
272 alpha *= 1.5;
273 it++;
274
275 oldResidual = residual;
276 std::copy_n(&x[0], n, &oldParam[0]);
277
278 if(m_debugBalance) {
279 if(it == maxNoIt) {
280 m_log << " * estimateParameters max iterations reached " << it << ", alpha " << alpha
281 << ", condition number " << cond_2 << std::endl;
282 } else {
283 m_log << " * estimateParameters increase alpha to " << alpha << ", condition number " << cond_2
284 << ", residual " << residual << std::endl;
285 }
286 }
287 } else {
288 // Take values of previous iteration
289 std::copy_n(&oldParam[0], n, &x[0]);
290
291 if(m_debugBalance) {
292 m_log << " * estimateParameters found solution! iteration " << it << ", condition number " << cond_2
293 << ", residual " << residual << ", previous " << oldResidual << ", maxDiff " << maxDiff;
294 for(MInt i = 0; i < n; i++) {
295 m_log << std::scientific << " " << x[i];
296 }
297 m_log << std::endl;
298 }
299 break;
300 }
301 } // alpha loop
302 }
303
304 // Prevent negative weights and Nans, i.e. set to zero
305 for(MInt i = 0; i < n; i++) {
306 if(x[i] < 0.0 || std::isnan(x[i])) {
307 x[i] = 0.0;
308 }
309 }
310 // Renormalize parameter vector using 1-norm, such that parameters are better suited for
311 // comparison and will add up to one.
312 const MFloat norm = std::accumulate(&x[0], &x[0] + n, 0.0);
313 for(MInt i = 0; i < n; i++) {
314 x[i] /= norm;
315 }
316}
317
318
344template <MInt nDim>
346 MLong* const partitionCellOffsets,
347 MLong* const globalIdOffsets) {
348 TRACE();
349
350 using namespace maia;
351
352 m_log << " * determine new partition" << std::endl;
353
354 MLongScratchSpace localPartitionCellCounts(noDomains(), AT_, "localPartitionCellCounts");
355 MLongScratchSpace localPartitionCellOffsets(noDomains() + 1, AT_, "localPartitionCellOffsets");
356 // Determine the number of partition cells on all domains and the partition cell offsets
357 gridb().determineNoPartitionCellsAndOffsets(&localPartitionCellCounts[0], &localPartitionCellOffsets[0]);
358
359 const MInt noLocalPartitionCells = localPartitionCellCounts[domainId()];
360
361 // Set the partition method
362 MInt partitionMethod = m_dlbPartitionMethod;
363
364 // Change the actual used partition method depending on the DLB step
365 switch(m_dlbPartitionMethod) {
367 // Default partition method: combination of 3 steps with pure weighting,
368 // the remaining steps use the offset shifting method
369 const MInt noWeightPartitionSteps = 3;
370 if(m_dlbStep < noWeightPartitionSteps) {
371 partitionMethod = DLB_PARTITION_WEIGHT;
372 } else {
373 partitionMethod = DLB_PARTITION_SHIFT_OFFSETS;
374 }
375 break;
376 }
377 // Else: dont change the partition method
381 break;
382 default:
383 TERMM(1, "Unknown DLB partition method.");
384 break;
385 }
386
387 // Initialize domain weights in first DLB step
388 if(m_dlbStep == 0) {
389 m_domainWeights.assign(noDomains(), 1.0);
390 m_lastOffsetShiftDirection.assign(noDomains() + 1, 0);
391 }
392
393 // Increase DLB step
394 m_dlbStep++;
395
396 // Compute new partitioning with the activated partition method
397 switch(partitionMethod) {
399 m_log << "Partition method #0 (compute weights, determine min cells workload and use the "
400 "partition() algorithm)"
401 << std::endl;
402 // Use load quantities, i.e. number of DOF or number of active cells, and loads to estimate
403 // new weights. The new domain distribution is then determined by the partition() algorithm.
404 std::vector<MFloat> weights;
405 computeWeights(loads, 1.0, weights);
406
407 updateWeightsAndWorkloads(weights, false);
408
409 // Note: in case of a partition level shift the global id offsets might not be correct here,
410 // i.e., they are not corrected. This is done later in loadBalancingCalcNewGlobalOffsets().
411 partition(&partitionCellOffsets[0], &globalIdOffsets[0], true);
412 break;
413 }
414 case DLB_PARTITION_TEST: {
415 // TESTING: use old offsets to check if solver still works after reinit
416 for(MInt i = 0; i < noDomains() + 1; i++) {
417 partitionCellOffsets[i] = localPartitionCellOffsets[i];
418 }
419 if(domainId() == 0) {
420 std::cerr << "Using original partition!" << std::endl;
421 }
422 break;
423 }
425 m_log << "Partition method #3 (compute weights and domain weights iteratively, determine "
426 "summed load error at each domain offset and vary min cell offset such that "
427 "predicted error is zero)"
428 << std::endl;
429
430 std::vector<MFloat> weights;
431 std::vector<MFloat> oldWeights;
432 std::vector<MFloat> oldDomainWeights;
433 oldDomainWeights = m_domainWeights;
434 std::fill(oldDomainWeights.begin(), oldDomainWeights.end(),
435 1.0); // TODO labels:CONTROLLER,DLB reset in each step?
436
437 MFloatScratchSpace domainWorkLoad(noDomains(), AT_, "domainWorkLoad");
438 MFloat averageWorkLoad = -1.0;
439
440 MFloatScratchSpace localPartitionCellsWorkload(noLocalPartitionCells, AT_, "localPartitionCellsWorkload");
441 MFloatScratchSpace partitionCellsWorkload(gridb().m_noPartitionCellsGlobal, AT_, "partitionCellsWorkload");
442
443 const MInt maxNoIt = 20;
444 // Iterate until new weights and domain weights are found, i.e. converged
445 for(MInt it = 0; it < maxNoIt; it++) {
446 // Compute weights
447 computeWeights(loads, m_domainWeights[domainId()], weights);
448
449 // @ansgar_mb TODO labels:CONTROLLER,DLB test this
450 updateWeightsAndWorkloads(weights, false);
451
452 // Assemble local partition-cell workloads
453 for(MInt i = 0; i < noLocalPartitionCells; i++) {
454 const MLong globalPartitionCellId = gridb().m_localPartitionCellGlobalIds[i];
455 const MInt partitionCellId = gridb().globalIdToLocalId(globalPartitionCellId, true);
456 TERMM_IF_NOT_COND(gridb().a_hasProperty(partitionCellId, Cell::IsPartitionCell),
457 "Error: cell is not a partition cell.");
458
459 const MFloat workload = gridb().a_workload(partitionCellId);
460 TERMM_IF_NOT_COND(workload > 0.0, "Error: partition cell workload needs to be > 0.0");
461 localPartitionCellsWorkload[i] = workload;
462 }
463
464 // @ansgar TODO labels:DLB Temporary fix; use MLong!? only an issue for more than 2billion partition cells...
465 MIntScratchSpace localPartitionCellCounts_(noDomains(), AT_, "localPartitionCellCounts_");
466 MIntScratchSpace localPartitionCellOffsets_(noDomains() + 1, AT_, "localPartitionCellOffsets_");
467 for(MInt i = 0; i < noDomains(); i++) {
468 localPartitionCellCounts_[i] = (MInt)localPartitionCellCounts[i];
469 localPartitionCellOffsets_[i] = (MInt)localPartitionCellOffsets[i];
470 }
471 localPartitionCellOffsets_[noDomains()] = localPartitionCellOffsets[noDomains()];
472
473 // Gather partition-cell workloads on root
474 MPI_Gatherv(&localPartitionCellsWorkload[0], noLocalPartitionCells, type_traits<MFloat>::mpiType(),
475 &partitionCellsWorkload[0], &localPartitionCellCounts_[0], &localPartitionCellOffsets_[0],
476 type_traits<MFloat>::mpiType(), 0, gridb().mpiComm(), AT_, "localPartitionCellsWorkload[0]",
477 "partitionCellsWorkload[0]");
478 MInt error = 0;
479 if(domainId() == 0) {
480 // Output some information on partition cell workloads
481 const MFloat maxPartitionWorkload =
482 *std::max_element(partitionCellsWorkload.begin(), partitionCellsWorkload.end());
483 const MFloat minPartitionWorkload =
484 *std::min_element(partitionCellsWorkload.begin(), partitionCellsWorkload.end());
485 const MFloat avgPartitionWorkload =
486 std::accumulate(partitionCellsWorkload.begin(), partitionCellsWorkload.end(), 0.0)
487 / static_cast<MFloat>(gridb().m_noPartitionCellsGlobal);
488 m_log << " * Maximum/minimum/average partition cell workload: " << maxPartitionWorkload << ", "
489 << minPartitionWorkload << ", " << avgPartitionWorkload << std::endl;
490 if(minPartitionWorkload <= 0) {
491 m_log << globalTimeStep << " ERROR: minimum partition cell workload is " << minPartitionWorkload
492 << std::endl;
493 cerr0 << globalTimeStep << " ERROR: minimum partition cell workload is " << minPartitionWorkload
494 << std::endl;
495 error = 1;
496 }
497
498 // Compute domain workloads
499 for(MInt i = 0; i < noDomains(); i++) {
500 const MInt offset = localPartitionCellOffsets[i];
501 const MInt count = localPartitionCellCounts[i];
502 domainWorkLoad[i] =
503 std::accumulate(&partitionCellsWorkload[offset], &partitionCellsWorkload[offset] + count, 0.0);
504 }
505 // Compute average workload
506 averageWorkLoad = std::accumulate(domainWorkLoad.begin(), domainWorkLoad.end(), 0.0) / noDomains();
507
508 m_log << " * Average domain workload: " << averageWorkLoad << std::endl;
509
510 // Update domain weights
511 for(MInt i = 0; i < noDomains(); i++) {
512 m_domainWeights[i] = (m_useDomainFactor) ? loads[i] * averageWorkLoad / domainWorkLoad[i] : 1.0;
513 }
514
515 const MFloat domainWeightMinLoadThreshold = 0.2;
516
517 MFloat averageDomainWeight = 0.0;
518 MInt countDomainWeights = 0;
519 for(MInt i = 0; i < noDomains(); i++) {
520 if(loads[i] >= domainWeightMinLoadThreshold) {
521 averageDomainWeight += m_domainWeights[i];
522 countDomainWeights++;
523 }
524 }
525 averageDomainWeight /= (MFloat)countDomainWeights;
526
527 for(MInt i = 0; i < noDomains(); i++) {
528 // For domains with low load reset domain weight since estimation might fail
529 if(loads[i] < domainWeightMinLoadThreshold) {
530 m_domainWeights[i] = 1.0;
531 } else {
532 // Scale domain weights with average such that the mean domain weight is one
533 m_domainWeights[i] /= averageDomainWeight;
534
535 // Weighting with previous values
536 m_domainWeights[i] = 0.5 * (m_domainWeights[i] + oldDomainWeights[i]);
537 }
538 }
539
540 const MFloat maxDomainWeight = *std::max_element(m_domainWeights.begin(), m_domainWeights.end());
541 const MFloat minDomainWeight = *std::min_element(m_domainWeights.begin(), m_domainWeights.end());
542 m_log << " * Domain weights: max = " << maxDomainWeight << ", min = " << minDomainWeight << std::endl;
543 }
544
545 // Broadcast error flag
546 MPI_Bcast(&error, 1, type_traits<MInt>::mpiType(), 0, gridb().mpiComm(), AT_, "error");
547 if(error > 0) {
548 return false;
549 }
550
551 // Broadcast new domain weights
552 MPI_Bcast(&m_domainWeights[0], noDomains(), type_traits<MFloat>::mpiType(), 0, gridb().mpiComm(), AT_,
553 "m_domainWeights[0]");
554
555 // Compute residuals and check for convergence
556 if(it > 0) {
557 const MInt noWeights = weights.size();
558 MFloat weightResidual = 0.0;
559 for(MInt i = 0; i < noWeights; i++) {
560 weightResidual += ABS(weights[i] - oldWeights[i]);
561 }
562 weightResidual /= noWeights;
563
564 MFloat domainWeightResidual = 0.0;
565 for(MInt i = 0; i < noDomains(); i++) {
566 domainWeightResidual += ABS(m_domainWeights[i] - oldDomainWeights[i]);
567 }
568 domainWeightResidual /= noDomains();
569
570 const MFloat weightResThreshold = 1e-3;
571 const MFloat domainWeightResThreshold = 1e-2;
572 if(weightResidual < weightResThreshold && domainWeightResidual < domainWeightResThreshold) {
573 m_log << " * weights and domain weights converged, residuals: " << weightResidual << ", "
574 << domainWeightResidual << std::endl;
575 break;
576 } else if(it == maxNoIt - 1) {
577 m_log << " * WARNING: Weights and domain weights not converged, but "
578 "maximum number of iterations reached. "
579 << weightResidual << " " << domainWeightResidual << std::endl;
580 } else {
581 m_log << " * Weights and domain weights iteration " << it << " " << weightResidual << " "
582 << domainWeightResidual << std::endl;
583 }
584 }
585 oldWeights = weights;
586 oldDomainWeights = m_domainWeights;
587 }
588
589 if(m_debugBalance) {
590 const MInt noLoadTypes = globalNoLoadTypes();
591 MIntScratchSpace loadQuantities(noLoadTypes, AT_, "loadQuantities");
592 getLoadQuantities(&loadQuantities[0]);
593 storeLoadsAndWeights(&loads[0], noLoadTypes, &loadQuantities[0], m_domainWeights[domainId()], &weights[0]);
594 }
595
596 // New Weights and domain weights are computed, rank 0 has already the partition
597 // cell workload information, now determine the new partition cell offsets
598 if(domainId() == 0) {
599 MFloatScratchSpace errors(noDomains(), AT_, "errors");
600 MFloatScratchSpace summedErrors(noDomains() + 1, AT_, "summedErrors");
601 MFloatScratchSpace summedErrAbs(noDomains() + 1, AT_, "summedErrAbs");
602 MIntScratchSpace summedErrDir(noDomains() + 1, AT_, "summedErrDir");
603
604 // Compute load error for each domain ...
605 for(MInt i = 0; i < noDomains(); i++) {
606 errors[i] = loads[i] - 1.0;
607 }
608 // ... and summed load errors (and their absolut value) at each domain offset
609 summedErrors[0] = 0.0;
610 summedErrAbs[0] = 0.0;
611 summedErrDir[0] = 0;
612 for(MInt i = 1; i < noDomains() + 1; i++) {
613 summedErrors[i] = summedErrors[i - 1] + errors[i - 1];
614 summedErrAbs[i] = ABS(summedErrors[i]);
615 summedErrDir[i] = -signum(summedErrors[i]);
616 }
617
618 const MFloat maxSummedErr = *std::max_element(summedErrors.begin(), summedErrors.end());
619 const MFloat minSummedErr = *std::min_element(summedErrors.begin(), summedErrors.end());
620 m_log << globalTimeStep << " * Summed load deviations: max = " << maxSummedErr << ", min = " << minSummedErr
621 << std::endl;
622
623 // Threshold below which an offset will not be changed
624 const MFloat errorThreshold = m_dlbImbalanceThreshold;
625
626 // Restrict the amount of imbalance to be shifted in a single step for higher degrees of
627 // parallelism
628 const MFloat restrictionFactor = 1.0 - std::min(0.5, 0.005 * noDomains());
629
630 MFloat loadVariance = 0.0;
631 for(MInt i = 0; i < noDomains(); i++) {
632 loadVariance += POW2(loads[i] - 1.0);
633 }
634 loadVariance /= noDomains();
635 const MFloat loadStd = std::sqrt(loadVariance);
636 m_log << globalTimeStep << " * load distribution: variance = " << loadVariance << ", stdev = " << loadStd
637 << std::endl;
638
639 const MFloat imbalancePenaltyThreshold = 2.0 * errorThreshold;
640 const MFloat imbPenaltyFactor =
641 (imbalance < imbalancePenaltyThreshold)
642 ? (1.0 + (imbalancePenaltyThreshold - imbalance) / imbalancePenaltyThreshold)
643 : 1.0;
644 m_log << globalTimeStep << " * Imbalance penalty factor " << imbPenaltyFactor << " (imbalance " << imbalance
645 << "); restriction factor " << restrictionFactor << std::endl;
646
647 // Final local shift for refining the best found partitioning
648 const MBool finalLocalShift =
649 m_dlbNoFinalLocalShifts > 0
650 && globalTimeStep >= m_loadBalancingStopTimeStep - m_loadBalancingInterval * (m_dlbNoFinalLocalShifts + 1);
651 // Intermediate local shift
652 const MBool intermediateLocalShift =
653 !finalLocalShift && (m_dlbNoLocalShifts > 0 && (m_dlbStep % (1 + m_dlbNoLocalShifts) != 1));
654 // Check for global shift step
655 const MBool globalShiftStep = !finalLocalShift && !intermediateLocalShift;
656
657 m_log << globalTimeStep << " * Shift step: global " << globalShiftStep << "; intermediate local "
658 << intermediateLocalShift << "; final local " << finalLocalShift << std::endl;
659
660 // Flag to indicate if a global shift is required for each offset
661 ScratchSpace<MInt> globalShiftFlag(noDomains() + 1, AT_, "globalShiftFlag");
662 globalShiftFlag.fill(0);
663 // Minimum distance of an offset to the next offset with a global shift
664 ScratchSpace<MInt> minGlobalDist(noDomains() + 1, AT_, "minGlobalDist");
665 minGlobalDist.fill(noDomains());
666
667 { // Determine offsets with a global shift
668 ScratchSpace<MInt> globalShiftFlagTmp(noDomains() + 1, AT_, "globalShiftFlagTmp");
669 globalShiftFlagTmp.fill(0);
670
671 MInt noGlobalShifts = 0;
672 // 1. Determine offsets which require a global shift, i.e., the load is too imbalanced
673 // among the neighboring domains
674 for(MInt i = 1; i < noDomains(); i++) {
675 const MInt dist = 4; // consider offsets in this distance to both sides
676 const MInt firstOffset = std::max(0, i - dist);
677 const MInt lastOffset = std::min(noDomains(), i + dist);
678 // Difference in summed imbalance
679 const MFloat summedErrorDiff = ABS(summedErrors[lastOffset] - summedErrors[firstOffset]);
680 const MBool sameSign = (summedErrDir[lastOffset] == summedErrDir[firstOffset]);
681 const MBool absErrCondition = (summedErrAbs[i] > errorThreshold);
682
683 const MFloat smoothShiftThreshold = (lastOffset - firstOffset) * errorThreshold; // 0.5
684 const MBool smoothShiftCondition =
685 (absErrCondition && (sameSign && summedErrorDiff > smoothShiftThreshold));
686 // Global shift required if the difference in the summed imbalance is too high
687 MBool globalShiftCondition = false;
688 if(globalShiftStep) {
689 globalShiftCondition = (m_dlbSmoothGlobalShifts) ? smoothShiftCondition : absErrCondition;
690 }
691
692 globalShiftFlagTmp[i] = (globalShiftCondition) ? summedErrDir[i] : 0;
693 if(globalShiftFlagTmp[i] != 0) {
694 noGlobalShifts++;
695 if(m_debugBalance) {
696 m_log << globalTimeStep << " * global shift " << i << " diff=" << summedErrorDiff
697 << " tr=" << smoothShiftThreshold << " s_first=" << summedErrors[firstOffset]
698 << " s_last=" << summedErrors[lastOffset] << " s_abs=" << summedErrAbs[i] << std::endl;
699 }
700 }
701 }
702 m_log << globalTimeStep << " * number of global shifts " << noGlobalShifts << std::endl;
703
704 // 2. For each offset marked with a global shift, mark also the nearby offsets up to a
705 // certain distance while reducing the size of the shift such that global shifts are
706 // smoothed out over some neighborhood. For this the minimum distance to a global shift is
707 // stored, which later reduces the summed imbalance that needs to be counterbalanced by
708 // the shift.
709 for(MInt i = 1; i < noDomains(); i++) {
710 // Check for offset with global shift
711 if(globalShiftFlagTmp[i] != 0) {
712 minGlobalDist[i] = 0;
713 globalShiftFlag[i] = globalShiftFlagTmp[i];
714
715 // Distance over which to smooth out the global shift
716 const MInt dist = std::ceil(summedErrAbs[i] / errorThreshold);
717 if(m_debugBalance) {
718 m_log << globalTimeStep << " * global shift " << i << " marking distance " << dist << " "
719 << summedErrAbs[i] << std::endl;
720 }
721
722 // Loop over lower offsets
723 for(MInt nId = i - 1; nId >= std::max(i - dist, 1); nId--) {
724 const MInt distance = ABS(nId - i);
725
726 // Stop marking offsets if shift direction is not the same anymore or if the
727 // summed imbalance is too small for this distance, which would result in a summed
728 // imbalance with opposite sign
729 if(summedErrDir[nId] != summedErrDir[i] || errorThreshold * distance > summedErrAbs[nId]) {
730 break;
731 }
732
733 minGlobalDist[nId] = std::min(minGlobalDist[nId], distance);
734 globalShiftFlag[nId] = summedErrDir[nId];
735 }
736 // Loop over higher offsets
737 for(MInt nId = i + 1; nId <= std::min(i + dist, noDomains() - 1); nId++) {
738 const MInt distance = ABS(nId - i);
739
740 if(summedErrDir[nId] != summedErrDir[i] || errorThreshold * distance > summedErrAbs[nId]) {
741 break;
742 }
743
744 minGlobalDist[nId] = std::min(minGlobalDist[nId], distance);
745 globalShiftFlag[nId] = summedErrDir[nId];
746 }
747 }
748 }
749
750 MInt noGlobalShiftsTotal = 0;
751 for(MInt i = 1; i < noDomains(); i++) {
752 if(globalShiftFlag[i] != 0) {
753 noGlobalShiftsTotal++;
754 }
755 }
756 m_log << globalTimeStep << " * number of total global shifts " << noGlobalShiftsTotal << std::endl;
757 }
758
759 // Variables for some user output about the shifts
760 MInt noShifts = 0;
761 MInt noShiftsWithoutChange = 0;
762 MFloat maxWeight = 0.0;
763 MFloat maxDiff = 0.0;
764 // Storage for shift information that is written to file in debug mode
765 ScratchSpace<MInt> shifts(noDomains() + 1, AT_, "shifts");
766 shifts.fill(0);
767 ScratchSpace<MFloat> shiftedWorkload(noDomains() + 1, AT_, "shiftedWorkload");
768 shiftedWorkload.fill(0.0);
769
770 partitionCellOffsets[0] = 0;
771 // vary min cell offsets individually
772 for(MInt offsetId = 1; offsetId < noDomains(); offsetId++) {
773 MFloat summedWorkload = 0.0;
774 // Determine current workload of the domain left to this offset (with new previous offset)
775 for(MInt i = partitionCellOffsets[offsetId - 1]; i < localPartitionCellOffsets[offsetId]; i++) {
776 summedWorkload += (m_useDomainFactor) ? m_domainWeights[offsetId - 1] * partitionCellsWorkload[i]
777 : partitionCellsWorkload[i];
778 }
779
780 // Check for a global or local shift
781 const MBool globalShiftCondition = (globalShiftFlag[offsetId] != 0);
782 MBool localShiftCondition = false;
783 MInt localShiftDir = 0;
784 MFloat localShiftDiff = 0.0;
785
786 // No global shift for this offset and the neighboring ones, use a local shift to
787 // distribute load among neighboring domains
788 if(!globalShiftCondition && globalShiftFlag[offsetId - 1] == 0 && globalShiftFlag[offsetId + 1] == 0) {
789 const MFloat errLeft = errors[offsetId - 1];
790 const MFloat errRight = errors[offsetId];
791 localShiftDiff = 0.5 * (errLeft - errRight);
792
793 if(errLeft > errorThreshold && errRight < errLeft) {
794 // overload left
795 localShiftDir = -1;
796 } else if(errRight > errorThreshold && errRight > errLeft) {
797 // overload right
798 localShiftDir = 1;
799 } else if(errLeft < -errorThreshold && errLeft < errRight) {
800 // underload left (l/r no overload)
801 localShiftDir = 1;
802 } else if(errRight < -errorThreshold && errRight < errLeft) {
803 // underload right (l/r no overload)
804 localShiftDir = -1;
805 }
806 localShiftCondition = (localShiftDir != 0);
807 }
808
809 const MBool shift = globalShiftCondition || localShiftCondition;
810
811 if(m_debugBalance) {
812 m_log << globalTimeStep << " * shift conditions " << offsetId << " shift=" << shift
813 << " g=" << globalShiftCondition << " l=" << localShiftCondition << " err=" << summedErrors[offsetId]
814 << " minDist=" << minGlobalDist[offsetId] << " lDir=" << localShiftDir << " diff=" << localShiftDiff
815 << " eL=" << errors[offsetId - 1] << " eR=" << errors[offsetId] << std::endl;
816 }
817
818 if(shift) {
819 // Set shift direction depending on global or local shift
820 const MInt dir = (globalShiftCondition) ? summedErrDir[offsetId] : localShiftDir;
821 MFloat penaltyFactor = 1.25;
822
823 const MInt lastDir = (globalShiftCondition) ? m_lastOffsetShiftDirection[offsetId] : 0;
824 // Check for current shift in opposite direction of last shift (only if shift is global)
825 if(lastDir != dir && lastDir != 0) {
826 penaltyFactor = 2.0;
827 if(m_debugBalance) {
828 m_log << "Opposite shift " << offsetId << ", lastDir " << lastDir << ", dir " << dir << std::endl;
829 }
830 }
831 penaltyFactor *= imbPenaltyFactor;
832
833 // Determine 'new' and 'old' domain id depending on the direction in
834 // which the partition cell offset will be shifted
835 const MInt domainOld = (dir < 0) ? offsetId - 1 : offsetId;
836 const MInt domainNew = (dir < 0) ? offsetId : offsetId - 1;
837
838 // Initialize estimated summed load error
839 // To smooth out the globally enforced shifts, the error threshold times
840 // the minimum distance to the next domain which requires a global shift is subtracted,
841 // i.e., the shift of an offset further away from a global shift is reduced
842 const MFloat summedErrorTmp =
843 restrictionFactor * (summedErrors[offsetId] + dir * errorThreshold * minGlobalDist[offsetId]);
844 //(dir > 0) ? std::max(-2.0, summedErrorTmp) : std::min(2.0, summedErrorTmp);
845 const MFloat summedError = summedErrorTmp;
846 MFloat oldEstimate = (globalShiftCondition) ? summedError : localShiftDiff;
847 const MFloat initialEstimate = oldEstimate;
848
849 MFloat penalty = 1.0;
850
851 // Domain id of the currently considered partition cell
852 MInt partitionCellDomain = domainOld;
853
854 if(m_debugBalance) {
855 m_log << "DLB_DEBUG: Start offset shift " << offsetId << ", domainOld " << domainOld << ", domainNew "
856 << domainNew << ", oldEstimate " << oldEstimate << ", summedError " << summedErrors[offsetId]
857 << ", penaltyFactor " << penaltyFactor << std::endl;
858 }
859
860 // Shift the partition cell offset such that the new estimated summed load error is
861 // minimized
862 for(MInt i = 1;; i++) {
863 // TODO labels:DLB check if first/last partition cell is reached and stop!
864 // Currently considered partition cell id
865 const MInt newPartitionCellId = localPartitionCellOffsets[offsetId] + dir * i;
866
867 // Update domain id on which the current partition cell belongs
868 if(dir > 0 && newPartitionCellId == localPartitionCellOffsets[partitionCellDomain + 1]) {
869 // Next partition cell offsets reached, increase domain id
870 partitionCellDomain++;
871 } else if(dir < 0 && newPartitionCellId == localPartitionCellOffsets[partitionCellDomain] - 1) {
872 // Previous partition cell offset exceeded, decrease domain id
873 partitionCellDomain--;
874 }
875
876 const MBool domainFactorShift = true; //(oldEstimate < 1.0); // TODO labels:CONTROLLER,DLB
877 // Performance factor of domains
878 const MFloat domainFactor = (m_useDomainFactor && domainFactorShift)
879 ? (m_domainWeights[domainNew] / m_domainWeights[partitionCellDomain])
880 : 1.0;
881
882 // Compute new estimate of summed load error
883 const MFloat estimate = oldEstimate
884 + dir * penaltyFactor * penalty * domainFactor * loads[partitionCellDomain]
885 * partitionCellsWorkload[newPartitionCellId]
886 / domainWorkLoad[partitionCellDomain];
887 const MFloat diff = estimate - oldEstimate;
888
889 // Reset summed workload if the previous new offset is still larger than the current
890 // one
891 if(newPartitionCellId <= partitionCellOffsets[offsetId - 1]) {
892 summedWorkload = 0;
893 }
894 const MFloat workloadIncrement =
895 (m_useDomainFactor) ? m_domainWeights[offsetId - 1] * partitionCellsWorkload[newPartitionCellId]
896 : partitionCellsWorkload[newPartitionCellId];
897 const MFloat summedWorkloadNew = summedWorkload + dir * workloadIncrement;
898 const MFloat summedWorkloadRel = summedWorkload / averageWorkLoad;
899
900#ifndef NDEBUG
901 // Only useful for debugging small cases
902 if(m_debugBalance) {
903 m_log << "DLB_DEBUG: offset " << offsetId << ", partCellId " << newPartitionCellId
904 << ", partCellDomain " << partitionCellDomain << ", domainFactor " << domainFactor
905 << ", oldEstimate " << oldEstimate << ", estimate " << estimate << ", diff " << diff
906 << ", penalty " << penalty << ", partCellWorkload " << partitionCellsWorkload[newPartitionCellId]
907 << ", domainWorkload " << domainWorkLoad[partitionCellDomain] << ", load "
908 << loads[partitionCellDomain] << std::endl;
909 }
910#endif
911
912 // Accept the previous new partition cell offset if the predicted summed load error
913 // reached its minimum or if the previous domain has only a single parititon cell left
914 // (when decreasing the current partition cell offset).
915 const MBool prevOffsetReached = (dir < 0 && newPartitionCellId == partitionCellOffsets[offsetId - 1]);
916 const MBool lastCellReached = (newPartitionCellId == gridb().m_noPartitionCellsGlobal - 1);
917
918 // Continue shift if the workload is too large
919 const MBool workloadCheck = (m_dlbMaxWorkloadLimit > 1.0 && globalShiftCondition)
920 ? (summedWorkloadRel < m_dlbMaxWorkloadLimit)
921 : true;
922
923 if(prevOffsetReached || lastCellReached || (ABS(estimate) >= ABS(oldEstimate) && workloadCheck)
924 || (dir == 1 && !workloadCheck)) {
925 // New domain offset
926 partitionCellOffsets[offsetId] = localPartitionCellOffsets[offsetId] + dir * (i - 1);
927
928 if(m_debugBalance) {
929 m_log << globalTimeStep << " * shifted offset " << offsetId << " " << dir * (i - 1) << " "
930 << localPartitionCellOffsets[offsetId] << " " << partitionCellOffsets[offsetId] << " "
931 << summedErrors[offsetId] << " " << estimate << " " << oldEstimate << " " << penalty << " "
932 << summedWorkloadRel << std::endl;
933 }
934 shifts[offsetId] = dir * (i - 1);
935
936 // Set direction of shift (if it was a global shift)
937 m_lastOffsetShiftDirection[offsetId] = (i > 1 && globalShiftCondition) ? dir : 0;
938
939 // Make sure the current domain has at least a single min cell
940 // TODO labels:DLB handle last domain offset!
941 if(partitionCellOffsets[offsetId] <= partitionCellOffsets[offsetId - 1]) {
942 partitionCellOffsets[offsetId] = partitionCellOffsets[offsetId - 1] + 1;
943 shifts[offsetId] = partitionCellOffsets[offsetId] - localPartitionCellOffsets[offsetId];
944 m_log << "WARNING: setting domain offset #" << offsetId
945 << " with just a single partition cell on this domain. " << partitionCellOffsets[offsetId - 1]
946 << " " << partitionCellOffsets[offsetId] << std::endl;
947 } else if(partitionCellOffsets[offsetId] == partitionCellOffsets[offsetId - 1] + 1) {
948 m_log << "WARNING: domain #" << offsetId - 1 << " has just a single partition cell. "
949 << partitionCellOffsets[offsetId - 1] << " " << partitionCellOffsets[offsetId] << std::endl;
950 }
951
952 if(shifts[offsetId] != 0) {
953 noShifts++;
954 } else {
955 noShiftsWithoutChange++;
956 }
957
958 // Continue with next domain offset
959 break;
960 } else {
961 // Update estimate and continue with next partition cell
962 oldEstimate = estimate;
963 // Update summed workload for the domain left to the offset
964 summedWorkload = summedWorkloadNew;
965
966 // Determine max difference and weight for some user output
967 maxDiff = std::max(maxDiff, ABS(diff));
968 maxWeight = std::max(maxWeight, partitionCellsWorkload[newPartitionCellId]);
969 // Increase shifted workload
970 shiftedWorkload[offsetId] += dir * partitionCellsWorkload[newPartitionCellId];
971
972 // Compute new penalty factor: 1.0/cos(pi/2*((s-e)/s))
973 // Goes to infinity as the estimate goes to zero and is intended to prevent
974 // 'overshooting' of the domain offsets, i.e. shifting the domain offsets too far
975 const MFloat s = initialEstimate;
976 const MFloat x = ABS((ABS(s) - ABS(estimate)) / ABS(s));
977 penalty = 1.0 / cos(M_PI / 2.0 * x);
978 }
979 }
980 } else {
981 // Accept old partition cell offset
982 partitionCellOffsets[offsetId] = localPartitionCellOffsets[offsetId];
983 m_lastOffsetShiftDirection[offsetId] = 0;
984 shifts[offsetId] = 0;
985
986 if(m_debugBalance) {
987 m_log << globalTimeStep << " * keep offset " << offsetId << " " << summedErrors[offsetId] << std::endl;
988 }
989 }
990 }
991
992 // Correct offsets which were not shifted to prevent domains with an invalid number of cells!
993 for(MInt offsetId = 1; offsetId < noDomains(); offsetId++) {
994 if(partitionCellOffsets[offsetId] <= partitionCellOffsets[offsetId - 1]) {
995 partitionCellOffsets[offsetId] = partitionCellOffsets[offsetId - 1] + 1;
996 shifts[offsetId] = partitionCellOffsets[offsetId] - localPartitionCellOffsets[offsetId];
997 m_log << "WARNING: correcting domain offset #" << offsetId
998 << " with now just a single partition cell on this domain. " << partitionCellOffsets[offsetId - 1]
999 << " " << partitionCellOffsets[offsetId] << std::endl;
1000 }
1001 }
1002
1003 m_log << " * number of shifted offsets: " << noShifts
1004 << "; offsets to shift without change: " << noShiftsWithoutChange << std::endl;
1005 m_log << " * max weight during shifting: " << maxWeight << "; max difference: " << maxDiff << std::endl;
1006
1007 // Debug: store partition offset shifts to file
1008 if(m_debugBalance) {
1009 FILE* shiftsFile = nullptr;
1010 const MString fileName = "shifts_" + std::to_string(globalTimeStep) + ".dat";
1011 shiftsFile = fopen(fileName.c_str(), "w");
1012
1013 fprintf(shiftsFile, "# 1:offsetId 2:partitionCellShift 3:shiftedWorkload "
1014 "4:summedImbalance 5:load 6:domainWorkload 7:domainWeight\n");
1015 for(MInt i = 0; i < noDomains(); i++) {
1016 fprintf(shiftsFile, "%d %d %.8e %.8e %.8e %.8e %.8e\n", i, shifts[i], shiftedWorkload[i], summedErrors[i],
1017 loads[i], domainWorkLoad[i], m_domainWeights[i]);
1018 }
1019 fclose(shiftsFile);
1020 }
1021 }
1022
1023 // Broadcast new partition cell offsets
1024 MPI_Bcast(&partitionCellOffsets[0], noDomains(), type_traits<MLong>::mpiType(), 0, gridb().mpiComm(), AT_,
1025 "partitionCellOffsets[0]");
1026 partitionCellOffsets[noDomains()] = gridb().m_noPartitionCellsGlobal;
1027
1028 break;
1029 }
1030 default: {
1031 TERMM(1, "Partition method not known.");
1032 break;
1033 }
1034 }
1035
1036 // Check if new distribution is different to the old one
1037 const MBool newPartition =
1038 !std::equal(&localPartitionCellOffsets[0], &localPartitionCellOffsets[0] + noDomains(), &partitionCellOffsets[0]);
1039
1040 m_log << " * checking new partition" << std::endl;
1041 MBool validPartition =
1042 (partitionCellOffsets[0] == 0) && (partitionCellOffsets[noDomains() - 1] < gridb().m_noPartitionCellsGlobal);
1043 if(!validPartition) {
1044 m_log << " * invalid first/last domain offset:" << partitionCellOffsets[0] << " "
1045 << partitionCellOffsets[noDomains() - 1] << "; noPartitionCellsGlobal = " << gridb().m_noPartitionCellsGlobal
1046 << std::endl;
1047 }
1048 // Check if new partitioning is valid, i.e. the domain offsets are in ascending order without duplicates
1049 for(MInt i = 1; i < noDomains(); i++) {
1050 if(partitionCellOffsets[i] <= partitionCellOffsets[i - 1]) {
1051 validPartition = false;
1052 m_log << " * invalid offsets " << i - 1 << " " << partitionCellOffsets[i - 1] << " and " << i << " "
1053 << partitionCellOffsets[i] << std::endl;
1054 }
1055 }
1056
1057 // Return if partitioning is not valid
1058 // Note: this may not work if m_forceLoadBalancing is active
1059 if(!validPartition) {
1060 m_log << " * new partition is not valid!" << std::endl;
1061 return false;
1062 }
1063
1064 // Return if partitioning did not change and DLB is not forced
1065 if(!newPartition && !m_forceLoadBalancing) {
1066 m_log << " * partitioning did not change, return." << std::endl;
1067 return false;
1068 } else {
1069 m_log << " * new partition (" << newPartition << "), forced balance (" << m_forceLoadBalancing << ")" << std::endl;
1070 }
1071
1072 // Calculate new global domain offsets from the new partition cell offsets
1073 loadBalancingCalcNewGlobalOffsets(&localPartitionCellOffsets[0], &partitionCellOffsets[0], &globalIdOffsets[0]);
1074
1075 return true;
1076}
1077
1078template class maia::grid::Controller<2>;
1079template class maia::grid::Controller<3>;
void mAlloc(T *&a, const MLong N, const MString &objectName, MString function)
allocates memory for one-dimensional array 'a' of size N
Definition: alloc.h:173
static MBool propertyExists(const MString &name, MInt solver=m_noSolvers)
This function checks if a property exists in general.
Definition: context.cpp:494
This class is a ScratchSpace.
Definition: scratch.h:758
pointer data()
Definition: scratch.h:289
void fill(T val)
fill the scratch with a given value
Definition: scratch.h:311
iterator end()
Definition: scratch.h:281
iterator begin()
Definition: scratch.h:273
Grid controller manages adaptation and load balancing in multi-solver environment.
void computeWeights(const MFloat *loads, const MFloat domainWeight, std::vector< MFloat > &weights)
Compute computational weights for different components in the simulation based on the current distrib...
MBool loadBalancingPartition(const MFloat *loads, const MFloat imbalance, MLong *const partitionCellOffsets, MLong *const globalIdOffsets)
Determine new partitioning for dynamic load balancing.
void estimateParameters(MInt m, MInt n, const MFloat *const A, const MFloat *const b, MFloat *const x)
Solve the parameter estimation problem A*x=b.
@ DLB_PARTITION_DEFAULT
Definition: enums.h:369
@ DLB_PARTITION_WEIGHT
Definition: enums.h:370
@ DLB_PARTITION_TEST
Definition: enums.h:372
@ DLB_PARTITION_SHIFT_OFFSETS
Definition: enums.h:371
std::enable_if< std::is_unsigned< T >::value, int >::type constexpr signum(T x)
Definition: functions.h:151
constexpr Real POW2(const Real x)
Definition: functions.h:119
Real ABS(const Real x)
Definition: functions.h:85
MInt globalTimeStep
InfoOutFile m_log
std::ostream cerr0
int32_t MInt
Definition: maiatypes.h:62
std::basic_string< char > MString
Definition: maiatypes.h:55
MInt noSolvers
Definition: maiatypes.h:73
double MFloat
Definition: maiatypes.h:52
int64_t MLong
Definition: maiatypes.h:64
bool MBool
Definition: maiatypes.h:58
int MPI_Gatherv(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, const int recvcounts[], const int displs[], MPI_Datatype recvtype, int root, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Gatherv
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm, const MString &name, const MString &varname)
same as MPI_Bcast
int MPI_Gather(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Gather
std::vector< MFloat > svd(MFloat *const A, MFloat *const b, const MInt m, const MInt n, MFloat *const x)
Definition: maiamath.cpp:514
Namespace for auxiliary functions/classes.
MFloat dist(const Point< DIM > &p, const Point< DIM > &q)
Definition: pointbox.h:54