Example #1
0
int main(int argc, char* argv[])
{
  // Choose a Butcher's table or define your own.
  ButcherTable bt(butcher_table_type);
  if (bt.is_explicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage explicit R-K method.", bt.get_size());
  if (bt.is_diagonally_implicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
  if (bt.is_fully_implicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage fully implicit R-K method.", bt.get_size());

  // Turn off adaptive time stepping if R-K method is not embedded.
  if (bt.is_embedded() == false && ADAPTIVE_TIME_STEP_ON == true) {
    Hermes::Mixins::Loggable::Static::warn("R-K method not embedded, turning off adaptive time stepping.");
    ADAPTIVE_TIME_STEP_ON = false;
  }

  // Load the mesh.
  MeshSharedPtr mesh(new Mesh), basemesh(new Mesh);
  MeshReaderH2D mloader;
  mloader.load("wall.mesh", basemesh);
  mesh->copy(basemesh);

  // Perform initial mesh refinements.
  for(int i = 0; i < INIT_REF_NUM; i++) mesh->refine_all_elements();
  mesh->refine_towards_boundary(BDY_RIGHT, 2);
  mesh->refine_towards_boundary(BDY_FIRE, INIT_REF_NUM_BDY);

  // Initialize essential boundary conditions (none).
  EssentialBCs<double> bcs;

  // Initialize an H1 space with default shapeset.
  SpaceSharedPtr<double> space(new H1Space<double>(mesh, &bcs, P_INIT));
  int ndof = Space<double>::get_num_dofs(space);
  Hermes::Mixins::Loggable::Static::info("ndof = %d.", ndof);

  // Convert initial condition into a Solution.
  MeshFunctionSharedPtr<double> sln_prev_time(new ConstantSolution<double> (mesh, TEMP_INIT));

  // Initialize the weak formulation.
  double current_time = 0;
  CustomWeakFormHeatRK wf(BDY_FIRE, BDY_AIR, ALPHA_FIRE, ALPHA_AIR,
    RHO, HEATCAP, TEMP_EXT_AIR, TEMP_INIT, &current_time);

  // Initialize the FE problem.
  DiscreteProblem<double> dp(&wf, space);

  // Create a refinement selector.
  H1ProjBasedSelector<double> selector(CAND_LIST);

  // Visualize initial condition.
  char title[100];
  ScalarView sln_view("Initial condition", new WinGeom(0, 0, 1500, 360));
  OrderView ordview("Initial mesh", new WinGeom(0, 410, 1500, 360));
  ScalarView time_error_view("Temporal error", new WinGeom(0, 800, 1500, 360));
  time_error_view.fix_scale_width(40);
  ScalarView space_error_view("Spatial error", new WinGeom(0, 1220, 1500, 360));
  space_error_view.fix_scale_width(40);
  sln_view.show(sln_prev_time);
  ordview.show(space);

  // Graph for time step history.
  SimpleGraph time_step_graph;
  if (ADAPTIVE_TIME_STEP_ON) Hermes::Mixins::Loggable::Static::info("Time step history will be saved to file time_step_history.dat.");

  // Class for projections.
  OGProjection<double> ogProjection;

  // Time stepping loop:
  int ts = 1;
  do 
  {
    Hermes::Mixins::Loggable::Static::info("Begin time step %d.", ts);
    // Periodic global derefinement.
    if (ts > 1 && ts % UNREF_FREQ == 0) 
    {
      Hermes::Mixins::Loggable::Static::info("Global mesh derefinement.");
      switch (UNREF_METHOD) {
      case 1: mesh->copy(basemesh);
        space->set_uniform_order(P_INIT);
        break;
      case 2: space->unrefine_all_mesh_elements();
        space->set_uniform_order(P_INIT);
        break;
      case 3: space->unrefine_all_mesh_elements();
        //space->adjust_element_order(-1, P_INIT);
        space->adjust_element_order(-1, -1, P_INIT, P_INIT);
        break;
      default: throw Hermes::Exceptions::Exception("Wrong global derefinement method.");
      }

      space->assign_dofs();
      ndof = Space<double>::get_num_dofs(space);
    }

    // Spatial adaptivity loop. Note: sln_prev_time must not be 
    // changed during spatial adaptivity. 
    MeshFunctionSharedPtr<double> ref_sln(new Solution<double>());
    MeshFunctionSharedPtr<double> time_error_fn(new Solution<double>(mesh));
    bool done = false; int as = 1;
    double err_est;
    do {
      // Construct globally refined reference mesh and setup reference space.
      Mesh::ReferenceMeshCreator refMeshCreator(mesh);
      MeshSharedPtr ref_mesh = refMeshCreator.create_ref_mesh();

      Space<double>::ReferenceSpaceCreator refSpaceCreator(space, ref_mesh);
      SpaceSharedPtr<double> ref_space = refSpaceCreator.create_ref_space();

      // Initialize Runge-Kutta time stepping on the reference mesh.
      RungeKutta<double> runge_kutta(&wf, ref_space, &bt);

      try
      {
        ogProjection.project_global(ref_space, sln_prev_time, 
          sln_prev_time);
      }
      catch(Exceptions::Exception& e)
      {
        std::cout << e.what() << std::endl;
        Hermes::Mixins::Loggable::Static::error("Projection failed.");

        return -1;
      }

      // Runge-Kutta step on the fine mesh->
      Hermes::Mixins::Loggable::Static::info("Runge-Kutta time step on fine mesh (t = %g s, tau = %g s, stages: %d).", 
        current_time, time_step, bt.get_size());
      bool verbose = true;
      bool jacobian_changed = false;

      try
      {
        runge_kutta.set_time(current_time);
        runge_kutta.set_time_step(time_step);
        runge_kutta.set_max_allowed_iterations(NEWTON_MAX_ITER);
        runge_kutta.set_tolerance(NEWTON_TOL_FINE);
        runge_kutta.rk_time_step_newton(sln_prev_time, ref_sln, bt.is_embedded() ? time_error_fn : NULL);
      }
      catch(Exceptions::Exception& e)
      {
        std::cout << e.what() << std::endl;
        Hermes::Mixins::Loggable::Static::error("Runge-Kutta time step failed");

        return -1;
      }

      /* If ADAPTIVE_TIME_STEP_ON == true, estimate temporal error. 
      If too large or too small, then adjust it and restart the time step. */

      double rel_err_time = 0;
      if (bt.is_embedded() == true) 
      {
        Hermes::Mixins::Loggable::Static::info("Calculating temporal error estimate.");

        // Show temporal error.
        char title[100];
        sprintf(title, "Temporal error est, spatial adaptivity step %d", as);     
        time_error_view.set_title(title);
        //time_error_view.show_mesh(false);
        time_error_view.show(time_error_fn);

        rel_err_time = Global<double>::calc_norm(time_error_fn.get(), HERMES_H1_NORM) 
          / Global<double>::calc_norm(ref_sln.get(), HERMES_H1_NORM) * 100;
        if (ADAPTIVE_TIME_STEP_ON == false) Hermes::Mixins::Loggable::Static::info("rel_err_time: %g%%", rel_err_time);
      }

      if (ADAPTIVE_TIME_STEP_ON) 
      {
        if (rel_err_time > TIME_ERR_TOL_UPPER) 
        {
          Hermes::Mixins::Loggable::Static::info("rel_err_time %g%% is above upper limit %g%%", rel_err_time, TIME_ERR_TOL_UPPER);
          Hermes::Mixins::Loggable::Static::info("Decreasing tau from %g to %g s and restarting time step.", 
            time_step, time_step * TIME_STEP_DEC_RATIO);
          time_step *= TIME_STEP_DEC_RATIO;
          continue;
        }
        else if (rel_err_time < TIME_ERR_TOL_LOWER) 
        {
          Hermes::Mixins::Loggable::Static::info("rel_err_time = %g%% is below lower limit %g%%", rel_err_time, TIME_ERR_TOL_LOWER);
          Hermes::Mixins::Loggable::Static::info("Increasing tau from %g to %g s.", time_step, time_step * TIME_STEP_INC_RATIO);
          time_step *= TIME_STEP_INC_RATIO;
        }
        else 
        {
          Hermes::Mixins::Loggable::Static::info("rel_err_time = %g%% is in acceptable interval (%g%%, %g%%)", 
            rel_err_time, TIME_ERR_TOL_LOWER, TIME_ERR_TOL_UPPER);
        }

        // Add entry to time step history graph.
        time_step_graph.add_values(current_time, time_step);
        time_step_graph.save("time_step_history.dat");
      }

      /* Estimate spatial errors and perform mesh refinement */

      Hermes::Mixins::Loggable::Static::info("Spatial adaptivity step %d.", as);

      // Project the fine mesh solution onto the coarse mesh.
      MeshFunctionSharedPtr<double> sln(new Solution<double>());
      Hermes::Mixins::Loggable::Static::info("Projecting fine mesh solution on coarse mesh for error estimation.");
      ogProjection.project_global(space, ref_sln, sln); 

      // Show spatial error.
      sprintf(title, "Spatial error est, spatial adaptivity step %d", as);  
      MeshFunctionSharedPtr<double> space_error_fn(new DiffFilter<double>(Hermes::vector<MeshFunctionSharedPtr<double> >(ref_sln, sln)));
      space_error_view.set_title(title);
      //space_error_view.show_mesh(false);
      MeshFunctionSharedPtr<double> abs_sef(new AbsFilter(space_error_fn));

      space_error_view.show(abs_sef);

      // Calculate element errors and spatial error estimate.
      Hermes::Mixins::Loggable::Static::info("Calculating spatial error estimate.");
      adaptivity.set_space(space);
      double err_rel_space = errorCalculator.get_total_error_squared() * 100;

      // Report results.
      Hermes::Mixins::Loggable::Static::info("ndof: %d, ref_ndof: %d, err_rel_space: %g%%", 
        Space<double>::get_num_dofs(space), Space<double>::get_num_dofs(ref_space), err_rel_space);

      // If err_est too large, adapt the mesh.
      if (err_rel_space < SPACE_ERR_TOL) done = true;
      else 
      {
        Hermes::Mixins::Loggable::Static::info("Adapting the coarse mesh.");
        done = adaptivity.adapt(&selector);

        if (Space<double>::get_num_dofs(space) >= NDOF_STOP) 
          done = true;
        else
          // Increase the counter of performed adaptivity steps.
          as++;
      }

      // Clean up.
      if(!done)

        
    }
    while (done == false);

    // Visualize the solution and mesh->
    char title[100];
    sprintf(title, "Solution, time %g s", current_time);
    sln_view.set_title(title);
    //sln_view.show_mesh(false);
    sln_view.show(ref_sln);
    sprintf(title, "Mesh, time %g s", current_time);
    ordview.set_title(title);
    ordview.show(space);

    // Copy last reference solution into sln_prev_time
    sln_prev_time->copy(ref_sln);

    // Increase current time and counter of time steps.
    current_time += time_step;
    ts++;
  }
  while (current_time < T_FINAL);

  // Wait for all views to be closed.
  View::wait();
  return 0;
}
Example #2
0
int main(int argc, char* argv[])
{
  // Choose a Butcher's table or define your own.
  ButcherTable bt(butcher_table_type);
  if (bt.is_explicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage explicit R-K method.", bt.get_size());
  if (bt.is_diagonally_implicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
  if (bt.is_fully_implicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage fully implicit R-K method.", bt.get_size());

  // Load the mesh.
  MeshSharedPtr mesh(new Mesh), basemesh(new Mesh);
  MeshReaderH2D mloader;
  mloader.load("square.mesh", basemesh);
  mesh->copy(basemesh);

  // Initial mesh refinements.
  for(int i = 0; i < INIT_GLOB_REF_NUM; i++) mesh->refine_all_elements();
  mesh->refine_towards_boundary("Top", INIT_REF_NUM_BDY);

  // Initialize boundary conditions.
  CustomEssentialBCNonConst bc_essential(Hermes::vector<std::string>("Bottom", "Right", "Top", "Left"));
  EssentialBCs<double> bcs(&bc_essential);

  // Create an H1 space with default shapeset.
  SpaceSharedPtr<double> space(new H1Space<double>(mesh, &bcs, P_INIT));
  int ndof_coarse = Space<double>::get_num_dofs(space);
  adaptivity.set_space(space);
  Hermes::Mixins::Loggable::Static::info("ndof_coarse = %d.", ndof_coarse);

  // Zero initial solution. This is why we use H_OFFSET.
  MeshFunctionSharedPtr<double> h_time_prev(new ZeroSolution<double>(mesh)), h_time_new(new ZeroSolution<double>(mesh));

  // Initialize the constitutive relations.
  ConstitutiveRelations* constitutive_relations;
  if(constitutive_relations_type == CONSTITUTIVE_GENUCHTEN)
    constitutive_relations = new ConstitutiveRelationsGenuchten(ALPHA, M, N, THETA_S, THETA_R, K_S, STORATIVITY);
  else
    constitutive_relations = new ConstitutiveRelationsGardner(ALPHA, THETA_S, THETA_R, K_S);

  // Initialize the weak formulation.
  CustomWeakFormRichardsRK wf(constitutive_relations);

  // Initialize the FE problem.
  DiscreteProblem<double> dp(&wf, space);

  // Create a refinement selector.
  H1ProjBasedSelector<double> selector(CAND_LIST);

  // Visualize initial condition.
  char title[100];
  ScalarView view("Initial condition", new WinGeom(0, 0, 440, 350));
  OrderView ordview("Initial mesh", new WinGeom(445, 0, 440, 350));
  view.show(h_time_prev);
  ordview.show(space);

  // DOF and CPU convergence graphs initialization.
  SimpleGraph graph_dof, graph_cpu;
  
  // Time measurement.
  Hermes::Mixins::TimeMeasurable cpu_time;
  cpu_time.tick();
  
  // Time stepping loop.
  double current_time = 0; int ts = 1;
  do 
  {
    // Periodic global derefinement.
    if (ts > 1 && ts % UNREF_FREQ == 0) 
    {
      Hermes::Mixins::Loggable::Static::info("Global mesh derefinement.");
      switch (UNREF_METHOD) {
        case 1: mesh->copy(basemesh);
                space->set_uniform_order(P_INIT);
                break;
        case 2: mesh->unrefine_all_elements();
                space->set_uniform_order(P_INIT);
                break;
        case 3: space->unrefine_all_mesh_elements();
                space->adjust_element_order(-1, -1, P_INIT, P_INIT);
                break;
        default: throw Hermes::Exceptions::Exception("Wrong global derefinement method.");
      }

      space->assign_dofs();
      ndof_coarse = Space<double>::get_num_dofs(space);
    }

    // Spatial adaptivity loop. Note: h_time_prev must not be changed 
    // during spatial adaptivity. 
    bool done = false; int as = 1;
    double err_est;
    do {
      Hermes::Mixins::Loggable::Static::info("Time step %d, adaptivity step %d:", ts, as);

      // Construct globally refined reference mesh and setup reference space.
      Mesh::ReferenceMeshCreator refMeshCreator(mesh);
      MeshSharedPtr ref_mesh = refMeshCreator.create_ref_mesh();

      Space<double>::ReferenceSpaceCreator refSpaceCreator(space, ref_mesh);
      SpaceSharedPtr<double> ref_space = refSpaceCreator.create_ref_space();
      int ndof_ref = Space<double>::get_num_dofs(ref_space);

      // Time measurement.
      cpu_time.tick();

      // Initialize Runge-Kutta time stepping.
      RungeKutta<double> runge_kutta(&wf, ref_space, &bt);

      // Perform one Runge-Kutta time step according to the selected Butcher's table.
      Hermes::Mixins::Loggable::Static::info("Runge-Kutta time step (t = %g s, tau = %g s, stages: %d).",
           current_time, time_step, bt.get_size());
      try
      {
        runge_kutta.set_time(current_time);
        runge_kutta.set_time_step(time_step);
        runge_kutta.set_max_allowed_iterations(NEWTON_MAX_ITER);
        runge_kutta.set_tolerance(NEWTON_TOL);
        runge_kutta.rk_time_step_newton(h_time_prev, h_time_new);
      }
      catch(Exceptions::Exception& e)
      {
        e.print_msg();
        throw Hermes::Exceptions::Exception("Runge-Kutta time step failed");
      }

      // Project the fine mesh solution onto the coarse mesh.
      MeshFunctionSharedPtr<double> sln_coarse(new Solution<double>);
      Hermes::Mixins::Loggable::Static::info("Projecting fine mesh solution on coarse mesh for error estimation.");
      OGProjection<double> ogProjection; ogProjection.project_global(space, h_time_new, sln_coarse); 

      // Calculate element errors and total error estimate.
      Hermes::Mixins::Loggable::Static::info("Calculating error estimate.");
      errorCalculator.calculate_errors(sln_coarse, h_time_new, true);
      double err_est_rel_total = errorCalculator.get_total_error_squared() * 100;

      // Report results.
      Hermes::Mixins::Loggable::Static::info("ndof_coarse: %d, ndof_ref: %d, err_est_rel: %g%%", 
           Space<double>::get_num_dofs(space), Space<double>::get_num_dofs(ref_space), err_est_rel_total);

      // Time measurement.
      cpu_time.tick();

      // If err_est too large, adapt the mesh.
      if (err_est_rel_total < ERR_STOP) done = true;
      else 
      {
        Hermes::Mixins::Loggable::Static::info("Adapting the coarse mesh.");
        done = adaptivity.adapt(&selector);

        // Increase the counter of performed adaptivity steps.
        as++;
      }
    }
    while (done == false);

    // Add entry to DOF and CPU convergence graphs.
    graph_dof.add_values(current_time, Space<double>::get_num_dofs(space));
    graph_dof.save("conv_dof_est.dat");
    graph_cpu.add_values(current_time, cpu_time.accumulated());
    graph_cpu.save("conv_cpu_est.dat");

    // Visualize the solution and mesh->
    char title[100];
    sprintf(title, "Solution, time %g", current_time);
    view.set_title(title);
    view.show_mesh(false);
    view.show(h_time_new);
    sprintf(title, "Mesh, time %g", current_time);
    ordview.set_title(title);
    ordview.show(space);

    // Copy last reference solution into h_time_prev.
    h_time_prev->copy(h_time_new);

    // Increase current time and counter of time steps.
    current_time += time_step;
    ts++;
  }
  while (current_time < T_FINAL);

  // Wait for all views to be closed.
  View::wait();
  return 0;
}
Example #3
0
int main(int argc, char* argv[])
{
  // Choose a Butcher's table or define your own.
  ButcherTable bt(butcher_table_type);
  if (bt.is_explicit()) info("Using a %d-stage explicit R-K method.", bt.get_size());
  if (bt.is_diagonally_implicit()) info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
  if (bt.is_fully_implicit()) info("Using a %d-stage fully implicit R-K method.", bt.get_size());

  // Load the mesh.
  Mesh mesh;
  H2DReader mloader;
  mloader.load("../domain.mesh", &mesh);

  // Convert to quadrilaterals.
  mesh.convert_triangles_to_quads();

  // Refine towards boundary.
  mesh.refine_towards_boundary("Bdy", 1, true);

  // Refine once towards vertex #4.
  mesh.refine_towards_vertex(4, 1);

  // Initialize solutions.
  CustomInitialConditionWave u_sln(&mesh);
  Solution v_sln(&mesh, 0.0);
  Hermes::vector<Solution*> slns(&u_sln, &v_sln);

  // Initialize the weak formulation.
  CustomWeakFormWave wf(time_step, C_SQUARED, &u_sln, &v_sln);
  
  // Initialize boundary conditions
  DefaultEssentialBCConst bc_essential("Bdy", 0.0);
  EssentialBCs bcs(&bc_essential);

  // Create x- and y- displacement space using the default H1 shapeset.
  H1Space u_space(&mesh, &bcs, P_INIT);
  H1Space v_space(&mesh, &bcs, P_INIT);
  info("ndof = %d.", Space::get_num_dofs(Hermes::vector<Space *>(&u_space, &v_space)));

  // Initialize the FE problem.
  DiscreteProblem dp(&wf, Hermes::vector<Space *>(&u_space, &v_space));

  // Initialize Runge-Kutta time stepping.
  RungeKutta runge_kutta(&dp, &bt, matrix_solver);

  // Time stepping loop.
  double current_time = 0; int ts = 1;
  do
  {
    // Perform one Runge-Kutta time step according to the selected Butcher's table.
    info("Runge-Kutta time step (t = %g s, time_step = %g s, stages: %d).", 
         current_time, time_step, bt.get_size());
    bool jacobian_changed = false;
    bool verbose = true;

    if (!runge_kutta.rk_time_step(current_time, time_step, slns, 
                                  slns, jacobian_changed, verbose))
      error("Runge-Kutta time step failed, try to decrease time step size.");

    // Update time.
    current_time += time_step;

  } while (current_time < T_FINAL);

  double coord_x[4] = {1, 3, 5, 7};
  double coord_y[4] = {1, 3, 5, 7};

  info("Coordinate (1.0, 0.0) value = %lf", u_sln.get_pt_value(coord_x[0], coord_y[0]));
  info("Coordinate (3.0, 3.0) value = %lf", u_sln.get_pt_value(coord_x[1], coord_y[1]));
  info("Coordinate (5.0, 5.0) value = %lf", u_sln.get_pt_value(coord_x[2], coord_y[2]));
  info("Coordinate (7.0, 7.0) value = %lf", u_sln.get_pt_value(coord_x[3], coord_y[3]));

  info("Coordinate (1.0, 0.0) value = %lf", v_sln.get_pt_value(coord_x[0], coord_y[0]));
  info("Coordinate (3.0, 3.0) value = %lf", v_sln.get_pt_value(coord_x[1], coord_y[1]));
  info("Coordinate (5.0, 5.0) value = %lf", v_sln.get_pt_value(coord_x[2], coord_y[2]));
  info("Coordinate (7.0, 7.0) value = %lf", v_sln.get_pt_value(coord_x[3], coord_y[3]));

  double t_value[8] = {0.212655, 0.000163, 0.000000, 0.000000, -0.793316, 0.007255, 0.000001, 0.000000};
  bool success = true;

  for (int i = 0; i < 4; i++)
  {
    if (fabs(t_value[i] - u_sln.get_pt_value(coord_x[i], coord_y[i])) > 1E-6) success = false;
  }

  for (int i = 4; i < 8; i++)
  {
    if (fabs(t_value[i] - v_sln.get_pt_value(coord_x[i-4], coord_y[i-4])) > 1E-6) success = false;
  }

  if (success) {
    printf("Success!\n");
    return ERR_SUCCESS;
  }
  else {
    printf("Failure!\n");
    return ERR_FAILURE;
  }

  // Wait for the view to be closed.
  View::wait();

  return 0;
}
Example #4
0
int main(int argc, char* argv[])
{
  // Choose a Butcher's table or define your own.
  ButcherTable bt(butcher_table_type);
  if (bt.is_explicit()) info("Using a %d-stage explicit R-K method.", bt.get_size());
  if (bt.is_diagonally_implicit()) info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
  if (bt.is_fully_implicit()) info("Using a %d-stage fully implicit R-K method.", bt.get_size());

  // Load the mesh.
  Mesh mesh;
  MeshReaderH2D mloader;
  mloader.load("domain.mesh", &mesh);

  // Perform initial mesh refinemets.
  for (int i = 0; i < INIT_REF_NUM; i++) mesh.refine_all_elements();

  // Initialize solutions.
  CustomInitialConditionWave E_sln(&mesh);
  ZeroSolution B_sln(&mesh);
  Hermes::vector<Solution<double>*> slns(&E_sln, &B_sln);

  // Initialize the weak formulation.
  CustomWeakFormWave wf(C_SQUARED);
  
  // Initialize boundary conditions
  DefaultEssentialBCConst<double> bc_essential("Perfect conductor", 0.0);
  EssentialBCs<double> bcs_E(&bc_essential);
  EssentialBCs<double> bcs_B;

  // Create x- and y- displacement space using the default H1 shapeset.
  HcurlSpace<double> E_space(&mesh, &bcs_E, P_INIT);
  H1Space<double> B_space(&mesh, &bcs_B, P_INIT);
  //L2Space<double> B_space(&mesh, P_INIT);
  Hermes::vector<Space<double> *> spaces = Hermes::vector<Space<double> *>(&E_space, &B_space);
  info("ndof = %d.", Space<double>::get_num_dofs(spaces));

  // Initialize the FE problem.
  DiscreteProblem<double> dp(&wf, spaces);

  // Initialize views.
  ScalarView E1_view("Solution E1", new WinGeom(0, 0, 400, 350));
  E1_view.fix_scale_width(50);
  ScalarView E2_view("Solution E2", new WinGeom(410, 0, 400, 350));
  E2_view.fix_scale_width(50);
  ScalarView B_view("Solution B", new WinGeom(0, 405, 400, 350));
  B_view.fix_scale_width(50);

  // Initialize Runge-Kutta time stepping.
  RungeKutta<double> runge_kutta(&dp, &bt, matrix_solver_type);

  // Time stepping loop.
  double current_time = time_step; int ts = 1;
  do
  {
    // Perform one Runge-Kutta time step according to the selected Butcher's table.
    info("Runge-Kutta time step (t = %g s, time_step = %g s, stages: %d).", 
         current_time, time_step, bt.get_size());
    bool jacobian_changed = false;
    bool verbose = true;
    
    try
    {
      runge_kutta.rk_time_step_newton(current_time, time_step, slns, slns, jacobian_changed, verbose);
    }
    catch(Exceptions::Exception& e)
    {
      e.printMsg();
      error("Runge-Kutta time step failed");
    }

    // Visualize the solutions.
    char title[100];
    sprintf(title, "E1, t = %g", current_time);
    E1_view.set_title(title);
    E1_view.show(&E_sln, HERMES_EPS_NORMAL, H2D_FN_VAL_0);
    sprintf(title, "E2, t = %g", current_time);
    E2_view.set_title(title);
    E2_view.show(&E_sln, HERMES_EPS_NORMAL, H2D_FN_VAL_1);
    sprintf(title, "B, t = %g", current_time);
    B_view.set_title(title);
    B_view.show(&B_sln, HERMES_EPS_NORMAL, H2D_FN_VAL_0);

    // Update time.
    current_time += time_step;
  
  } while (current_time < T_FINAL);

  // Wait for the view to be closed.
  View::wait();

  return 0;
}
Example #5
0
int main(int argc, char* argv[])
{
  // Choose a Butcher's table or define your own.
  ButcherTable bt(butcher_table_type);
  if (bt.is_explicit()) info("Using a %d-stage explicit R-K method.", bt.get_size());
  if (bt.is_diagonally_implicit()) info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
  if (bt.is_fully_implicit()) info("Using a %d-stage fully implicit R-K method.", bt.get_size());

  // Load the mesh.
  Mesh mesh;
  H2DReader mloader;
  mloader.load("../domain.mesh", &mesh);

  // Perform initial mesh refinemets.
  for (int i = 0; i < INIT_REF_NUM; i++) mesh.refine_all_elements();

  // Initialize solutions.
  CustomInitialConditionWave E_sln(&mesh);
  Solution F_sln(&mesh, 0.0, 0.0);
  Hermes::vector<Solution*> slns(&E_sln, &F_sln);

  // Initialize the weak formulation.
  CustomWeakFormWave wf(C_SQUARED);
  
  // Initialize boundary conditions
  DefaultEssentialBCConst bc_essential(BDY, 0.0);
  EssentialBCs bcs(&bc_essential);

  // Create x- and y- displacement space using the default H1 shapeset.
  HcurlSpace E_space(&mesh, &bcs, P_INIT);
  HcurlSpace F_space(&mesh, &bcs, P_INIT);
  Hermes::vector<Space *> spaces = Hermes::vector<Space *>(&E_space, &F_space);
  info("ndof = %d.", Space::get_num_dofs(spaces));

  // Initialize the FE problem.
  DiscreteProblem dp(&wf, spaces);

  // Initialize Runge-Kutta time stepping.
  RungeKutta runge_kutta(&dp, &bt, matrix_solver);

  // Time stepping loop.
  double current_time = 0; int ts = 1;
  do
  {
    // Perform one Runge-Kutta time step according to the selected Butcher's table.
    info("Runge-Kutta time step (t = %g s, time_step = %g s, stages: %d).", 
         current_time, time_step, bt.get_size());
    bool verbose = true;
    bool jacobian_changed = true;
    if (!runge_kutta.rk_time_step(current_time, time_step, slns, slns, jacobian_changed, verbose))
      error("Runge-Kutta time step failed, try to decrease time step size.");

    // Update time.
    current_time += time_step;
  
  } while (current_time < T_FINAL);

  double coord_x[4] = {0.3, 0.6, 0.9, 1.4};
  double coord_y[4] = {0, 0.3, 0.5, 0.7};

  info("Coordinate (0.3, 0.0) value = %lf", F_sln.get_pt_value(coord_x[0], coord_y[0]));
  info("Coordinate (0.6, 0.3) value = %lf", F_sln.get_pt_value(coord_x[1], coord_y[1]));
  info("Coordinate (0.9, 0.5) value = %lf", F_sln.get_pt_value(coord_x[2], coord_y[2]));
  info("Coordinate (1.4, 0.7) value = %lf", F_sln.get_pt_value(coord_x[3], coord_y[3]));

  double t_value[4] = {-0.144673, -0.264077, -0.336536, -0.368983};
  bool success = true;

  for (int i = 0; i < 4; i++)
  {
    if (fabs(t_value[i] - F_sln.get_pt_value(coord_x[i], coord_y[i])) > 1E-6) success = false;
  }

  if (success) {
    printf("Success!\n");
    return ERR_SUCCESS;
  }
  else {
    printf("Failure!\n");
    return ERR_FAILURE;
  }
}
Example #6
0
int main(int argc, char* argv[])
{
  // Choose a Butcher's table or define your own.
  ButcherTable bt(butcher_table_type);
  if (bt.is_explicit()) info("Using a %d-stage explicit R-K method.", bt.get_size());
  if (bt.is_diagonally_implicit()) info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
  if (bt.is_fully_implicit()) info("Using a %d-stage fully implicit R-K method.", bt.get_size());

  // Load the mesh.
  Mesh mesh, basemesh;
  MeshReaderH2D mloader;
  mloader.load("square.mesh", &basemesh);
  mesh.copy(&basemesh);

  // Initial mesh refinements.
  for(int i = 0; i < INIT_GLOB_REF_NUM; i++) mesh.refine_all_elements();
  mesh.refine_towards_boundary("Top", INIT_REF_NUM_BDY);

  // Initialize boundary conditions.
  CustomEssentialBCNonConst bc_essential(Hermes::vector<std::string>("Bottom", "Right", "Top", "Left"));
  EssentialBCs<double> bcs(&bc_essential);

  // Create an H1 space with default shapeset.
  H1Space<double> space(&mesh, &bcs, P_INIT);
  int ndof_coarse = Space<double>::get_num_dofs(&space);
  info("ndof_coarse = %d.", ndof_coarse);

  // Zero initial solution. This is why we use H_OFFSET.
  ZeroSolution h_time_prev(&mesh), h_time_new(&mesh);

  // Initialize the constitutive relations.
  ConstitutiveRelations* constitutive_relations;
  if(constitutive_relations_type == CONSTITUTIVE_GENUCHTEN)
    constitutive_relations = new ConstitutiveRelationsGenuchten(ALPHA, M, N, THETA_S, THETA_R, K_S, STORATIVITY);
  else
    constitutive_relations = new ConstitutiveRelationsGardner(ALPHA, THETA_S, THETA_R, K_S);

  // Initialize the weak formulation.
  CustomWeakFormRichardsRK wf(constitutive_relations);

  // Initialize the FE problem.
  DiscreteProblem<double> dp(&wf, &space);

  // Create a refinement selector.
  H1ProjBasedSelector<double> selector(CAND_LIST, CONV_EXP, H2DRS_DEFAULT_ORDER);

  // Visualize initial condition.
  char title[100];
  ScalarView view("Initial condition", new WinGeom(0, 0, 440, 350));
  OrderView ordview("Initial mesh", new WinGeom(445, 0, 440, 350));
  view.show(&h_time_prev);
  ordview.show(&space);

  // DOF and CPU convergence graphs initialization.
  SimpleGraph graph_dof, graph_cpu;
  
  // Time measurement.
  TimePeriod cpu_time;
  cpu_time.tick();
  
  // Time stepping loop.
  double current_time = 0; int ts = 1;
  do 
  {
    // Periodic global derefinement.
    if (ts > 1 && ts % UNREF_FREQ == 0) 
    {
      info("Global mesh derefinement.");
      switch (UNREF_METHOD) {
        case 1: mesh.copy(&basemesh);
                space.set_uniform_order(P_INIT);
                break;
        case 2: mesh.unrefine_all_elements();
                space.set_uniform_order(P_INIT);
                break;
        case 3: space.unrefine_all_mesh_elements();
                space.adjust_element_order(-1, -1, P_INIT, P_INIT);
                break;
        default: error("Wrong global derefinement method.");
      }

      ndof_coarse = Space<double>::get_num_dofs(&space);
    }

    // Spatial adaptivity loop. Note: h_time_prev must not be changed 
    // during spatial adaptivity. 
    bool done = false; int as = 1;
    double err_est;
    do {
      info("Time step %d, adaptivity step %d:", ts, as);

      // Construct globally refined reference mesh and setup reference space.
      Space<double>* ref_space = Space<double>::construct_refined_space(&space);
      int ndof_ref = Space<double>::get_num_dofs(ref_space);

      // Time measurement.
      cpu_time.tick();

      // Initialize Runge-Kutta time stepping.
      RungeKutta<double> runge_kutta(&wf, ref_space, &bt, matrix_solver);

      // Perform one Runge-Kutta time step according to the selected Butcher's table.
      info("Runge-Kutta time step (t = %g s, tau = %g s, stages: %d).",
           current_time, time_step, bt.get_size());
      bool freeze_jacobian = false;
      bool block_diagonal_jacobian = false;
      bool verbose = true;
      double damping_coeff = 1.0;
      double max_allowed_residual_norm = 1e10;

      try
      {
        runge_kutta.rk_time_step_newton(current_time, time_step, &h_time_prev, 
            &h_time_new, freeze_jacobian, block_diagonal_jacobian, verbose,
            NEWTON_TOL, NEWTON_MAX_ITER, damping_coeff, max_allowed_residual_norm);
      }
      catch(Exceptions::Exception& e)
      {
        e.printMsg();
        error("Runge-Kutta time step failed");
      }

      // Project the fine mesh solution onto the coarse mesh.
      Solution<double> sln_coarse;
      info("Projecting fine mesh solution on coarse mesh for error estimation.");
      OGProjection<double>::project_global(&space, &h_time_new, &sln_coarse, matrix_solver); 

      // Calculate element errors and total error estimate.
      info("Calculating error estimate.");
      Adapt<double>* adaptivity = new Adapt<double>(&space);
      double err_est_rel_total = adaptivity->calc_err_est(&sln_coarse, &h_time_new) * 100;

      // Report results.
      info("ndof_coarse: %d, ndof_ref: %d, err_est_rel: %g%%", 
           Space<double>::get_num_dofs(&space), Space<double>::get_num_dofs(ref_space), err_est_rel_total);

      // Time measurement.
      cpu_time.tick();

      // If err_est too large, adapt the mesh.
      if (err_est_rel_total < ERR_STOP) done = true;
      else 
      {
        info("Adapting the coarse mesh.");
        done = adaptivity->adapt(&selector, THRESHOLD, STRATEGY, MESH_REGULARITY);

        if (Space<double>::get_num_dofs(&space) >= NDOF_STOP) 
          done = true;
        else
          // Increase the counter of performed adaptivity steps.
          as++;
      }
      
      // Clean up.
      delete adaptivity;
      if(!done)
      {
        delete h_time_new.get_space();
        delete h_time_new.get_mesh();
      }
    }
    while (done == false);

    // Add entry to DOF and CPU convergence graphs.
    graph_dof.add_values(current_time, Space<double>::get_num_dofs(&space));
    graph_dof.save("conv_dof_est.dat");
    graph_cpu.add_values(current_time, cpu_time.accumulated());
    graph_cpu.save("conv_cpu_est.dat");

    // Visualize the solution and mesh.
    char title[100];
    sprintf(title, "Solution, time %g", current_time);
    view.set_title(title);
    view.show_mesh(false);
    view.show(&h_time_new);
    sprintf(title, "Mesh, time %g", current_time);
    ordview.set_title(title);
    ordview.show(&space);

    // Copy last reference solution into h_time_prev.
    h_time_prev.copy(&h_time_new);
    delete h_time_new.get_mesh();

    // Increase current time and counter of time steps.
    current_time += time_step;
    ts++;
  }
  while (current_time < T_FINAL);

  // Wait for all views to be closed.
  View::wait();
  return 0;
}
Example #7
0
int main(int argc, char* argv[])
{
  try
  {
    // Sanity check for omega. 
    double K_squared = Hermes::sqr(OMEGA / M_PI) * (OMEGA - 2) / (1 - OMEGA);
    if (K_squared <= 0) throw Hermes::Exceptions::Exception("Wrong choice of omega, K_squared < 0!");
    double K_norm_coeff = std::sqrt(K_squared) / std::sqrt(Hermes::sqr(K_x) + Hermes::sqr(K_y));
    Hermes::Mixins::Loggable::Static::info("Wave number K = %g", std::sqrt(K_squared));
    K_x *= K_norm_coeff;
    K_y *= K_norm_coeff;

    // Wave number.
    double K = std::sqrt(Hermes::sqr(K_x) + Hermes::sqr(K_y));

    // Choose a Butcher's table or define your own.
    ButcherTable bt(butcher_table);
    if (bt.is_explicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage explicit R-K method.", bt.get_size());
    if (bt.is_diagonally_implicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
    if (bt.is_fully_implicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage fully implicit R-K method.", bt.get_size());

    // Load the mesh.
    MeshSharedPtr E_mesh(new Mesh), H_mesh(new Mesh), P_mesh(new Mesh);
    MeshReaderH2D mloader;
    mloader.load("domain.mesh", E_mesh);
    mloader.load("domain.mesh", H_mesh);
    mloader.load("domain.mesh", P_mesh);

    // Perform initial mesh refinemets.
    for (int i = 0; i < INIT_REF_NUM; i++)
    {
      E_mesh->refine_all_elements();
      H_mesh->refine_all_elements();
      P_mesh->refine_all_elements();
    }

    // Initialize solutions.
    double current_time = 0;
    MeshFunctionSharedPtr<double> E_time_prev(new CustomInitialConditionE(E_mesh, current_time, OMEGA, K_x, K_y));
    MeshFunctionSharedPtr<double> H_time_prev(new CustomInitialConditionH(H_mesh, current_time, OMEGA, K_x, K_y));
    MeshFunctionSharedPtr<double> P_time_prev(new CustomInitialConditionP(P_mesh, current_time, OMEGA, K_x, K_y));
    Hermes::vector<MeshFunctionSharedPtr<double> > slns_time_prev(E_time_prev, H_time_prev, P_time_prev);
    MeshFunctionSharedPtr<double> E_time_new(new Solution<double>(E_mesh)), H_time_new(new Solution<double>(H_mesh)), P_time_new(new Solution<double>(P_mesh));
    MeshFunctionSharedPtr<double> E_time_new_coarse(new Solution<double>(E_mesh)), H_time_new_coarse(new Solution<double>(H_mesh)), P_time_new_coarse(new Solution<double>(P_mesh));
    Hermes::vector<MeshFunctionSharedPtr<double> > slns_time_new(E_time_new, H_time_new, P_time_new);

    // Initialize the weak formulation.
    CustomWeakFormMD wf(OMEGA, K_x, K_y, MU_0, EPS_0, EPS_INF, EPS_Q, TAU);

    // Initialize boundary conditions
    DefaultEssentialBCConst<double> bc_essential("Bdy", 0.0);
    EssentialBCs<double> bcs(&bc_essential);

    SpaceSharedPtr<double> E_space(new HcurlSpace<double>(E_mesh, &bcs, P_INIT));
    SpaceSharedPtr<double> H_space(new H1Space<double>(H_mesh, NULL, P_INIT));
    //L2Space<double> H_space(mesh, P_INIT));
    SpaceSharedPtr<double> P_space(new HcurlSpace<double>(P_mesh, &bcs, P_INIT));

    Hermes::vector<SpaceSharedPtr<double> > spaces = Hermes::vector<SpaceSharedPtr<double> >(E_space, H_space, P_space);

    // Initialize views.
    ScalarView E1_view("Solution E1", new WinGeom(0, 0, 400, 350));
    E1_view.fix_scale_width(50);
    ScalarView E2_view("Solution E2", new WinGeom(410, 0, 400, 350));
    E2_view.fix_scale_width(50);
    ScalarView H_view("Solution H", new WinGeom(0, 410, 400, 350));
    H_view.fix_scale_width(50);
    ScalarView P1_view("Solution P1", new WinGeom(410, 410, 400, 350));
    P1_view.fix_scale_width(50);
    ScalarView P2_view("Solution P2", new WinGeom(820, 410, 400, 350));
    P2_view.fix_scale_width(50);

    // Visualize initial conditions.
    char title[100];
    sprintf(title, "E1 - Initial Condition");
    E1_view.set_title(title);
    E1_view.show(E_time_prev, H2D_FN_VAL_0);
    sprintf(title, "E2 - Initial Condition");
    E2_view.set_title(title);
    E2_view.show(E_time_prev, H2D_FN_VAL_1);

    sprintf(title, "H - Initial Condition");
    H_view.set_title(title);
    H_view.show(H_time_prev);

    sprintf(title, "P1 - Initial Condition");
    P1_view.set_title(title);
    P1_view.show(P_time_prev, H2D_FN_VAL_0);
    sprintf(title, "P2 - Initial Condition");
    P2_view.set_title(title);
    P2_view.show(P_time_prev, H2D_FN_VAL_1);

    // Initialize Runge-Kutta time stepping.
    RungeKutta<double> runge_kutta(&wf, spaces, &bt);
    runge_kutta.set_max_allowed_iterations(NEWTON_MAX_ITER);
    runge_kutta.set_tolerance(NEWTON_TOL);
    runge_kutta.set_verbose_output(true);

    // Initialize refinement selector.
    H1ProjBasedSelector<double> H1selector(CAND_LIST);
    HcurlProjBasedSelector<double> HcurlSelector(CAND_LIST);

    // Time stepping loop.
    int ts = 1;
    do
    {
      // Perform one Runge-Kutta time step according to the selected Butcher's table.
      Hermes::Mixins::Loggable::Static::info("\nRunge-Kutta time step (t = %g s, time_step = %g s, stages: %d).",
        current_time, time_step, bt.get_size());

      // Periodic global derefinements.
      if (ts > 1 && ts % UNREF_FREQ == 0 && REFINEMENT_COUNT > 0)
      {
        Hermes::Mixins::Loggable::Static::info("Global mesh derefinement.");
        REFINEMENT_COUNT = 0;

        E_space->unrefine_all_mesh_elements(true);
        H_space->unrefine_all_mesh_elements(true);
        P_space->unrefine_all_mesh_elements(true);

        E_space->adjust_element_order(-1, P_INIT);
        H_space->adjust_element_order(-1, P_INIT);
        P_space->adjust_element_order(-1, P_INIT);

        E_space->assign_dofs();
        H_space->assign_dofs();
        P_space->assign_dofs();
      }

      // Adaptivity loop:
      int as = 1;
      bool done = false;
      do
      {
        Hermes::Mixins::Loggable::Static::info("Adaptivity step %d:", as);

        // Construct globally refined reference mesh and setup reference space.
        int order_increase = 1;

        Mesh::ReferenceMeshCreator refMeshCreatorE(E_mesh);
        Mesh::ReferenceMeshCreator refMeshCreatorH(H_mesh);
        Mesh::ReferenceMeshCreator refMeshCreatorP(P_mesh);
        MeshSharedPtr ref_mesh_E = refMeshCreatorE.create_ref_mesh();
        MeshSharedPtr ref_mesh_H = refMeshCreatorH.create_ref_mesh();
        MeshSharedPtr ref_mesh_P = refMeshCreatorP.create_ref_mesh();

        Space<double>::ReferenceSpaceCreator refSpaceCreatorE(E_space, ref_mesh_E, order_increase);
        SpaceSharedPtr<double> ref_space_E = refSpaceCreatorE.create_ref_space();
        Space<double>::ReferenceSpaceCreator refSpaceCreatorH(H_space, ref_mesh_H, order_increase);
        SpaceSharedPtr<double> ref_space_H = refSpaceCreatorH.create_ref_space();
        Space<double>::ReferenceSpaceCreator refSpaceCreatorP(P_space, ref_mesh_P, order_increase);
        SpaceSharedPtr<double> ref_space_P = refSpaceCreatorP.create_ref_space();
        Hermes::vector<SpaceSharedPtr<double> > ref_spaces(ref_space_E, ref_space_H, ref_space_P);
       
        int ndof = Space<double>::get_num_dofs(ref_spaces);
        Hermes::Mixins::Loggable::Static::info("ndof = %d.", ndof);

        try
        {
          runge_kutta.set_spaces(ref_spaces);
          runge_kutta.set_time(current_time);
          runge_kutta.set_time_step(time_step);
          runge_kutta.rk_time_step_newton(slns_time_prev, slns_time_new);
        }
        catch (Exceptions::Exception& e)
        {
          e.print_msg();
          throw Hermes::Exceptions::Exception("Runge-Kutta time step failed");
        }

        // Visualize the solutions.
        char title[100];
        sprintf(title, "E1, t = %g", current_time + time_step);
        E1_view.set_title(title);
        E1_view.show(E_time_new, H2D_FN_VAL_0);
        sprintf(title, "E2, t = %g", current_time + time_step);
        E2_view.set_title(title);
        E2_view.show(E_time_new, H2D_FN_VAL_1);

        sprintf(title, "H, t = %g", current_time + time_step);
        H_view.set_title(title);
        H_view.show(H_time_new);

        sprintf(title, "P1, t = %g", current_time + time_step);
        P1_view.set_title(title);
        P1_view.show(P_time_new, H2D_FN_VAL_0);
        sprintf(title, "P2, t = %g", current_time + time_step);
        P2_view.set_title(title);
        P2_view.show(P_time_new, H2D_FN_VAL_1);

        // Project the fine mesh solution onto the coarse mesh.
        Hermes::Mixins::Loggable::Static::info("Projecting reference solution on coarse mesh.");
        OGProjection<double> ogProjection; ogProjection.project_global(Hermes::vector<SpaceSharedPtr<double> >(E_space, H_space,
          P_space), Hermes::vector<MeshFunctionSharedPtr<double> >(E_time_new, H_time_new, P_time_new), Hermes::vector<MeshFunctionSharedPtr<double> >(E_time_new_coarse, H_time_new_coarse, P_time_new_coarse));

        // Calculate element errors and total error estimate.
        Hermes::Mixins::Loggable::Static::info("Calculating error estimate.");
        adaptivity.set_spaces(Hermes::vector<SpaceSharedPtr<double> >(E_space, H_space, P_space));
        errorCalculator.calculate_errors(Hermes::vector<MeshFunctionSharedPtr<double> >(E_time_new_coarse, H_time_new_coarse, P_time_new_coarse),
          Hermes::vector<MeshFunctionSharedPtr<double> >(E_time_new, H_time_new, P_time_new));

        double err_est_rel_total = errorCalculator.get_total_error_squared() * 100.;

        // Report results.
        Hermes::Mixins::Loggable::Static::info("Error estimate: %g%%", err_est_rel_total);

        // If err_est too large, adapt the mesh.
        if (err_est_rel_total < ERR_STOP)
        {
          Hermes::Mixins::Loggable::Static::info("Error estimate under the specified threshold -> moving to next time step.");
          done = true;
        }
        else
        {
          Hermes::Mixins::Loggable::Static::info("Adapting coarse mesh.");
          REFINEMENT_COUNT++;
          done = adaptivity.adapt(Hermes::vector<RefinementSelectors::Selector<double> *>(&HcurlSelector, &H1selector, &HcurlSelector));

          if (!done)
            as++;
        }
      } while (!done);

      //View::wait();
      E_time_prev->copy(E_time_new);
      H_time_prev->copy(H_time_new);
      P_time_prev->copy(P_time_new);

      // Update time.
      current_time += time_step;
      ts++;

    } while (current_time < T_FINAL);

    // Wait for the view to be closed.
    View::wait();
  }
  catch (std::exception& e)
  {
    std::cout << e.what();
  }

  return 0;
}
Example #8
0
// Main function.
int main(int argc, char* argv[])
{
  ConstitutiveRelationsGenuchtenWithLayer constitutive_relations(CONSTITUTIVE_TABLE_METHOD, NUM_OF_INSIDE_PTS, LOW_LIMIT, TABLE_PRECISION, TABLE_LIMIT, K_S_vals, ALPHA_vals, N_vals, M_vals, THETA_R_vals, THETA_S_vals, STORATIVITY_vals);

  // Either use exact constitutive relations (slow) (method 0) or precalculate 
  // their linear approximations (faster) (method 1) or
  // precalculate their quintic polynomial approximations (method 2) -- managed by 
  // the following loop "Initializing polynomial approximation".
  if (CONSTITUTIVE_TABLE_METHOD == 1)
    constitutive_relations.constitutive_tables_ready = get_constitutive_tables(1, &constitutive_relations, MATERIAL_COUNT);  // 1 stands for the Newton's method.
  

  // The van Genuchten + Mualem K(h) function is approximated by polynomials close 
  // to zero in case of CONSTITUTIVE_TABLE_METHOD==1.
  // In case of CONSTITUTIVE_TABLE_METHOD==2, all constitutive functions are approximated by polynomials.
  info("Initializing polynomial approximations.");
  for (int i=0; i < MATERIAL_COUNT; i++)
  {
    // Points to be used for polynomial approximation of K(h).
    double* points = new double[NUM_OF_INSIDE_PTS];

    init_polynomials(6 + NUM_OF_INSIDE_PTS, LOW_LIMIT, points, NUM_OF_INSIDE_PTS, i, &constitutive_relations, MATERIAL_COUNT, NUM_OF_INTERVALS, INTERVALS_4_APPROX);
  }
  
  constitutive_relations.polynomials_ready = true;
  if (CONSTITUTIVE_TABLE_METHOD == 2)
  {
    constitutive_relations.constitutive_tables_ready = true;
    //Assign table limit to global definition.
    constitutive_relations.table_limit = INTERVALS_4_APPROX[NUM_OF_INTERVALS-1];
  }
  
  // Choose a Butcher's table or define your own.
  ButcherTable bt(butcher_table_type);
  if (bt.is_explicit()) info("Using a %d-stage explicit R-K method.", bt.get_size());
  if (bt.is_diagonally_implicit()) info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
  if (bt.is_fully_implicit()) info("Using a %d-stage fully implicit R-K method.", bt.get_size());

  // Load the mesh.
  Mesh mesh, basemesh;
  MeshReaderH2D mloader;
  mloader.load(mesh_file, &basemesh);
  
  // Perform initial mesh refinements.
  mesh.copy(&basemesh);
  for(int i = 0; i < INIT_REF_NUM; i++) mesh.refine_all_elements();
  mesh.refine_towards_boundary("Top", INIT_REF_NUM_BDY_TOP);

  // Initialize boundary conditions.
  RichardsEssentialBC bc_essential("Top", H_ELEVATION, PULSE_END_TIME, H_INIT, STARTUP_TIME);
  EssentialBCs<double> bcs(&bc_essential);

  // Create an H1 space with default shapeset.
  H1Space<double> space(&mesh, &bcs, P_INIT);
  int ndof = space.get_num_dofs();
  info("ndof = %d.", ndof);

  // Convert initial condition into a Solution.
  ZeroSolution h_time_prev(&mesh), h_time_new(&mesh), time_error_fn(&mesh);

  // Initialize views.
  ScalarView view("Initial condition", new WinGeom(0, 0, 600, 500));
  view.fix_scale_width(80);

  // Visualize the initial condition.
  view.show(&h_time_prev);

  // Initialize the weak formulation.
  CustomWeakFormRichardsRK wf(&constitutive_relations);

   // Visualize the projection and mesh.
  ScalarView sview("Initial condition", new WinGeom(0, 0, 400, 350));
  sview.fix_scale_width(50);
  sview.show(&h_time_prev, HERMES_EPS_VERYHIGH);
  ScalarView eview("Temporal error", new WinGeom(405, 0, 400, 350));
  eview.fix_scale_width(50);
  eview.show(&time_error_fn, HERMES_EPS_VERYHIGH);
  OrderView oview("Initial mesh", new WinGeom(810, 0, 350, 350));
  oview.show(&space);

  // Graph for time step history.
  SimpleGraph time_step_graph;
  info("Time step history will be saved to file time_step_history.dat.");

  // Initialize Runge-Kutta time stepping.
  RungeKutta<double> runge_kutta(&wf, &space, &bt, matrix_solver);

  // Time stepping:
  double current_time = 0;
  int ts = 1;
  do 
  {
    info("---- Time step %d, time %3.5f s", ts, current_time);

    Space<double>::update_essential_bc_values(&space, current_time);

    // Perform one Runge-Kutta time step according to the selected Butcher's table.
    info("Runge-Kutta time step (t = %g s, time step = %g s, stages: %d).", 
         current_time, time_step, bt.get_size());
    bool freeze_jacobian = false;
    bool block_diagonal_jacobian = false;
    bool verbose = true;
    double damping_coeff = 1.0;
    double max_allowed_residual_norm = 1e10;

    try
    {
      runge_kutta.rk_time_step_newton(current_time, time_step, &h_time_prev, 
          &h_time_new, &time_error_fn, freeze_jacobian, block_diagonal_jacobian, verbose,
          NEWTON_TOL, NEWTON_MAX_ITER, damping_coeff, max_allowed_residual_norm);
    }
    catch(Exceptions::Exception& e)
    {
      info("Runge-Kutta time step failed, decreasing time step size from %g to %g days.", 
           time_step, time_step * time_step_dec);
      time_step *= time_step_dec;
      if (time_step < time_step_min) 
        error("Time step became too small.");
      continue;
    }
    
    // Copy solution for the new time step.
    h_time_prev.copy(&h_time_new);

    // Show error function.
    char title[100];
    sprintf(title, "Temporal error, t = %g", current_time);
    eview.set_title(title);
    eview.show(&time_error_fn, HERMES_EPS_VERYHIGH);

    // Calculate relative time stepping error and decide whether the 
    // time step can be accepted. If not, then the time step size is 
    // reduced and the entire time step repeated. If yes, then another
    // check is run, and if the relative error is very low, time step 
    // is increased.
    double rel_err_time = Global<double>::calc_norm(&time_error_fn, HERMES_H1_NORM) / Global<double>::calc_norm(&h_time_new, HERMES_H1_NORM) * 100;
    info("rel_err_time = %g%%", rel_err_time);
    if (rel_err_time > time_tol_upper) {
      info("rel_err_time above upper limit %g%% -> decreasing time step from %g to %g days and repeating time step.", 
           time_tol_upper, time_step, time_step * time_step_dec);
      time_step *= time_step_dec;
      continue;
    }
    if (rel_err_time < time_tol_lower) {
      info("rel_err_time = below lower limit %g%% -> increasing time step from %g to %g days", 
           time_tol_lower, time_step, time_step * time_step_inc);
      time_step *= time_step_inc;
    }

    // Add entry to the timestep graph.
    time_step_graph.add_values(current_time, time_step);
    time_step_graph.save("time_step_history.dat");

    // Update time.
    current_time += time_step;

    // Show the new time level solution.
    sprintf(title, "Solution, t = %g", current_time);
    sview.set_title(title);
    sview.show(&h_time_new, HERMES_EPS_VERYHIGH);
    oview.show(&space);

    // Save complete Solution.
    char filename[100];
    sprintf(filename, "outputs/tsln_%f.dat", current_time);
    h_time_new.save(filename);
    info("Solution at time %g saved to file %s.", current_time, filename);

    // Save solution for the next time step.
    h_time_prev.copy(&h_time_new);

    // Increase time step counter.
    ts++;
  } 
  while (current_time < T_FINAL);

  // Wait for the view to be closed.
  View::wait();
  return 0;
}
Example #9
0
int main(int argc, char* argv[])
{
  // Choose a Butcher's table or define your own.
  ButcherTable bt(butcher_table_type);
  if (bt.is_explicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage explicit R-K method.", bt.get_size());
  if (bt.is_diagonally_implicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
  if (bt.is_fully_implicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage fully implicit R-K method.", bt.get_size());

  // Turn off adaptive time stepping if R-K method is not embedded.
  if (bt.is_embedded() == false && ADAPTIVE_TIME_STEP_ON == true) {
    Hermes::Mixins::Loggable::Static::warn("R-K method not embedded, turning off adaptive time stepping.");
    ADAPTIVE_TIME_STEP_ON = false;
  }

  // Load the mesh.
  MeshSharedPtr mesh(new Mesh), basemesh(new Mesh);
  MeshReaderH2D mloader;
  mloader.load("square.mesh", basemesh);
  mesh->copy(basemesh);

  // Initial mesh refinements.
  for(int i = 0; i < INIT_REF_NUM; i++) mesh->refine_all_elements();

  // Convert initial condition into a Solution<complex>.
  MeshFunctionSharedPtr<complex> psi_time_prev(new CustomInitialCondition(mesh));

  // Initialize the weak formulation.
  double current_time = 0;

  // Initialize weak formulation.
  CustomWeakFormGPRK wf(h, m, g, omega);

  // Initialize boundary conditions.
  DefaultEssentialBCConst<complex> bc_essential("Bdy", 0.0);
  EssentialBCs<complex> bcs(&bc_essential);

  // Create an H1 space with default shapeset.
  SpaceSharedPtr<complex> space(new H1Space<complex> (mesh, &bcs, P_INIT));
  int ndof = space->get_num_dofs();
  Hermes::Mixins::Loggable::Static::info("ndof = %d", ndof);

  // Initialize the FE problem.
  DiscreteProblem<complex> dp(&wf, space);

  // Create a refinement selector.
  H1ProjBasedSelector<complex> selector(CAND_LIST);

  // Visualize initial condition.
  char title[100];

  ScalarView sview_real("Initial condition - real part", new WinGeom(0, 0, 600, 500));
  ScalarView sview_imag("Initial condition - imaginary part", new WinGeom(610, 0, 600, 500));

  sview_real.show_mesh(false);
  sview_imag.show_mesh(false);
  sview_real.fix_scale_width(50);
  sview_imag.fix_scale_width(50);
  OrderView ord_view("Initial mesh", new WinGeom(445, 0, 440, 350));
  ord_view.fix_scale_width(50);
  ScalarView time_error_view("Temporal error", new WinGeom(0, 400, 440, 350));
  time_error_view.fix_scale_width(50);
  time_error_view.fix_scale_width(60);
  ScalarView space_error_view("Spatial error", new WinGeom(445, 400, 440, 350));
  space_error_view.fix_scale_width(50);
  MeshFunctionSharedPtr<double> real(new RealFilter(psi_time_prev));

  MeshFunctionSharedPtr<double> imag(new ImagFilter(psi_time_prev));

  sview_real.show(real);
  sview_imag.show(imag);
  ord_view.show(space);

  // Graph for time step history.
  SimpleGraph time_step_graph;
  if (ADAPTIVE_TIME_STEP_ON) Hermes::Mixins::Loggable::Static::info("Time step history will be saved to file time_step_history.dat.");

  // Time stepping:
  int num_time_steps = (int)(T_FINAL/time_step + 0.5);
  for(int ts = 1; ts <= num_time_steps; ts++)
    // Time stepping loop.
      double current_time = 0.0; int ts = 1;
  do 
  {
    Hermes::Mixins::Loggable::Static::info("Begin time step %d.", ts);
    // Periodic global derefinement.
    if (ts > 1 && ts % UNREF_FREQ == 0) 
    {
      Hermes::Mixins::Loggable::Static::info("Global mesh derefinement.");
      switch (UNREF_METHOD) {
      case 1: mesh->copy(basemesh);
        space->set_uniform_order(P_INIT);
        break;
      case 2: space->unrefine_all_mesh_elements();
        space->set_uniform_order(P_INIT);
        break;
      case 3: space->unrefine_all_mesh_elements();
        space->adjust_element_order(-1, -1, P_INIT, P_INIT);
        break;
      default: throw Hermes::Exceptions::Exception("Wrong global derefinement method.");
      }

      ndof = Space<complex>::get_num_dofs(space);
    }
    Hermes::Mixins::Loggable::Static::info("ndof: %d", ndof);

    // Spatial adaptivity loop. Note: psi_time_prev must not be 
    // changed during spatial adaptivity. 
    MeshFunctionSharedPtr<complex> ref_sln(new Solution<complex>());
    MeshFunctionSharedPtr<complex> time_error_fn(new Solution<complex>);
    bool done = false;
    int as = 1;
    double err_est;
    do {
      // Construct globally refined reference mesh and setup reference space.
      Mesh::ReferenceMeshCreator refMeshCreator(mesh);
      MeshSharedPtr ref_mesh = refMeshCreator.create_ref_mesh();

      Space<complex>::ReferenceSpaceCreator refSpaceCreator(space, ref_mesh);
      SpaceSharedPtr<complex> ref_space = refSpaceCreator.create_ref_space();

      // Initialize discrete problem on reference mesh.
      DiscreteProblem<complex>* ref_dp = new DiscreteProblem<complex>(&wf, ref_space);

      RungeKutta<complex> runge_kutta(&wf, ref_space, &bt);

      // Runge-Kutta step on the fine mesh->
      Hermes::Mixins::Loggable::Static::info("Runge-Kutta time step on fine mesh (t = %g s, time step = %g s, stages: %d).", 
        current_time, time_step, bt.get_size());
      bool verbose = true;

      try
      {
        runge_kutta.set_time(current_time);
        runge_kutta.set_time_step(time_step);
        runge_kutta.set_max_allowed_iterations(NEWTON_MAX_ITER);
        runge_kutta.set_tolerance(NEWTON_TOL_FINE);
        runge_kutta.rk_time_step_newton(psi_time_prev, ref_sln, time_error_fn);
      }
      catch(Exceptions::Exception& e)
      {
        e.print_msg();
        throw Hermes::Exceptions::Exception("Runge-Kutta time step failed");
      }

      /* If ADAPTIVE_TIME_STEP_ON == true, estimate temporal error. 
      If too large or too small, then adjust it and restart the time step. */

      double rel_err_time = 0;
      if (bt.is_embedded() == true) {
        Hermes::Mixins::Loggable::Static::info("Calculating temporal error estimate.");

        // Show temporal error.
        char title[100];
        sprintf(title, "Temporal error est, spatial adaptivity step %d", as);     
        time_error_view.set_title(title);
        time_error_view.show_mesh(false);
        MeshFunctionSharedPtr<double> abs_time(new RealFilter(time_error_fn));

        MeshFunctionSharedPtr<double> abs_tef(new AbsFilter(abs_time));

        time_error_view.show(abs_tef);

        rel_err_time = Global<complex>::calc_norm(time_error_fn.get(), HERMES_H1_NORM) / 
          Global<complex>::calc_norm(ref_sln.get(), HERMES_H1_NORM) * 100;
        if (ADAPTIVE_TIME_STEP_ON == false) Hermes::Mixins::Loggable::Static::info("rel_err_time: %g%%", rel_err_time);
      }

      if (ADAPTIVE_TIME_STEP_ON) {
        if (rel_err_time > TIME_ERR_TOL_UPPER) {
          Hermes::Mixins::Loggable::Static::info("rel_err_time %g%% is above upper limit %g%%", rel_err_time, TIME_ERR_TOL_UPPER);
          Hermes::Mixins::Loggable::Static::info("Decreasing time step from %g to %g s and restarting time step.", 
            time_step, time_step * TIME_STEP_DEC_RATIO);
          time_step *= TIME_STEP_DEC_RATIO;

          delete ref_dp;
          continue;
        }
        else if (rel_err_time < TIME_ERR_TOL_LOWER) {
          Hermes::Mixins::Loggable::Static::info("rel_err_time = %g%% is below lower limit %g%%", rel_err_time, TIME_ERR_TOL_LOWER);
          Hermes::Mixins::Loggable::Static::info("Increasing time step from %g to %g s.", time_step, time_step * TIME_STEP_INC_RATIO);
          time_step *= TIME_STEP_INC_RATIO;

          delete ref_dp;
          continue;
        }
        else {
          Hermes::Mixins::Loggable::Static::info("rel_err_time = %g%% is in acceptable interval (%g%%, %g%%)", 
            rel_err_time, TIME_ERR_TOL_LOWER, TIME_ERR_TOL_UPPER);
        }

        // Add entry to time step history graph.
        time_step_graph.add_values(current_time, time_step);
        time_step_graph.save("time_step_history.dat");
      }

      /* Estimate spatial errors and perform mesh refinement */

      Hermes::Mixins::Loggable::Static::info("Spatial adaptivity step %d.", as);

      // Project the fine mesh solution onto the coarse mesh.
      MeshFunctionSharedPtr<complex> sln(new Solution<complex>);
      Hermes::Mixins::Loggable::Static::info("Projecting fine mesh solution on coarse mesh for error estimation.");
      OGProjection<complex> ogProjection; ogProjection.project_global(space, ref_sln, sln); 

      // Show spatial error.
      sprintf(title, "Spatial error est, spatial adaptivity step %d", as);  
      MeshFunctionSharedPtr<complex> space_error_fn(new DiffFilter<complex>(Hermes::vector<MeshFunctionSharedPtr<complex> >(ref_sln, sln)));

      space_error_view.set_title(title);
      space_error_view.show_mesh(false);

      MeshFunctionSharedPtr<double> abs_space(new RealFilter(space_error_fn));
      MeshFunctionSharedPtr<double> abs_sef(new AbsFilter(abs_space));

      space_error_view.show(abs_sef);

      // Calculate element errors and spatial error estimate.
      Hermes::Mixins::Loggable::Static::info("Calculating spatial error estimate.");
      Adapt<complex> adaptivity(space);
      double err_rel_space = errorCalculator.get_total_error_squared() * 100;

      // Report results.
      Hermes::Mixins::Loggable::Static::info("ndof: %d, ref_ndof: %d, err_rel_space: %g%%", 
        Space<complex>::get_num_dofs(space), Space<complex>::get_num_dofs(ref_space), err_rel_space);

      // If err_est too large, adapt the mesh.
      if (err_rel_space < SPACE_ERR_TOL) done = true;
      else 
      {
        Hermes::Mixins::Loggable::Static::info("Adapting the coarse mesh.");
        done = adaptivity.adapt(&selector);

        // Increase the counter of performed adaptivity steps.
        as++;
      }

      // Clean up.
      
      delete ref_dp;
    }
    while (done == false);

    // Visualize the solution and mesh->
    char title[100];
    sprintf(title, "Solution - real part, Time %3.2f s", current_time);
    sview_real.set_title(title);
    sprintf(title, "Solution - imaginary part, Time %3.2f s", current_time);
    sview_imag.set_title(title);
    MeshFunctionSharedPtr<double> real(new RealFilter(ref_sln));
    MeshFunctionSharedPtr<double> imag(new ImagFilter(ref_sln));
    sview_real.show(real);
    sview_imag.show(imag);
    sprintf(title, "Mesh, time %g s", current_time);
    ord_view.set_title(title);
    ord_view.show(space);

    // Copy last reference solution into psi_time_prev.
    psi_time_prev->copy(ref_sln);

    // Increase current time and counter of time steps.
    current_time += time_step;
    ts++;
  }
  while (current_time < T_FINAL);

  // Wait for all views to be closed.
  View::wait();
  return 0;
}
Example #10
0
int main(int argc, char* argv[])
{
  // Choose a Butcher's table or define your own.
  ButcherTable bt(butcher_table_type);
  if (bt.is_explicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage explicit R-K method.", bt.get_size());
  if (bt.is_diagonally_implicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
  if (bt.is_fully_implicit()) Hermes::Mixins::Loggable::Static::info("Using a %d-stage fully implicit R-K method.", bt.get_size());

  // Load the mesh.
  Mesh mesh;
  MeshReaderH2D mloader;
  mloader.load("square.mesh", &mesh);

  // Initial mesh refinements.
  for(int i = 0; i < INIT_REF_NUM; i++) mesh.refine_all_elements();

  // Convert initial condition into a Solution<std::complex<double> >.
  CustomInitialCondition psi_time_prev(&mesh);
  Solution<std::complex<double> > psi_time_new(&mesh);

  // Initialize the weak formulation.
  double current_time = 0;

  CustomWeakFormGPRK wf(h, m, g, omega);
  
  // Initialize boundary conditions.
  DefaultEssentialBCConst<std::complex<double> > bc_essential("Bdy", 0.0);
  EssentialBCs<std::complex<double> > bcs(&bc_essential);

  // Create an H1 space with default shapeset.
  H1Space<std::complex<double> > space(&mesh, &bcs, P_INIT);
  int ndof = space.get_num_dofs();
  Hermes::Mixins::Loggable::Static::info("ndof = %d", ndof);
 
  // Initialize the FE problem.
  DiscreteProblem<std::complex<double> > dp(&wf, &space);

  // Initialize views.
  ScalarView sview_real("Solution - real part", new WinGeom(0, 0, 600, 500));
  ScalarView sview_imag("Solution - imaginary part", new WinGeom(610, 0, 600, 500));
  sview_real.fix_scale_width(80);
  sview_imag.fix_scale_width(80);

  // Initialize Runge-Kutta time stepping.
  RungeKutta<std::complex<double> > runge_kutta(&wf, &space, &bt);
  
  // Time stepping:
  int ts = 1;
  int nstep = (int)(T_FINAL/time_step + 0.5);
  for(int ts = 1; ts <= nstep; ts++)
  {
    // Perform one Runge-Kutta time step according to the selected Butcher's table.
    Hermes::Mixins::Loggable::Static::info("Runge-Kutta time step (t = %g s, time step = %g s, stages: %d).", 
         current_time, time_step, bt.get_size());
    
    try
    {
      runge_kutta.setTime(current_time);
      runge_kutta.setTimeStep(time_step);
      runge_kutta.rk_time_step_newton(&psi_time_prev, &psi_time_new);
    }
    catch(Exceptions::Exception& e)
    {
      e.printMsg();
      throw Hermes::Exceptions::Exception("Runge-Kutta time step failed");
    }

    // Show the new time level solution.
    char title[100];
    sprintf(title, "Solution - real part, Time %3.2f s", current_time);
    sview_real.set_title(title);
    sprintf(title, "Solution - imaginary part, Time %3.2f s", current_time);
    sview_imag.set_title(title);
    RealFilter real(&psi_time_new);
    ImagFilter imag(&psi_time_new);
    sview_real.show(&real);
    sview_imag.show(&imag);

    // Copy solution for the new time step.
    psi_time_prev.copy(&psi_time_new);

    // Increase current time and time step counter.
    current_time += time_step;
    ts++;
  }

  // Wait for the view to be closed.
  View::wait();
  return 0;
}
Example #11
0
int main(int argc, char* argv[])
{
  // Choose a Butcher's table or define your own.
  ButcherTable bt(butcher_table_type);
  if (bt.is_explicit()) info("Using a %d-stage explicit R-K method.", bt.get_size());
  if (bt.is_diagonally_implicit()) info("Using a %d-stage diagonally implicit R-K method.", bt.get_size());
  if (bt.is_fully_implicit()) info("Using a %d-stage fully implicit R-K method.", bt.get_size());

  // Load the mesh.
  Mesh mesh;
  MeshReaderH2D mloader;
  mloader.load("wall.mesh", &mesh);

  // Perform initial mesh refinements.
  for(int i = 0; i < INIT_REF_NUM; i++) mesh.refine_all_elements();
  mesh.refine_towards_boundary(BDY_RIGHT, 2);
  mesh.refine_towards_boundary(BDY_FIRE, INIT_REF_NUM_BDY);

  // Initialize essential boundary conditions (none).
  EssentialBCs<double> bcs;

  // Initialize an H1 space with default shapeset.
  H1Space<double> space(&mesh, &bcs, P_INIT);
  int ndof = Space<double>::get_num_dofs(&space);
  info("ndof = %d.", ndof);
 
  // Previous and next time level solutions.
  ConstantSolution<double> sln_time_prev(&mesh, TEMP_INIT);
  ZeroSolution sln_time_new(&mesh);
  ConstantSolution<double> time_error_fn(&mesh, 0.0);

  // Initialize the weak formulation.
  double current_time = 0;
  CustomWeakFormHeatRK wf(BDY_FIRE, BDY_AIR, ALPHA_FIRE, ALPHA_AIR,
                          RHO, HEATCAP, TEMP_EXT_AIR, TEMP_INIT, &current_time);

  // Initialize the FE problem.
  DiscreteProblem<double> dp(&wf, &space);

  // Initialize views.
  ScalarView Tview("Temperature", new WinGeom(0, 0, 1500, 400));
  Tview.fix_scale_width(40);
  ScalarView eview("Temporal error", new WinGeom(0, 450, 1500, 400));
  eview.fix_scale_width(40);

  // Graph for time step history.
  SimpleGraph time_step_graph;
  info("Time step history will be saved to file time_step_history.dat.");

  // Initialize Runge-Kutta time stepping.
  RungeKutta<double> runge_kutta(&dp, &bt, matrix_solver_type);

  // Time stepping loop:
  int ts = 1;
  do 
  {
    // Perform one Runge-Kutta time step according to the selected Butcher's table.
    info("Runge-Kutta time step (t = %g s, tau = %g s, stages: %d).", 
         current_time, time_step, bt.get_size());
    bool jacobian_changed = false;
    bool verbose = true;

    try
    {
      runge_kutta.rk_time_step_newton(current_time, time_step, &sln_time_prev, &sln_time_new, 
                                  &time_error_fn, !jacobian_changed, false, verbose);
    }
    catch(Exceptions::Exception& e)
    {
      e.printMsg();
      error("Runge-Kutta time step failed");
    }

    // Plot error function.
    char title[100];
    sprintf(title, "Temporal error, t = %g", current_time);
    eview.set_title(title);
    AbsFilter abs_tef(&time_error_fn);
    eview.show(&abs_tef, HERMES_EPS_VERYHIGH);

    // Calculate relative time stepping error and decide whether the 
    // time step can be accepted. If not, then the time step size is 
    // reduced and the entire time step repeated. If yes, then another
    // check is run, and if the relative error is very low, time step 
    // is increased.
    double rel_err_time = Global<double>::calc_norm(&time_error_fn, HERMES_H1_NORM) 
                          / Global<double>::calc_norm(&sln_time_new, HERMES_H1_NORM) * 100;
    info("rel_err_time = %g%%", rel_err_time);
    if (rel_err_time > TIME_TOL_UPPER) {
      info("rel_err_time above upper limit %g%% -> decreasing time step from %g to %g and restarting time step.", 
           TIME_TOL_UPPER, time_step, time_step * TIME_STEP_DEC_RATIO);
      time_step *= TIME_STEP_DEC_RATIO;
      continue;
    }
    if (rel_err_time < TIME_TOL_LOWER) {
      info("rel_err_time = below lower limit %g%% -> increasing time step from %g to %g", 
           TIME_TOL_UPPER, time_step, time_step * TIME_STEP_INC_RATIO);
      time_step *= TIME_STEP_INC_RATIO;
    }

    // Add entry to the timestep graph.
    time_step_graph.add_values(current_time, time_step);
    time_step_graph.save("time_step_history.dat");

    // Update time.
    current_time += time_step;

    // Show the new time level solution.
    sprintf(title, "Time %3.2f s", current_time);
    Tview.set_title(title);
    Tview.show(&sln_time_new);

    // Copy solution for the new time step.
    sln_time_prev.copy(&sln_time_new);

    // Increase counter of time steps.
    ts++;
  } 
  while (current_time < T_FINAL);

  // Wait for the view to be closed.
  View::wait();
  return 0;
}