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; }
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; }
// 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; }