Flow123d  jenkins-Flow123d-linux-release-multijob-282
mass_balance.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2007 Technical University of Liberec. All rights reserved.
4  *
5  * Please make a following refer to Flow123d on your project site if you use the program for any purpose,
6  * especially for academic research:
7  * Flow123d, Research Centre: Advanced Remedial Technologies, Technical University of Liberec, Czech Republic
8  *
9  * This program is free software; you can redistribute it and/or modify it under the terms
10  * of the GNU General Public License version 3 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  *
17  * You should have received a copy of the GNU General Public License along with this program; if not,
18  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
19  *
20  *
21  * $Id$
22  * $Revision$
23  * $LastChangedBy$
24  * $LastChangedDate$
25  *
26  * @file
27  * @ingroup transport
28  * @brief Mass balance
29  *
30  *
31  */
32 
33 #include <iostream>
34 #include <iomanip>
35 
36 #include "system/system.hh"
37 #include "system/sys_profiler.hh"
38 #include "system/xio.h"
39 
40 #include <petscmat.h>
41 #include "mesh/mesh.h"
42 
44 #include "fields/unit_si.hh"
45 
46 using namespace Input::Type;
47 
49  = Selection("Balance_output_format", "Format of output file for balance.")
50  .add_value(Balance::legacy, "legacy", "Legacy format used by previous program versions.")
51  .add_value(Balance::txt, "txt", "Excel format with tab delimiter.")
52  .add_value(Balance::gnuplot, "gnuplot", "Format compatible with GnuPlot datafile with fixed column width.");
53 
55  = Record("Balance", "Balance of a conservative quantity, boundary fluxes and sources.")
56  .declare_key("balance_on", Bool(), Default("true"), "Balance is computed if the value is true.")
57  .declare_key("format", Balance::format_selection_input_type, Default("txt"), "Format of output file.")
58  .declare_key("cumulative", Bool(), Default("false"), "Compute cumulative balance over time. "
59  "If true, then balance is calculated at each computational time step, which can slow down the program.")
60  .declare_key("file", FileName::output(), Default::read_time("FileName balance.*"), "File name for output of balance.")
61  .allow_auto_conversion("balance_on");
62 
63 
64 
65 
66 
67 Balance::Balance(const std::string &file_prefix,
68  const Mesh *mesh,
69  const Distribution *el_ds,
70  const int *el_4_loc,
71  const Input::Record &in_rec)
72  : regions_(mesh->region_db()),
73  initial_time_(),
74  last_time_(),
75  initial_(true),
76  allocation_done_(false),
77  output_line_counter_(0)
78 {
79  MPI_Comm_rank(PETSC_COMM_WORLD, &rank_);
80 
81  cumulative_ = in_rec.val<bool>("cumulative");
82  output_format_ = in_rec.val<OutputFormat>("format");
83 
84  if (rank_ == 0) {
85  // set default value by output_format_
86  std::string default_file_name;
87  switch (output_format_)
88  {
89  case txt:
90  default_file_name = file_prefix + "_balance.txt";
91  break;
92  case gnuplot:
93  default_file_name = file_prefix + "_balance.dat";
94  break;
95  case legacy:
96  default_file_name = file_prefix + "_balance.txt";
97  break;
98  }
99 
100  output_.open(string(in_rec.val<FilePath>("file", FilePath(default_file_name, FilePath::output_file))).c_str());
101  }
102 
103  // construct vector of regions of boundary edges
104  for (unsigned int loc_el = 0; loc_el < el_ds->lsize(); loc_el++)
105  {
106  Element *elm = mesh->element(el_4_loc[loc_el]);
107  if (elm->boundary_idx_ != nullptr)
108  {
109  FOR_ELEMENT_SIDES(elm,si)
110  {
111  Boundary *b = elm->side(si)->cond();
112  if (b != nullptr)
113  be_regions_.push_back(b->region().boundary_idx());
114  }
115  }
116  }
117 }
118 
119 
121 {
122  if (rank_ == 0) output_.close();
123  for (unsigned int c=0; c<quantities_.size(); ++c)
124  {
125  MatDestroy(&(region_mass_matrix_[c]));
126  MatDestroy(&(be_flux_matrix_[c]));
127  MatDestroy(&(region_source_matrix_[c]));
128  MatDestroy(&(region_source_rhs_[c]));
129  VecDestroy(&(be_flux_vec_[c]));
130  VecDestroy(&(region_source_vec_[c]));
131  }
132  delete[] region_mass_matrix_;
133  delete[] be_flux_matrix_;
134  delete[] be_flux_vec_;
135  delete[] region_source_matrix_;
136  delete[] region_source_rhs_;
137  delete[] region_source_vec_;
138 
139  MatDestroy(&region_be_matrix_);
140  VecDestroy(&ones_);
141  VecDestroy(&ones_be_);
142 }
143 
144 
145 unsigned int Balance::add_quantity(const string &name)
146 {
147  ASSERT(!allocation_done_, "Attempt to add quantity after allocation.");
148 
149  Quantity q(quantities_.size(), name);
150  quantities_.push_back(q);
151 
152  return q.index_;
153 }
154 
155 
157 {
158  ASSERT(!allocation_done_, "Attempt to add quantity after allocation.");
159 
160  vector<unsigned int> indices;
161 
162  for (auto name : names)
163  indices.push_back(add_quantity(name));
164 
165  return indices;
166 }
167 
168 
169 void Balance::allocate(unsigned int n_loc_dofs,
170  unsigned int max_dofs_per_boundary)
171 {
172  ASSERT(!allocation_done_, "Attempt to allocate Balance object multiple times.");
173  // Max. number of regions to which a single dof can contribute.
174  // TODO: estimate or compute this number directly (from mesh or dof handler).
175  const int n_bulk_regs_per_dof = min(10, (int)regions_.bulk_size());
176  const unsigned int n_quant = quantities_.size();
177  const unsigned int n_bdr_reg = regions_.boundary_size();
178  const unsigned int n_blk_reg = regions_.bulk_size();
179 
180 
181  fluxes_ .resize(n_quant, vector<double>(n_bdr_reg, 0));
182  fluxes_in_ .resize(n_quant, vector<double>(n_bdr_reg, 0));
183  fluxes_out_.resize(n_quant, vector<double>(n_bdr_reg, 0));
184 
185  masses_ .resize(n_quant, vector<double>(n_blk_reg, 0));
186  sources_ .resize(n_quant, vector<double>(n_blk_reg, 0));
187  sources_in_ .resize(n_quant, vector<double>(n_blk_reg, 0));
188  sources_out_.resize(n_quant, vector<double>(n_blk_reg, 0));
189 
190  sum_fluxes_ .resize(n_quant, 0);
191  sum_fluxes_in_ .resize(n_quant, 0);
192  sum_fluxes_out_ .resize(n_quant, 0);
193  sum_masses_ .resize(n_quant, 0);
194  sum_sources_ .resize(n_quant, 0);
195  sum_sources_in_ .resize(n_quant, 0);
196  sum_sources_out_.resize(n_quant, 0);
197 
198  if (cumulative_)
199  {
200  initial_mass_ .resize(n_quant, 0);
201  integrated_fluxes_ .resize(n_quant, 0);
202  integrated_sources_.resize(n_quant, 0);
203  increment_sources_.resize(n_quant, 0);
204  increment_fluxes_.resize(n_quant, 0);
205  }
206 
207 
208 
209  region_mass_matrix_ = new Mat[n_quant];
210  be_flux_matrix_ = new Mat[n_quant];
211  region_source_matrix_ = new Mat[n_quant];
212  region_source_rhs_ = new Mat[n_quant];
213  be_flux_vec_ = new Vec[n_quant];
214  region_source_vec_ = new Vec[n_quant];
215 
216  for (unsigned int c=0; c<n_quant; ++c)
217  {
218  MatCreateAIJ(PETSC_COMM_WORLD,
219  n_loc_dofs,
220  (rank_==0)?regions_.bulk_size():0,
221  PETSC_DECIDE,
222  PETSC_DECIDE,
223  (rank_==0)?n_bulk_regs_per_dof:0,
224  0,
225  (rank_==0)?0:n_bulk_regs_per_dof,
226  0,
227  &(region_mass_matrix_[c]));
228 
229  MatCreateAIJ(PETSC_COMM_WORLD,
230  be_regions_.size(),
231  n_loc_dofs,
232  PETSC_DECIDE,
233  PETSC_DECIDE,
234  max_dofs_per_boundary,
235  0,
236  0,
237  0,
238  &(be_flux_matrix_[c]));
239 
240  MatCreateAIJ(PETSC_COMM_WORLD,
241  n_loc_dofs,
242  (rank_==0)?regions_.bulk_size():0,
243  PETSC_DECIDE,
244  PETSC_DECIDE,
245  (rank_==0)?n_bulk_regs_per_dof:0,
246  0,
247  (rank_==0)?0:n_bulk_regs_per_dof,
248  0,
249  &(region_source_matrix_[c]));
250 
251  MatCreateAIJ(PETSC_COMM_WORLD,
252  n_loc_dofs,
253  (rank_==0)?regions_.bulk_size():0,
254  PETSC_DECIDE,
255  PETSC_DECIDE,
256  (rank_==0)?n_bulk_regs_per_dof:0,
257  0,
258  (rank_==0)?0:n_bulk_regs_per_dof,
259  0,
260  &(region_source_rhs_[c]));
261 
262  VecCreateMPI(PETSC_COMM_WORLD,
263  be_regions_.size(),
264  PETSC_DECIDE,
265  &(be_flux_vec_[c]));
266 
267  VecCreateMPI(PETSC_COMM_WORLD,
268  (rank_==0)?regions_.bulk_size():0,
269  PETSC_DECIDE,
270  &(region_source_vec_[c]));
271  }
272 
273  MatCreateAIJ(PETSC_COMM_WORLD,
274  be_regions_.size(),
275  (rank_==0)?regions_.boundary_size():0,
276  PETSC_DECIDE,
277  PETSC_DECIDE,
278  (rank_==0)?1:0,
279  0,
280  (rank_==0)?0:1,
281  0,
283  );
284  VecGetOwnershipRange(be_flux_vec_[0], &be_offset_, NULL);
285  for (unsigned int loc_el=0; loc_el<be_regions_.size(); ++loc_el)
286  {
287  MatSetValue(region_be_matrix_,
288  be_offset_+loc_el,
289  be_regions_[loc_el],
290  1,
291  INSERT_VALUES);
292  }
293  MatAssemblyBegin(region_be_matrix_, MAT_FINAL_ASSEMBLY);
294  MatAssemblyEnd(region_be_matrix_, MAT_FINAL_ASSEMBLY);
295 
296  double *ones_array;
297  VecCreateMPI(PETSC_COMM_WORLD,
298  n_loc_dofs,
299  PETSC_DECIDE,
300  &ones_);
301  VecGetArray(ones_, &ones_array);
302  fill_n(ones_array, n_loc_dofs, 1);
303  VecRestoreArray(ones_, &ones_array);
304 
305  VecCreateMPI(PETSC_COMM_WORLD,
306  be_regions_.size(),
307  PETSC_DECIDE,
308  &ones_be_);
309  VecGetArray(ones_be_, &ones_array);
310  fill_n(ones_array, be_regions_.size(), 1);
311  VecRestoreArray(ones_be_, &ones_array);
312 
313  allocation_done_ = true;
314 }
315 
316 
317 void Balance::start_mass_assembly(unsigned int quantity_idx)
318 {
319  ASSERT(allocation_done_, "Balance structures are not allocated!");
320  MatZeroEntries(region_mass_matrix_[quantity_idx]);
321 }
322 
323 
324 void Balance::start_flux_assembly(unsigned int quantity_idx)
325 {
326  ASSERT(allocation_done_, "Balance structures are not allocated!");
327  MatZeroEntries(be_flux_matrix_[quantity_idx]);
328  VecZeroEntries(be_flux_vec_[quantity_idx]);
329 }
330 
331 
332 void Balance::start_source_assembly(unsigned int quantity_idx)
333 {
334  ASSERT(allocation_done_, "Balance structures are not allocated!");
335  MatZeroEntries(region_source_matrix_[quantity_idx]);
336  MatZeroEntries(region_source_rhs_[quantity_idx]);
337  VecZeroEntries(region_source_vec_[quantity_idx]);
338 }
339 
340 
341 void Balance::finish_mass_assembly(unsigned int quantity_idx)
342 {
343  ASSERT(allocation_done_, "Balance structures are not allocated!");
344  MatAssemblyBegin(region_mass_matrix_[quantity_idx], MAT_FINAL_ASSEMBLY);
345  MatAssemblyEnd(region_mass_matrix_[quantity_idx], MAT_FINAL_ASSEMBLY);
346 }
347 
348 void Balance::finish_flux_assembly(unsigned int quantity_idx)
349 {
350  ASSERT(allocation_done_, "Balance structures are not allocated!");
351  MatAssemblyBegin(be_flux_matrix_[quantity_idx], MAT_FINAL_ASSEMBLY);
352  MatAssemblyEnd(be_flux_matrix_[quantity_idx], MAT_FINAL_ASSEMBLY);
353  VecAssemblyBegin(be_flux_vec_[quantity_idx]);
354  VecAssemblyEnd(be_flux_vec_[quantity_idx]);
355 }
356 
357 void Balance::finish_source_assembly(unsigned int quantity_idx)
358 {
359  ASSERT(allocation_done_, "Balance structures are not allocated!");
360  MatAssemblyBegin(region_source_matrix_[quantity_idx], MAT_FINAL_ASSEMBLY);
361  MatAssemblyEnd(region_source_matrix_[quantity_idx], MAT_FINAL_ASSEMBLY);
362  MatAssemblyBegin(region_source_rhs_[quantity_idx], MAT_FINAL_ASSEMBLY);
363  MatAssemblyEnd(region_source_rhs_[quantity_idx], MAT_FINAL_ASSEMBLY);
364  MatMultTranspose(region_source_rhs_[quantity_idx], ones_, region_source_vec_[quantity_idx]);
365 }
366 
367 
368 
369 
370 void Balance::add_mass_matrix_values(unsigned int quantity_idx,
371  unsigned int region_idx,
372  const vector<int> &dof_indices,
373  const vector<double> &values)
374 {
375  PetscInt reg_array[1] = { (int)region_idx };
376 
377  MatSetValues(region_mass_matrix_[quantity_idx],
378  dof_indices.size(),
379  &(dof_indices[0]),
380  1,
381  reg_array,
382  &(values[0]),
383  ADD_VALUES);
384 }
385 
386 
387 void Balance::add_flux_matrix_values(unsigned int quantity_idx,
388  unsigned int elem_idx,
389  const vector<int> &dof_indices,
390  const vector<double> &values)
391 {
392  PetscInt elem_array[1] = { int(be_offset_+elem_idx) };
393 
394  MatSetValues(be_flux_matrix_[quantity_idx],
395  1,
396  elem_array,
397  dof_indices.size(),
398  &(dof_indices[0]),
399  &(values[0]),
400  ADD_VALUES);
401 }
402 
403 
404 void Balance::add_source_matrix_values(unsigned int quantity_idx,
405  unsigned int region_idx,
406  const vector<int> &dof_indices,
407  const vector<double> &values)
408 {
409  PetscInt reg_array[1] = { (int)region_idx };
410 
411  MatSetValues(region_source_matrix_[quantity_idx],
412  dof_indices.size(),
413  &(dof_indices[0]),
414  1,
415  reg_array,
416  &(values[0]),
417  ADD_VALUES);
418 }
419 
420 
421 void Balance::add_flux_vec_value(unsigned int quantity_idx,
422  unsigned int elem_idx,
423  double value)
424 {
425  VecSetValue(be_flux_vec_[quantity_idx],
426  be_offset_+elem_idx,
427  value,
428  ADD_VALUES);
429 }
430 
431 
432 void Balance::add_source_rhs_values(unsigned int quantity_idx,
433  unsigned int region_idx,
434  const vector<int> &dof_indices,
435  const vector<double> &values)
436 {
437  PetscInt reg_array[1] = { (int)region_idx };
438 
439  MatSetValues(region_source_rhs_[quantity_idx],
440  dof_indices.size(),
441  &(dof_indices[0]),
442  1,
443  reg_array,
444  &(values[0]),
445  ADD_VALUES);
446 }
447 
448 
449 void Balance::calculate_cumulative_sources(unsigned int quantity_idx,
450  const Vec &solution,
451  double dt)
452 {
453  if (!cumulative_) return;
454 
455  ASSERT(allocation_done_, "Balance structures are not allocated!");
456 
457  Vec bulk_vec;
458 
459  VecCreateMPIWithArray(PETSC_COMM_WORLD,
460  1,
461  (rank_==0)?regions_.bulk_size():0,
462  PETSC_DECIDE,
463  &(sources_[quantity_idx][0]),
464  &bulk_vec);
465 
466  // compute sources on bulk regions: S'.u + s
467  VecZeroEntries(bulk_vec);
468  MatMultTransposeAdd(region_source_matrix_[quantity_idx], solution, region_source_vec_[quantity_idx], bulk_vec);
469 
470  double sum_sources;
471  VecSum(bulk_vec, &sum_sources);
472  VecDestroy(&bulk_vec);
473 
474  if (rank_ == 0)
475  // sum sources in one step
476  increment_sources_[quantity_idx] += sum_sources*dt;
477 }
478 
479 
480 void Balance::calculate_cumulative_fluxes(unsigned int quantity_idx,
481  const Vec &solution,
482  double dt)
483 {
484  if (!cumulative_) return;
485 
486  ASSERT(allocation_done_, "Balance structures are not allocated!");
487 
488  Vec boundary_vec;
489 
490  VecCreateMPIWithArray(PETSC_COMM_WORLD,
491  1,
492  (rank_==0)?regions_.boundary_size():0,
493  PETSC_DECIDE,
494  &(fluxes_[quantity_idx][0]),
495  &boundary_vec);
496 
497  // compute fluxes on boundary regions: R'.(F.u + f)
498  VecZeroEntries(boundary_vec);
499  Vec temp;
500  VecDuplicate(ones_be_, &temp);
501  MatMultAdd(be_flux_matrix_[quantity_idx], solution, be_flux_vec_[quantity_idx], temp);
502  MatMultTranspose(region_be_matrix_, temp, boundary_vec);
503  VecDestroy(&temp);
504 
505  double sum_fluxes;
506  VecSum(boundary_vec, &sum_fluxes);
507  VecDestroy(&boundary_vec);
508 
509  if (rank_ == 0)
510  // sum fluxes in one step
511  increment_fluxes_[quantity_idx] += sum_fluxes*dt;
512 }
513 
514 
515 void Balance::calculate_mass(unsigned int quantity_idx,
516  const Vec &solution,
517  vector<double> &output_array)
518 {
519  ASSERT(allocation_done_, "Balance structures are not allocated!");
520  Vec bulk_vec;
521 
522  VecCreateMPIWithArray(PETSC_COMM_WORLD,
523  1,
524  (rank_==0)?regions_.bulk_size():0,
525  PETSC_DECIDE,
526  &(output_array[0]),
527  &bulk_vec);
528 
529  // compute mass on regions: M'.u
530  VecZeroEntries(bulk_vec);
531  MatMultTranspose(region_mass_matrix_[quantity_idx], solution, bulk_vec);
532  VecDestroy(&bulk_vec);
533 }
534 
535 
536 void Balance::calculate_source(unsigned int quantity_idx,
537  const Vec &solution)
538 {
539  ASSERT(allocation_done_, "Balance structures are not allocated!");
540  Vec bulk_vec;
541 
542  VecCreateMPIWithArray(PETSC_COMM_WORLD,
543  1,
544  (rank_==0)?regions_.bulk_size():0,
545  PETSC_DECIDE,
546  &(sources_[quantity_idx][0]),
547  &bulk_vec);
548 
549  // compute sources on bulk regions: S'.u + s
550  VecZeroEntries(bulk_vec);
551  MatMultTransposeAdd(region_source_matrix_[quantity_idx],
552  solution,
553  region_source_vec_[quantity_idx],
554  bulk_vec);
555 
556  // compute positive/negative sources
557  int lsize;
558  Vec mat_r, rhs_r;
559  const double *sol_array, *mat_array, *rhs_array;
560  VecGetLocalSize(solution, &lsize);
561  VecDuplicate(solution, &mat_r);
562  VecDuplicate(solution, &rhs_r);
563  VecGetArrayRead(solution, &sol_array);
564  for (unsigned int r=0; r<regions_.bulk_size(); ++r)
565  {
566  MatGetColumnVector(region_source_matrix_[quantity_idx], mat_r, r);
567  MatGetColumnVector(region_source_rhs_[quantity_idx], rhs_r, r);
568 
569  VecGetArrayRead(mat_r, &mat_array);
570  VecGetArrayRead(rhs_r, &rhs_array);
571 
572  sources_in_[quantity_idx][r] = 0;
573  sources_out_[quantity_idx][r] = 0;
574  for (int i=0; i<lsize; ++i)
575  {
576  double f = mat_array[i]*sol_array[i] + rhs_array[i];
577  if (f > 0) sources_in_[quantity_idx][r] += f;
578  else sources_out_[quantity_idx][r] += f;
579  }
580 
581  VecRestoreArrayRead(mat_r, &mat_array);
582  VecRestoreArrayRead(rhs_r, &rhs_array);
583  }
584  VecRestoreArrayRead(solution, &sol_array);
585  VecDestroy(&rhs_r);
586  VecDestroy(&mat_r);
587  VecDestroy(&bulk_vec);
588 }
589 
590 
591 void Balance::calculate_flux(unsigned int quantity_idx,
592  const Vec &solution)
593 {
594  ASSERT(allocation_done_, "Balance structures are not allocated!");
595  Vec boundary_vec;
596 
597  VecCreateMPIWithArray(PETSC_COMM_WORLD, 1, (rank_==0)?regions_.boundary_size():0, PETSC_DECIDE, &(fluxes_[quantity_idx][0]), &boundary_vec);
598 
599  // compute fluxes on boundary regions: R'.(F.u + f)
600  VecZeroEntries(boundary_vec);
601  Vec temp;
602  VecDuplicate(ones_be_, &temp);
603  MatMultAdd(be_flux_matrix_[quantity_idx], solution, be_flux_vec_[quantity_idx], temp);
604  MatMultTranspose(region_be_matrix_, temp, boundary_vec);
605 
606  // compute positive/negative fluxes
607  fluxes_in_[quantity_idx].assign(regions_.boundary_size(), 0);
608  fluxes_out_[quantity_idx].assign(regions_.boundary_size(), 0);
609  const double *flux_array;
610  int lsize;
611  VecGetArrayRead(temp, &flux_array);
612  VecGetLocalSize(temp, &lsize);
613  for (int e=0; e<lsize; ++e)
614  {
615  if (flux_array[e] > 0)
616  fluxes_out_[quantity_idx][be_regions_[e]] += flux_array[e];
617  else
618  fluxes_in_[quantity_idx][be_regions_[e]] += flux_array[e];
619  }
620  VecRestoreArrayRead(temp, &flux_array);
621  VecDestroy(&temp);
622  VecDestroy(&boundary_vec);
623 }
624 
625 void Balance::add_cumulative_source(unsigned int quantity_idx, double source)
626 {
627  if (rank_ == 0)
628  increment_sources_[quantity_idx] += source;
629 }
630 
631 
632 
633 
634 
635 void Balance::output(double time)
636 {
637  ASSERT(allocation_done_, "Balance structures are not allocated!");
638 
639  // gather results from processes and sum them up
640  const unsigned int n_quant = quantities_.size();
641  const unsigned int n_blk_reg = regions_.bulk_size();
642  const unsigned int n_bdr_reg = regions_.boundary_size();
643  const int buf_size = n_quant*2*n_blk_reg + n_quant*2*n_bdr_reg;
644  double sendbuffer[buf_size], recvbuffer[buf_size];
645  for (unsigned int qi=0; qi<n_quant; qi++)
646  {
647  for (unsigned int ri=0; ri<n_blk_reg; ri++)
648  {
649  sendbuffer[qi*2*n_blk_reg + + ri] = sources_in_[qi][ri];
650  sendbuffer[qi*2*n_blk_reg + n_blk_reg + ri] = sources_out_[qi][ri];
651  }
652  for (unsigned int ri=0; ri<n_bdr_reg; ri++)
653  {
654  sendbuffer[n_quant*2*n_blk_reg + qi*2*n_bdr_reg + + ri] = fluxes_in_[qi][ri];
655  sendbuffer[n_quant*2*n_blk_reg + qi*2*n_bdr_reg + n_bdr_reg + ri] = fluxes_out_[qi][ri];
656  }
657  }
658  MPI_Reduce(&sendbuffer,recvbuffer,buf_size,MPI_DOUBLE,MPI_SUM,0,PETSC_COMM_WORLD);
659  // for other than 0th process update last_time and finish,
660  // on process #0 sum balances over all regions and calculate
661  // cumulative balance over time.
662  if (rank_ == 0)
663  {
664  // update balance vectors
665  for (unsigned int qi=0; qi<n_quant; qi++)
666  {
667  for (unsigned int ri=0; ri<n_blk_reg; ri++)
668  {
669  sources_in_[qi][ri] = recvbuffer[qi*2*n_blk_reg + + ri];
670  sources_out_[qi][ri] = recvbuffer[qi*2*n_blk_reg + n_blk_reg + ri];
671  }
672  for (unsigned int ri=0; ri<n_bdr_reg; ri++)
673  {
674  fluxes_in_[qi][ri] = recvbuffer[n_quant*2*n_blk_reg + qi*2*n_bdr_reg + + ri];
675  fluxes_out_[qi][ri] = recvbuffer[n_quant*2*n_blk_reg + qi*2*n_bdr_reg + n_bdr_reg + ri];
676  }
677  }
678  }
679 
680 
681  if (rank_ == 0)
682  {
683  sum_fluxes_.assign(n_quant, 0);
684  sum_fluxes_in_.assign(n_quant, 0);
685  sum_fluxes_out_.assign(n_quant, 0);
686  sum_masses_.assign(n_quant, 0);
687  sum_sources_.assign(n_quant, 0);
688  sum_sources_in_.assign(n_quant, 0);
689  sum_sources_out_.assign(n_quant, 0);
690 
691  // sum all boundary fluxes
692  const RegionSet & b_set = regions_.get_region_set("BOUNDARY");
693  for( RegionSet::const_iterator reg = b_set.begin(); reg != b_set.end(); ++reg)
694  {
695  for (unsigned int qi=0; qi<n_quant; qi++)
696  {
697  sum_fluxes_[qi] += fluxes_ [qi][reg->boundary_idx()];
698  sum_fluxes_in_[qi] += fluxes_in_ [qi][reg->boundary_idx()];
699  sum_fluxes_out_[qi] += fluxes_out_[qi][reg->boundary_idx()];
700  }
701  }
702 
703  // sum all volume sources
704  const RegionSet & bulk_set = regions_.get_region_set("BULK");
705  for( RegionSet::const_iterator reg = bulk_set.begin(); reg != bulk_set.end(); ++reg)
706  {
707  for (unsigned int qi=0; qi<n_quant; qi++)
708  {
709  sum_masses_[qi] += masses_[qi][reg->bulk_idx()];
710  sum_sources_[qi] += sources_[qi][reg->bulk_idx()];
711  sum_sources_in_[qi] += sources_in_[qi][reg->bulk_idx()];
712  sum_sources_out_[qi] += sources_out_[qi][reg->bulk_idx()];
713  }
714  }
715 
716  // cumulative balance over time
717  if (cumulative_)
718  {
719  // save initial time and mass
720  if (initial_)
721  {
722  initial_time_ = time;
724  for (unsigned int qi=0; qi<n_quant; qi++)
725  initial_mass_[qi] = sum_masses_[qi];
726  initial_ = false;
727  }
728 
729  for (unsigned int qi=0; qi<n_quant; qi++)
730  {
733  }
734  }
735  }
736 
737  last_time_ = time;
738 
739 
740  // perform actual output
741  switch (output_format_)
742  {
743  case txt:
744  output_csv(time, '\t', "");
745  break;
746  case gnuplot:
747  output_csv(time, ' ', "#", 30);
748  break;
749  case legacy:
750  output_legacy(time);
751  break;
752  }
753 
754  if (rank_ == 0)
755  {
756  sum_fluxes_.assign(n_quant, 0);
757  sum_sources_.assign(n_quant, 0);
758  increment_fluxes_.assign(n_quant, 0);
759  increment_sources_.assign(n_quant, 0);
760  }
761 }
762 
763 
764 void Balance::output_legacy(double time)
765 {
766  // write output only on process #0
767  if (rank_ != 0) return;
768 
769  const unsigned int n_quant = quantities_.size();
770 
771  // print the head of mass balance file
772  unsigned int c = 6; //column number without label
773  unsigned int w = 14; //column width
774  unsigned int wl = 2*(w-5)+7; //label column width
775  string bc_head_format = "# %-*s%-*s%-*s%-*s%-*s%-*s\n",
776  bc_format = "%*s%-*d%-*s%-*s%-*g%-*g%-*g\n",
777  bc_total_format = "# %-*s%-*s%-*g%-*g%-*g\n";
778 
779  output_ << "# " << setw((w*c+wl-14)/2) << setfill('-') << "--"
780  << " MASS BALANCE "
781  << setw((w*c+wl-14)/2) << setfill('-') << "" << endl
782  << "# Time: " << time << "\n\n\n";
783 
784  // header for table of boundary fluxes
785  output_ << "# Mass flux through boundary [M/T]:\n# "
786  << setiosflags(ios::left) << setfill(' ')
787  << setw(w) << "[boundary_id]"
788  << setw(wl) << "[label]"
789  << setw(w) << "[substance]"
790  << setw(w) << "[total flux]"
791  << setw(w) << "[outward flux]"
792  << setw(w) << "[inward flux]"
793  << endl;
794 
795  // draw long line
796  output_ << "# " << setw(w*c+wl) << setfill('-') << "" << setfill(' ') << endl;
797 
798  // print mass fluxes over boundaries
799  const RegionSet & b_set = regions_.get_region_set("BOUNDARY");
800  for( RegionSet::const_iterator reg = b_set.begin(); reg != b_set.end(); ++reg) {
801  for (unsigned int qi=0; qi<n_quant; qi++) {
802  output_ << setw(2) << ""
803  << setw(w) << (int)reg->id()
804  << setw(wl) << reg->label().c_str()
805  << setw(w) << quantities_[qi].name_.c_str()
806  << setw(w) << fluxes_[qi][reg->boundary_idx()]
807  << setw(w) << fluxes_out_[qi][reg->boundary_idx()]
808  << setw(w) << fluxes_in_[qi][reg->boundary_idx()]
809  << endl;
810  }
811  }
812 
813  // draw long line
814  output_ << "# " << setw(w*c+wl) << setfill('-') << "" << setfill(' ') << endl;
815 
816  // total boundary balance
817  for (unsigned int qi=0; qi<n_quant; qi++)
818  output_ << "# " << setiosflags(ios::left)
819  << setw(w+wl) << "Total mass flux of substance [M/T]"
820  << setw(w) << quantities_[qi].name_.c_str()
821  << setw(w) << sum_fluxes_[qi]
822  << setw(w) << sum_fluxes_out_[qi]
823  << setw(w) << sum_fluxes_in_[qi]
824  << endl;
825  output_ << "\n\n";
826 
827 
828  // header for table of volume sources and masses
829  string src_head_format = "# %-*s%-*s%-*s%-*s%-*s\n",
830  src_format = "%*s%-*d%-*s%-*s%-*g%-*g\n",
831  src_total_format = "# %-*s%-*s%-*g%-*g\n";
832  output_ << "# Mass [M] and sources [M/T] on regions:\n"
833  << "# " << setiosflags(ios::left)
834  << setw(w) << "[region_id]"
835  << setw(wl) << "[label]"
836  << setw(w) << "[substance]"
837  << setw(w) << "[total_mass]"
838  << setw(w) << "[total_source]"
839  << endl;
840 
841  // draw long line
842  output_ << "# " << setw(w*c+wl) << setfill('-') << "" << setfill(' ') << endl;
843 
844  // print balance of volume sources and masses
845  const RegionSet & bulk_set = regions_.get_region_set("BULK");
846  for( RegionSet::const_iterator reg = bulk_set.begin(); reg != bulk_set.end(); ++reg)
847  {
848  for (unsigned int qi=0; qi<n_quant; qi++)
849  {
850  output_ << setw(2) << ""
851  << setw(w) << (int)reg->id()
852  << setw(wl) << reg->label().c_str()
853  << setw(w) << quantities_[qi].name_.c_str()
854  << setw(w) << masses_[qi][reg->bulk_idx()]
855  << setw(w) << sources_[qi][reg->bulk_idx()]
856  << endl;
857  }
858  }
859 
860  // draw long line
861  output_ << "# " << setw(w*c+wl) << setfill('-') << "" << setfill(' ') << endl;
862 
863  // total sources balance
864  for (unsigned int qi=0; qi<n_quant; qi++)
865  output_ << "# " << setiosflags(ios::left) << setw(w+wl) << "Total mass [M] and sources [M/T]"
866  << setw(w) << quantities_[qi].name_.c_str()
867  << setw(w) << sum_masses_[qi]
868  << setw(w) << sum_sources_[qi]
869  << endl;
870 
871  if (cumulative_)
872  {
873  // Print cumulative sources
874  output_ << "\n\n# Cumulative mass balance on time interval ["
875  << setiosflags(ios::left) << initial_time_ << ","
876  << setiosflags(ios::left) << time << "]\n"
877  << "# Initial mass [M] + sources integrated over time [M] - flux integrated over time [M] = current mass [M]\n"
878  << "# " << setiosflags(ios::left)
879  << setw(w) << "[substance]"
880  << setw(w) << "[A=init. mass]"
881  << setw(w) << "[B=source]"
882  << setw(w) << "[C=flux]"
883  << setw(w) << "[A+B-C]"
884  << setw(w) << "[D=curr. mass]"
885  << setw(w) << "[A+B-C-D=err.]"
886  << setw(w) << "[rel. error]"
887  << endl;
888 
889  for (unsigned int qi=0; qi<n_quant; qi++)
890  {
891  double denominator = max(fabs(initial_mass_[qi]+integrated_sources_[qi]-integrated_fluxes_[qi]),fabs(sum_masses_[qi]));
892  output_ << " " << setiosflags(ios::left)
893  << setw(w) << quantities_[qi].name_.c_str()
894  << setw(w) << initial_mass_[qi]
895  << setw(w) << integrated_sources_[qi]
896  << setw(w) << integrated_fluxes_[qi]
897  << setw(w) << initial_mass_[qi]+integrated_sources_[qi]-integrated_fluxes_[qi]
898  << setw(w) << sum_masses_[qi]
900  << setw(w) << fabs(initial_mass_[qi]+integrated_sources_[qi]-integrated_fluxes_[qi]-sum_masses_[qi])/(denominator==0?1:denominator)
901  << endl;
902  }
903  }
904 
905  output_ << endl << endl;
906 }
907 
908 
909 std::string Balance::csv_zero_vals(unsigned int cnt, char delimiter)
910 {
911  std::stringstream ss;
912  for (unsigned int i=0; i<cnt; i++) ss << format_csv_val(0, delimiter);
913  return ss.str();
914 }
915 
916 
917 void Balance::output_csv(double time, char delimiter, const std::string& comment_string, unsigned int repeat)
918 {
919  // write output only on process #0
920  if (rank_ != 0) return;
921 
922  const unsigned int n_quant = quantities_.size();
923 
924  // print data header only on first line
925  if (repeat==0 && output_line_counter_==0) format_csv_output_header(delimiter, comment_string);
926 
927  // print sources and masses over bulk regions
928  const RegionSet & bulk_set = regions_.get_region_set("BULK");
929  for( RegionSet::const_iterator reg = bulk_set.begin(); reg != bulk_set.end(); ++reg)
930  {
931  for (unsigned int qi=0; qi<n_quant; qi++)
932  {
933  // print data header (repeat header after every "repeat" lines)
934  if (repeat && (output_line_counter_%repeat == 0)) format_csv_output_header(delimiter, comment_string);
935 
936  output_ << format_csv_val(time, delimiter, true)
937  << format_csv_val(reg->label(), delimiter)
938  << format_csv_val(quantities_[qi].name_, delimiter)
939  << csv_zero_vals(3, delimiter)
940  << format_csv_val(masses_[qi][reg->bulk_idx()], delimiter)
941  << format_csv_val(sources_[qi][reg->bulk_idx()], delimiter)
942  << format_csv_val(sources_in_[qi][reg->bulk_idx()], delimiter)
943  << format_csv_val(sources_out_[qi][reg->bulk_idx()], delimiter)
944  << csv_zero_vals(5, delimiter) << endl;
946  }
947  }
948 
949  // print mass fluxes over boundaries
950  const RegionSet & b_set = regions_.get_region_set("BOUNDARY");
951  for( RegionSet::const_iterator reg = b_set.begin(); reg != b_set.end(); ++reg)
952  {
953  for (unsigned int qi=0; qi<n_quant; qi++) {
954  // print data header (repeat header after every "repeat" lines)
955  if (repeat && (output_line_counter_%repeat == 0)) format_csv_output_header(delimiter, comment_string);
956 
957  output_ << format_csv_val(time, delimiter, true)
958  << format_csv_val(reg->label(), delimiter)
959  << format_csv_val(quantities_[qi].name_, delimiter)
960  << format_csv_val(fluxes_[qi][reg->boundary_idx()], delimiter)
961  << format_csv_val(fluxes_in_[qi][reg->boundary_idx()], delimiter)
962  << format_csv_val(fluxes_out_[qi][reg->boundary_idx()], delimiter)
963  << csv_zero_vals(9, delimiter) << endl;
965  }
966  }
967 
968  if (cumulative_)
969  {
970  for (unsigned int qi=0; qi<n_quant; qi++)
971  {
972  // print data header (repeat header after every "repeat" lines)
973  if (repeat && (output_line_counter_%repeat == 0)) format_csv_output_header(delimiter, comment_string);
974 
975  double error = sum_masses_[qi] - (initial_mass_[qi] + integrated_sources_[qi] - integrated_fluxes_[qi]);
976  output_ << format_csv_val(time, delimiter, true)
977  << format_csv_val("ALL", delimiter)
978  << format_csv_val(quantities_[qi].name_, delimiter)
979  << format_csv_val(sum_fluxes_[qi], delimiter)
980  << format_csv_val(sum_fluxes_in_[qi], delimiter)
981  << format_csv_val(sum_fluxes_out_[qi], delimiter)
982  << format_csv_val(sum_masses_[qi], delimiter)
983  << format_csv_val(sum_sources_[qi], delimiter)
984  << format_csv_val(sum_sources_in_[qi], delimiter)
985  << format_csv_val(sum_sources_out_[qi], delimiter)
986  << format_csv_val(increment_fluxes_[qi], delimiter)
987  << format_csv_val(increment_sources_[qi], delimiter)
988  << format_csv_val(integrated_fluxes_[qi], delimiter)
989  << format_csv_val(integrated_sources_[qi], delimiter)
990  << format_csv_val(error, delimiter) << endl;
992  }
993  }
994 
995 }
996 
997 
998 void Balance::format_csv_output_header(char delimiter, const std::string& comment_string)
999 {
1000  std::stringstream ss;
1001  if (delimiter == ' ') {
1002  ss << setw(output_column_width-comment_string.size()) << "\"time\"";
1003  } else {
1004  ss << "\"time\"";
1005  }
1006 
1007  output_ << comment_string << ss.str()
1008  << format_csv_val("region", delimiter)
1009  << format_csv_val("quantity [" + units_.format_text() + "]", delimiter)
1010  << format_csv_val("flux", delimiter)
1011  << format_csv_val("flux_in", delimiter)
1012  << format_csv_val("flux_out", delimiter)
1013  << format_csv_val("mass", delimiter)
1014  << format_csv_val("source", delimiter)
1015  << format_csv_val("source_in", delimiter)
1016  << format_csv_val("source_out", delimiter)
1017  << format_csv_val("flux_increment", delimiter)
1018  << format_csv_val("source_increment", delimiter)
1019  << format_csv_val("flux_cumulative", delimiter)
1020  << format_csv_val("source_cumulative", delimiter)
1021  << format_csv_val("error", delimiter)
1022  << endl;
1023 }
1024 
1025 std::string Balance::format_csv_val(std::string val, char delimiter, bool initial)
1026 {
1027  std::stringstream ss;
1028  std::replace( val.begin(), val.end(), '\"', '\'');
1029 
1030  if (!initial) ss << delimiter;
1031  if (delimiter == ' ') {
1032  std::stringstream sval;
1033  sval << "\"" << val << "\"";
1034  ss << " " << setw(output_column_width-1) << sval.str();
1035  } else {
1036  ss << "\"" << val << "\"";
1037  }
1038 
1039  return ss.str();
1040 }
1041 
1042 std::string Balance::format_csv_val(double val, char delimiter, bool initial)
1043 {
1044  std::stringstream ss;
1045 
1046  if (!initial) ss << delimiter;
1047  if (delimiter == ' ') {
1048  ss << " " << setw(output_column_width-1) << val;
1049  } else {
1050  ss << val;
1051  }
1052  return ss.str();
1053 }
1054 
1055 
1056 
1057 
1058 
UnitSI units_
Units of conserved quantities.
void calculate_cumulative_sources(unsigned int quantity_idx, const Vec &solution, double dt)
std::vector< double > integrated_sources_
unsigned int add_quantity(const string &name)
std::vector< double > sum_fluxes_out_
void calculate_mass(unsigned int quantity_idx, const Vec &solution, vector< double > &output_array)
void allocate(unsigned int n_loc_dofs, unsigned int max_dofs_per_boundary)
unsigned int * boundary_idx_
Definition: elements.h:94
double initial_time_
initial time
std::string format_text() const
Definition: unit_si.cc:112
void calculate_cumulative_fluxes(unsigned int quantity_idx, const Vec &solution, double dt)
void finish_source_assembly(unsigned int quantity_idx)
This method must be called after assembling the matrix and vectors for computing source.
unsigned int bulk_size() const
Definition: region.cc:252
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:39
Class for declaration of the input of type Bool.
Definition: type_base.hh:332
bool initial_
true before calculating the mass at initial time, otherwise false
Boundary * cond() const
Definition: side_impl.hh:59
void add_flux_vec_value(unsigned int quantity_idx, unsigned int elem_idx, double value)
std::vector< std::vector< double > > sources_in_
static Input::Type::Record input_type
Main balance input record type.
void add_cumulative_source(unsigned int quantity_idx, double source)
std::vector< std::vector< double > > fluxes_in_
std::vector< double > sum_fluxes_in_
std::vector< std::vector< double > > fluxes_out_
Balance(const std::string &file_prefix, const Mesh *mesh, const Distribution *el_ds, const int *el_4_loc, const Input::Record &in_rec)
Definition: mass_balance.cc:67
???
Vec ones_
auxiliary vectors for summation of matrix columns
#define MPI_SUM
Definition: mpi.h:196
Mat * region_source_rhs_
Matrices for calculation of signed source (n_dofs x n_bulk_regions).
RegionSet get_region_set(const string &set_name) const
Definition: region.cc:361
Definition: mesh.h:109
void add_mass_matrix_values(unsigned int quantity_idx, unsigned int region_idx, const std::vector< int > &dof_indices, const std::vector< double > &values)
ofstream output_
Handle for file for output of balance and total fluxes over individual regions and region sets...
Vec ones_be_
#define FOR_ELEMENT_SIDES(i, j)
Definition: elements.h:151
void output_legacy(double time)
Perform output in old format (for compatibility)
Mat region_be_matrix_
Region region()
Definition: boundaries.h:74
bool allocation_done_
true before allocating necessary internal structures (Petsc matrices etc.)
I/O functions with filename storing, able to track current line in opened file. All standard stdio fu...
#define MPI_Reduce(sendbuf, recvbuf, count, datatype, op, root, comm)
Definition: mpi.h:608
const RegionDB & regions_
Database of bulk and boundary regions.
std::vector< double > sum_fluxes_
std::vector< double > increment_sources_
Vec * be_flux_vec_
Vectors for calculation of flux (n_boundary_edges).
std::vector< double > integrated_fluxes_
unsigned int boundary_idx() const
Returns index of the region in the boundary set.
Definition: region.hh:75
std::vector< Quantity > quantities_
Names of conserved quantities.
std::string csv_zero_vals(unsigned int cnt, char delimiter)
Return part of output represented by zero values. Count of zero values is given by cnt parameter...
std::vector< std::vector< double > > sources_out_
Mat * be_flux_matrix_
Matrices for calculation of flux (n_boundary_edges x n_dofs).
double last_time_
time of last calculated balance
void add_source_rhs_values(unsigned int quantity_idx, unsigned int region_idx, const std::vector< int > &dof_values, const std::vector< double > &values)
std::vector< std::vector< double > > fluxes_
Record & allow_auto_conversion(const string &from_key)
Definition: type_record.cc:111
#define ASSERT(...)
Definition: global_defs.h:121
void calculate_source(unsigned int quantity_idx, const Vec &solution)
void add_flux_matrix_values(unsigned int quantity_idx, unsigned int elem_idx, const std::vector< int > &dof_indices, const std::vector< double > &values)
void finish_mass_assembly(unsigned int quantity_idx)
This method must be called after assembling the matrix for computing mass.
void calculate_flux(unsigned int quantity_idx, const Vec &solution)
void format_csv_output_header(char delimiter, const std::string &comment_string)
Print output header.
int rank_
MPI rank.
Selection & add_value(const int value, const std::string &key, const std::string &description="")
Accessor to the data with type Type::Record.
Definition: accessors.hh:327
const Ret val(const string &key) const
Mat * region_mass_matrix_
Matrices for calculation of mass (n_dofs x n_bulk_regions).
std::vector< std::vector< double > > sources_
std::vector< double > sum_sources_out_
bool cumulative_
if true then cumulative balance is computed
Mat * region_source_matrix_
Matrices for calculation of source (n_dofs x n_bulk_regions).
std::vector< double > increment_fluxes_
SideIter side(const unsigned int loc_index)
void output_csv(double time, char delimiter, const std::string &comment_string, unsigned int repeat=0)
Perform output in csv format.
#define MPI_DOUBLE
Definition: mpi.h:156
#define MPI_Comm_rank
Definition: mpi.h:236
std::vector< unsigned int > add_quantities(const std::vector< string > &names)
Dedicated class for storing path to input and output files.
Definition: file_path.hh:32
static Default read_time(const std::string &description)
Definition: type_record.hh:77
std::vector< double > sum_sources_
void start_flux_assembly(unsigned int quantity_idx)
std::vector< double > initial_mass_
static const unsigned int output_column_width
Size of column in output (used if delimiter is space)
unsigned int boundary_size() const
Definition: region.cc:245
static FileName output()
Definition: type_base.hh:470
void finish_flux_assembly(unsigned int quantity_idx)
This method must be called after assembling the matrix and vector for computing flux.
std::vector< unsigned int > be_regions_
Number of boundary region for each local boundary edge.
OutputFormat output_format_
Format of output file.
int be_offset_
Offset for local part of vector of boundary edges.
std::vector< std::vector< double > > masses_
Vec * region_source_vec_
Vectors for calculation of source (n_bulk_regions).
void start_mass_assembly(unsigned int quantity_idx)
Record type proxy class.
Definition: type_record.hh:169
std::vector< double > sum_masses_
unsigned int output_line_counter_
hold count of line printed into output_
void output(double time)
Perform output to file for given time instant.
void add_source_matrix_values(unsigned int quantity_idx, unsigned int region_idx, const std::vector< int > &dof_indices, const std::vector< double > &values)
void start_source_assembly(unsigned int quantity_idx)
Template for classes storing finite set of named values.
static Input::Type::Selection format_selection_input_type
Input selection for file format.
std::string format_csv_val(std::string val, char delimiter, bool initial=false)
Format string value of csv output. Wrap string into quotes and if delimiter is space, align text to column.
ElementVector element
Vector of elements of the mesh.
Definition: mesh.h:205
std::vector< double > sum_sources_in_
unsigned int lsize(int proc) const
get local size
Record & declare_key(const string &key, const KeyType &type, const Default &default_value, const string &description)
Definition: type_record.cc:430