Contents

Diffusion-Reaction

Introduction

This tutorial provides a step-by-step guide for implementing a diffusion-reaction problem using the Finite Element Method (FEM) in the deal.II library. We will focus on setting up the weak form of the equations, assembling the finite element system, applying appropriate boundary conditions, and solving the resulting linear system. This tutorial assumes familiarity with basic FEM concepts and provides a practical foundation for more advanced developments within the deal.II framework.

Theory

TBA

Implementation

Full code breakdown TBA

Results

Results for various Diffusion-Reaction constants TBA

Complete code

#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/function.h>
#include <deal.II/base/timer.h>
#include <deal.II/lac/generic_linear_algebra.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/petsc_precondition.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/affine_constraints.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/grid/grid_refinement.h>
#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/matrix_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/error_estimator.h>
#include <deal.II/base/utilities.h>
#include <deal.II/base/conditional_ostream.h>
#include <deal.II/base/index_set.h>
#include <deal.II/lac/sparsity_tools.h>
#include <deal.II/distributed/tria.h>
#include <deal.II/distributed/grid_refinement.h>
#include <deal.II/distributed/solution_transfer.h>
#include <deal.II/base/quadrature_point_data.h>
#include <deal.II/base/tensor_function.h>
#include <fstream>
#include <iostream>
#include <random>

namespace diff
{
    using namespace dealii;
    
    template <int dim>
    class Diffusion
    {
        public:
            Diffusion();
            void run();

        private:
            // Mesh generation and refinement
            void create_mesh();
            void refine_mesh();

            // Diffusion problem functions
            void set_initial_data();
            void setup_constraints ();     // Posso creare funz che applica BC o mettere a fine assembly matrix
            void setup();
            void assemble();
            void solve_linear_problem();

            void evaluate_reactions();

            // Save the results - Disp, stress, reaction, energies
            void output_results(const unsigned int time) const;
            
            // Parallel setup and print on screen
            MPI_Comm mpi_communicator;
            const unsigned int n_mpi_processes;
            const unsigned int this_mpi_process;

            parallel::distributed::Triangulation<dim> triangulation;

            // Objects for Diffusion
            const FESystem<dim> fe;                         // FE System
            DoFHandler<dim> dof_handler;                    // DoF Handler

            IndexSet locally_owned_dofs;                  // IndexSet - Locally Owned
            IndexSet locally_relevant_dofs;               // IndexSet - Locally relevant 
            AffineConstraints<double> constraints;        // Affine Constraint  

            PETScWrappers::MPI::SparseMatrix system_matrix;           // diffusion Matrix
            PETScWrappers::MPI::Vector system_rhs;                    // RHS 

            PETScWrappers::MPI::Vector local_solution_C1;             // MPI Split solution
            PETScWrappers::MPI::Vector local_solution_old_C1;         // MPI Split solution
            PETScWrappers::MPI::Vector distributed_solution_C1;       // Full Solution
            PETScWrappers::MPI::Vector distributed_old_C1;            // This is for the refinement

            PETScWrappers::MPI::Vector local_solution_C2;             // MPI Split solution
            PETScWrappers::MPI::Vector local_solution_old_C2;         // MPI Split solution
            PETScWrappers::MPI::Vector distributed_solution_C2;       // Full Solution
            PETScWrappers::MPI::Vector distributed_old_C2;            // This is for the refinement

            PETScWrappers::MPI::Vector local_solution_C3;             // MPI Split solution
            PETScWrappers::MPI::Vector local_solution_old_C3;         // MPI Split solution
            PETScWrappers::MPI::Vector distributed_solution_C3;       // Full Solution
            PETScWrappers::MPI::Vector distributed_old_C3;            // This is for the refinement

            const QGauss<dim> quadrature_formula;          // Quadrature formula

            // Domain dimensions
            double L = 1;
            double H = 1;
            double W = 1;

            const unsigned int nx = 30;
            const unsigned int ny = 30;
            const unsigned int nz = 30;

            // Diffusion coefficients [m^2/s]
            double D_x = 5e-2;
            double D_y = 1e-2;      
            double D_z = 5e-3;

            // Reaction coefficient [m^3/(mol s)]
            double K_12 = 10;

            // Initial concentrations [mol/m^3]
            double C1_0 = 1;
            double C2_0 = 0;
            double C3_0 = 0;

            // Boundary/Equilibrium concentration [mol/m^3]
            double C1_eq = 0.2; 
            double C2_bc = 1; 

            // Time stepping [s]
            double dt = 0.1;
            double T_tot = 10;
            const unsigned int n_steps = static_cast<unsigned int>(std::round(T_tot / dt));

            ConditionalOStream pcout;
            TimerOutput computing_timer;       
    }; // End of diff class

    // Constructor
    template <int dim>
    Diffusion<dim>::Diffusion()
        : mpi_communicator(MPI_COMM_WORLD)
        , n_mpi_processes(Utilities::MPI::n_mpi_processes(mpi_communicator))
        , this_mpi_process(Utilities::MPI::this_mpi_process(mpi_communicator))
        , triangulation (mpi_communicator)
        , fe (FE_Q<dim> (1), 1)
        , dof_handler (triangulation)
        , quadrature_formula (fe.degree + 1)
        , pcout (std::cout, this_mpi_process == 0)
        , computing_timer (mpi_communicator, pcout, TimerOutput::never, TimerOutput::wall_times)
    {}

    // -----------------------------
    // ----------- MESH ------------    
    // -----------------------------
    template <int dim>
    void Diffusion<dim>::create_mesh()
    {
        // 1) Definisco la suddivisione in base a dim
        std::vector<unsigned int> repetitions;
        if constexpr (dim == 2)
            repetitions = {nx, ny};
        else // dim == 3
            repetitions = {nx, ny, nz};

        // 2) Crea la mesh
        Point<dim> p1, p2;
        if constexpr (dim == 2)
        {
            p1 = Point<dim>(0, 0);
            p2 = Point<dim>(L, H);
        }
        else
        {
            p1 = Point<dim>(0, 0, 0);
            p2 = Point<dim>(L, H, W);
        }
        GridGenerator::subdivided_hyper_rectangle(triangulation,
                                                repetitions,
                                                p1, p2);
        // Set Fce IDs
        for (const auto &cell : triangulation.active_cell_iterators ())
        {
            for (const auto &face : cell->face_iterators ())
            {
                if (face->at_boundary ())
                {
                    const auto center = face->center ();
                    if (std::fabs (center(0) - 0) < 1e-12)
                        face->set_boundary_id (0);

                    else if (std::fabs (center(0) - L) < 1e-12)
                        face->set_boundary_id (1);

                    else if (std::fabs (center(1) - 0) < 1e-12)
                        face->set_boundary_id (2);

                    else if (std::fabs (center(1) - H) < 1e-12)
                        face->set_boundary_id (3);

                        // Controlla se siamo in 3D prima di accedere a center(2)
                    if constexpr (dim == 3)
                    {
                        if (std::fabs(center(2) - 0) < 1e-12)
                            face->set_boundary_id(4);
                        else if (std::fabs(center(2) - W) < 1e-12)
                            face->set_boundary_id(5);
                    }
                }
            }
        }

        // Initialize dof handler and extact Owned/Relevant DoFs
        dof_handler.distribute_dofs (fe);
        
        locally_owned_dofs = dof_handler.locally_owned_dofs ();
        locally_relevant_dofs = DoFTools::extract_locally_relevant_dofs (dof_handler);
        
        // Initialize the old solution for updates purposes
        local_solution_old_C1.reinit (locally_owned_dofs, locally_relevant_dofs, mpi_communicator);
        local_solution_old_C2.reinit (locally_owned_dofs, locally_relevant_dofs, mpi_communicator);
        local_solution_old_C3.reinit (locally_owned_dofs, locally_relevant_dofs, mpi_communicator);

        // Initialize the old solution for refinement purposes
        distributed_old_C1.reinit (locally_owned_dofs, mpi_communicator);
        distributed_old_C2.reinit (locally_owned_dofs, mpi_communicator);
        distributed_old_C3.reinit (locally_owned_dofs, mpi_communicator);
        
        pcout << "No. of levels in triangulation: " << triangulation.n_global_levels () << std::endl;
        pcout << "   Number of locally owned cells on the process:       " << triangulation.n_locally_owned_active_cells () << std::endl;
        pcout << "Number of global cells:" << triangulation.n_global_active_cells () << std::endl;
    
        pcout << "  Total Number of globally active cells:       "  << triangulation.n_global_active_cells () << std::endl
              << "   Number of degrees of freedom for Diffusion: " << dof_handler.n_dofs () << std::endl;
    }

    template <int dim>
    void Diffusion<dim>::refine_mesh()
    {
        // 1) Stima dell’errore su C2
        Vector<float> estimated_error_per_cell(triangulation.n_locally_owned_active_cells());
        KellyErrorEstimator<dim>::estimate(
            dof_handler,
            QGauss<dim-1>(fe.degree + 1),
            std::map<types::boundary_id, const Function<dim>*>(),
            local_solution_C2,
            estimated_error_per_cell);

        // 2) Prepare il solution transfer (ghosted!)
        parallel::distributed::SolutionTransfer<dim, PETScWrappers::MPI::Vector> soltransC1(dof_handler);
        parallel::distributed::SolutionTransfer<dim, PETScWrappers::MPI::Vector> soltransC2(dof_handler);
        parallel::distributed::SolutionTransfer<dim, PETScWrappers::MPI::Vector> soltransC3(dof_handler);

        // 3) Refinement/coarsening
        parallel::distributed::GridRefinement::refine_and_coarsen_fixed_fraction(
            triangulation, estimated_error_per_cell,
            0.25, 0.1);

        if (triangulation.n_global_levels() >= 4)
        {
            for (const auto &cell : triangulation.active_cell_iterators_on_level(3))
                if (cell->is_locally_owned())
                    cell->clear_refine_flag();
        }

        // 4) Esegui coarsening/refinement
        triangulation.prepare_coarsening_and_refinement();

        soltransC1.prepare_for_coarsening_and_refinement (local_solution_C1);
        soltransC2.prepare_for_coarsening_and_refinement (local_solution_C2);
        soltransC3.prepare_for_coarsening_and_refinement (local_solution_C3);

        triangulation.execute_coarsening_and_refinement();

        // 5) Redistribuisci DoF
        dof_handler.distribute_dofs(fe);
        locally_owned_dofs = dof_handler.locally_owned_dofs();
        locally_relevant_dofs = DoFTools::extract_locally_relevant_dofs(dof_handler);

        // 6) Reinit dei nuovi vettori distrib
        distributed_old_C1.reinit(locally_owned_dofs, mpi_communicator);
        distributed_old_C2.reinit(locally_owned_dofs, mpi_communicator);
        distributed_old_C3.reinit(locally_owned_dofs, mpi_communicator);
        
        soltransC1.interpolate (distributed_old_C1);
        soltransC2.interpolate (distributed_old_C2);
        soltransC3.interpolate (distributed_old_C3);

        // 8) Riapplico vincoli
        setup();

        constraints.distribute(distributed_old_C1);
        constraints.distribute(distributed_old_C2);
        constraints.distribute(distributed_old_C3);

        // 9) Aggiorna anche i ghost
        local_solution_old_C1.reinit(locally_owned_dofs, locally_relevant_dofs, mpi_communicator);
        local_solution_old_C2.reinit(locally_owned_dofs, locally_relevant_dofs, mpi_communicator);
        local_solution_old_C3.reinit(locally_owned_dofs, locally_relevant_dofs, mpi_communicator);

        local_solution_C1 = distributed_old_C1;         // Per output
        local_solution_C2 = distributed_old_C2;
        local_solution_C3 = distributed_old_C3;
        
        local_solution_old_C1 = distributed_old_C1;     // Per estrarre valore sol al passo next
        local_solution_old_C2 = distributed_old_C2;        
        local_solution_old_C3 = distributed_old_C3;
    }


    // -----------------------------
    // -------- Diffusion ---------
    // -----------------------------
    template<int dim>
    void Diffusion<dim>::setup_constraints ()
    {
        constraints.clear();
        constraints.reinit(locally_relevant_dofs);
    
        DoFTools::make_hanging_node_constraints(dof_handler, constraints);
    
        constraints.close();
    }

    template<int dim>
    void Diffusion<dim>::setup()
    {
        TimerOutput::Scope ts(computing_timer, "setup_system");
        // Initialize the dofs contained locally as well as those of the near cells
        locally_owned_dofs = dof_handler.locally_owned_dofs ();
        locally_relevant_dofs = DoFTools::extract_locally_relevant_dofs (dof_handler);
        
        local_solution_C1.reinit (locally_owned_dofs, locally_relevant_dofs, mpi_communicator);
        local_solution_C2.reinit (locally_owned_dofs, locally_relevant_dofs, mpi_communicator);
        local_solution_C3.reinit (locally_owned_dofs, locally_relevant_dofs, mpi_communicator);

        // Initialize the contraints objects for BC and Hanging Nodes during refinement
        system_rhs.reinit (locally_owned_dofs, mpi_communicator);
        distributed_solution_C1.reinit (locally_owned_dofs, mpi_communicator);
        distributed_solution_C2.reinit (locally_owned_dofs, mpi_communicator);
        distributed_solution_C3.reinit (locally_owned_dofs, mpi_communicator);
        
        setup_constraints ();  // Qui unizializzo gli hanging nodes constrains (e le BC volendo)
        
        // Sparsity pattern initialization (where matrix has nonzero entries)
        DynamicSparsityPattern dsp(locally_relevant_dofs);
        DoFTools::make_sparsity_pattern (dof_handler,
                                         dsp, 
                                         constraints, 
                                         false);
        SparsityTools::distribute_sparsity_pattern (dsp,
                                                    dof_handler.locally_owned_dofs (),
                                                    mpi_communicator,
                                                    locally_relevant_dofs);
        
        // Diffusion Matrix
        system_matrix.reinit (locally_owned_dofs,
                                      locally_owned_dofs, 
                                      dsp, 
                                      mpi_communicator);
    }

    template <int dim>
    void Diffusion<dim>::set_initial_data()
    {
        TimerOutput::Scope ts(computing_timer, "set_initial_data");
    
        // 1) Inizializza i vettori globali con i valori iniziali costanti
        distributed_solution_C1 = C1_0;
        distributed_solution_C2 = C2_0;
        distributed_solution_C3 = C3_0;
    
        // 2) Sincronizza i ghost‑values sui vettori globali
        distributed_solution_C1.compress(VectorOperation::insert);
        distributed_solution_C2.compress(VectorOperation::insert);
        distributed_solution_C3.compress(VectorOperation::insert);
    
        // 3) Applica le condizioni di Dirichlet (especialmente su C2)
        //    Questo usa setup_constraints e constraints già definiti
        setup_constraints();
        constraints.distribute(distributed_solution_C1);
        constraints.distribute(distributed_solution_C2);
        constraints.distribute(distributed_solution_C3);
    
        // 4) Copia nei vettori locali “split” e in quelli “old”
        local_solution_C1     = distributed_solution_C1;
        local_solution_C2     = distributed_solution_C2;
        local_solution_C3     = distributed_solution_C3;
        local_solution_old_C1 = distributed_solution_C1;
        local_solution_old_C2 = distributed_solution_C2;
        local_solution_old_C3 = distributed_solution_C3;
    
        // 5) Aggiorna i ghost‑values sui vettori locali
        local_solution_C1.update_ghost_values();
        local_solution_C2.update_ghost_values();
        local_solution_C3.update_ghost_values();
        local_solution_old_C1.update_ghost_values();
        local_solution_old_C2.update_ghost_values();
        local_solution_old_C3.update_ghost_values();
    }

    template<int dim>
    void Diffusion<dim>::assemble()
    {
        TimerOutput::Scope ts (computing_timer, "assembly");

        // Clean the current matrices 
        system_rhs = 0;
        system_matrix = 0;

        // Initialize the FEValues Objects
        FEValues<dim> fe_values (fe, quadrature_formula,
                                update_values | update_gradients |
                                update_quadrature_points | update_JxW_values);

        // Number of DoFs and Gauss points per cell
        const unsigned int n_q_points = quadrature_formula.size ();
        const unsigned int dofs_per_cell = fe.n_dofs_per_cell ();
        
        // Local Matrices and Vector
        FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
        Vector<double> cell_rhs (dofs_per_cell);

        // Save cell values for C1
        std::vector<double> sol_C1(n_q_points);
        std::vector<double> sol_C2(n_q_points);

        // Create vector to store the local dof indices
        std::vector<types::global_dof_index> local_dof_indices (dofs_per_cell);
        
        // Create the diffusion tensor
        SymmetricTensor<2, dim> diffusion_tensor;

        // Set values on the diagonal based on the dimension
        if constexpr (dim == 2)
        {
            diffusion_tensor[0][0] = D_x; // component xx
            diffusion_tensor[1][1] = D_y; // component yy
        }
        else if constexpr (dim == 3)
        {
            diffusion_tensor[0][0] = D_x; // component xx
            diffusion_tensor[1][1] = D_y; // component yy
            diffusion_tensor[2][2] = D_z; // component zz
        }

        // loop over the cells
        for (const auto &cell : dof_handler.active_cell_iterators ())
        {
            if (cell->is_locally_owned ())
            {
                // Reset the local matrix/rhs of the cell
                cell_matrix = 0;
                cell_rhs = 0;

                fe_values.reinit (cell);
                fe_values.get_function_values(local_solution_old_C1, sol_C1);
                fe_values.get_function_values(local_solution_old_C2, sol_C2);
                
                // loop over the gauss point of the cell
                for (const unsigned int q_point : fe_values.quadrature_point_indices ())
                {
                    double C1_conc = std::max(sol_C1[q_point] - C1_eq, 0.0);
                    double react = C1_conc * K_12;
                    
                    for (const unsigned int i : fe_values.dof_indices ())
                    {
                        for (const unsigned int j : fe_values.dof_indices ())
                        {
                            cell_matrix(i, j) += ((1.0 / dt + react) *
                                                    fe_values.shape_value(i, q_point) *
                                                    fe_values.shape_value(j, q_point)
                                                    +
                                                    (diffusion_tensor * fe_values.shape_grad(i, q_point)) *
                                                    fe_values.shape_grad(j, q_point)
                                                 ) * fe_values.JxW(q_point);
                            
                        }
                        
                        cell_rhs(i) += fe_values.shape_value(i, q_point) * (sol_C2[q_point] / dt) * fe_values.JxW(q_point);

                    }
                }
                cell->get_dof_indices (local_dof_indices);
                constraints.distribute_local_to_global (cell_matrix,
                                                        cell_rhs, 
                                                        local_dof_indices, 
                                                        system_matrix,
                                                        system_rhs);
            }
        }
        system_matrix.compress (VectorOperation::add);
        system_rhs.compress (VectorOperation::add);

        // Now set the BC
        std::map<types::global_dof_index, double> boundary_values;
        VectorTools::interpolate_boundary_values(dof_handler,
                                                0,
                                                Functions::ConstantFunction<dim>(C2_bc),
                                                boundary_values);

        VectorTools::interpolate_boundary_values(dof_handler,
                                                2,
                                                Functions::ConstantFunction<dim>(C2_bc),
                                                boundary_values);

        if constexpr (dim == 3)
        {
            VectorTools::interpolate_boundary_values(dof_handler,
                                                    4,
                                                    Functions::ConstantFunction<dim>(C2_bc),
                                                    boundary_values);
        }

        PETScWrappers::MPI::Vector tmp(locally_owned_dofs, mpi_communicator);
        MatrixTools::apply_boundary_values(boundary_values, system_matrix, tmp, system_rhs, false);       
        distributed_solution_C2 = tmp;
    }

    template<int dim>
    void Diffusion<dim>::solve_linear_problem()
    {
        TimerOutput::Scope ts (computing_timer, "solve_linear_system");
        // CG
        SolverControl solver_control (100000, 1e-12* system_rhs.l2_norm());
        SolverCG<PETScWrappers::MPI::Vector> solver (solver_control);

        PETScWrappers::PreconditionBlockJacobi preconditioner(system_matrix);
        
        solver.solve (system_matrix,
                      distributed_solution_C2,
                      system_rhs,
                      preconditioner);
    
        pcout << "   Solved in " << solver_control.last_step () << " iterations." << std::endl;
    
        constraints.distribute (distributed_solution_C2);
    }

    template <int dim>
    void Diffusion<dim>::evaluate_reactions()
    {
        TimerOutput::Scope ts(computing_timer, "evaluate_reactions");
    
        // 1) Assicuriamoci che i vecchi C1 e C3 siano sincronizzati nei vector locali
        local_solution_old_C1.update_ghost_values();
        local_solution_old_C3.update_ghost_values();
    
        // 3) Prepara i vettori per i risultati
        PETScWrappers::MPI::Vector new_solution_C1(locally_owned_dofs, mpi_communicator);
        PETScWrappers::MPI::Vector new_solution_C3(locally_owned_dofs, mpi_communicator);
    
        // 4) Copia iniziale: partiamo da C^n, poi sottrai/aggiungi reazione
        new_solution_C1 = local_solution_old_C1;
        new_solution_C3 = local_solution_old_C3;
    
        // 5) Loop sui DoF locali
        for (auto it = locally_owned_dofs.begin(); it != locally_owned_dofs.end(); ++it)
        {
            const unsigned int i = *it;
            const double c1_old = local_solution_old_C1[i];
            const double c2_new = local_solution_C2[i];
            const double c3_old = local_solution_old_C3[i];
    
            // tasso di reazione
            const double c1_eff = std::max(c1_old - C1_eq, 0.0);
            const double r      = K_12 * c1_eff * c2_new;
    
            // esplicito forward
            new_solution_C1[i] = c1_old - dt * r;
            new_solution_C3[i] = c3_old + dt * r;
        }
    
        // 6) Sincronizza i ghost‑values dei nuovi vettori
        new_solution_C1.compress(VectorOperation::insert);
        new_solution_C3.compress(VectorOperation::insert);
    
        // 7) Applica i vincoli su C1 e C3
        constraints.distribute(new_solution_C1);
        constraints.distribute(new_solution_C3);
    
        // 8) Salva nei vettori globali
        distributed_solution_C1 = new_solution_C1;
        distributed_solution_C3 = new_solution_C3;
    }
    
    // -----------------------------
    // ---------- OUTPUT -----------
    // -----------------------------
    // Solution
    template <int dim>
    void Diffusion<dim>::output_results(const unsigned int load_step) const
    {
        DataOut<dim> data_out;

        // Aggiungo i tre campi SCALARI: C1, C2, C3
        data_out.add_data_vector(dof_handler, local_solution_C1, "C1");
        data_out.add_data_vector(dof_handler, local_solution_C2, "C2");
        data_out.add_data_vector(dof_handler, local_solution_C3, "C3");

        // Subdomain (per mappare MPI rank)
        Vector<double> subdomain(triangulation.n_active_cells());
        for (unsigned int i = 0; i < subdomain.size(); ++i)
            subdomain(i) = triangulation.locally_owned_subdomain();
        
        data_out.add_data_vector(subdomain, "subdomain");
        data_out.build_patches();

        const std::string filename = 
        data_out.write_vtu_with_pvtu_record("./",
                                            "solution",
                                            load_step,
                                            mpi_communicator);

        if (this_mpi_process == 0)
        {
            static std::vector<std::pair<double, std::string>> times_and_names;
            times_and_names.emplace_back(load_step, filename);
            std::ofstream pvd_output("./solution.pvd");
            DataOutBase::write_pvd_record(pvd_output, times_and_names);
        }
    }

    // -----------------------------
    // ----------- RUN -------------
    // -----------------------------
    template <int dim>
    void Diffusion<dim>::run()
    {
        Timer timer;
        timer.start ();
        pcout << "Running on " << Utilities::MPI::n_mpi_processes (mpi_communicator) << " MPI rank(s)..." << std::endl;

        create_mesh();
        setup();
        set_initial_data();

        for (unsigned int step = 0; step <= n_steps; ++step)
        {
            const double time = step * dt;
            pcout << " --- Time step : " << time << std::endl;

            assemble();
            solve_linear_problem();

            // --- C2: update & backup ---
            local_solution_C2 = distributed_solution_C2;
            local_solution_C2.update_ghost_values();
            local_solution_old_C2 = distributed_solution_C2;
            local_solution_old_C2.update_ghost_values();

            // --- Reazioni su C1 e C3 ---
            evaluate_reactions();

            // --- C1 & C3: update & backup ---
            local_solution_C1 = distributed_solution_C1;
            local_solution_C1.update_ghost_values();
            local_solution_old_C1 = distributed_solution_C1;
            local_solution_old_C1.update_ghost_values();

            local_solution_C3 = distributed_solution_C3;
            local_solution_C3.update_ghost_values();
            local_solution_old_C3 = distributed_solution_C3;
            local_solution_old_C3.update_ghost_values();

            // ---------- Refinement & Output ----------
            refine_mesh();  // dentro questa viene fatto setup() + interpolate
            pcout << "  Total New Number of globally active cells:       "  << triangulation.n_global_active_cells () << std::endl
                  << "  New Number of degrees of freedom: " << dof_handler.n_dofs () << std::endl;

            output_results(step);

            computing_timer.print_summary();
            computing_timer.reset();
        }

        timer.stop ();
        pcout << "Total run time: " << timer.wall_time () << " seconds." << std::endl;
    }
} // End of namespace Diffusion

// -----------------------------
// ---------- MAIIN ------------
// -----------------------------
int main (int argc, char *argv[])
{
    try
    {
        using namespace dealii;
        using namespace diff;

        Utilities::MPI::MPI_InitFinalize mpi_initialization (argc, argv, 1);

        Diffusion<2> Diffusion;
        Diffusion.run ();
    }
    catch (std::exception &exc)
    {
        std::cerr << std::endl << std::endl
        << "----------------------------------------------------" << std::endl;
        std::cerr << "Exception on processing: " << std::endl << exc.what ()
        << std::endl << "Aborting!" << std::endl
        << "----------------------------------------------------" << std::endl;

        return 1;
    }
    catch (...)
    {
        std::cerr << std::endl << std::endl
        << "----------------------------------------------------" << std::endl;
        std::cerr << "Unknown exception!" << std::endl << "Aborting!" << std::endl
        << "----------------------------------------------------" << std::endl;
        return 1;
    }

    return 0;
}
        
Back