• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmc-dev / openmc / 12385490469

18 Dec 2024 02:58AM UTC coverage: 82.673% (-2.2%) from 84.823%
12385490469

Pull #3087

github

web-flow
Merge 3317476f5 into 775c41512
Pull Request #3087: wheel building with scikit build core

106679 of 129038 relevant lines covered (82.67%)

12084526.77 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

4.49
/src/cmfd_solver.cpp
1
#include "openmc/cmfd_solver.h"
2

3
#include <cmath>
4

5
#ifdef _OPENMP
6
#include <omp.h>
7
#endif
8
#include "xtensor/xtensor.hpp"
9

10
#include "openmc/bank.h"
11
#include "openmc/capi.h"
12
#include "openmc/constants.h"
13
#include "openmc/error.h"
14
#include "openmc/mesh.h"
15
#include "openmc/message_passing.h"
16
#include "openmc/tallies/filter_energy.h"
17
#include "openmc/tallies/filter_mesh.h"
18
#include "openmc/tallies/tally.h"
19
#include "openmc/vector.h"
20

21
namespace openmc {
22

23
namespace cmfd {
24

25
//==============================================================================
26
// Global variables
27
//==============================================================================
28

29
vector<int> indptr;
30

31
vector<int> indices;
32

33
int dim;
34

35
double spectral;
36

37
int nx, ny, nz, ng;
38

39
xt::xtensor<int, 2> indexmap;
40

41
int use_all_threads;
42

43
StructuredMesh* mesh;
44

45
vector<double> egrid;
46

47
double norm;
48

49
} // namespace cmfd
50

51
//==============================================================================
52
// GET_CMFD_ENERGY_BIN returns the energy bin for a source site energy
53
//==============================================================================
54

55
int get_cmfd_energy_bin(const double E)
×
56
{
57
  // Check if energy is out of grid bounds
58
  if (E < cmfd::egrid[0]) {
×
59
    // throw warning message
60
    warning("Detected source point below energy grid");
×
61
    return 0;
×
62
  } else if (E >= cmfd::egrid[cmfd::ng]) {
×
63
    // throw warning message
64
    warning("Detected source point above energy grid");
×
65
    return cmfd::ng - 1;
×
66
  } else {
67
    // Iterate through energy grid to find matching bin
68
    for (int g = 0; g < cmfd::ng; g++) {
×
69
      if (E >= cmfd::egrid[g] && E < cmfd::egrid[g + 1]) {
×
70
        return g;
×
71
      }
72
    }
73
  }
74
  // Return -1 by default
75
  return -1;
×
76
}
77

78
//==============================================================================
79
// COUNT_BANK_SITES bins fission sites according to CMFD mesh and energy
80
//==============================================================================
81

82
xt::xtensor<double, 1> count_bank_sites(
×
83
  xt::xtensor<int, 1>& bins, bool* outside)
84
{
85
  // Determine shape of array for counts
86
  std::size_t cnt_size = cmfd::nx * cmfd::ny * cmfd::nz * cmfd::ng;
×
87
  vector<std::size_t> cnt_shape = {cnt_size};
×
88

89
  // Create array of zeros
90
  xt::xarray<double> cnt {cnt_shape, 0.0};
×
91
  bool outside_ = false;
×
92

93
  auto bank_size = simulation::source_bank.size();
×
94
  for (int i = 0; i < bank_size; i++) {
×
95
    const auto& site = simulation::source_bank[i];
×
96

97
    // determine scoring bin for CMFD mesh
98
    int mesh_bin = cmfd::mesh->get_bin(site.r);
×
99

100
    // if outside mesh, skip particle
101
    if (mesh_bin < 0) {
×
102
      outside_ = true;
×
103
      continue;
×
104
    }
105

106
    // determine scoring bin for CMFD energy
107
    int energy_bin = get_cmfd_energy_bin(site.E);
×
108

109
    // add to appropriate bin
110
    cnt(mesh_bin * cmfd::ng + energy_bin) += site.wgt;
×
111

112
    // store bin index which is used again when updating weights
113
    bins[i] = mesh_bin * cmfd::ng + energy_bin;
×
114
  }
115

116
  // Create copy of count data. Since ownership will be acquired by xtensor,
117
  // std::allocator must be used to avoid Valgrind mismatched free() / delete
118
  // warnings.
119
  int total = cnt.size();
×
120
  double* cnt_reduced = std::allocator<double> {}.allocate(total);
×
121

122
#ifdef OPENMC_MPI
123
  // collect values from all processors
124
  MPI_Reduce(
125
    cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0, mpi::intracomm);
126

127
  // Check if there were sites outside the mesh for any processor
128
  MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm);
129

130
#else
131
  std::copy(cnt.data(), cnt.data() + total, cnt_reduced);
132
  *outside = outside_;
133
#endif
134

135
  // Adapt reduced values in array back into an xarray
136
  auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), cnt_shape);
×
137
  xt::xarray<double> counts = arr;
×
138

139
  return counts;
×
140
}
141

142
//==============================================================================
143
// OPENMC_CMFD_REWEIGHT performs reweighting of particles in source bank
144
//==============================================================================
145

146
extern "C" void openmc_cmfd_reweight(
×
147
  const bool feedback, const double* cmfd_src)
148
{
149
  // Get size of source bank and cmfd_src
150
  auto bank_size = simulation::source_bank.size();
×
151
  std::size_t src_size = cmfd::nx * cmfd::ny * cmfd::nz * cmfd::ng;
×
152

153
  // count bank sites for CMFD mesh, store bins in bank_bins for reweighting
154
  xt::xtensor<int, 1> bank_bins({bank_size}, 0);
×
155
  bool sites_outside;
156
  xt::xtensor<double, 1> sourcecounts =
157
    count_bank_sites(bank_bins, &sites_outside);
×
158

159
  // Compute CMFD weightfactors
160
  xt::xtensor<double, 1> weightfactors = xt::xtensor<double, 1>({src_size}, 1.);
×
161
  if (mpi::master) {
×
162
    if (sites_outside) {
×
163
      fatal_error("Source sites outside of the CMFD mesh");
×
164
    }
165

166
    double norm = xt::sum(sourcecounts)() / cmfd::norm;
×
167
    for (int i = 0; i < src_size; i++) {
×
168
      if (sourcecounts[i] > 0 && cmfd_src[i] > 0) {
×
169
        weightfactors[i] = cmfd_src[i] * norm / sourcecounts[i];
×
170
      }
171
    }
172
  }
173

174
  if (!feedback)
×
175
    return;
×
176

177
#ifdef OPENMC_MPI
178
  // Send weightfactors to all processors
179
  MPI_Bcast(weightfactors.data(), src_size, MPI_DOUBLE, 0, mpi::intracomm);
180
#endif
181

182
  // Iterate through fission bank and update particle weights
183
  for (int64_t i = 0; i < bank_size; i++) {
×
184
    auto& site = simulation::source_bank[i];
×
185
    site.wgt *= weightfactors(bank_bins(i));
×
186
  }
187
}
188

189
//==============================================================================
190
// OPENMC_INITIALIZE_MESH_EGRID sets the mesh and energy grid for CMFD reweight
191
//==============================================================================
192

193
extern "C" void openmc_initialize_mesh_egrid(
×
194
  const int meshtally_id, const int* cmfd_indices, const double norm)
195
{
196
  // Make sure all CMFD memory is freed
197
  free_memory_cmfd();
×
198

199
  // Set CMFD indices
200
  cmfd::nx = cmfd_indices[0];
×
201
  cmfd::ny = cmfd_indices[1];
×
202
  cmfd::nz = cmfd_indices[2];
×
203
  cmfd::ng = cmfd_indices[3];
×
204

205
  // Set CMFD reweight properties
206
  cmfd::norm = norm;
×
207

208
  // Find index corresponding to tally id
209
  int32_t tally_index;
210
  openmc_get_tally_index(meshtally_id, &tally_index);
×
211

212
  // Get filters assocaited with tally
213
  const auto& tally_filters = model::tallies[tally_index]->filters();
×
214

215
  // Get mesh filter index
216
  auto meshfilter_index = tally_filters[0];
×
217

218
  // Store energy filter index if defined, otherwise set to -1
219
  auto energy_index = (tally_filters.size() == 2) ? tally_filters[1] : -1;
×
220

221
  // Get mesh index from mesh filter index
222
  int32_t mesh_index;
223
  openmc_mesh_filter_get_mesh(meshfilter_index, &mesh_index);
×
224

225
  // Get mesh from mesh index
226
  cmfd::mesh = dynamic_cast<StructuredMesh*>(model::meshes[mesh_index].get());
×
227

228
  // Get energy bins from energy index, otherwise use default
229
  if (energy_index != -1) {
×
230
    auto efilt_base = model::tally_filters[energy_index].get();
×
231
    auto* efilt = dynamic_cast<EnergyFilter*>(efilt_base);
×
232
    cmfd::egrid = efilt->bins();
×
233
  } else {
234
    cmfd::egrid = {0.0, INFTY};
×
235
  }
236
}
237

238
//==============================================================================
239
// MATRIX_TO_INDICES converts a matrix index to spatial and group
240
// indices
241
//==============================================================================
242

243
void matrix_to_indices(int irow, int& g, int& i, int& j, int& k)
×
244
{
245
  g = irow % cmfd::ng;
×
246
  i = cmfd::indexmap(irow / cmfd::ng, 0);
×
247
  j = cmfd::indexmap(irow / cmfd::ng, 1);
×
248
  k = cmfd::indexmap(irow / cmfd::ng, 2);
×
249
}
250

251
//==============================================================================
252
// GET_DIAGONAL_INDEX returns the index in CSR index array corresponding to
253
// the diagonal element of a specified row
254
//==============================================================================
255

256
int get_diagonal_index(int row)
×
257
{
258
  for (int j = cmfd::indptr[row]; j < cmfd::indptr[row + 1]; j++) {
×
259
    if (cmfd::indices[j] == row)
×
260
      return j;
×
261
  }
262

263
  // Return -1 if not found
264
  return -1;
×
265
}
266

267
//==============================================================================
268
// SET_INDEXMAP sets the elements of indexmap based on input coremap
269
//==============================================================================
270

271
void set_indexmap(const int* coremap)
×
272
{
273
  for (int z = 0; z < cmfd::nz; z++) {
×
274
    for (int y = 0; y < cmfd::ny; y++) {
×
275
      for (int x = 0; x < cmfd::nx; x++) {
×
276
        int idx = (z * cmfd::ny * cmfd::nx) + (y * cmfd::nx) + x;
×
277
        if (coremap[idx] != CMFD_NOACCEL) {
×
278
          int counter = coremap[idx];
×
279
          cmfd::indexmap(counter, 0) = x;
×
280
          cmfd::indexmap(counter, 1) = y;
×
281
          cmfd::indexmap(counter, 2) = z;
×
282
        }
283
      }
284
    }
285
  }
286
}
287

288
//==============================================================================
289
// CMFD_LINSOLVER_1G solves a one group CMFD linear system
290
//==============================================================================
291

292
int cmfd_linsolver_1g(
×
293
  const double* A_data, const double* b, double* x, double tol)
294
{
295
  // Set overrelaxation parameter
296
  double w = 1.0;
×
297

298
  // Perform Gauss-Seidel iterations
299
  for (int igs = 1; igs <= 10000; igs++) {
×
300
    double err = 0.0;
×
301

302
    // Copy over x vector
303
    vector<double> tmpx {x, x + cmfd::dim};
×
304

305
    // Perform red/black Gauss-Seidel iterations
306
    for (int irb = 0; irb < 2; irb++) {
×
307

308
// Loop around matrix rows
309
#pragma omp parallel for reduction(+ : err) if (cmfd::use_all_threads)
310
      for (int irow = 0; irow < cmfd::dim; irow++) {
311
        int g, i, j, k;
312
        matrix_to_indices(irow, g, i, j, k);
313

314
        // Filter out black cells
315
        if ((i + j + k) % 2 != irb)
316
          continue;
317

318
        // Get index of diagonal for current row
319
        int didx = get_diagonal_index(irow);
320

321
        // Perform temporary sums, first do left of diag, then right of diag
322
        double tmp1 = 0.0;
323
        for (int icol = cmfd::indptr[irow]; icol < didx; icol++)
324
          tmp1 += A_data[icol] * x[cmfd::indices[icol]];
325
        for (int icol = didx + 1; icol < cmfd::indptr[irow + 1]; icol++)
326
          tmp1 += A_data[icol] * x[cmfd::indices[icol]];
327

328
        // Solve for new x
329
        double x1 = (b[irow] - tmp1) / A_data[didx];
330

331
        // Perform overrelaxation
332
        x[irow] = (1.0 - w) * x[irow] + w * x1;
333

334
        // Compute residual and update error
335
        double res = (tmpx[irow] - x[irow]) / tmpx[irow];
336
        err += res * res;
337
      }
338
    }
339

340
    // Check convergence
341
    err = std::sqrt(err / cmfd::dim);
×
342
    if (err < tol)
×
343
      return igs;
×
344

345
    // Calculate new overrelaxation parameter
346
    w = 1.0 / (1.0 - 0.25 * cmfd::spectral * w);
×
347
  }
348

349
  // Throw error, as max iterations met
350
  fatal_error("Maximum Gauss-Seidel iterations encountered.");
×
351

352
  // Return -1 by default, although error thrown before reaching this point
353
  return -1;
354
}
355

356
//==============================================================================
357
// CMFD_LINSOLVER_2G solves a two group CMFD linear system
358
//==============================================================================
359

360
int cmfd_linsolver_2g(
×
361
  const double* A_data, const double* b, double* x, double tol)
362
{
363
  // Set overrelaxation parameter
364
  double w = 1.0;
×
365

366
  // Perform Gauss-Seidel iterations
367
  for (int igs = 1; igs <= 10000; igs++) {
×
368
    double err = 0.0;
×
369

370
    // Copy over x vector
371
    vector<double> tmpx {x, x + cmfd::dim};
×
372

373
    // Perform red/black Gauss-Seidel iterations
374
    for (int irb = 0; irb < 2; irb++) {
×
375

376
// Loop around matrix rows
377
#pragma omp parallel for reduction(+ : err) if (cmfd::use_all_threads)
378
      for (int irow = 0; irow < cmfd::dim; irow += 2) {
379
        int g, i, j, k;
380
        matrix_to_indices(irow, g, i, j, k);
381

382
        // Filter out black cells
383
        if ((i + j + k) % 2 != irb)
384
          continue;
385

386
        // Get index of diagonals for current row and next row
387
        int d1idx = get_diagonal_index(irow);
388
        int d2idx = get_diagonal_index(irow + 1);
389

390
        // Get block diagonal
391
        double m11 = A_data[d1idx]; // group 1 diagonal
392
        double m12 =
393
          A_data[d1idx + 1]; // group 1 right of diagonal (sorted by col)
394
        double m21 =
395
          A_data[d2idx - 1];        // group 2 left of diagonal (sorted by col)
396
        double m22 = A_data[d2idx]; // group 2 diagonal
397

398
        // Analytically invert the diagonal
399
        double dm = m11 * m22 - m12 * m21;
400
        double d11 = m22 / dm;
401
        double d12 = -m12 / dm;
402
        double d21 = -m21 / dm;
403
        double d22 = m11 / dm;
404

405
        // Perform temporary sums, first do left of diag, then right of diag
406
        double tmp1 = 0.0;
407
        double tmp2 = 0.0;
408
        for (int icol = cmfd::indptr[irow]; icol < d1idx; icol++)
409
          tmp1 += A_data[icol] * x[cmfd::indices[icol]];
410
        for (int icol = cmfd::indptr[irow + 1]; icol < d2idx - 1; icol++)
411
          tmp2 += A_data[icol] * x[cmfd::indices[icol]];
412
        for (int icol = d1idx + 2; icol < cmfd::indptr[irow + 1]; icol++)
413
          tmp1 += A_data[icol] * x[cmfd::indices[icol]];
414
        for (int icol = d2idx + 1; icol < cmfd::indptr[irow + 2]; icol++)
415
          tmp2 += A_data[icol] * x[cmfd::indices[icol]];
416

417
        // Adjust with RHS vector
418
        tmp1 = b[irow] - tmp1;
419
        tmp2 = b[irow + 1] - tmp2;
420

421
        // Solve for new x
422
        double x1 = d11 * tmp1 + d12 * tmp2;
423
        double x2 = d21 * tmp1 + d22 * tmp2;
424

425
        // Perform overrelaxation
426
        x[irow] = (1.0 - w) * x[irow] + w * x1;
427
        x[irow + 1] = (1.0 - w) * x[irow + 1] + w * x2;
428

429
        // Compute residual and update error
430
        double res = (tmpx[irow] - x[irow]) / tmpx[irow];
431
        err += res * res;
432
      }
433
    }
434

435
    // Check convergence
436
    err = std::sqrt(err / cmfd::dim);
×
437
    if (err < tol)
×
438
      return igs;
×
439

440
    // Calculate new overrelaxation parameter
441
    w = 1.0 / (1.0 - 0.25 * cmfd::spectral * w);
×
442
  }
443

444
  // Throw error, as max iterations met
445
  fatal_error("Maximum Gauss-Seidel iterations encountered.");
×
446

447
  // Return -1 by default, although error thrown before reaching this point
448
  return -1;
449
}
450

451
//==============================================================================
452
// CMFD_LINSOLVER_NG solves a general CMFD linear system
453
//==============================================================================
454

455
int cmfd_linsolver_ng(
×
456
  const double* A_data, const double* b, double* x, double tol)
457
{
458
  // Set overrelaxation parameter
459
  double w = 1.0;
×
460

461
  // Perform Gauss-Seidel iterations
462
  for (int igs = 1; igs <= 10000; igs++) {
×
463
    double err = 0.0;
×
464

465
    // Copy over x vector
466
    vector<double> tmpx {x, x + cmfd::dim};
×
467

468
    // Loop around matrix rows
469
    for (int irow = 0; irow < cmfd::dim; irow++) {
×
470
      // Get index of diagonal for current row
471
      int didx = get_diagonal_index(irow);
×
472

473
      // Perform temporary sums, first do left of diag, then right of diag
474
      double tmp1 = 0.0;
×
475
      for (int icol = cmfd::indptr[irow]; icol < didx; icol++)
×
476
        tmp1 += A_data[icol] * x[cmfd::indices[icol]];
×
477
      for (int icol = didx + 1; icol < cmfd::indptr[irow + 1]; icol++)
×
478
        tmp1 += A_data[icol] * x[cmfd::indices[icol]];
×
479

480
      // Solve for new x
481
      double x1 = (b[irow] - tmp1) / A_data[didx];
×
482

483
      // Perform overrelaxation
484
      x[irow] = (1.0 - w) * x[irow] + w * x1;
×
485

486
      // Compute residual and update error
487
      double res = (tmpx[irow] - x[irow]) / tmpx[irow];
×
488
      err += res * res;
×
489
    }
490

491
    // Check convergence
492
    err = std::sqrt(err / cmfd::dim);
×
493
    if (err < tol)
×
494
      return igs;
×
495

496
    // Calculate new overrelaxation parameter
497
    w = 1.0 / (1.0 - 0.25 * cmfd::spectral * w);
×
498
  }
499

500
  // Throw error, as max iterations met
501
  fatal_error("Maximum Gauss-Seidel iterations encountered.");
×
502

503
  // Return -1 by default, although error thrown before reaching this point
504
  return -1;
505
}
506

507
//==============================================================================
508
// OPENMC_INITIALIZE_LINSOLVER sets the fixed variables that are used for the
509
// linear solver
510
//==============================================================================
511

512
extern "C" void openmc_initialize_linsolver(const int* indptr, int len_indptr,
×
513
  const int* indices, int n_elements, int dim, double spectral, const int* map,
514
  bool use_all_threads)
515
{
516
  // Store elements of indptr
517
  for (int i = 0; i < len_indptr; i++)
×
518
    cmfd::indptr.push_back(indptr[i]);
×
519

520
  // Store elements of indices
521
  for (int i = 0; i < n_elements; i++)
×
522
    cmfd::indices.push_back(indices[i]);
×
523

524
  // Set dimenion of CMFD problem and specral radius
525
  cmfd::dim = dim;
×
526
  cmfd::spectral = spectral;
×
527

528
  // Set indexmap if 1 or 2 group problem
529
  if (cmfd::ng == 1 || cmfd::ng == 2) {
×
530
    // Resize indexmap and set its elements
531
    cmfd::indexmap.resize({static_cast<size_t>(dim), 3});
×
532
    set_indexmap(map);
×
533
  }
534

535
  // Use all threads allocated to OpenMC simulation to run CMFD solver
536
  cmfd::use_all_threads = use_all_threads;
×
537
}
538

539
//==============================================================================
540
// OPENMC_RUN_LINSOLVER runs a Gauss Seidel linear solver to solve CMFD matrix
541
// equations
542
//==============================================================================
543

544
extern "C" int openmc_run_linsolver(
×
545
  const double* A_data, const double* b, double* x, double tol)
546
{
547
  switch (cmfd::ng) {
×
548
  case 1:
×
549
    return cmfd_linsolver_1g(A_data, b, x, tol);
×
550
  case 2:
×
551
    return cmfd_linsolver_2g(A_data, b, x, tol);
×
552
  default:
×
553
    return cmfd_linsolver_ng(A_data, b, x, tol);
×
554
  }
555
}
556

557
void free_memory_cmfd()
4,469✔
558
{
559
  // Clear vectors
560
  cmfd::indptr.clear();
4,469✔
561
  cmfd::indices.clear();
4,469✔
562
  cmfd::egrid.clear();
4,469✔
563

564
  // Resize xtensors to be empty
565
  cmfd::indexmap.resize({0});
4,469✔
566

567
  // Set pointers to null
568
  cmfd::mesh = nullptr;
4,469✔
569
}
4,469✔
570

571
} // namespace openmc
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc