KratosMultiphysics
KRATOS Multiphysics (Kratos) is a framework for building parallel, multi-disciplinary simulation software, aiming at modularity, extensibility, and high performance. Kratos is written in C++, and counts with an extensive Python interface.
flux_corrected_transport_convection_process.h
Go to the documentation of this file.
1 // | / |
2 // ' / __| _` | __| _ \ __|
3 // . \ | ( | | ( |\__ \.
4 // _|\_\_| \__,_|\__|\___/ ____/
5 // Multi-Physics
6 //
7 // License: BSD License
8 // Kratos default license: kratos/license.txt
9 //
10 // Main authors: Ruben Zorrilla
11 //
12 
13 #pragma once
14 
15 // System includes
16 #include <string>
17 #include <numeric>
18 #include <variant>
19 #include <iostream>
20 #include <algorithm>
21 
22 // External includes
23 
24 // Project includes
26 #include "includes/define.h"
27 #include "processes/process.h"
32 
33 namespace Kratos
34 {
37 
41 
45 
49 
53 
56 template<unsigned int TDim>
58 {
59 public:
62 
63  using SizeType = std::size_t;
64 
65  using IndexType = std::size_t;
66 
67  using VariantTableauType = std::variant<ButcherTableauForwardEuler, ButcherTableauMidPointMethod, ButcherTableauRK3TVD, ButcherTableauRK4>;
68 
72 
75 
79 
81  Model& rModel,
82  Parameters ThisParameters)
83  : Process()
84  {
85  // Validate the common settings as well as the element formulation specific ones
87 
88  // Reference to the model part to which the convection is to be performed
89  mpModelPart = &(rModel.GetModelPart(ThisParameters["model_part_name"].GetString()));
90 
91  // Set the edge-based data structure pointer
92  mpEdgeDataStructure = Kratos::make_unique<EdgeBasedDataStructure<TDim>>();
93 
94  // Assign all the required member variables
95  mEchoLevel = ThisParameters["echo_level"].GetInt();
96  mTimeSchemeName = ThisParameters["time_scheme"].GetString();
97  mMaxAllowedCFL = ThisParameters["max_CFL"].GetDouble();
98  mMaxAllowedDt = ThisParameters["max_delta_time"].GetDouble();
99  mDiffusionConstant = ThisParameters["diffusion_constant"].GetDouble();
100  mpConvectedVar = &KratosComponents<Variable<double>>::Get(ThisParameters["convected_variable_name"].GetString());
101  mpConvectionVar = &KratosComponents<Variable<array_1d<double, 3>>>::Get(ThisParameters["convection_variable_name"].GetString());
102 
103  // Check provided values
104  KRATOS_ERROR_IF(mDiffusionConstant < 1.0e-12 || mDiffusionConstant > 1.0)
105  << "Provided 'diffusion_constant' " << mDiffusionConstant << " is not valid. Value must be between 0 and 1." << std::endl;
106  }
107 
110 
113 
117 
120 
124 
125  void ExecuteInitialize() override
126  {
127  // Check that there are DOFs for the convected variable
128  // These are required to check the fixity of the convected variable
129  block_for_each(mpModelPart->Nodes(), [this](auto& rNode){
130  KRATOS_ERROR_IF_NOT(rNode.HasDofFor(*mpConvectedVar))
131  << "No DOF for convected variable '" << mpConvectedVar->Name() << "' in node " << rNode.Id() << std::endl;
132  });
133 
134  // Fill the edge-based data structure
135  mpEdgeDataStructure->CalculateEdgeDataStructure(*mpModelPart);
136  KRATOS_INFO_IF("FluxCorrectedTransportConvectionProcess", mEchoLevel > 0) << "Edge-based data structure completed." << std::endl;
137 
138  // Auxiliary flag to avoid resetting the edge data structure at each Execute call
139  mPerformInitialize = false;
140 
141  // Allocate auxiliary arrays
142  // Note that we give the size such that the index matches the id of the corresponding node
143  // Also note that this implies having a "fake" 0-id entry in the first position of the arrays
144  mAuxSize = mpModelPart->NumberOfNodes() + 1;
145  mSolution.resize(mAuxSize);
146  mSolutionOld.resize(mAuxSize);
147  mLowOrderUpdate.resize(mAuxSize);
148  mHighOrderUpdate.resize(mAuxSize);
149  mConvectionValues.resize(mAuxSize);
150  }
151 
152  void Execute() override
153  {
154  KRATOS_TRY;
155 
156  // Check if the edge data structure is already set
157  if (mPerformInitialize) {
158  this->ExecuteInitialize();
159  }
160 
161  // Evaluate current time (n) and target time (n+1)
162  // Note that it is assumed that the CloneTimeStep of the model part has been already done
163  const auto& r_process_info = mpModelPart->GetProcessInfo();
164  const auto &r_prev_process_info = r_process_info.GetPreviousTimeStepInfo();
165  double prev_time = r_prev_process_info[TIME];
166  const double target_time = r_process_info[TIME];
167 
168  // Initialize data vectors
169  const SizeType n_nodes = mpModelPart->NumberOfNodes();
171  const auto it_node = mpModelPart->NodesBegin() + iNode;
172  const IndexType i_node_id = it_node->Id();
173  mSolutionOld[i_node_id] = it_node->GetSolutionStepValue(*mpConvectedVar,1); //solution at n
174  mConvectionValues[i_node_id] = it_node->GetSolutionStepValue(*mpConvectionVar,1); //convective velocity at n
175  });
176 
177  // Get maximum time increment according to global CFL
178  const double target_dt = target_time - prev_time; // Time to be covered by the substepping loop
179  const double cfl_dt = this->CalculateSubStepDeltaTime(); // Maximum time increment according to edge mesh CFL
180  const double max_dt = std::min(cfl_dt, mMaxAllowedDt); // Check if the user defined delta time is more restrictive than the CFL one
181  const SizeType n_steps = std::ceil(target_dt / max_dt); // Required number of substeps (rounded up)
182  const double dt = target_dt / n_steps; // Time increment to be used in the substep loop
183  KRATOS_INFO_IF("FluxCorrectedTransportConvectionProcess", mEchoLevel > 0) << u8"Solving FCT convection with \u0394t = " << dt << u8" (max allowed \u0394t = " << max_dt << ")" << std::endl;
184 
185  // Substepping time loop
186  for (IndexType step = 1; step <= n_steps; ++step) {
187  // Solve current substep
188  KRATOS_INFO_IF("FluxCorrectedTransportConvectionProcess", mEchoLevel > 1) << "Substep = " << step << " - Time = " << prev_time << u8" - \u0394t = " << dt << std::endl;
189  this->SolveSubStep(dt);
190 
191  // Advance in time
192  prev_time += dt;
193  }
194 
195  // Set final solution in the model part database
196  IndexPartition<IndexType>(mpModelPart->NumberOfNodes()).for_each([this](IndexType iNode){
197  auto it_node = mpModelPart->NodesBegin() + iNode;
198  if (!it_node->IsFixed(*mpConvectedVar)) {
199  it_node->FastGetSolutionStepValue(*mpConvectedVar) = mSolution[it_node->Id()];
200  }
201  });
202 
203  KRATOS_CATCH("")
204  }
205 
206  void Clear() override
207  {
208  mSolution.clear();
209  mSolutionOld.clear();
210  mLowOrderUpdate.clear();
211  mHighOrderUpdate.clear();
212  mConvectionValues.clear();
213  mpEdgeDataStructure->Clear();
214  }
215 
216  const Parameters GetDefaultParameters() const override
217  {
218  Parameters default_parameters = Parameters(R"({
219  "echo_level" : 0,
220  "model_part_name" : "",
221  "convected_variable_name" : "DISTANCE",
222  "convection_variable_name" : "VELOCITY",
223  "max_CFL" : 1.0,
224  "max_delta_time" : 1.0,
225  "diffusion_constant" : 1.0,
226  "time_scheme" : "RK3-TVD"
227  })");
228 
229  return default_parameters;
230  }
231 
235 
239 
243 
245  std::string Info() const override
246  {
247  return "FluxCorrectedTransportConvectionProcess";
248  }
249 
251  void PrintInfo(std::ostream& rOStream) const override
252  {
253  rOStream << "FluxCorrectedTransportConvectionProcess";
254  }
255 
257  void PrintData(std::ostream& rOStream) const override
258  {
259  }
260 
264 
266 private:
269 
273 
274  ModelPart* mpModelPart;
275 
276  std::string mTimeSchemeName;
277 
278  SizeType mAuxSize;
279 
280  SizeType mEchoLevel;
281 
282  double mMaxAllowedDt;
283 
284  double mMaxAllowedCFL;
285 
286  double mDiffusionConstant;
287 
288  bool mPerformInitialize = true;
289 
290  const Variable<double>* mpConvectedVar = nullptr;
291 
292  const Variable<array_1d<double,3>>* mpConvectionVar = nullptr;
293 
294  std::vector<double> mSolution;
295 
296  std::vector<double> mSolutionOld;
297 
298  std::vector<double> mLowOrderUpdate;
299 
300  std::vector<double> mHighOrderUpdate;
301 
302  std::vector<array_1d<double,3>> mConvectionValues;
303 
304  typename EdgeBasedDataStructure<TDim>::UniquePointer mpEdgeDataStructure;
305 
309 
313 
314  //TODO: Do it with the edge projected velocity
315  double CalculateSubStepDeltaTime()
316  {
317  // Get edge data structure containers
318  const auto& r_row_indices = mpEdgeDataStructure->GetRowIndices();
319  const auto& r_col_indices = mpEdgeDataStructure->GetColIndices();
320 
321  // Calculate the CFL number at each edge to get the minimum allowed time step
322  // Note that we don't loop the last entry of the row container as it is NNZ
323  SizeType n_nodes = r_row_indices.size() - 1;
324  const double min_dt = IndexPartition<IndexType>(n_nodes).for_each<MinReduction<double>>([&](IndexType iNode){
325  double dt_ij = std::numeric_limits<double>::max();
326  const auto it_row = r_row_indices.begin() + iNode;
327  const IndexType i_col_index = *it_row;
328  const SizeType n_cols = *(it_row+1) - i_col_index;
329  // Check that there are CSR columns (i.e. that current node involves an edge)
330  if (n_cols != 0) {
331  // i-node nodal data
332  const double i_vel_norm = norm_2(mConvectionValues[iNode]);
333 
334  // j-node nodal loop (i.e. loop ij-edges)
335  const auto i_col_begin = r_col_indices.begin() + i_col_index;
336  for (IndexType j_node = 0; j_node < n_cols; ++j_node) {
337  // j-node nodal data
338  IndexType j_node_id = *(i_col_begin + j_node);
339  const double j_vel_norm = norm_2(mConvectionValues[j_node_id]);
340 
341  // Get ij-edge length from CSR data structure
342  const auto& r_ij_edge_data = mpEdgeDataStructure->GetEdgeData(iNode, j_node_id);
343  const double ij_length = r_ij_edge_data.GetLength();
344 
345  // Calculate the max time step from the velocities at both edge ends and get the minimum
346  dt_ij = CalculateEdgeLocalDeltaTime(ij_length, i_vel_norm, j_vel_norm);
347  }
348  }
349 
350  return dt_ij;
351  });
352 
353  // Synchronize among MPI nodes
354  mpModelPart->GetCommunicator().GetDataCommunicator().MinAll(min_dt);
355 
356  // Return maximum allowable time step
357  return min_dt;
358  }
359 
360  double CalculateEdgeLocalDeltaTime(
361  const double Length,
362  const double NormVelI,
363  const double NormVelJ)
364  {
365  const double dx_CFL = Length * mMaxAllowedCFL;
366  const double dt_i = NormVelI > 1.0e-12 ? dx_CFL / NormVelI : mMaxAllowedDt;
367  const double dt_j = NormVelJ > 1.0e-12 ? dx_CFL / NormVelJ : mMaxAllowedDt;
368  return std::min(dt_i, dt_j);
369  }
370 
371  void SolveSubStep(const double DeltaTime)
372  {
373  // Calculate the low order Runge-Kutta update
374  const double low_order_max_iteration = 1;
375  const double low_order_diff_constant = mDiffusionConstant;
376 
377  // Calculate the high order Runge-Kutta update
378  const double high_order_max_iteration = 3;
379  const double high_order_diff_constant = 0.0;
380 
381  // Get Butcher tableau variant with the selected Runge-Kutta scheme
382  const auto butcher_tableau_variant = GetButcherTableauVariant();
383 
384  // Allocate auxiliary Runge-Kutta arrays
385  const SizeType rk_steps = std::visit([](const auto& rTableau){return rTableau.SubstepCount();}, butcher_tableau_variant);
386  std::vector<std::vector<double>> rk_residuals_lo(mAuxSize, std::vector<double>(rk_steps, 0.0));
387  std::vector<std::vector<double>> rk_residuals_ho(mAuxSize, std::vector<double>(rk_steps, 0.0));
388 
389  // Initialize intermediate substep solution vector
390  std::vector<double> rk_u_lo = mSolutionOld;
391  std::vector<double> rk_u_ho = mSolutionOld;
392 
393  // Perform the Runge-Kutta intermediate steps
394  for (IndexType step = 1; step <= rk_steps; ++step) {
395  // Solve current Runge-Kutta step
396  const IndexType residual_column = step - 1;
397  const double rk_step_theta = std::visit([&step](const auto &rTableau) {return rTableau.GetIntegrationTheta(step);}, butcher_tableau_variant); // Runge-Kutta step integration theta
398 
399  // Calculate low order residual
400  CalculateResidual(rk_residuals_lo, rk_u_lo, residual_column, low_order_diff_constant, rk_step_theta * DeltaTime);
401 
402  // Calculate high order residual
403  CalculateResidual(rk_residuals_ho, rk_u_ho, residual_column, high_order_diff_constant, rk_step_theta * DeltaTime);
404 
405  if (step != rk_steps) {
406  // Do the explicit lumped mass matrix solve to obtain the intermediate solutions
407  const auto [rk_mat_row_begin, rk_mat_row_end] = std::visit([&step](const auto& rTableau) {return rTableau.GetMatrixRow(step);}, butcher_tableau_variant); // Runge-Kutta matrix row
408  ExplicitSolveSolution(rk_u_lo, rk_residuals_lo, DeltaTime, rk_mat_row_begin, rk_mat_row_end, low_order_max_iteration);
409  ExplicitSolveSolution(rk_u_ho, rk_residuals_ho, DeltaTime, rk_mat_row_begin, rk_mat_row_end, high_order_max_iteration);
410 
411  // Set the auxiliary arrays required to calculate the antidiffusive fluxes
412  IndexPartition<IndexType>(mAuxSize).for_each([&](IndexType i){
413  mSolution[i] = rk_u_lo[i];
414  mHighOrderUpdate[i] = rk_u_ho[i] - mSolutionOld[i];
415  });
416 
417  // Current substep time advance coefficient to calculate the limiting
418  double time_coeff = 0.0;
419  for (auto it = rk_mat_row_begin; it != rk_mat_row_end; ++it) {
420  time_coeff += *it;
421  }
422 
423  // Calculate the antidiffusive edge contributions
424  CalculateAntidiffusiveEdgeContributions(time_coeff * DeltaTime);
425 
426  // Evaluate limiter
427  EvaluateLimiter(time_coeff * DeltaTime);
428 
429  // Set low and high order solutions to the limited one
430  rk_u_lo = mSolution;
431  rk_u_ho = mSolution;
432  }
433  }
434 
435  // Do the final solution update
436  const auto rk_weights = std::visit([](const auto& rTableau) -> auto {return rTableau.GetWeights();}, butcher_tableau_variant);
437  ExplicitSolveSolution(mSolution, rk_residuals_lo, DeltaTime, rk_weights.begin(), rk_weights.end(), low_order_max_iteration);
438  ExplicitSolveUpdate(mHighOrderUpdate, rk_residuals_ho, DeltaTime, rk_weights.begin(), rk_weights.end(), high_order_max_iteration);
439 
440  // Calculate the antidiffusive edge contributions
441  CalculateAntidiffusiveEdgeContributions(DeltaTime);
442 
443  // Evaluate limiter
444  EvaluateLimiter(DeltaTime);
445 
446  // Do the current step solution update
447  IndexPartition<IndexType>(mAuxSize).for_each([this](IndexType i){
448  mSolutionOld[i] = mSolution[i];
449  });
450  }
451 
452  auto GetButcherTableauVariant()
453  {
454  if (mTimeSchemeName == "forward_euler") {
455  return VariantTableauType(ButcherTableauForwardEuler());
456  } else if (mTimeSchemeName == "midpoint_method") {
457  return VariantTableauType(ButcherTableauMidPointMethod());
458  } else if (mTimeSchemeName == "RK3-TVD") {
459  return VariantTableauType(ButcherTableauRK3TVD());
460  } else if (mTimeSchemeName == "RK4") {
461  return VariantTableauType(ButcherTableauRK4());
462  } else {
463  KRATOS_ERROR << "Butcher tableau cannot be found for time scheme '" << mTimeSchemeName << "'. Available options are 'forward_euler','midpoint_method','RK3-TVD' and 'RK4'." << std::endl;
464  }
465  }
466 
467  template<class TSolveCoeffsIterType>
468  void ExplicitSolveSolution(
469  std::vector<double>& rSolution,
470  const std::vector<std::vector<double>>& rResiduals,
471  const double DeltaTime,
472  const TSolveCoeffsIterType& rSolveCoeffBegin,
473  const TSolveCoeffsIterType& rSolveCoeffEnd,
474  const IndexType MaxIteration)
475  {
476  auto solution_updater = [&](std::vector<double>& rSolution, const IndexType NodeId, const double UpdateValue)
477  { rSolution[NodeId] = mSolutionOld[NodeId] + UpdateValue; };
478  ExplicitSolve(rSolution, rResiduals, DeltaTime, rSolveCoeffBegin, rSolveCoeffEnd, solution_updater, MaxIteration);
479  }
480 
481  template<class TSolveCoeffsIterType>
482  void ExplicitSolveUpdate(
483  std::vector<double>& rUpdate,
484  const std::vector<std::vector<double>>& rResiduals,
485  const double DeltaTime,
486  const TSolveCoeffsIterType& rSolveCoeffBegin,
487  const TSolveCoeffsIterType& rSolveCoeffEnd,
488  const IndexType MaxIteration)
489  {
490  auto solution_updater = [&](std::vector<double> &rUpdate, const IndexType NodeId, const double UpdateValue)
491  { rUpdate[NodeId] = UpdateValue; };
492  ExplicitSolve(rUpdate, rResiduals, DeltaTime, rSolveCoeffBegin, rSolveCoeffEnd, solution_updater, MaxIteration);
493  }
494 
495  template<class TSolveCoeffsIterType>
496  void ExplicitSolve(
497  std::vector<double>& rSolution,
498  const std::vector<std::vector<double>>& rResiduals,
499  const double DeltaTime,
500  const TSolveCoeffsIterType& rSolveCoeffBegin,
501  const TSolveCoeffsIterType& rSolveCoeffEnd,
502  const std::function<void(std::vector<double>&, const IndexType, const double)>& rUpdater,
503  const IndexType MaxIteration)
504  {
505  if (MaxIteration == 1) {
506  // Standard lumped mass matrix solve
507  IndexPartition<IndexType>(mpModelPart->NumberOfNodes()).for_each([&](IndexType iNode){
508  // Get nodal data
509  const auto it_node = mpModelPart->NodesBegin() + iNode;
510  const IndexType i_node_id = it_node->Id();
511 
512  // Do the explicit lumped mass matrix solve
513  const double M_l = mpEdgeDataStructure->GetLumpedMassMatrixDiagonal(i_node_id);
514  const bool is_fixed = it_node->IsFixed(*mpConvectedVar);
515  const double update = is_fixed ? 0.0 : (DeltaTime / M_l) * std::inner_product(rSolveCoeffBegin, rSolveCoeffEnd, rResiduals[i_node_id].begin(), 0.0);
516  rUpdater(rSolution, i_node_id, update);
517  });
518  } else {
519  // Get edge data structure containers
520  const auto &r_row_indices = mpEdgeDataStructure->GetRowIndices();
521  const auto &r_col_indices = mpEdgeDataStructure->GetColIndices();
522  SizeType aux_n_rows = r_row_indices.size() - 1; // Note that the last entry of the row container is the NNZ
523 
524  // First explicit lumped mass matrix solve
525  // Note that in the case of iterative explicit solve this initializes the update and Galerkin residual vectors
526  std::vector<double> aux_update(mAuxSize);
527  std::vector<double> galerkin_residual(mAuxSize);
528  IndexPartition<IndexType>(mpModelPart->NumberOfNodes()).for_each([&](IndexType iNode){
529  // Get nodal data
530  const auto it_node = mpModelPart->NodesBegin() + iNode;
531  const IndexType i_node_id = it_node->Id();
532 
533  // Do the explicit lumped mass matrix solve
534  if (it_node->IsFixed(*mpConvectedVar)) {
535  aux_update[i_node_id] = 0.0;
536  } else {
537  const double M_l = mpEdgeDataStructure->GetLumpedMassMatrixDiagonal(i_node_id);
538  const double i_node_res = std::inner_product(rSolveCoeffBegin, rSolveCoeffEnd, rResiduals[i_node_id].begin(), 0.0);
539  galerkin_residual[i_node_id] = i_node_res;
540  aux_update[i_node_id] = (DeltaTime / M_l) * i_node_res;
541  }
542  });
543 
544  // Do the consistent mass matrix solve (iteratively with lumped mass matrix)
545  std::vector<double> it_residual(mAuxSize);
546  for (IndexType i = 1; i < MaxIteration; ++i) {
547  // Initialize current iteration residual to the Galerkin one
548  IndexPartition<IndexType>(mAuxSize).for_each([&](IndexType i){
549  it_residual[i] = galerkin_residual[i];
550  });
551 
552  // Add the lumping correction
553  IndexPartition<IndexType>(aux_n_rows).for_each([&](IndexType iRow){
554  // Get current row (node) storage data
555  const auto it_row = r_row_indices.begin() + iRow;
556  const IndexType i_col_index = *it_row;
557  const SizeType n_cols = *(it_row+1) - i_col_index;
558 
559  // Check that there are CSR columns (i.e. that current node involves an edge)
560  if (n_cols != 0) {
561  // i-node nodal data
562  const double delta_u_h_i = aux_update[iRow];
563 
564  // j-node nodal loop (i.e. loop ij-edges)
565  const auto i_col_begin = r_col_indices.begin() + i_col_index;
566  for (IndexType j_node = 0; j_node < n_cols; ++j_node) {
567  // j-node nodal data
568  IndexType j_node_id = *(i_col_begin + j_node);
569  const double delta_u_h_j = aux_update[j_node_id];
570 
571  // Add previous iteration correction
572  const auto& r_ij_edge_data = mpEdgeDataStructure->GetEdgeData(iRow, j_node_id);
573  const double Mc_ij = r_ij_edge_data.GetOffDiagonalConsistentMass();
574  const double res_edge_i = Mc_ij * (delta_u_h_i - delta_u_h_j) / DeltaTime;
575  const double res_edge_j = Mc_ij * (delta_u_h_j - delta_u_h_i) / DeltaTime;
576 
577  // Atomic additions
578  AtomicAdd(it_residual[iRow], res_edge_i);
579  AtomicAdd(it_residual[j_node_id], res_edge_j);
580  }
581  }
582  });
583 
584  // Solve current iteration residual
585  IndexPartition<IndexType>(mpModelPart->NumberOfNodes()).for_each([&](IndexType iNode){
586  // Get nodal data
587  const auto it_node = mpModelPart->NodesBegin() + iNode;
588  const IndexType i_node_id = it_node->Id();
589 
590  // Do the explicit lumped mass matrix solve
591  if (it_node->IsFixed(*mpConvectedVar)) {
592  aux_update[i_node_id] = 0.0;
593  } else {
594  const double M_l = mpEdgeDataStructure->GetLumpedMassMatrixDiagonal(i_node_id);
595  aux_update[i_node_id] = (DeltaTime / M_l) * it_residual[i_node_id];
596  }
597  });
598  }
599 
600  // Add the iterative update to the solution vector
601  IndexPartition<IndexType>(mpModelPart->NumberOfNodes()).for_each([&](IndexType iNode){
602  const auto it_node = mpModelPart->NodesBegin() + iNode;
603  if (!it_node->IsFixed(*mpConvectedVar)) {
604  const IndexType i_node_id = it_node->Id();
605  rUpdater(rSolution, i_node_id, aux_update[i_node_id]);
606  }
607  });
608  }
609  }
610 
611  void CalculateResidual(
612  std::vector<std::vector<double>>& rResiduals,
613  const std::vector<double>& rSolutionVector,
614  const IndexType ResidualColumn,
615  const double DiffusionConstant,
616  const double DeltaTime)
617  {
618  // Get edge data structure containers
619  const auto &r_row_indices = mpEdgeDataStructure->GetRowIndices();
620  const auto &r_col_indices = mpEdgeDataStructure->GetColIndices();
621  SizeType aux_n_rows = r_row_indices.size() - 1; // Note that the last entry of the row container is the NNZ
622 
623  // Aux TLS container
624  struct AuxTLS
625  {
626  array_1d<double, TDim> F_i;
627  array_1d<double, TDim> F_j;
628  array_1d<double, TDim> d_ij;
629  array_1d<double, TDim> b_ij;
630  array_1d<double, TDim> F_ij_num;
631  // array_1d<double, 3> vel_ij_half;
632  };
633 
634  // Off-diagonal (edge) contributions assembly
635  IndexPartition<IndexType>(aux_n_rows).for_each(AuxTLS(), [&](IndexType iRow, AuxTLS& rTLS){
636  // Get current row (node) storage data
637  const auto it_row = r_row_indices.begin() + iRow;
638  const IndexType i_col_index = *it_row;
639  const SizeType n_cols = *(it_row+1) - i_col_index;
640 
641  // Check that there are CSR columns (i.e. that current node involves an edge)
642  if (n_cols != 0) {
643  // Get TLS data
644  auto& F_i = rTLS.F_i;
645  auto& F_j = rTLS.F_j;
646  auto& d_ij = rTLS.d_ij;
647  auto& b_ij = rTLS.b_ij;
648  auto& F_ij_num = rTLS.F_ij_num;
649  // auto& vel_ij_half = rTLS.vel_ij_half;
650 
651  // i-node nodal data
652  const double u_i = rSolutionVector[iRow];
653  const auto& r_i_vel = mConvectionValues[iRow];
654 
655  // i-node convective flux calculation
656  for (IndexType d = 0; d < TDim; ++d) {
657  F_i[d] = u_i * r_i_vel[d];
658  }
659 
660  // j-node nodal loop (i.e. loop ij-edges)
661  const auto i_col_begin = r_col_indices.begin() + i_col_index;
662  for (IndexType j_node = 0; j_node < n_cols; ++j_node) {
663  // j-node nodal data
664  IndexType j_node_id = *(i_col_begin + j_node);
665  const double u_j = rSolutionVector[j_node_id];
666  const auto& r_j_vel = mConvectionValues[j_node_id];
667 
668  // j-node convective flux calculation
669  for (IndexType d = 0; d < TDim; ++d) {
670  F_j[d] = u_j * r_j_vel[d];
671  }
672 
673  // Get ij-edge operators data from CSR data structure
674  const auto& r_ij_edge_data = mpEdgeDataStructure->GetEdgeData(iRow, j_node_id);
675  const auto& r_Ni_DNj = r_ij_edge_data.GetOffDiagonalConvective();
676  const auto& r_DNi_Nj = r_ij_edge_data.GetOffDiagonalConvectiveTranspose();
677  d_ij = 0.5 * (r_Ni_DNj - r_DNi_Nj);
678  double D_ij = 0.0;
679  for (IndexType d = 0; d < TDim; ++d) {
680  D_ij += std::pow(d_ij[d],2);
681  }
682 
683  // // Calculate numerical flux "upwind" contribution at the edge midpoint
684  // // Note that this numerical flux corresponds to the Lax-Wendroff scheme
685  // const double u_ij_half = 0.5 * (u_i + u_j) - 0.5 * DeltaTime * inner_prod(-d_ij, F_i - F_j) / D_ij;
686  // noalias(vel_ij_half) = 0.5 * (r_i_vel + r_j_vel);
687  // for (IndexType d = 0; d < TDim; ++d) {
688  // F_ij_num[d] = 2.0 * (vel_ij_half[d] * u_ij_half);
689  // }
690 
691  // Standard flux
692  F_ij_num = F_i + F_j;
693 
694  // Calculate convection volume residual contributions
695  double res_edge_i = inner_prod(-d_ij, F_ij_num);
696  double res_edge_j = inner_prod(d_ij, F_ij_num);
697 
698  // If current edge belogs to a boundary, add the corresponding convection boundary integrals
699  if (r_ij_edge_data.IsBoundary()) {
700  // Get ij-edge boundary operators from CSR data structure
701  const auto &r_N_N_normal = r_ij_edge_data.GetOffDiagonalConvectiveBoundary();
702  b_ij = 0.5 * r_N_N_normal;
703 
704  // Add boundary contribution to the residual
705  const double res_edge_bd = inner_prod(b_ij, F_ij_num);
706  res_edge_i -= res_edge_bd;
707  res_edge_j += res_edge_bd;
708  }
709 
710  // // Laplacian contribution for the testing
711  // const double c_edge = r_ij_edge_data.GetOffDiagonalLaplacian();
712  // const double res_edge_i = 1.0 * c_edge * (u_i - u_j);
713  // const double res_edge_j = 1.0 * c_edge * (u_j - u_i);
714 
715  // Add low order scheme diffusion
716  if (DiffusionConstant > 0.0) {
717  const double local_dt = CalculateEdgeLocalDeltaTime(r_ij_edge_data.GetLength(), norm_2(r_i_vel), norm_2(r_j_vel));
718  const double c_tau = mDiffusionConstant / local_dt;
719  const double Mc_ij = r_ij_edge_data.GetOffDiagonalConsistentMass();
720  const double ij_low_order_diff = c_tau * Mc_ij;
721  res_edge_i += ij_low_order_diff * (u_j - u_i);
722  res_edge_j += ij_low_order_diff * (u_i - u_j);
723  }
724 
725  // Atomic additions
726  AtomicAdd(rResiduals[iRow][ResidualColumn], res_edge_i);
727  AtomicAdd(rResiduals[j_node_id][ResidualColumn], res_edge_j);
728  }
729  }
730  });
731 
732  // Add the diagonal boundary term coming from the convective flux split
733  IndexPartition<IndexType>(mpModelPart->NumberOfNodes()).for_each(array_1d<double,TDim>(), [&](IndexType iNode, array_1d<double,TDim>& rTLS){
734  // Get nodal data
735  const auto it_node = mpModelPart->NodesBegin() + iNode;
736  const IndexType i_node_id = it_node->Id();
737 
738  // Add the diagonal contribution to the residual
739  rTLS = mpEdgeDataStructure->GetBoundaryMassMatrixDiagonal(i_node_id);
740  double aux_res = 0.0;
741  const double u_i = rSolutionVector[i_node_id];
742  const auto &r_i_vel = mConvectionValues[i_node_id];
743  for (IndexType d = 0; d < TDim; ++d) {
744  aux_res += u_i * r_i_vel[d] * rTLS[d];
745  }
746  rResiduals[i_node_id][ResidualColumn] -= aux_res;
747  });
748  }
749 
750  void CalculateAntidiffusiveEdgeContributions(const double DeltaTime)
751  {
752  // Check input time increment
753  KRATOS_ERROR_IF(DeltaTime < 1.0e-12)
754  << "Antidiffusive edge contributions cannot be computed as provided time increment " << DeltaTime << "is close to zero." << std::endl;
755 
756  // Get edge data structure containers
757  const auto &r_row_indices = mpEdgeDataStructure->GetRowIndices();
758  const auto &r_col_indices = mpEdgeDataStructure->GetColIndices();
759  SizeType aux_n_rows = r_row_indices.size() - 1; // Note that the last entry of the row container is the NNZ
760 
761  // Add the diffusion to the Taylor-Galerkin already computed residual
762  IndexPartition<IndexType>(aux_n_rows).for_each([&](IndexType iRow){
763  // Get current row (node) storage data
764  const auto it_row = r_row_indices.begin() + iRow;
765  const IndexType i_col_index = *it_row;
766  const SizeType n_cols = *(it_row+1) - i_col_index;
767 
768  // Check that there are CSR columns (i.e. that current node involves an edge)
769  if (n_cols != 0) {
770  // i-node nodal data
771  const double u_i = mSolution[iRow]; // i-node low order solution (prediction)
772  const double du_h_i = mHighOrderUpdate[iRow]; // i-node high order solution update
773  const auto& r_i_vel = mConvectionValues[iRow];
774 
775  // j-node nodal loop (i.e. loop ij-edges)
776  const auto i_col_begin = r_col_indices.begin() + i_col_index;
777  for (IndexType j_node = 0; j_node < n_cols; ++j_node) {
778  // j-node nodal data
779  IndexType j_node_id = *(i_col_begin + j_node);
780  const double u_j = mSolution[j_node_id]; // j-node low order solution (prediction)
781  const double du_h_j = mHighOrderUpdate[j_node_id]; // j-node high order solution update
782  const auto& r_j_vel = mConvectionValues[j_node_id];
783 
784  // Calculate and store the antidiffusive flux edge contribution
785  auto& r_ij_edge_data = mpEdgeDataStructure->GetEdgeData(iRow, j_node_id);
786  const double local_dt = CalculateEdgeLocalDeltaTime(r_ij_edge_data.GetLength(), norm_2(r_i_vel), norm_2(r_j_vel));
787  const double c_tau = mDiffusionConstant * DeltaTime / local_dt;
788  const double Mc_ij = r_ij_edge_data.GetOffDiagonalConsistentMass();
789  const double AEC_ij = Mc_ij * (c_tau * (u_i - u_j) + (du_h_i - du_h_j)) / DeltaTime;
790 
791  // Prelimiting step (avoids steepening)
792  // This step removes the antidiffusive fluxes that flatten the solution profile instead of steepening it
793  if (AEC_ij * (u_j - u_i) > 0.0) {
794  r_ij_edge_data.SetAntidiffusiveEdgeContribution(0.0);
795  } else {
796  r_ij_edge_data.SetAntidiffusiveEdgeContribution(AEC_ij);
797  }
798  }
799  }
800  });
801  }
802 
803  void EvaluateLimiter(const double DeltaTime)
804  {
805  // Allocate and initialize auxiliary limiter arrays
806  std::vector<double> P_min(mAuxSize, 0.0);
807  std::vector<double> P_max(mAuxSize, 0.0);
808  std::vector<double> Q_min(mAuxSize, 0.0);
809  std::vector<double> Q_max(mAuxSize, 0.0);
810 
811  // Get edge data structure containers
812  const auto &r_row_indices = mpEdgeDataStructure->GetRowIndices();
813  const auto &r_col_indices = mpEdgeDataStructure->GetColIndices();
814  SizeType aux_n_rows = r_row_indices.size() - 1; // Note that the last entry of the row container is the NNZ
815 
816  // Calculate the summation of positive/negative antidiffusive fluxes and bound fluxes
817  IndexPartition<IndexType>(aux_n_rows).for_each([&](IndexType iRow){
818  // Get current row (node) storage data
819  const auto it_row = r_row_indices.begin() + iRow;
820  const IndexType i_col_index = *it_row;
821  const SizeType n_cols = *(it_row+1) - i_col_index;
822 
823  // Check that there are CSR columns (i.e. that current node involves an edge)
824  if (n_cols != 0) {
825  // i-node nodal data
826  const double u_i = mSolution[iRow]; //u_l
827  // const double u_i = mSolutionOld[iRow]; //u_n
828  const double M_l_i = mpEdgeDataStructure->GetLumpedMassMatrixDiagonal(iRow);
829 
830  // j-node nodal loop (i.e. loop ij-edges)
831  const auto i_col_begin = r_col_indices.begin() + i_col_index;
832  for (IndexType j_node = 0; j_node < n_cols; ++j_node) {
833  // j-node nodal data
834  IndexType j_node_id = *(i_col_begin + j_node);
835  const double u_j = mSolution[j_node_id]; //u_l
836  // const double u_j = mSolutionOld[j_node_id]; //u_n
837  const double M_l_j = mpEdgeDataStructure->GetLumpedMassMatrixDiagonal(j_node_id);
838 
839  // Get the antidiffusive flux edge contribution
840  const auto& r_ij_edge_data = mpEdgeDataStructure->GetEdgeData(iRow, j_node_id);
841  const double AEC_ij = r_ij_edge_data.GetAntidiffusiveEdgeContribution();
842 
843  // Assemble the positive/negative antidiffusive fluxes
844  AtomicAdd(P_min[iRow], std::min(0.0, AEC_ij));
845  AtomicAdd(P_max[iRow], std::max(0.0, AEC_ij));
846  AtomicAdd(P_min[j_node_id], std::min(0.0, -AEC_ij));
847  AtomicAdd(P_max[j_node_id], std::max(0.0, -AEC_ij));
848 
849  // Assemble the positive/negative antidiffusive bounds
850  const double aux_val_i = M_l_i * (u_j - u_i) / DeltaTime;
851  const double aux_val_j = M_l_j * (u_i - u_j) / DeltaTime;
852  Q_min[iRow] = std::min(Q_min[iRow], aux_val_i);
853  Q_max[iRow] = std::max(Q_max[iRow], aux_val_i);
854  Q_min[j_node_id] = std::min(Q_min[j_node_id], aux_val_j);
855  Q_max[j_node_id] = std::max(Q_max[j_node_id], aux_val_j);
856  }
857  }
858  });
859 
860  // Calculate the nodal correction factors
861  const double zero_tol = 1.0e-12;
862  std::vector<double> R_min(mAuxSize);
863  std::vector<double> R_max(mAuxSize);
864  IndexPartition<IndexType>(mAuxSize).for_each([&](IndexType i){
865  // R_{i}^{+} nodal correction factor calculation
866  // Note that we check if the summation of positive antidiffusive fluxes is zero
867  const double P_max_i = std::abs(P_max[i]) < zero_tol ? zero_tol : P_max[i];
868  R_max[i] = std::min(1.0, Q_max[i] / P_max_i);
869 
870  // R_{i}^{-} nodal correction factor calculation
871  // Note that we check if the summation of negative antidiffusive fluxes is zero
872  const double P_min_i = std::abs(P_min[i]) < zero_tol ? -zero_tol : P_min[i];
873  R_min[i] = std::min(1.0, Q_min[i] / P_min_i);
874  });
875 
876  // Assembly of the final antidiffusive contribution
877  std::vector<double> antidiff_assembly(mAuxSize, 0.0);
878  IndexPartition<IndexType>(aux_n_rows).for_each([&](IndexType iRow){
879  // Get current row (node) storage data
880  const auto it_row = r_row_indices.begin() + iRow;
881  const IndexType i_col_index = *it_row;
882  const SizeType n_cols = *(it_row+1) - i_col_index;
883 
884  // Check that there are CSR columns (i.e. that current node involves an edge)
885  if (n_cols != 0) {
886  // j-node nodal loop (i.e. loop ij-edges)
887  const auto i_col_begin = r_col_indices.begin() + i_col_index;
888  for (IndexType j_node = 0; j_node < n_cols; ++j_node) {
889  // j-node nodal data
890  IndexType j_node_id = *(i_col_begin + j_node);
891 
892  // Get the antidiffusive flux edge contribution
893  const auto& r_ij_edge_data = mpEdgeDataStructure->GetEdgeData(iRow, j_node_id);
894  const double AEC_ij = r_ij_edge_data.GetAntidiffusiveEdgeContribution();
895 
896  // Set as correction factor the minimum in the edge
897  const double alpha_ij = AEC_ij > 0.0 ? std::min(R_max[iRow], R_min[j_node_id]) : std::min(R_min[iRow], R_max[j_node_id]);
898  KRATOS_ERROR_IF(alpha_ij < 0.0 || alpha_ij > 1.0) << "Correction factor is " << alpha_ij << " for edge " << iRow << "-" << j_node_id << std::endl;
899 
900  // Assemble current edge antidiffusive contribution
901  const double aux = alpha_ij * AEC_ij;
902  AtomicAdd(antidiff_assembly[iRow], aux);
903  AtomicAdd(antidiff_assembly[j_node_id], -aux);
904  }
905  }
906  });
907 
908  // Solve and add final antidiffusive contributions
909  // Note that in here it is assumed that the low order solution is already added
910  IndexPartition<IndexType>(mpModelPart->NumberOfNodes()).for_each([&](IndexType iNode){
911  // Get nodal data
912  const auto it_node = mpModelPart->NodesBegin() + iNode;
913  const IndexType i_node_id = it_node->Id();
914 
915  // Solve antidiffusive contribution
916  const double M_l = mpEdgeDataStructure->GetLumpedMassMatrixDiagonal(i_node_id);
917  const double solve_coeff = DeltaTime / M_l;
918  mSolution[i_node_id] += solve_coeff * antidiff_assembly[i_node_id];
919  });
920  }
921 
925 
929 
933 
935 }; // Class FluxCorrectedTransportConvectionProcess
936 
940 
944 
946 template<unsigned int TDim>
947 inline std::istream& operator >> (
948  std::istream& rIStream,
950 
952 template< unsigned int TDim>
953 inline std::ostream& operator << (
954  std::ostream& rOStream,
956 {
957  rThis.PrintInfo(rOStream);
958  rOStream << std::endl;
959  rThis.PrintData(rOStream);
960 
961  return rOStream;
962 }
964 
965 } // namespace Kratos.
virtual const DataCommunicator & GetDataCommunicator() const
Definition: communicator.cpp:340
Definition: edge_based_data_structure.h:52
const IndicesVectorType & GetRowIndices() const
Definition: edge_based_data_structure.h:253
const IndicesVectorType & GetColIndices() const
Definition: edge_based_data_structure.h:258
const BoundaryMassMatrixVectorType & GetBoundaryMassMatrixDiagonal() const
Definition: edge_based_data_structure.h:273
const MassMatrixVectorType & GetLumpedMassMatrixDiagonal() const
Definition: edge_based_data_structure.h:283
const EdgeDataVectorType & GetEdgeData() const
Definition: edge_based_data_structure.h:293
std::size_t IndexType
Definition: flags.h:74
Process to solve a pure convection problem using a Flux Corrected Transport scheme and an edge-based ...
Definition: flux_corrected_transport_convection_process.h:58
void Execute() override
Execute method is used to execute the Process algorithms.
Definition: flux_corrected_transport_convection_process.h:152
FluxCorrectedTransportConvectionProcess(FluxCorrectedTransportConvectionProcess const &rOther)=delete
Copy constructor.
void PrintInfo(std::ostream &rOStream) const override
Print information about this object.
Definition: flux_corrected_transport_convection_process.h:251
FluxCorrectedTransportConvectionProcess(Model &rModel, Parameters ThisParameters)
Definition: flux_corrected_transport_convection_process.h:80
KRATOS_CLASS_POINTER_DEFINITION(FluxCorrectedTransportConvectionProcess)
Pointer definition of FluxCorrectedTransportConvectionProcess.
std::variant< ButcherTableauForwardEuler, ButcherTableauMidPointMethod, ButcherTableauRK3TVD, ButcherTableauRK4 > VariantTableauType
Definition: flux_corrected_transport_convection_process.h:67
void ExecuteInitialize() override
This function is designed for being called at the beginning of the computations right after reading t...
Definition: flux_corrected_transport_convection_process.h:125
std::size_t SizeType
Definition: flux_corrected_transport_convection_process.h:63
const Parameters GetDefaultParameters() const override
This method provides the defaults parameters to avoid conflicts between the different constructors.
Definition: flux_corrected_transport_convection_process.h:216
FluxCorrectedTransportConvectionProcess & operator=(FluxCorrectedTransportConvectionProcess const &rOther)=delete
Assignment operator.
std::size_t IndexType
Definition: flux_corrected_transport_convection_process.h:65
void Clear() override
This method clears the assignation of the conditions.
Definition: flux_corrected_transport_convection_process.h:206
std::string Info() const override
Turn back information as a string.
Definition: flux_corrected_transport_convection_process.h:245
~FluxCorrectedTransportConvectionProcess()=default
Destructor.
void PrintData(std::ostream &rOStream) const override
Print object's data.
Definition: flux_corrected_transport_convection_process.h:257
This class is useful for index iteration over containers.
Definition: parallel_utilities.h:451
void for_each(TUnaryFunction &&f)
Definition: parallel_utilities.h:514
KratosComponents class encapsulates a lookup table for a family of classes in a generic way.
Definition: kratos_components.h:49
Definition: reduction_utilities.h:190
This class aims to manage different model parts across multi-physics simulations.
Definition: model.h:60
ModelPart & GetModelPart(const std::string &rFullModelPartName)
This method returns a model part given a certain name.
Definition: model.cpp:107
This class aims to manage meshes for multi-physics simulations.
Definition: model_part.h:77
Communicator & GetCommunicator()
Definition: model_part.h:1821
NodeIterator NodesBegin(IndexType ThisIndex=0)
Definition: model_part.h:487
ProcessInfo & GetProcessInfo()
Definition: model_part.h:1746
SizeType NumberOfNodes(IndexType ThisIndex=0) const
Definition: model_part.h:341
NodesContainerType & Nodes(IndexType ThisIndex=0)
Definition: model_part.h:507
This class provides to Kratos a data structure for I/O based on the standard of JSON.
Definition: kratos_parameters.h:59
double GetDouble() const
This method returns the double contained in the current Parameter.
Definition: kratos_parameters.cpp:657
int GetInt() const
This method returns the integer contained in the current Parameter.
Definition: kratos_parameters.cpp:666
void ValidateAndAssignDefaults(const Parameters &rDefaultParameters)
This function is designed to verify that the parameters under testing match the form prescribed by th...
Definition: kratos_parameters.cpp:1306
std::string GetString() const
This method returns the string contained in the current Parameter.
Definition: kratos_parameters.cpp:684
The base class for all processes in Kratos.
Definition: process.h:49
ProcessInfo & GetPreviousTimeStepInfo(IndexType StepsBefore=1)
Definition: process_info.h:187
#define KRATOS_CATCH(MoreInfo)
Definition: define.h:110
#define KRATOS_TRY
Definition: define.h:109
std::size_t IndexType
The definition of the index type.
Definition: key_hash.h:35
#define KRATOS_ERROR
Definition: exception.h:161
#define KRATOS_ERROR_IF(conditional)
Definition: exception.h:162
#define KRATOS_INFO_IF(label, conditional)
Definition: logger.h:251
dt
Definition: DEM_benchmarks.py:173
static double max(double a, double b)
Definition: GeometryFunctions.h:79
static double min(double a, double b)
Definition: GeometryFunctions.h:71
REF: G. R. Cowper, GAUSSIAN QUADRATURE FORMULAS FOR TRIANGLES.
Definition: mesh_condition.cpp:21
TExpressionType::data_type norm_2(AMatrix::MatrixExpression< TExpressionType, TCategory > const &TheExpression)
Definition: amatrix_interface.h:625
void AtomicAdd(TDataType &target, const TDataType &value)
Definition: atomic_utilities.h:55
void block_for_each(TIterator itBegin, TIterator itEnd, TFunction &&rFunction)
Execute a functor on all items of a range in parallel.
Definition: parallel_utilities.h:299
TExpression1Type::data_type inner_prod(AMatrix::MatrixExpression< TExpression1Type, TCategory1 > const &First, AMatrix::MatrixExpression< TExpression2Type, TCategory2 > const &Second)
Definition: amatrix_interface.h:592
std::size_t SizeType
The definition of the size type.
Definition: mortar_classes.h:43
std::istream & operator>>(std::istream &rIStream, LinearMasterSlaveConstraint &rThis)
input stream function
std::ostream & operator<<(std::ostream &rOStream, const LinearMasterSlaveConstraint &rThis)
output stream function
Definition: linear_master_slave_constraint.h:432
int n_steps
Definition: bombardelli_test.py:27
int step
Definition: face_heat.py:88
int n_nodes
Definition: generate_total_lagrangian_mixed_volumetric_strain_element.py:15
int d
Definition: ode_solve.py:397
namespace
Definition: array_1d.h:793
integer i
Definition: TensorModule.f:17