Hermes::vector<Space<Scalar>*> CalculationContinuity<Scalar>::Record::load_spaces(Hermes::vector<Mesh*> meshes, Hermes::vector<Shapeset*> shapesets) { Hermes::vector<Space<Scalar>*> spaces; if(shapesets == Hermes::vector<Shapeset*>()) for(unsigned int i = 0; i < meshes.size(); i++) shapesets.push_back(NULL); for(unsigned int i = 0; i < meshes.size(); i++) { std::stringstream filename; filename << CalculationContinuity<Scalar>::space_file_name << i << '_' << (std::string)"t = " << this->time << (std::string)"n = " << this->number << (std::string)".h2d"; try { spaces.push_back(Space<Scalar>::load(filename.str().c_str(), meshes[i], false, NULL, shapesets[i])); } catch(Hermes::Exceptions::SpaceLoadFailureException& e) { throw IOCalculationContinuityException(CalculationContinuityException::spaces, IOCalculationContinuityException::input, filename.str().c_str(), e.what()); } catch(std::exception& e) { throw IOCalculationContinuityException(CalculationContinuityException::spaces, IOCalculationContinuityException::input, filename.str().c_str(), e.what()); } } }
Adapt<Scalar>::Adapt(Hermes::vector<Space<Scalar>*> spaces, Hermes::vector<ProjNormType> proj_norms) : spaces(spaces), num_act_elems(-1), have_errors(false), have_coarse_solutions(false), have_reference_solutions(false) { // sanity check if (proj_norms.size() > 0 && spaces.size() != proj_norms.size()) error("Mismatched numbers of spaces and projection types in Adapt<Scalar>::Adapt()."); this->num = spaces.size(); // sanity checks error_if(this->num <= 0, "Too few components (%d), only %d supported.", this->num, H2D_MAX_COMPONENTS); error_if(this->num > H2D_MAX_COMPONENTS, "Too many components (%d), only %d supported.", this->num, H2D_MAX_COMPONENTS); // reset values memset(errors, 0, sizeof(errors)); memset(sln, 0, sizeof(sln)); memset(rsln, 0, sizeof(rsln)); // if norms were not set by the user, set them to defaults // according to spaces if (proj_norms.size() == 0) { for (int i = 0; i < this->num; i++) { switch (spaces[i]->get_type()) { case HERMES_H1_SPACE: proj_norms.push_back(HERMES_H1_NORM); break; case HERMES_HCURL_SPACE: proj_norms.push_back(HERMES_HCURL_NORM); break; case HERMES_HDIV_SPACE: proj_norms.push_back(HERMES_HDIV_NORM); break; case HERMES_L2_SPACE: proj_norms.push_back(HERMES_L2_NORM); break; default: error("Unknown space type in Adapt<Scalar>::Adapt()."); } } } // assign norm weak forms according to norms selection for (int i = 0; i < this->num; i++) for (int j = 0; j < this->num; j++) { error_form[i][j] = NULL; norm_form[i][j] = NULL; } for (int i = 0; i < this->num; i++) { error_form[i][i] = new MatrixFormVolError(proj_norms[i]); norm_form[i][i] = error_form[i][i]; } }
MeshFunction<double>* FilterVectorPotential::clone() { Hermes::vector<MeshFunction<double>*> fns; Hermes::vector<int> items; for(int i = 0; i < this->num; i++) { fns.push_back(this->sln[i]->clone()); items.push_back(item[i]); } return new FilterVectorPotential(fns, items); }
// Set Dirichlet boundary values. void ModuleBasic::set_dirichlet_values(const std::vector<int> &bdy_markers_dirichlet, const std::vector<double> &bdy_values_dirichlet) { Hermes::vector<int> tm; for (unsigned int i = 0; i < bdy_markers_dirichlet.size(); i++) { tm.push_back(bdy_markers_dirichlet[i]); } Hermes::vector<double> tv; for (unsigned int i = 0; i < bdy_values_dirichlet.size(); i++) { tv.push_back(bdy_values_dirichlet[i]); } if (tm.size() != tv.size()) error("Mismatched numbers of Dirichlet boundary markers and values."); for (unsigned int i = 0; i < tm.size(); i++) this->bc_values.add_const(tm[i], tv[i]); }
void SimpleFilter::precalculate(int order, int mask) { if (mask & (H2D_FN_DX | H2D_FN_DY | H2D_FN_DXX | H2D_FN_DYY | H2D_FN_DXY)) error("Filter not defined for derivatives."); Quad2D* quad = quads[cur_quad]; int np = quad->get_num_points(order); Node* node = new_node(H2D_FN_VAL, np); // precalculate all solutions for (int i = 0; i < num; i++) sln[i]->set_quad_order(order, item[i]); for (int j = 0; j < num_components; j++) { // obtain corresponding tables scalar* tab[10]; for (int i = 0; i < num; i++) { int a = 0, b = 0, mask = item[i]; if (mask >= 0x40) { a = 1; mask >>= 6; } while (!(mask & 1)) { mask >>= 1; b++; } tab[i] = sln[i]->get_values(num_components == 1 ? a : j, b); if (tab[i] == NULL) error("Value of 'item%d' is incorrect in filter definition.", i+1); } Hermes::vector<scalar*> values; for(int i = 0; i < this->num; i++) values.push_back(tab[i]); // apply the filter filter_fn(np, values, node->values[j][0]); }
MeshFunction<double>* FilterFluxDensity::clone() { Hermes::vector<MeshFunction<double>*> fns; for(int i = 0; i < this->num; i++) fns.push_back(this->sln[i]->clone()); return new FilterFluxDensity(fns); }
void SimpleFilter<Scalar>::precalculate(int order, int mask) { if(mask & (H2D_FN_DX | H2D_FN_DY | H2D_FN_DXX | H2D_FN_DYY | H2D_FN_DXY)) throw Hermes::Exceptions::Exception("Filter not defined for derivatives."); Quad2D* quad = this->quads[this->cur_quad]; int np = quad->get_num_points(order, this->element->get_mode()); struct Function<Scalar>::Node* node = this->new_node(H2D_FN_VAL, np); // precalculate all solutions for (int i = 0; i < this->num; i++) this->sln[i]->set_quad_order(order, item[i]); for (int j = 0; j < this->num_components; j++) { // obtain corresponding tables Scalar* tab[H2D_MAX_COMPONENTS]; for (int i = 0; i < this->num; i++) { int a = 0, b = 0, mask = item[i]; if(mask >= 0x40) { a = 1; mask >>= 6; } while (!(mask & 1)) { mask >>= 1; b++; } tab[i] = this->sln[i]->get_values(this->num_components == 1 ? a : j, b); if(tab[i] == nullptr) throw Hermes::Exceptions::Exception("Value of 'item%d' is incorrect in filter definition.", i + 1); } Hermes::vector<Scalar*> values; for(int i = 0; i < this->num; i++) values.push_back(tab[i]); // apply the filter filter_fn(np, values, node->values[j][0]); }
void Continuity<Scalar>::Record::load_spaces(Hermes::vector<Space<Scalar>*> spaces, Hermes::vector<SpaceType> space_types, Hermes::vector<Mesh*> meshes, Hermes::vector<Shapeset*> shapesets) { if(shapesets == Hermes::vector<Shapeset*>()) for(unsigned int i = 0; i < spaces.size(); i++) shapesets.push_back(NULL); for(unsigned int i = 0; i < spaces.size(); i++) { std::stringstream filename; filename << Continuity<Scalar>::spaceFileName << i << '_' << (std::string)"t = " << this->time << (std::string)"n = " << this->number << (std::string)".h2d"; spaces[i]->free(); switch(space_types[i]) { case HERMES_H1_SPACE: dynamic_cast<H1Space<Scalar>*>(spaces[i])->load(filename.str().c_str(), meshes[i], shapesets[i]); break; case HERMES_HCURL_SPACE: dynamic_cast<HcurlSpace<Scalar>*>(spaces[i])->load(filename.str().c_str(), meshes[i], shapesets[i]); break; case HERMES_HDIV_SPACE: dynamic_cast<HdivSpace<Scalar>*>(spaces[i])->load(filename.str().c_str(), meshes[i], shapesets[i]); break; case HERMES_L2_SPACE: dynamic_cast<L2Space<Scalar>*>(spaces[i])->load(filename.str().c_str(), meshes[i], shapesets[i]); break; } } }
// Set material markers, and check compatibility with mesh file. void ModuleBasic::set_material_markers(const std::vector<int> &m_markers) { this->mat_markers = m_markers; // FIXME: these global arrays need to be removed. for (unsigned int i = 0; i < m_markers.size(); i++) { _global_mat_markers.push_back(m_markers[i]);; } }
// Set Dirichlet boundary markers. void ModuleBasic::set_dirichlet_markers(const std::vector<int> &bdy_markers_dirichlet) { //this->bdy_markers_dirichlet = bdy_markers_dirichlet; Hermes::vector<int> t; for (unsigned int i = 0; i < bdy_markers_dirichlet.size(); i++) { t.push_back(bdy_markers_dirichlet[i]); } this->bc_types.add_bc_dirichlet(t); }
// Set Newton boundary markers. void ModuleBasic::set_newton_markers(const std::vector<int> &bdy_markers_newton) { this->bdy_markers_newton = bdy_markers_newton; Hermes::vector<int> t; for (unsigned int i = 0; i < bdy_markers_newton.size(); i++) { t.push_back(bdy_markers_newton[i]); } this->bc_types.add_bc_newton(t); }
void WeakForm<Scalar>::cloneMembers(const WeakForm<Scalar>* otherWf) { this->mfvol.clear(); this->mfsurf.clear(); this->mfDG.clear(); this->vfvol.clear(); this->vfsurf.clear(); this->vfDG.clear(); this->forms.clear(); this->ext.clear(); for(unsigned int i = 0; i < otherWf->forms.size(); i++) { if(dynamic_cast<MatrixFormVol<Scalar>*>(otherWf->forms[i]) != NULL) this->forms.push_back((dynamic_cast<MatrixFormVol<Scalar>*>(otherWf->forms[i]))->clone()); if(dynamic_cast<MatrixFormSurf<Scalar>*>(otherWf->forms[i]) != NULL) this->forms.push_back((dynamic_cast<MatrixFormSurf<Scalar>*>(otherWf->forms[i]))->clone()); if(dynamic_cast<MatrixFormDG<Scalar>*>(otherWf->forms[i]) != NULL) this->forms.push_back((dynamic_cast<MatrixFormDG<Scalar>*>(otherWf->forms[i]))->clone()); if(dynamic_cast<VectorFormVol<Scalar>*>(otherWf->forms[i]) != NULL) this->forms.push_back((dynamic_cast<VectorFormVol<Scalar>*>(otherWf->forms[i]))->clone()); if(dynamic_cast<VectorFormSurf<Scalar>*>(otherWf->forms[i]) != NULL) this->forms.push_back((dynamic_cast<VectorFormSurf<Scalar>*>(otherWf->forms[i]))->clone()); if(dynamic_cast<VectorFormDG<Scalar>*>(otherWf->forms[i]) != NULL) this->forms.push_back((dynamic_cast<VectorFormDG<Scalar>*>(otherWf->forms[i]))->clone()); Hermes::vector<MeshFunction<Scalar>*> newExt; for(unsigned int ext_i = 0; ext_i < otherWf->forms[i]->ext.size(); ext_i++) newExt.push_back(otherWf->forms[i]->ext[ext_i]->clone()); this->forms.back()->set_ext(newExt); this->forms.back()->wf = this; if(dynamic_cast<MatrixFormVol<Scalar>*>(otherWf->forms[i]) != NULL) this->mfvol.push_back(dynamic_cast<MatrixFormVol<Scalar>*>(this->forms.back())); if(dynamic_cast<MatrixFormSurf<Scalar>*>(otherWf->forms[i]) != NULL) this->mfsurf.push_back(dynamic_cast<MatrixFormSurf<Scalar>*>(this->forms.back())); if(dynamic_cast<MatrixFormDG<Scalar>*>(otherWf->forms[i]) != NULL) this->mfDG.push_back(dynamic_cast<MatrixFormDG<Scalar>*>(this->forms.back())); if(dynamic_cast<VectorFormVol<Scalar>*>(otherWf->forms[i]) != NULL) this->vfvol.push_back(dynamic_cast<VectorFormVol<Scalar>*>(this->forms.back())); if(dynamic_cast<VectorFormSurf<Scalar>*>(otherWf->forms[i]) != NULL) this->vfsurf.push_back(dynamic_cast<VectorFormSurf<Scalar>*>(this->forms.back())); if(dynamic_cast<VectorFormDG<Scalar>*>(otherWf->forms[i]) != NULL) this->vfDG.push_back(dynamic_cast<VectorFormDG<Scalar>*>(this->forms.back())); } for(unsigned int i = 0; i < otherWf->ext.size(); i++) { this->ext.push_back(otherWf->ext[i]->clone()); if(dynamic_cast<Solution<Scalar>*>(otherWf->ext[i]) != NULL) { dynamic_cast<Solution<Scalar>*>(this->ext.back())->set_type(dynamic_cast<Solution<Scalar>*>(otherWf->ext[i])->get_type()); } } }
// Calculate norm of a (possibly vector-valued) solution. // Take norm from spaces where these solutions belong. double calc_norms(Hermes::vector<Solution*> slns) { // Calculate norms for all solutions. Hermes::vector<double> norms; int n = slns.size(); for (int i=0; i<n; i++) { switch (slns[i]->get_space_type()) { case HERMES_H1_SPACE: norms.push_back(calc_norm(slns[i], HERMES_H1_NORM)); break; case HERMES_HCURL_SPACE: norms.push_back(calc_norm(slns[i], HERMES_HCURL_NORM)); break; case HERMES_HDIV_SPACE: norms.push_back(calc_norm(slns[i], HERMES_HDIV_NORM)); break; case HERMES_L2_SPACE: norms.push_back(calc_norm(slns[i], HERMES_L2_NORM)); break; default: error("Internal in calc_norms(): unknown space type."); } } // Calculate the resulting norm. double result = 0; for (int i=0; i<n; i++) result += norms[i]*norms[i]; return sqrt(result); }
Hermes::vector<Cand> create_candidates(Element* e, int quad_order) { Hermes::vector<Cand> candidates; // Get the current order range. int current_min_order, current_max_order; this->get_current_order_range(e, current_min_order, current_max_order); int order_h = H2D_GET_H_ORDER(quad_order), order_v = H2D_GET_V_ORDER(quad_order); if(current_max_order < std::max(order_h, order_v)) current_max_order = std::max(order_h, order_v); int last_order_h = std::min(current_max_order, order_h + 1), last_order_v = std::min(current_max_order, order_v + 1); int last_order = H2D_MAKE_QUAD_ORDER(last_order_h, last_order_v); switch(strategy) { case(hORpSelectionBasedOnDOFs): { candidates.push_back(Cand(H2D_REFINEMENT_P, quad_order)); } case(hXORpSelectionBasedOnError): { candidates.push_back(Cand(H2D_REFINEMENT_P, last_order)); candidates.push_back(Cand(H2D_REFINEMENT_H, quad_order, quad_order, quad_order, quad_order)); return candidates; } break; case(isoHPSelectionBasedOnDOFs): { this->cand_list = H2D_HP_ISO; return H1ProjBasedSelector<complex>::create_candidates(e, quad_order); } break; case(anisoHPSelectionBasedOnDOFs): { this->cand_list = H2D_HP_ANISO; return H1ProjBasedSelector<complex>::create_candidates(e, quad_order); } break; } }
MeshFunctionSharedPtr<Scalar> Limiter<Scalar>::get_solution() { // A check. warn_if(this->component_count > 1, "One solution asked from a Limiter, but multiple solutions exist for limiting."); MeshFunctionSharedPtr<Scalar> solution(new Solution<Scalar>()); Hermes::vector<MeshFunctionSharedPtr<Scalar> > solutions; solutions.push_back(solution); this->get_solutions(solutions); return solutions.back(); }
void OGProjection<Scalar>::project_global(Space<Scalar>* space, MeshFunction<Scalar>* source_meshfn, Scalar* target_vec, Hermes::MatrixSolverType matrix_solver_type, ProjNormType proj_norm) { Hermes::vector<Space<Scalar>*> spaces; spaces.push_back(space); Hermes::vector<MeshFunction<Scalar>*> source_meshfns; source_meshfns.push_back(source_meshfn); Hermes::vector<ProjNormType> proj_norms; proj_norms.push_back(proj_norm); project_global(spaces, source_meshfns, target_vec, matrix_solver_type, proj_norms); }
bool calc_errors(Hermes::vector<Solution* > left, Hermes::vector<Solution *> right, Hermes::vector<double> & err_abs, Hermes::vector<double> & norm_vals, double & err_abs_total, double & norm_total, double & err_rel_total, Hermes::vector<ProjNormType> norms) { bool default_norms = false; // Checks. if(left.size() != right.size()) return false; if (norms != Hermes::vector<ProjNormType>()) { if(left.size() != norms.size()) return false; } else default_norms = true; // Zero the resulting Tuples. err_abs.clear(); norm_vals.clear(); // Zero the sums. err_abs_total = 0; norm_total = 0; err_rel_total = 0; // Calculation. for(unsigned int i = 0; i < left.size(); i++) { err_abs.push_back(calc_abs_error(left[i], right[i], default_norms ? HERMES_H1_NORM : norms[i])); norm_vals.push_back(calc_norm(right[i], default_norms ? HERMES_H1_NORM : norms[i])); err_abs_total += err_abs[i] * err_abs[i]; norm_total += norm_vals[i] * norm_vals[i]; } err_abs_total = sqrt(err_abs_total); norm_total = sqrt(norm_total); err_rel_total = err_abs_total / norm_total * 100.; // Everything went well, return appropriate flag. return true; }
Hermes::vector<unsigned int> NeighborSearch<Scalar>::get_transforms(uint64_t sub_idx) const { Hermes::vector<unsigned int> transformations_backwards; while (sub_idx > 0) { transformations_backwards.push_back((sub_idx - 1) & 7); sub_idx = (sub_idx - 1) >> 3; } Hermes::vector<unsigned int> transformations; for(unsigned int i = 0; i < transformations_backwards.size(); i++) transformations.push_back(transformations_backwards[transformations_backwards.size() - 1 - i]); return transformations; }
void OGProjection<Scalar>::project_global(Space<Scalar>* space, Solution<Scalar>* sol_src, Solution<Scalar>* sol_dest, Hermes::MatrixSolverType matrix_solver_type, ProjNormType proj_norm) { Hermes::vector<Space<Scalar>*> spaces; spaces.push_back(space); Hermes::vector<Solution<Scalar>*> sols_src; sols_src.push_back(sol_src); Hermes::vector<Solution<Scalar>*> sols_dest; sols_dest.push_back(sol_dest); Hermes::vector<ProjNormType> proj_norms; if(proj_norm != HERMES_UNSET_NORM) proj_norms.push_back(proj_norm); project_global(spaces, sols_src, sols_dest, matrix_solver_type, proj_norms); }
void CalculationContinuity<Scalar>::Record::load_spaces(Hermes::vector<Space<Scalar>*> spaces, Hermes::vector<SpaceType> space_types, Hermes::vector<Mesh*> meshes, Hermes::vector<Shapeset*> shapesets) { if(shapesets == Hermes::vector<Shapeset*>()) for(unsigned int i = 0; i < spaces.size(); i++) shapesets.push_back(NULL); for(unsigned int i = 0; i < spaces.size(); i++) { std::stringstream filename; filename << CalculationContinuity<Scalar>::spaceFileName << i << '_' << (std::string)"t = " << this->time << (std::string)"n = " << this->number << (std::string)".h2d"; spaces[i]->free(); try { switch(space_types[i]) { case HERMES_H1_SPACE: dynamic_cast<H1Space<Scalar>*>(spaces[i])->load(filename.str().c_str(), meshes[i], shapesets[i]); break; case HERMES_HCURL_SPACE: dynamic_cast<HcurlSpace<Scalar>*>(spaces[i])->load(filename.str().c_str(), meshes[i], shapesets[i]); break; case HERMES_HDIV_SPACE: dynamic_cast<HdivSpace<Scalar>*>(spaces[i])->load(filename.str().c_str(), meshes[i], shapesets[i]); break; case HERMES_L2_SPACE: dynamic_cast<L2Space<Scalar>*>(spaces[i])->load(filename.str().c_str(), meshes[i], shapesets[i]); break; } } catch(Hermes::Exceptions::SpaceLoadFailureException& e) { throw IOCalculationContinuityException(CalculationContinuityException::spaces, IOCalculationContinuityException::input, filename.str().c_str(), e.what()); } catch(std::exception& e) { throw IOCalculationContinuityException(CalculationContinuityException::spaces, IOCalculationContinuityException::input, filename.str().c_str(), e.what()); } } }
KellyTypeAdapt::KellyTypeAdapt(Hermes::vector< Space* > spaces_, Hermes::vector< ProjNormType > norms_, bool ignore_visited_segments_, Hermes::vector<interface_estimator_scaling_fn_t> interface_scaling_fns_) : Adapt(spaces_, norms_) { error_estimators_surf.reserve(num); error_estimators_vol.reserve(num); if (interface_scaling_fns_.size() == 0) { interface_scaling_fns_.reserve(num); for (int i = 0; i < num; i++) interface_scaling_fns_.push_back(scale_by_element_diameter); } use_aposteriori_interface_scaling = true; interface_scaling_fns = interface_scaling_fns_; interface_scaling_const = boundary_scaling_const = volumetric_scaling_const = 1.0; ignore_visited_segments = ignore_visited_segments_; element_markers_conversion = spaces_[0]->get_mesh()->element_markers_conversion; boundary_markers_conversion = spaces_[0]->get_mesh()->boundary_markers_conversion; }
int main(int argc, char* argv[]) { if (NUMBER_OF_EIGENVALUES > 6) error("Maximum number of eigenvalues is 6."); info("Desired number of eigenvalues: %d.", NUMBER_OF_EIGENVALUES); // Load the mesh. Mesh mesh; H2DReader mloader; mloader.load("domain.mesh", &mesh); // Perform initial mesh refinements (optional). for (int i = 0; i < INIT_REF_NUM; i++) mesh.refine_all_elements(); // Enter boundary markers. // Note: "essential" means that solution value is prescribed. BCTypes bc_types; bc_types.add_bc_dirichlet(Hermes::vector<int>(BDY_BOTTOM, BDY_RIGHT, BDY_TOP, BDY_LEFT)); // Enter Dirichlet boudnary values. BCValues bc_values; bc_values.add_zero(Hermes::vector<int>(BDY_BOTTOM, BDY_RIGHT, BDY_TOP, BDY_LEFT)); // Create an H1 space with default shapeset. H1Space space(&mesh, &bc_types, &bc_values, P_INIT); // Initialize the weak formulation for the left hand side i.e. H WeakForm wf_left, wf_right; wf_left.add_matrix_form(callback(bilinear_form_left)); wf_right.add_matrix_form(callback(bilinear_form_right)); // Initialize refinement selector. H1ProjBasedSelector selector(CAND_LIST, CONV_EXP, H2DRS_DEFAULT_ORDER); // Initialize views. ScalarView sview_1("", new WinGeom(0, 0, 350, 250)); sview_1.show_mesh(false); sview_1.fix_scale_width(60); ScalarView sview_2("", new WinGeom(360, 0, 350, 250)); sview_2.show_mesh(false); sview_2.fix_scale_width(60); ScalarView sview_3("", new WinGeom(720, 0, 350, 250)); sview_3.show_mesh(false); sview_3.fix_scale_width(60); ScalarView sview_4("", new WinGeom(0, 305, 350, 250)); sview_4.show_mesh(false); sview_4.fix_scale_width(60); ScalarView sview_5("", new WinGeom(360, 305, 350, 250)); sview_5.show_mesh(false); sview_5.fix_scale_width(60); ScalarView sview_6("", new WinGeom(720, 305, 350, 250)); sview_6.show_mesh(false); sview_6.fix_scale_width(60); OrderView oview("Polynomial orders", new WinGeom(1080, 0, 410, 350)); // DOF and CPU convergence graphs. SimpleGraph graph_dof_est, graph_cpu_est; // Time measurement. TimePeriod cpu_time; cpu_time.tick(); Solution sln[NUMBER_OF_EIGENVALUES], ref_sln[NUMBER_OF_EIGENVALUES]; // Adaptivity loop: int as = 1; bool done = false; do { info("---- Adaptivity step %d:", as); info("Solving on reference mesh."); // Construct globally refined reference mesh and setup reference space. Space* ref_space = construct_refined_space(&space); int ref_ndof = Space::get_num_dofs(ref_space); info("ref_ndof: %d.", ref_ndof); // Initialize matrices and matrix solver on referenc emesh. SparseMatrix* matrix_left = create_matrix(matrix_solver); SparseMatrix* matrix_right = create_matrix(matrix_solver); Solver* solver = create_linear_solver(matrix_solver, matrix_left); // Assemble the matrices on reference mesh. bool is_linear = true; DiscreteProblem* dp_left = new DiscreteProblem(&wf_left, ref_space, is_linear); dp_left->assemble(matrix_left); DiscreteProblem* dp_right = new DiscreteProblem(&wf_right, ref_space, is_linear); dp_right->assemble(matrix_right); // Time measurement. cpu_time.tick(); // Write matrix_left in MatrixMarket format. write_matrix_mm("mat_left.mtx", matrix_left); // Write matrix_left in MatrixMarket format. write_matrix_mm("mat_right.mtx", matrix_right); // Time measurement. cpu_time.tick(HERMES_SKIP); // Calling Python eigensolver. Solution will be written to "eivecs.dat". info("Calling Pysparse..."); char call_cmd[255]; sprintf(call_cmd, "python solveGenEigenFromMtx.py mat_left.mtx mat_right.mtx %g %d %g %d", TARGET_VALUE, NUMBER_OF_EIGENVALUES, TOL, MAX_ITER); system(call_cmd); info("Pysparse finished."); // Initializing solution vector, solution and ScalarView. double* ref_coeff_vec = new double[ref_ndof]; //Solution sln[NUMBER_OF_EIGENVALUES], ref_sln[NUMBER_OF_EIGENVALUES]; //ScalarView view("Solution", new WinGeom(0, 0, 440, 350)); // Reading solution vectors from file and visualizing. double eigenval[NUMBER_OF_EIGENVALUES]; FILE *file = fopen("eivecs.dat", "r"); char line [64]; // Maximum line size. fgets(line, sizeof line, file); // ref_ndof int n = atoi(line); if (n != ref_ndof) error("Mismatched ndof in the eigensolver output file."); fgets(line, sizeof line, file); // Number of eigenvectors in the file. int neig = atoi(line); if (neig != NUMBER_OF_EIGENVALUES) error("Mismatched number of eigenvectors in the eigensolver output file."); for (int ieig = 0; ieig < NUMBER_OF_EIGENVALUES; ieig++) { // Get next eigenvalue from the file fgets(line, sizeof line, file); // eigenval eigenval[ieig] = atof(line); // Get the corresponding eigenvector. for (int i = 0; i < ref_ndof; i++) { fgets(line, sizeof line, file); ref_coeff_vec[i] = atof(line); } // Convert coefficient vector into a Solution. Solution::vector_to_solution(ref_coeff_vec, ref_space, &(ref_sln[ieig])); // Project the fine mesh solution onto the coarse mesh. info("Projecting reference solution %d on coarse mesh.", ieig); OGProjection::project_global(&space, &(ref_sln[ieig]), &(sln[ieig]), matrix_solver); } fclose(file); delete [] ref_coeff_vec; // FIXME: Below, the adaptivity is done for the last eigenvector only, // this needs to be changed to take into account all eigenvectors. // View the coarse mesh solution and polynomial orders. char title[100]; if (NUMBER_OF_EIGENVALUES > 0) { sprintf(title, "Solution 0, val = %g", eigenval[0]); sview_1.set_title(title); sview_1.show(&(sln[0])); } if (NUMBER_OF_EIGENVALUES > 1) { sprintf(title, "Solution 1, val = %g", eigenval[1]); sview_2.set_title(title); sview_2.show(&(sln[1])); } if (NUMBER_OF_EIGENVALUES > 2) { sprintf(title, "Solution 2, val = %g", eigenval[2]); sview_3.set_title(title); sview_3.show(&(sln[2])); } if (NUMBER_OF_EIGENVALUES > 3) { sprintf(title, "Solution 3, val = %g", eigenval[3]); sview_4.set_title(title); sview_4.show(&(sln[3])); } if (NUMBER_OF_EIGENVALUES > 4) { sprintf(title, "Solution 4, val = %g", eigenval[4]); sview_5.set_title(title); sview_5.show(&(sln[4])); } if (NUMBER_OF_EIGENVALUES > 5) { sprintf(title, "Solution 5, val = %g", eigenval[5]); sview_6.set_title(title); sview_6.show(&(sln[5])); } oview.show(&space); // Calculate element errors and total error estimate. info("Calculating error estimate."); Hermes::vector<Space *> spaces; for(int i = 0; i < NUMBER_OF_EIGENVALUES; i++) spaces.push_back(&space); Adapt* adaptivity = new Adapt(spaces); Hermes::vector<Solution *> slns; if (NUMBER_OF_EIGENVALUES > 0) slns.push_back(&sln[0]); if (NUMBER_OF_EIGENVALUES > 1) slns.push_back(&sln[1]); if (NUMBER_OF_EIGENVALUES > 2) slns.push_back(&sln[2]); if (NUMBER_OF_EIGENVALUES > 3) slns.push_back(&sln[3]); if (NUMBER_OF_EIGENVALUES > 4) slns.push_back(&sln[4]); if (NUMBER_OF_EIGENVALUES > 5) slns.push_back(&sln[5]); Hermes::vector<Solution *> ref_slns; if (NUMBER_OF_EIGENVALUES > 0) ref_slns.push_back(&ref_sln[0]); if (NUMBER_OF_EIGENVALUES > 1) ref_slns.push_back(&ref_sln[1]); if (NUMBER_OF_EIGENVALUES > 2) ref_slns.push_back(&ref_sln[2]); if (NUMBER_OF_EIGENVALUES > 3) ref_slns.push_back(&ref_sln[3]); if (NUMBER_OF_EIGENVALUES > 4) ref_slns.push_back(&ref_sln[4]); if (NUMBER_OF_EIGENVALUES > 5) ref_slns.push_back(&ref_sln[5]); Hermes::vector<double> component_errors; double err_est_rel = adaptivity->calc_err_est(slns, ref_slns, &component_errors) * 100; // Report results. info("ndof_coarse: %d, ndof_fine: %d.", Space::get_num_dofs(&space), Space::get_num_dofs(ref_space)); if (NUMBER_OF_EIGENVALUES > 0) info("err_est_rel[0]: %g%%", component_errors[0] * 100); if (NUMBER_OF_EIGENVALUES > 1) info("err_est_rel[1]: %g%%", component_errors[1] * 100); if (NUMBER_OF_EIGENVALUES > 2) info("err_est_rel[2]: %g%%", component_errors[2] * 100); if (NUMBER_OF_EIGENVALUES > 3) info("err_est_rel[3]: %g%%", component_errors[3] * 100); if (NUMBER_OF_EIGENVALUES > 4) info("err_est_rel[4]: %g%%", component_errors[4] * 100); if (NUMBER_OF_EIGENVALUES > 5) info("err_est_rel[5]: %g%%", component_errors[5] * 100); // Time measurement. cpu_time.tick(); // Add entry to DOF and CPU convergence graphs. graph_dof_est.add_values(Space::get_num_dofs(&space), err_est_rel); graph_dof_est.save("conv_dof_est.dat"); graph_cpu_est.add_values(cpu_time.accumulated(), err_est_rel); graph_cpu_est.save("conv_cpu_est.dat"); // If err_est too large, adapt the mesh. if (err_est_rel < ERR_STOP) done = true; else { info("Adapting coarse mesh."); Hermes::vector<RefinementSelectors::Selector *> selectors; for(int i = 0; i < NUMBER_OF_EIGENVALUES; i++) selectors.push_back(&selector); done = adaptivity->adapt(selectors, THRESHOLD, STRATEGY, MESH_REGULARITY); // Increase the counter of performed adaptivity steps. if (done == false) as++; } if (Space::get_num_dofs(&space) >= NDOF_STOP) done = true; // Clean up. delete solver; delete matrix_left; delete matrix_right; delete adaptivity; if(done == false) delete ref_space->get_mesh(); delete ref_space; delete dp_left; delete dp_right; } while (done == false); // Wait for all views to be closed. View::wait(); return 0; };
int main(int argc, char* argv[]) { // Time measurement. Hermes::Mixins::TimeMeasurable cpu_time; cpu_time.tick(); // Load physical data of the problem. MaterialPropertyMaps matprop(N_GROUPS); matprop.set_D(D); matprop.set_Sigma_r(Sr); matprop.set_Sigma_s(Ss); matprop.set_Sigma_a(Sa); matprop.set_Sigma_f(Sf); matprop.set_nu(nu); matprop.set_chi(chi); matprop.validate(); std::cout << matprop; // Use multimesh, i.e. create one mesh for each energy group. Hermes::vector<Mesh *> meshes; for (unsigned int g = 0; g < matprop.get_G(); g++) meshes.push_back(new Mesh()); // Load the mesh for the 1st group. MeshReaderH2D mloader; mloader.load(mesh_file.c_str(), meshes[0]); for (unsigned int g = 1; g < matprop.get_G(); g++) { // Obtain meshes for the 2nd to 4th group by cloning the mesh loaded for the 1st group. meshes[g]->copy(meshes[0]); // Initial uniform refinements. for (int i = 0; i < INIT_REF_NUM[g]; i++) meshes[g]->refine_all_elements(); } for (int i = 0; i < INIT_REF_NUM[0]; i++) meshes[0]->refine_all_elements(); // Create pointers to solutions on coarse and fine meshes and from the latest power iteration, respectively. Hermes::vector<Solution<double>*> coarse_solutions, fine_solutions; Hermes::vector<MeshFunction<double>*> power_iterates; // Initialize all the new solution variables. for (unsigned int g = 0; g < matprop.get_G(); g++) { coarse_solutions.push_back(new Solution<double>()); fine_solutions.push_back(new Solution<double>()); power_iterates.push_back(new ConstantSolution<double>(meshes[g], 1.0)); } // Create the approximation spaces with the default shapeset. H1Space<double> space1(meshes[0], P_INIT[0]); H1Space<double> space2(meshes[1], P_INIT[1]); H1Space<double> space3(meshes[2], P_INIT[2]); H1Space<double> space4(meshes[3], P_INIT[3]); Hermes::vector<const Space<double>*> const_spaces(&space1, &space2, &space3, &space4); Hermes::vector<Space<double>*> spaces(&space1, &space2, &space3, &space4); // Initialize the weak formulation. CustomWeakForm wf(matprop, power_iterates, k_eff, bdy_vacuum); // Initialize the discrete algebraic representation of the problem and its solver. // // Create the matrix and right-hand side vector for the solver. SparseMatrix<double>* mat = create_matrix<double>(); Vector<double>* rhs = create_vector<double>(); // Instantiate the solver itself. LinearMatrixSolver<double>* solver = create_linear_solver<double>( mat, rhs); // Initialize views. /* for 1280x800 display */ ScalarView view1("Neutron flux 1", new WinGeom(0, 0, 320, 400)); ScalarView view2("Neutron flux 2", new WinGeom(330, 0, 320, 400)); ScalarView view3("Neutron flux 3", new WinGeom(660, 0, 320, 400)); ScalarView view4("Neutron flux 4", new WinGeom(990, 0, 320, 400)); OrderView oview1("Mesh for group 1", new WinGeom(0, 450, 320, 500)); OrderView oview2("Mesh for group 2", new WinGeom(330, 450, 320, 500)); OrderView oview3("Mesh for group 3", new WinGeom(660, 450, 320, 500)); OrderView oview4("Mesh for group 4", new WinGeom(990, 450, 320, 500)); /* for adjacent 1280x800 and 1680x1050 displays ScalarView view1("Neutron flux 1", new WinGeom(0, 0, 640, 480)); ScalarView view2("Neutron flux 2", new WinGeom(650, 0, 640, 480)); ScalarView view3("Neutron flux 3", new WinGeom(1300, 0, 640, 480)); ScalarView view4("Neutron flux 4", new WinGeom(1950, 0, 640, 480)); OrderView oview1("Mesh for group 1", new WinGeom(1300, 500, 340, 500)); OrderView oview2("Mesh for group 2", new WinGeom(1650, 500, 340, 500)); OrderView oview3("Mesh for group 3", new WinGeom(2000, 500, 340, 500)); OrderView oview4("Mesh for group 4", new WinGeom(2350, 500, 340, 500)); */ Hermes::vector<ScalarView *> sviews(&view1, &view2, &view3, &view4); Hermes::vector<OrderView *> oviews(&oview1, &oview2, &oview3, &oview4); for (unsigned int g = 0; g < matprop.get_G(); g++) { sviews[g]->show_mesh(false); sviews[g]->set_3d_mode(true); } // DOF and CPU convergence graphs GnuplotGraph graph_dof("Error convergence", "NDOF", "log(error)"); graph_dof.add_row("H1 err. est. [%]", "r", "-", "o"); graph_dof.add_row("L2 err. est. [%]", "g", "-", "s"); graph_dof.add_row("Keff err. est. [milli-%]", "b", "-", "d"); graph_dof.set_log_y(); graph_dof.show_legend(); graph_dof.show_grid(); GnuplotGraph graph_dof_evol("Evolution of NDOF", "Adaptation step", "NDOF"); graph_dof_evol.add_row("group 1", "r", "-", "o"); graph_dof_evol.add_row("group 2", "g", "-", "x"); graph_dof_evol.add_row("group 3", "b", "-", "+"); graph_dof_evol.add_row("group 4", "m", "-", "*"); graph_dof_evol.set_log_y(); graph_dof_evol.set_legend_pos("bottom right"); graph_dof_evol.show_grid(); GnuplotGraph graph_cpu("Error convergence", "CPU time [s]", "log(error)"); graph_cpu.add_row("H1 err. est. [%]", "r", "-", "o"); graph_cpu.add_row("L2 err. est. [%]", "g", "-", "s"); graph_cpu.add_row("Keff err. est. [milli-%]", "b", "-", "d"); graph_cpu.set_log_y(); graph_cpu.show_legend(); graph_cpu.show_grid(); // Initialize the refinement selectors. H1ProjBasedSelector<double> selector(CAND_LIST, CONV_EXP, H2DRS_DEFAULT_ORDER); Hermes::vector<RefinementSelectors::Selector<double>*> selectors; for (unsigned int g = 0; g < matprop.get_G(); g++) selectors.push_back(&selector); Hermes::vector<MatrixFormVol<double>*> projection_jacobian; Hermes::vector<VectorFormVol<double>*> projection_residual; for (unsigned int g = 0; g < matprop.get_G(); g++) { projection_jacobian.push_back(new H1AxisymProjectionJacobian(g)); projection_residual.push_back(new H1AxisymProjectionResidual(g, power_iterates[g])); } Hermes::vector<ProjNormType> proj_norms_h1, proj_norms_l2; for (unsigned int g = 0; g < matprop.get_G(); g++) { proj_norms_h1.push_back(HERMES_H1_NORM); proj_norms_l2.push_back(HERMES_L2_NORM); } // Initial power iteration to obtain a coarse estimate of the eigenvalue and the fission source. Hermes::Mixins::Loggable::Static::info("Coarse mesh power iteration, %d + %d + %d + %d = %d ndof:", report_num_dofs(spaces)); power_iteration(matprop, const_spaces, &wf, power_iterates, core, TOL_PIT_CM, matrix_solver); // Adaptivity loop: int as = 1; bool done = false; do { Hermes::Mixins::Loggable::Static::info("---- Adaptivity step %d:", as); // Construct globally refined meshes and setup reference spaces on them. Hermes::vector<const Space<double>*> ref_spaces_const; Hermes::vector<Mesh *> ref_meshes; for (unsigned int g = 0; g < matprop.get_G(); g++) { ref_meshes.push_back(new Mesh()); Mesh *ref_mesh = ref_meshes.back(); ref_mesh->copy(spaces[g]->get_mesh()); ref_mesh->refine_all_elements(); int order_increase = 1; ref_spaces_const.push_back(spaces[g]->dup(ref_mesh, order_increase)); } #ifdef WITH_PETSC // PETSc assembling is currently slow for larger matrices, so we switch to // UMFPACK when matrices of order >8000 start to appear. if (Space<double>::get_num_dofs(ref_spaces_const) > 8000 && matrix_solver == SOLVER_PETSC) { // Delete the old solver. delete mat; delete rhs; delete solver; // Create a new one. matrix_solver = SOLVER_UMFPACK; mat = create_matrix<double>(); rhs = create_vector<double>(); solver = create_linear_solver<double>( mat, rhs); } #endif // Solve the fine mesh problem. Hermes::Mixins::Loggable::Static::info("Fine mesh power iteration, %d + %d + %d + %d = %d ndof:", report_num_dofs(ref_spaces_const)); power_iteration(matprop, ref_spaces_const, &wf, power_iterates, core, TOL_PIT_RM, matrix_solver); // Store the results. for (unsigned int g = 0; g < matprop.get_G(); g++) fine_solutions[g]->copy((static_cast<Solution<double>*>(power_iterates[g]))); Hermes::Mixins::Loggable::Static::info("Projecting fine mesh solutions on coarse meshes."); // This is commented out as the appropriate method was deleted in the commit // "Cleaning global projections" (b282194946225014faa1de37f20112a5a5d7ab5a). //OGProjection<double> ogProjection; ogProjection.project_global(spaces, projection_jacobian, projection_residual, coarse_solutions); // Time measurement. cpu_time.tick(); // View the coarse mesh solution and meshes. for (unsigned int g = 0; g < matprop.get_G(); g++) { sviews[g]->show(coarse_solutions[g]); oviews[g]->show(spaces[g]); } // Skip visualization time. cpu_time.tick(Hermes::Mixins::TimeMeasurable::HERMES_SKIP); // Report the number of negative eigenfunction values. Hermes::Mixins::Loggable::Static::info("Num. of negative values: %d, %d, %d, %d", get_num_of_neg(coarse_solutions[0]), get_num_of_neg(coarse_solutions[1]), get_num_of_neg(coarse_solutions[2]), get_num_of_neg(coarse_solutions[3])); // Calculate element errors and total error estimate. Adapt<double> adapt_h1(spaces); Adapt<double> adapt_l2(spaces); for (unsigned int g = 0; g < matprop.get_G(); g++) { adapt_h1.set_error_form(g, g, new ErrorForm(proj_norms_h1[g])); adapt_l2.set_error_form(g, g, new ErrorForm(proj_norms_l2[g])); } // Calculate element errors and error estimates in H1 and L2 norms. Use the H1 estimate to drive adaptivity. Hermes::Mixins::Loggable::Static::info("Calculating errors."); Hermes::vector<double> h1_group_errors, l2_group_errors; double h1_err_est = adapt_h1.calc_err_est(coarse_solutions, fine_solutions, &h1_group_errors) * 100; double l2_err_est = adapt_l2.calc_err_est(coarse_solutions, fine_solutions, &l2_group_errors, false) * 100; // Time measurement. cpu_time.tick(); double cta = cpu_time.accumulated(); // Report results. Hermes::Mixins::Loggable::Static::info("ndof_coarse: %d + %d + %d + %d = %d", report_num_dofs(spaces)); // Millipercent eigenvalue error w.r.t. the reference value (see physical_parameters.cpp). double keff_err = 1e5*fabs(wf.get_keff() - REF_K_EFF)/REF_K_EFF; Hermes::Mixins::Loggable::Static::info("per-group err_est_coarse (H1): %g%%, %g%%, %g%%, %g%%", report_errors(h1_group_errors)); Hermes::Mixins::Loggable::Static::info("per-group err_est_coarse (L2): %g%%, %g%%, %g%%, %g%%", report_errors(l2_group_errors)); Hermes::Mixins::Loggable::Static::info("total err_est_coarse (H1): %g%%", h1_err_est); Hermes::Mixins::Loggable::Static::info("total err_est_coarse (L2): %g%%", l2_err_est); Hermes::Mixins::Loggable::Static::info("k_eff err: %g milli-percent", keff_err); // Add entry to DOF convergence graph. int ndof_coarse = spaces[0]->get_num_dofs() + spaces[1]->get_num_dofs() + spaces[2]->get_num_dofs() + spaces[3]->get_num_dofs(); graph_dof.add_values(0, ndof_coarse, h1_err_est); graph_dof.add_values(1, ndof_coarse, l2_err_est); graph_dof.add_values(2, ndof_coarse, keff_err); // Add entry to CPU convergence graph. graph_cpu.add_values(0, cta, h1_err_est); graph_cpu.add_values(1, cta, l2_err_est); graph_cpu.add_values(2, cta, keff_err); for (unsigned int g = 0; g < matprop.get_G(); g++) graph_dof_evol.add_values(g, as, Space<double>::get_num_dofs(spaces[g])); cpu_time.tick(Hermes::Mixins::TimeMeasurable::HERMES_SKIP); // If err_est too large, adapt the mesh (L2 norm chosen since (weighted integrals of) solution values // are more important for further analyses than the derivatives. if (l2_err_est < ERR_STOP) done = true; else { Hermes::Mixins::Loggable::Static::info("Adapting the coarse mesh."); done = adapt_h1.adapt(selectors, THRESHOLD, STRATEGY, MESH_REGULARITY); if (spaces[0]->get_num_dofs() + spaces[1]->get_num_dofs() + spaces[2]->get_num_dofs() + spaces[3]->get_num_dofs() >= NDOF_STOP) done = true; } // Free reference meshes and spaces. for (unsigned int g = 0; g < matprop.get_G(); g++) { delete ref_spaces_const[g]; delete ref_meshes[g]; } as++; if (as >= MAX_ADAPT_NUM) done = true; } while(done == false); Hermes::Mixins::Loggable::Static::info("Total running time: %g s", cpu_time.accumulated()); for (unsigned int g = 0; g < matprop.get_G(); g++) { delete spaces[g]; delete meshes[g]; delete coarse_solutions[g], delete fine_solutions[g]; delete power_iterates[g]; } delete mat; delete rhs; delete solver; graph_dof.save("conv_dof.gp"); graph_cpu.save("conv_cpu.gp"); graph_dof_evol.save("dof_evol.gp"); // Wait for all views to be closed. View::wait(); return 0; }
bool NewtonSolver<Scalar>::solve_keep_jacobian(Scalar* coeff_vec, double newton_tol, int newton_max_iter, bool residual_as_function) { // Obtain the number of degrees of freedom. int ndof = this->dp->get_num_dofs(); // The Newton's loop. double residual_norm; int it = 1; while (1) { // Assemble the residual vector. this->dp->assemble(coeff_vec, residual); // Measure the residual norm. if (residual_as_function) { // Prepare solutions for measuring residual norm. Hermes::vector<Solution<Scalar>*> solutions; Hermes::vector<bool> dir_lift_false; for (unsigned int i = 0; i < static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces().size(); i++) { solutions.push_back(new Solution<Scalar>()); dir_lift_false.push_back(false); } Solution<Scalar>::vector_to_solutions(residual, static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces(), solutions, dir_lift_false); // Calculate the norm. residual_norm = Global<Scalar>::calc_norms(solutions); // Clean up. for (unsigned int i = 0; i < static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces().size(); i++) delete solutions[i]; } else { // Calculate the l2-norm of residual vector, this is the traditional way. residual_norm = Global<Scalar>::get_l2_norm(residual); } // Info for the user. if(it == 1) { if(this->verbose_output) info("---- Newton initial residual norm: %g", residual_norm); } else if(this->verbose_output) info("---- Newton iter %d, residual norm: %g", it - 1, residual_norm); // If maximum allowed residual norm is exceeded, fail. if (residual_norm > max_allowed_residual_norm) { if (this->verbose_output) { info("Current residual norm: %g", residual_norm); info("Maximum allowed residual norm: %g", max_allowed_residual_norm); info("Newton solve not successful, returning false."); } break; } // If residual norm is within tolerance, return 'true'. // This is the only correct way of ending. if (residual_norm < newton_tol && it > 1) { // We want to return the solution in a different structure. this->sln_vector = new Scalar[ndof]; for (int i = 0; i < ndof; i++) this->sln_vector[i] = coeff_vec[i]; return true; } // Assemble and keep the jacobian if this has not been done before. // Also declare that LU-factorization in case of a direct solver will be done only once and reused afterwards. if(kept_jacobian == NULL) { kept_jacobian = create_matrix<Scalar>(this->matrix_solver_type); // Give the matrix solver the correct Jacobian. NOTE: It would be cleaner if the whole decision whether to keep // Jacobian or not was made in the constructor. // // Delete the matrix solver created in the constructor. delete linear_solver; // Create new matrix solver with correct matrix. linear_solver = create_linear_solver<Scalar>(this->matrix_solver_type, kept_jacobian, residual); this->dp->assemble(coeff_vec, kept_jacobian); linear_solver->set_factorization_scheme(HERMES_REUSE_FACTORIZATION_COMPLETELY); } // Multiply the residual vector with -1 since the matrix // equation reads J(Y^n) \deltaY^{n+1} = -F(Y^n). residual->change_sign(); // Solve the linear system. if(!linear_solver->solve()) { if (this->verbose_output) info ("Matrix<Scalar> solver failed. Returning false.\n"); break; } // Add \deltaY^{n+1} to Y^n. for (int i = 0; i < ndof; i++) coeff_vec[i] += linear_solver->get_sln_vector()[i]; // Increase the number of iterations and test if we are still under the limit. if (it++ >= newton_max_iter) { if (this->verbose_output) info("Maximum allowed number of Newton iterations exceeded, returning false."); break; } } // Return false. // All 'bad' situations end here. return false; }
bool NewtonSolver<Scalar>::solve(Scalar* coeff_vec, double newton_tol, int newton_max_iter, bool residual_as_function) { // Delete the old solution vector, if there is any. if(this->sln_vector != NULL) { delete [] this->sln_vector; this->sln_vector = NULL; } // Obtain the number of degrees of freedom. int ndof = this->dp->get_num_dofs(); // The Newton's loop. double residual_norm; int it = 1; bool delete_timer = false; if (this->timer == NULL) { this->timer = new TimePeriod; delete_timer = true; } this->timer->tick(); setup_time += this->timer->last(); while (1) { // Assemble just the residual vector. if(it > 1) static_cast<DiscreteProblem<Scalar>*>(this->dp)->temp_disable_adaptivity_cache(); this->dp->assemble(coeff_vec, residual); this->timer->tick(); assemble_time += this->timer->last(); // Measure the residual norm. if (residual_as_function) { // Prepare solutions for measuring residual norm. Hermes::vector<Solution<Scalar>*> solutions; Hermes::vector<bool> dir_lift_false; for (unsigned int i = 0; i < static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces().size(); i++) { solutions.push_back(new Solution<Scalar>()); dir_lift_false.push_back(false); } Solution<Scalar>::vector_to_solutions(residual, static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces(), solutions, dir_lift_false); // Calculate the norm. residual_norm = Global<Scalar>::calc_norms(solutions); // Clean up. for (unsigned int i = 0; i < static_cast<DiscreteProblem<Scalar>*>(this->dp)->get_spaces().size(); i++) delete solutions[i]; } else { // Calculate the l2-norm of residual vector, this is the traditional way. residual_norm = Global<Scalar>::get_l2_norm(residual); } // Info for the user. if(it == 1) { if(this->verbose_output) info("---- Newton initial residual norm: %g", residual_norm); } else if(this->verbose_output) info("---- Newton iter %d, residual norm: %g", it - 1, residual_norm); // If maximum allowed residual norm is exceeded, fail. if (residual_norm > max_allowed_residual_norm) { if (this->verbose_output) { info("Current residual norm: %g", residual_norm); info("Maximum allowed residual norm: %g", max_allowed_residual_norm); info("Newton solve not successful, returning false."); } break; } // If residual norm is within tolerance, return 'true'. // This is the only correct way of ending. if (residual_norm < newton_tol && it > 1) { // We want to return the solution in a different structure. this->sln_vector = new Scalar[ndof]; for (int i = 0; i < ndof; i++) this->sln_vector[i] = coeff_vec[i]; this->timer->tick(); solve_time += this->timer->last(); if (delete_timer) { delete this->timer; this->timer = NULL; } static_cast<DiscreteProblem<Scalar>*>(this->dp)->temp_enable_adaptivity_cache(); return true; } this->timer->tick(); solve_time += this->timer->last(); // Assemble just the jacobian. this->dp->assemble(coeff_vec, jacobian); this->timer->tick(); assemble_time += this->timer->last(); // Multiply the residual vector with -1 since the matrix // equation reads J(Y^n) \deltaY^{n+1} = -F(Y^n). residual->change_sign(); // Solve the linear system. if(!linear_solver->solve()) { if (this->verbose_output) info ("Matrix<Scalar> solver failed. Returning false.\n"); break; } // Add \deltaY^{n+1} to Y^n. for (int i = 0; i < ndof; i++) coeff_vec[i] += linear_solver->get_sln_vector()[i]; // Increase the number of iterations and test if we are still under the limit. if (it++ >= newton_max_iter) { if (this->verbose_output) info("Maximum allowed number of Newton iterations exceeded, returning false."); break; } this->timer->tick(); solve_time += this->timer->last(); } // Return false. // All 'bad' situations end here. if (delete_timer) { delete this->timer; this->timer = NULL; } return false; }
double KellyTypeAdapt::eval_interface_estimator(KellyTypeAdapt::ErrorEstimatorForm* err_est_form, RefMap *rm, SurfPos* surf_pos, LightArray<NeighborSearch*>& neighbor_searches, int neighbor_index) { NeighborSearch* nbs = neighbor_searches.get(neighbor_index); Hermes::vector<MeshFunction*> slns; for (int i = 0; i < num; i++) slns.push_back(this->sln[i]); // Determine integration order. ExtData<Ord>* fake_ui = dp.init_ext_fns_ord(slns, neighbor_searches); // Order of additional external functions. // ExtData<Ord>* fake_ext = dp.init_ext_fns_ord(err_est_form->ext, nbs); // Order of geometric attributes (eg. for multiplication of a solution with coordinates, normals, etc.). Geom<Ord>* fake_e = new InterfaceGeom<Ord>(init_geom_ord(), nbs->neighb_el->marker, nbs->neighb_el->id, nbs->neighb_el->get_diameter()); double fake_wt = 1.0; Ord o = err_est_form->ord(1, &fake_wt, fake_ui->fn, fake_ui->fn[err_est_form->i], fake_e, NULL); int order = rm->get_inv_ref_order(); order += o.get_order(); limit_order(order); // Clean up. if (fake_ui != NULL) { for (int i = 0; i < num; i++) delete fake_ui->fn[i]; fake_ui->free_ord(); delete fake_ui; } delete fake_e; //delete fake_ext; Quad2D* quad = this->sln[err_est_form->i]->get_quad_2d(); int eo = quad->get_edge_points(surf_pos->surf_num, order); int np = quad->get_num_points(eo); double3* pt = quad->get_points(eo); // Init geometry and jacobian*weights (do not use the NeighborSearch caching mechanism). double3* tan = rm->get_tangent(surf_pos->surf_num, eo); double* jwt = new double[np]; for(int i = 0; i < np; i++) jwt[i] = pt[i][2] * tan[i][2]; Geom<double>* e = new InterfaceGeom<double>(init_geom_surf(rm, surf_pos, eo), nbs->neighb_el->marker, nbs->neighb_el->id, nbs->neighb_el->get_diameter()); // function values ExtData<scalar>* ui = dp.init_ext_fns(slns, neighbor_searches, order); //ExtData<scalar>* ext = dp.init_ext_fns(err_est_form->ext, nbs); scalar res = interface_scaling_const * err_est_form->value(np, jwt, ui->fn, ui->fn[err_est_form->i], e, NULL); if (ui != NULL) { ui->free(); delete ui; } //if (ext != NULL) { ext->free(); delete ext; } e->free(); delete e; delete [] jwt; return std::abs(0.5*res); // Edges are parameterized from 0 to 1 while integration weights // are defined in (-1, 1). Thus multiplying with 0.5 to correct // the weights. }
Adapt::Adapt(Hermes::vector< Space* > spaces_, Hermes::vector<ProjNormType> proj_norms) : num_act_elems(-1), have_errors(false), have_coarse_solutions(false), have_reference_solutions(false) { // sanity check if (proj_norms.size() > 0 && spaces_.size() != proj_norms.size()) error("Mismatched numbers of spaces and projection types in Adapt::Adapt()."); this->num = spaces_.size(); // sanity checks error_if(this->num <= 0, "Too few components (%d), only %d supported.", this->num, H2D_MAX_COMPONENTS); error_if(this->num > H2D_MAX_COMPONENTS, "Too many components (%d), only %d supported.", this->num, H2D_MAX_COMPONENTS); for (int i = 0; i < this->num; i++) { if (spaces_[i] == NULL) error("spaces[%d] is NULL in Adapt::Adapt().", i); this->spaces.push_back(spaces_[i]); } // reset values memset(errors, 0, sizeof(errors)); memset(error_form, 0, sizeof(error_form)); memset(error_ord, 0, sizeof(error_ord)); memset(sln, 0, sizeof(sln)); memset(rsln, 0, sizeof(rsln)); // if norms were not set by the user, set them to defaults // according to spaces if (proj_norms.size() == 0) { for (int i = 0; i < this->num; i++) { switch (spaces[i]->get_type()) { case HERMES_H1_SPACE: proj_norms.push_back(HERMES_H1_NORM); break; case HERMES_HCURL_SPACE: proj_norms.push_back(HERMES_HCURL_NORM); break; case HERMES_HDIV_SPACE: proj_norms.push_back(HERMES_HDIV_NORM); break; case HERMES_L2_SPACE: proj_norms.push_back(HERMES_L2_NORM); break; default: error("Unknown space type in Adapt::Adapt()."); } } } // assign norm weak forms according to norms selection for (int i = 0; i < this->num; i++) { switch (proj_norms[i]) { case HERMES_H1_NORM: error_form[i][i] = h1_error_form<double, scalar>; error_ord[i][i] = h1_error_form<Ord, Ord>; //printf("H1 norm.\n"); break; case HERMES_H1_SEMINORM: error_form[i][i] = h1_error_semi_form<double, scalar>; error_ord[i][i] = h1_error_semi_form<Ord, Ord>; //printf("H1 semi norm.\n"); break; case HERMES_HCURL_NORM: error_form[i][i] = hcurl_error_form<double, scalar>; error_ord[i][i] = hcurl_error_form<Ord, Ord>; //printf("Hcurl norm.\n"); break; case HERMES_HDIV_NORM: error_form[i][i] = hdiv_error_form<double, scalar>; error_ord[i][i] = hdiv_error_form<Ord, Ord>; //printf("Hdiv norm.\n"); break; case HERMES_L2_NORM: error_form[i][i] = l2_error_form<double, scalar>; error_ord[i][i] = l2_error_form<Ord, Ord>; //printf("L2 norm.\n"); break; default: error("Unknown projection type in Adapt::Adapt()."); } } }
void Adapt::fix_shared_mesh_refinements(Mesh** meshes, Hermes::vector<ElementToRefine>& elems_to_refine, int** idx, Hermes::vector<RefinementSelectors::Selector *> refinement_selectors) { int num_elem_to_proc = elems_to_refine.size(); for(int inx = 0; inx < num_elem_to_proc; inx++) { ElementToRefine& elem_ref = elems_to_refine[inx]; int current_quad_order = this->spaces[elem_ref.comp]->get_element_order(elem_ref.id); Element* current_elem = meshes[elem_ref.comp]->get_element(elem_ref.id); //select a refinement used by all components that share a mesh which is about to be refined int selected_refinement = elem_ref.split; for (int j = 0; j < this->num; j++) { if (selected_refinement == H2D_REFINEMENT_H) break; // iso refinement is max what can be recieved if (j != elem_ref.comp && meshes[j] == meshes[elem_ref.comp]) { // if a mesh is shared int ii = idx[elem_ref.id][j]; if (ii >= 0) { // and the sample element is about to be refined by another compoment const ElementToRefine& elem_ref_ii = elems_to_refine[ii]; if (elem_ref_ii.split != selected_refinement && elem_ref_ii.split != H2D_REFINEMENT_P) { //select more complicated refinement if ((elem_ref_ii.split == H2D_REFINEMENT_ANISO_H || elem_ref_ii.split == H2D_REFINEMENT_ANISO_V) && selected_refinement == H2D_REFINEMENT_P) selected_refinement = elem_ref_ii.split; else selected_refinement = H2D_REFINEMENT_H; } } } } //fix other refinements according to the selected refinement if (selected_refinement != H2D_REFINEMENT_P) { //get suggested orders for the selected refinement const int* suggested_orders = NULL; if (selected_refinement == H2D_REFINEMENT_H) suggested_orders = elem_ref.q; //update orders for (int j = 0; j < this->num; j++) { if (j != elem_ref.comp && meshes[j] == meshes[elem_ref.comp]) { // if components share the mesh // change currently processed refinement if (elem_ref.split != selected_refinement) { elem_ref.split = selected_refinement; refinement_selectors[j]->generate_shared_mesh_orders(current_elem, current_quad_order, elem_ref.split, elem_ref.p, suggested_orders); } // change other refinements int ii = idx[elem_ref.id][j]; if (ii >= 0) { ElementToRefine& elem_ref_ii = elems_to_refine[ii]; if (elem_ref_ii.split != selected_refinement) { elem_ref_ii.split = selected_refinement; refinement_selectors[j]->generate_shared_mesh_orders(current_elem, current_quad_order, elem_ref_ii.split, elem_ref_ii.p, suggested_orders); } } else { // element (of the other comp.) not refined at all: assign refinement ElementToRefine elem_ref_new(elem_ref.id, j); elem_ref_new.split = selected_refinement; refinement_selectors[j]->generate_shared_mesh_orders(current_elem, current_quad_order, elem_ref_new.split, elem_ref_new.p, suggested_orders); elems_to_refine.push_back(elem_ref_new); } } } } } }
int main(int argc, char* argv[]) { if (NUMBER_OF_EIGENVALUES > 6) error("Maximum number of eigenvalues is 6."); info("Desired number of eigenvalues: %d.", NUMBER_OF_EIGENVALUES); // Load the mesh. Mesh mesh; H2DReader mloader; mloader.load("../domain.mesh", &mesh); // Perform initial mesh refinements (optional). for (int i = 0; i < INIT_REF_NUM; i++) mesh.refine_all_elements(); // Initialize boundary conditions. DefaultEssentialBCConst bc_essential(Hermes::vector<std::string>(BDY_BOTTOM, BDY_RIGHT, BDY_TOP, BDY_LEFT), 0.0); EssentialBCs bcs(&bc_essential); // Create an H1 space with default shapeset. H1Space space(&mesh, &bcs, P_INIT); // Initialize the weak formulation. WeakFormEigenLeft wf_left; WeakFormEigenRight wf_right; // Initialize refinement selector. H1ProjBasedSelector selector(CAND_LIST, CONV_EXP, H2DRS_DEFAULT_ORDER); // Initialize views. ScalarView sview_1("", new WinGeom(0, 0, 400, 360)); sview_1.show_mesh(false); sview_1.fix_scale_width(60); ScalarView sview_2("", new WinGeom(405, 0, 400, 360)); sview_2.show_mesh(false); sview_2.fix_scale_width(60); ScalarView sview_3("", new WinGeom(810, 0, 400, 360)); sview_3.show_mesh(false); sview_3.fix_scale_width(60); ScalarView sview_4("", new WinGeom(0, 410, 400, 360)); sview_4.show_mesh(false); sview_4.fix_scale_width(60); ScalarView sview_5("", new WinGeom(405, 410, 400, 360)); sview_5.show_mesh(false); sview_5.fix_scale_width(60); ScalarView sview_6("", new WinGeom(810, 410, 400, 360)); sview_6.show_mesh(false); sview_6.fix_scale_width(60); OrderView oview("Polynomial orders", new WinGeom(1215, 0, 400, 360)); // DOF and CPU convergence graphs. SimpleGraph graph_dof_est, graph_cpu_est; // Time measurement. TimePeriod cpu_time; cpu_time.tick(); Solution sln[NUMBER_OF_EIGENVALUES], ref_sln[NUMBER_OF_EIGENVALUES]; // Adaptivity loop: int as = 1; bool done = false; do { info("---- Adaptivity step %d:", as); info("Solving on reference mesh."); // Construct globally refined reference mesh and setup reference space. Space* ref_space = Space::construct_refined_space(&space); int ref_ndof = Space::get_num_dofs(ref_space); info("ref_ndof: %d.", ref_ndof); // Initialize matrices and matrix solver on referenc emesh. RCP<SparseMatrix> matrix_left = rcp(new CSCMatrix()); RCP<SparseMatrix> matrix_right = rcp(new CSCMatrix()); Solver* solver = create_linear_solver(matrix_solver, matrix_left.get()); // Assemble the matrices on reference mesh. DiscreteProblem* dp_left = new DiscreteProblem(&wf_left, ref_space); dp_left->assemble(matrix_left.get()); DiscreteProblem* dp_right = new DiscreteProblem(&wf_right, ref_space); dp_right->assemble(matrix_right.get()); // Time measurement. cpu_time.tick(); // Time measurement. cpu_time.tick(HERMES_SKIP); EigenSolver es(matrix_left, matrix_right); info("Calling Pysparse..."); es.solve(NUMBER_OF_EIGENVALUES, TARGET_VALUE, TOL, MAX_ITER); info("Pysparse finished."); es.print_eigenvalues(); // Initializing solution vector, solution and ScalarView. double* ref_coeff_vec; //Solution sln[NUMBER_OF_EIGENVALUES], ref_sln[NUMBER_OF_EIGENVALUES]; //ScalarView view("Solution", new WinGeom(0, 0, 440, 350)); // Reading solution vectors from file and visualizing. double eigenval[NUMBER_OF_EIGENVALUES]; int neig = es.get_n_eigs(); if (neig != NUMBER_OF_EIGENVALUES) error("Mismatched number of eigenvectors in the eigensolver output file."); for (int ieig = 0; ieig < NUMBER_OF_EIGENVALUES; ieig++) { eigenval[ieig] = es.get_eigenvalue(ieig); int n; es.get_eigenvector(ieig, &ref_coeff_vec, &n); // Convert coefficient vector into a Solution. Solution::vector_to_solution(ref_coeff_vec, ref_space, &(ref_sln[ieig])); // Project the fine mesh solution onto the coarse mesh. info("Projecting reference solution %d on coarse mesh.", ieig); OGProjection::project_global(&space, &(ref_sln[ieig]), &(sln[ieig]), matrix_solver); } // FIXME: Below, the adaptivity is done for the last eigenvector only, // this needs to be changed to take into account all eigenvectors. // View the coarse mesh solution and polynomial orders. //char title[100]; //if (NUMBER_OF_EIGENVALUES > 0) { // sprintf(title, "Solution 0, val = %g", eigenval[0]); // sview_1.set_title(title); // sview_1.show(&(sln[0])); //} //if (NUMBER_OF_EIGENVALUES > 1) { // sprintf(title, "Solution 1, val = %g", eigenval[1]); // sview_2.set_title(title); // sview_2.show(&(sln[1])); //} //if (NUMBER_OF_EIGENVALUES > 2) { // sprintf(title, "Solution 2, val = %g", eigenval[2]); // sview_3.set_title(title); // sview_3.show(&(sln[2])); //} //if (NUMBER_OF_EIGENVALUES > 3) { // sprintf(title, "Solution 3, val = %g", eigenval[3]); // sview_4.set_title(title); // sview_4.show(&(sln[3])); //} //if (NUMBER_OF_EIGENVALUES > 4) { // sprintf(title, "Solution 4, val = %g", eigenval[4]); // sview_5.set_title(title); // sview_5.show(&(sln[4])); //} //if (NUMBER_OF_EIGENVALUES > 5) { // sprintf(title, "Solution 5, val = %g", eigenval[5]); // sview_6.set_title(title); // sview_6.show(&(sln[5])); //} //oview.show(&space); // Calculate element errors and total error estimate. info("Calculating error estimate."); Hermes::vector<Space *> spaces; for(int i = 0; i < NUMBER_OF_EIGENVALUES; i++) spaces.push_back(&space); Adapt* adaptivity = new Adapt(spaces); Hermes::vector<Solution *> slns; if (NUMBER_OF_EIGENVALUES > 0) slns.push_back(&sln[0]); if (NUMBER_OF_EIGENVALUES > 1) slns.push_back(&sln[1]); if (NUMBER_OF_EIGENVALUES > 2) slns.push_back(&sln[2]); if (NUMBER_OF_EIGENVALUES > 3) slns.push_back(&sln[3]); if (NUMBER_OF_EIGENVALUES > 4) slns.push_back(&sln[4]); if (NUMBER_OF_EIGENVALUES > 5) slns.push_back(&sln[5]); Hermes::vector<Solution *> ref_slns; if (NUMBER_OF_EIGENVALUES > 0) ref_slns.push_back(&ref_sln[0]); if (NUMBER_OF_EIGENVALUES > 1) ref_slns.push_back(&ref_sln[1]); if (NUMBER_OF_EIGENVALUES > 2) ref_slns.push_back(&ref_sln[2]); if (NUMBER_OF_EIGENVALUES > 3) ref_slns.push_back(&ref_sln[3]); if (NUMBER_OF_EIGENVALUES > 4) ref_slns.push_back(&ref_sln[4]); if (NUMBER_OF_EIGENVALUES > 5) ref_slns.push_back(&ref_sln[5]); Hermes::vector<double> component_errors; double err_est_rel = adaptivity->calc_err_est(slns, ref_slns, &component_errors) * 100; // Report results. info("ndof_coarse: %d, ndof_fine: %d.", Space::get_num_dofs(&space), Space::get_num_dofs(ref_space)); if (NUMBER_OF_EIGENVALUES > 0) info("err_est_rel[0]: %g%%", component_errors[0] * 100); if (NUMBER_OF_EIGENVALUES > 1) info("err_est_rel[1]: %g%%", component_errors[1] * 100); if (NUMBER_OF_EIGENVALUES > 2) info("err_est_rel[2]: %g%%", component_errors[2] * 100); if (NUMBER_OF_EIGENVALUES > 3) info("err_est_rel[3]: %g%%", component_errors[3] * 100); if (NUMBER_OF_EIGENVALUES > 4) info("err_est_rel[4]: %g%%", component_errors[4] * 100); if (NUMBER_OF_EIGENVALUES > 5) info("err_est_rel[5]: %g%%", component_errors[5] * 100); // Time measurement. cpu_time.tick(); // Add entry to DOF and CPU convergence graphs. graph_dof_est.add_values(Space::get_num_dofs(&space), err_est_rel); graph_dof_est.save("conv_dof_est.dat"); graph_cpu_est.add_values(cpu_time.accumulated(), err_est_rel); graph_cpu_est.save("conv_cpu_est.dat"); // Wait for keypress. //View::wait(HERMES_WAIT_KEYPRESS); // If err_est too large, adapt the mesh. if (err_est_rel < ERR_STOP) done = true; else { info("Adapting coarse mesh."); Hermes::vector<RefinementSelectors::Selector *> selectors; for(int i = 0; i < NUMBER_OF_EIGENVALUES; i++) selectors.push_back(&selector); done = adaptivity->adapt(selectors, THRESHOLD, STRATEGY, MESH_REGULARITY); // Increase the counter of performed adaptivity steps. if (done == false) as++; } if (Space::get_num_dofs(&space) >= NDOF_STOP) done = true; // Clean up. delete solver; delete adaptivity; if(done == false) delete ref_space->get_mesh(); delete ref_space; delete dp_left; delete dp_right; } while (done == false); int ndof = Space::get_num_dofs(&space); if (ndof < 450) { // Was 401 when this test was created. printf("Success!\n"); return ERR_SUCCESS; } else { printf("Failure!\n"); return ERR_FAILURE; } }
bool rk_time_step(double current_time, double time_step, ButcherTable* const bt, scalar* coeff_vec, scalar* err_vec, DiscreteProblem* dp, MatrixSolverType matrix_solver, bool verbose, bool is_linear, double newton_tol, int newton_max_iter, double newton_damping_coeff, double newton_max_allowed_residual_norm) { // Check for not implemented features. if (matrix_solver != SOLVER_UMFPACK) error("Sorry, rk_time_step() still only works with UMFpack."); if (dp->get_weak_formulation()->get_neq() > 1) error("Sorry, rk_time_step() does not work with systems yet."); // Get number of stages from the Butcher's table. int num_stages = bt->get_size(); // Check whether the user provided a second B-row if he wants // err_vec. if(err_vec != NULL) { double b2_coeff_sum = 0; for (int i=0; i < num_stages; i++) b2_coeff_sum += fabs(bt->get_B2(i)); if (b2_coeff_sum < 1e-10) error("err_vec != NULL but the B2 row in the Butcher's table is zero in rk_time_step()."); } // Matrix for the time derivative part of the equation (left-hand side). UMFPackMatrix* matrix_left = new UMFPackMatrix(); // Matrix and vector for the rest (right-hand side). UMFPackMatrix* matrix_right = new UMFPackMatrix(); UMFPackVector* vector_right = new UMFPackVector(); // Create matrix solver. Solver* solver = create_linear_solver(matrix_solver, matrix_right, vector_right); // Get original space, mesh, and ndof. dp->get_space(0); Mesh* mesh = dp->get_space(0)->get_mesh(); int ndof = dp->get_space(0)->get_num_dofs(); // Create spaces for stage solutions. This is necessary // to define a num_stages x num_stages block weak formulation. Hermes::vector<Space*> stage_spaces; stage_spaces.push_back(dp->get_space(0)); for (int i = 1; i < num_stages; i++) { stage_spaces.push_back(dp->get_space(0)->dup(mesh)); } Space::assign_dofs(stage_spaces); // Create a multistage weak formulation. WeakForm stage_wf_left; // For the matrix M (size ndof times ndof). WeakForm stage_wf_right(num_stages); // For the rest of equation (written on the right), // size num_stages*ndof times num_stages*ndof. create_stage_wf(current_time, time_step, bt, dp, &stage_wf_left, &stage_wf_right); // Initialize discrete problems for the assembling of the // matrix M and the stage Jacobian matrix and residual. DiscreteProblem stage_dp_left(&stage_wf_left, dp->get_space(0)); DiscreteProblem stage_dp_right(&stage_wf_right, stage_spaces); // Vector K_vector of length num_stages * ndof. will represent // the 'k_i' vectors in the usual R-K notation. scalar* K_vector = new scalar[num_stages*ndof]; memset(K_vector, 0, num_stages * ndof * sizeof(scalar)); // Vector u_prev_vec will represent y_n + h \sum_{j=1}^s a_{ij}k_i // in the usual R-K notation. scalar* u_prev_vec = new scalar[num_stages*ndof]; // Vector for the left part of the residual. scalar* vector_left = new scalar[num_stages*ndof]; // Prepare residuals of stage solutions. Hermes::vector<Solution*> residuals; Hermes::vector<bool> add_dir_lift; for (int i = 0; i < num_stages; i++) { residuals.push_back(new Solution(mesh)); add_dir_lift.push_back(false); } // Assemble the block-diagonal mass matrix M of size ndof times ndof. // The corresponding part of the global residual vector is obtained // just by multiplication. stage_dp_left.assemble(matrix_left); // The Newton's loop. double residual_norm; int it = 1; while (true) { // Prepare vector Y_n + h\sum_{j=1}^s a_{ij} K_j. for (int i = 0; i < num_stages; i++) { // block row for (int idx = 0; idx < ndof; idx++) { scalar increment = 0; for (int j = 0; j < num_stages; j++) { increment += bt->get_A(i, j) * K_vector[j*ndof + idx]; } u_prev_vec[i*ndof + idx] = coeff_vec[idx] + time_step * increment; } } multiply_as_diagonal_block_matrix(matrix_left, num_stages, K_vector, vector_left); // Assemble the block Jacobian matrix of the stationary residual F // Diagonal blocks are created even if empty, so that matrix_left // can be added later. bool rhs_only = false; bool force_diagonal_blocks = true; stage_dp_right.assemble(u_prev_vec, matrix_right, vector_right, rhs_only, force_diagonal_blocks); matrix_right->add_to_diagonal_blocks(num_stages, matrix_left); vector_right->add_vector(vector_left); // Multiply the residual vector with -1 since the matrix // equation reads J(Y^n) \deltaY^{n+1} = -F(Y^n). vector_right->change_sign(); // Measure the residual norm. if (HERMES_RESIDUAL_AS_VECTOR_RK) { // Calculate the l2-norm of residual vector. residual_norm = get_l2_norm(vector_right); } else { // Translate residual vector into residual functions. Solution::vector_to_solutions(vector_right, stage_dp_right.get_spaces(), residuals, add_dir_lift); residual_norm = calc_norms(residuals); } // Info for the user. if (verbose) info("---- Newton iter %d, ndof %d, residual norm %g", it, ndof, residual_norm); // If maximum allowed residual norm is exceeded, fail. if (residual_norm > newton_max_allowed_residual_norm) { if (verbose) { info("Current residual norm: %g", residual_norm); info("Maximum allowed residual norm: %g", newton_max_allowed_residual_norm); info("Newton solve not successful, returning false."); } return false; } // If residual norm is within tolerance, or the maximum number // of iteration has been reached, or the problem is linear, then quit. if ((residual_norm < newton_tol || it > newton_max_iter) && it > 1) break; // Solve the linear system. if(!solver->solve()) error ("Matrix solver failed.\n"); // Add \deltaY^{n+1} to Y^n. for (int i = 0; i < num_stages*ndof; i++) { K_vector[i] += newton_damping_coeff * solver->get_solution()[i]; } // If the problem is linear, quit. if (is_linear) { if (verbose) { info("Terminating Newton's loop as problem is linear."); } break; } // Increase iteration counter. it++; } // If max number of iterations was exceeded, fail. if (it >= newton_max_iter) { if (verbose) info("Maximum allowed number of Newton iterations exceeded, returning false."); return false; } // Calculate the vector \sum_{j=1}^s b_j k_j. Vector* rk_increment_vector = create_vector(matrix_solver); rk_increment_vector->alloc(ndof); for (int i = 0; i < ndof; i++) { rk_increment_vector->set(i, 0); for (int j = 0; j < num_stages; j++) { rk_increment_vector->add(i, bt->get_B(j) * K_vector[j*ndof + i]); } } // Calculate Y^{n+1} = Y^n + h \sum_{j=1}^s b_j k_j. for (int i = 0; i < ndof; i++) coeff_vec[i] += time_step * rk_increment_vector->get(i); // If err_vec is not NULL, use the second B-row in the Butcher's // table to calculate the second approximation Y_{n+1}. Then // subtract the original one from it, and return this as an // error vector err_vec. if (err_vec != NULL) { for (int i = 0; i < ndof; i++) { rk_increment_vector->set(i, 0); for (int j = 0; j < num_stages; j++) { rk_increment_vector->add(i, bt->get_B2(j) * K_vector[j*ndof + i]); } } for (int i = 0; i < ndof; i++) err_vec[i] = time_step * rk_increment_vector->get(i); for (int i = 0; i < ndof; i++) err_vec[i] = err_vec[i] - coeff_vec[i]; } // Clean up. delete matrix_left; delete matrix_right; delete vector_right; delete solver; delete rk_increment_vector; // Delete stage spaces, but not the first (original) one. for (int i = 1; i < num_stages; i++) delete stage_spaces[i]; // Delete all residuals. for (int i = 0; i < num_stages; i++) delete residuals[i]; // TODO: Delete stage_wf, in particular its external solutions // stage_time_sol[i], i = 0, 1, ..., num_stages-1. // Delete stage_vec and u_prev_vec. delete [] K_vector; delete [] u_prev_vec; // debug delete [] vector_left; return true; }