/// Calculates the absolute error between sln1 and sln2 using function fn double calc_abs_error(double (*fn)(MeshFunction*, MeshFunction*, RefMap*, RefMap*), MeshFunction* sln1, MeshFunction* sln2) { // sanity checks if (fn == NULL) error("error norm function is NULL in calc_abs_error()."); if (sln1 == NULL) error("sln1 is NULL in calc_abs_error()."); if (sln2 == NULL) error("sln2 is NULL in calc_abs_error()."); Quad2D* quad = &g_quad_2d_std; sln1->set_quad_2d(quad); sln2->set_quad_2d(quad); Mesh* meshes[2] = { sln1->get_mesh(), sln2->get_mesh() }; Transformable* tr[2] = { sln1, sln2 }; Traverse trav; trav.begin(2, meshes, tr); double error = 0.0; Element** ee; while ((ee = trav.get_next_state(NULL, NULL)) != NULL) { update_limit_table(ee[0]->get_mode()); RefMap* ru = sln1->get_refmap(); RefMap* rv = sln2->get_refmap(); error += fn(sln1, sln2, ru, rv); } trav.finish(); return sqrt(error); }
/// Calculates the absolute error between sln1 and sln2 using function fn double calc_error(double (*fn)(MeshFunction*, MeshFunction*, int, QuadPt3D*), MeshFunction *sln1, MeshFunction *sln2) { _F_ Mesh *meshes[2] = { sln1->get_mesh(), sln2->get_mesh() }; Transformable *tr[2] = { sln1, sln2 }; Traverse trav; trav.begin(2, meshes, tr); double error = 0.0; Element **ee; while ((ee = trav.get_next_state(NULL, NULL)) != NULL) { ElementMode3D mode = ee[0]->get_mode(); RefMap *ru = sln1->get_refmap(); Ord3 order = max(sln1->get_fn_order(), sln2->get_fn_order()) + ru->get_inv_ref_order(); order.limit(); Quad3D *quad = get_quadrature(mode); int np = quad->get_num_points(order); QuadPt3D *pt = quad->get_points(order); error += fn(sln1, sln2, np, pt); } trav.finish(); return error > H3D_TINY ? sqrt(error) : error; // do not ruin the precision by taking the sqrt }
void Filter::init() { _F_ // construct the union mesh, if necessary Mesh *meshes[4] = { sln[0]->get_mesh(), (num >= 2) ? sln[1]->get_mesh() : NULL, (num >= 3) ? sln[2]->get_mesh() : NULL, (num >= 4) ? sln[3]->get_mesh() : NULL }; mesh = meshes[0]; unimesh = false; // FIXME: better detection of same meshes for (int i = 1; i < num; i++) if (meshes[0] != meshes[1]) unimesh = true; if (unimesh) { Traverse trav; trav.begin(num, meshes); mesh = new Mesh; MEM_CHECK(mesh); unidata = trav.construct_union_mesh(mesh); trav.finish(); } refmap->set_mesh(mesh); // misc init num_components = 1; memset(tables, 0, sizeof(tables)); memset(sln_sub, 0, sizeof(sln_sub)); }
void DiscreteProblem::precalculate_sparse_structure(Page** pages) { int i, j, m, n; AsmList* al = new AsmList[neq]; // init multi-mesh traversal Mesh** meshes = new Mesh*[neq]; for (i = 0; i < neq; i++) meshes[i] = spaces[i]->get_mesh(); Traverse trav; trav.begin(neq, meshes); // loop through all triangles Element** e; while ((e = trav.get_next_state(NULL, NULL)) != NULL) { // obtain assembly lists for the element at all spaces for (i = 0; i < neq; i++) spaces[i]->get_element_assembly_list(e[i], al + i); // todo: neziskavat znova, pokud se element nezmenil // go through all equation-blocks of the local stiffness matrix for (m = 0; m < neq; m++) { for (n = 0; n < neq; n++) { BiForm* bf = biform[m] + n; if (bf->sym == NULL && bf->unsym == NULL && bf->surf == NULL) continue; // pretend assembling of the element stiffness matrix for (j = 0; j < al[n].cnt; j++) { // skip dirichlet dofs in 'j' if (al[n].dof[j] < 0) continue; for (i = 0; i < al[m].cnt; i++) { // skip dirichlet dofs in 'i' if (al[m].dof[i] < 0) continue; // register the corresponding nonzero matrix element page_add_ij(pages, al[n].dof[j], al[m].dof[i]); // column-oriented (UMFPACK) //page_add_ij(pages, al[m].dof[i], al[n].dof[j]); // row-oriented (PETSc) } } } } } trav.finish(); delete [] meshes; delete [] al; }
void Filter<Scalar>::init() { // construct the union mesh, if necessary const Mesh* meshes[10]; for(int i = 0; i < this->num; i++) meshes[i] = this->sln[i]->get_mesh(); this->mesh = meshes[0]; unimesh = false; for (int i = 1; i < num; i++) { if(meshes[i] == NULL) { this->warn("You may be initializing a Filter with Solution that is missing a Mesh."); throw Hermes::Exceptions::Exception("this->meshes[%d] is NULL in Filter<Scalar>::init().", i); } if(meshes[i]->get_seq() != this->mesh->get_seq()) { unimesh = true; break; } } if(unimesh) { Traverse trav; trav.begin(num, meshes); this->mesh = new Mesh; unidata = trav.construct_union_mesh(const_cast<Hermes2D::Mesh*>(this->mesh)); trav.finish(); } // misc init this->num_components = 1; this->order = 0; memset(sln_sub, 0, sizeof(sln_sub)); set_quad_2d(&g_quad_2d_std); this->deleteSolutions = false; }
bool adapt_velocity(Mesh* mesh, Mesh* rmesh, MeshFunction* sln, MeshFunction* rsln, Space* space) { int i, j; int ne = mesh->get_max_element_id() + 1; double *elem_error = new double[ne]; memset(elem_error, 0, sizeof(double) * ne); double error; int num = mesh->get_num_active_elements(); ElemList* elist = new ElemList[num]; Quad2D* quad = &g_quad_2d_std; sln->set_quad_2d(quad); rsln->set_quad_2d(quad); Mesh* meshes[2] = { mesh, rmesh }; Transformable* tr[2] = { sln, rsln }; Traverse trav; trav.begin(2, meshes, tr); Element** ee; while ((ee = trav.get_next_state(NULL, NULL)) != NULL) { RefMap* ru = sln->get_refmap(); RefMap* rr = rsln->get_refmap(); elem_error[ee[0]->id] += int_l2_error(sln, rsln, ru, rr); } trav.finish(); Element* e; double total_err = 0.0; int n = 0; for_all_active_elements(e, mesh) { elist[n].id = e->id; elist[n].error = elem_error[e->id]; total_err += elem_error[e->id]; n++; }
void Filter::init() { // construct the union mesh, if necessary Mesh* meshes[10]; for(int i = 0; i < this->num; i++) meshes[i] = this->sln[i]->get_mesh(); mesh = meshes[0]; unimesh = false; for (int i = 1; i < num; i++) { if (meshes[i] == NULL) { warn("You may be initializing a Filter with Solution that is missing a Mesh."); error("this->meshes[%d] is NULL in Filter::init().", i); } if (meshes[i]->get_seq() != mesh->get_seq()) { unimesh = true; break; } } if (unimesh) { Traverse trav; trav.begin(num, meshes); mesh = new Mesh; unidata = trav.construct_union_mesh(mesh); trav.finish(); } // misc init num_components = 1; order = 0; for(int i = 0; i < 10; i++) tables[i] = new LightArray<LightArray<Node*>*>; memset(sln_sub, 0, sizeof(sln_sub)); set_quad_2d(&g_quad_2d_std); }
double HcurlOrthoHP::calc_error_n(int n, ...) { int i, j, k; if (n != num) error("Wrong number of solutions."); // obtain solutions and bilinear forms va_list ap; va_start(ap, n); for (i = 0; i < n; i++) { sln[i] = va_arg(ap, Solution*); sln[i]->set_quad_2d(&g_quad_2d_std); } for (i = 0; i < n; i++) { rsln[i] = va_arg(ap, Solution*); rsln[i]->set_quad_2d(&g_quad_2d_std); } va_end(ap); // prepare multi-mesh traversal and error arrays AUTOLA_OR(Mesh*, meshes, 2*num); AUTOLA_OR(Transformable*, tr, 2*num); Traverse trav; nact = 0; for (i = 0; i < num; i++) { meshes[i] = sln[i]->get_mesh(); meshes[i+num] = rsln[i]->get_mesh(); tr[i] = sln[i]; tr[i+num] = rsln[i]; nact += sln[i]->get_mesh()->get_num_active_elements(); int max = meshes[i]->get_max_element_id(); if (errors[i] != NULL) delete [] errors[i]; errors[i] = new double[max]; memset(errors[i], 0, sizeof(double) * max); } double total_norm = 0.0; AUTOLA_OR(double, norms, num); memset(norms, 0, num*sizeof(double)); double total_error = 0.0; if (esort != NULL) delete [] esort; esort = new int2[nact]; Element** ee; trav.begin(2*num, meshes, tr); while ((ee = trav.get_next_state(NULL, NULL)) != NULL) { for (i = 0; i < num; i++) { RefMap* rmi = sln[i]->get_refmap(); RefMap* rrmi = rsln[i]->get_refmap(); for (j = 0; j < num; j++) { RefMap* rmj = sln[j]->get_refmap(); RefMap* rrmj = rsln[j]->get_refmap(); double e, t; if (form[i][j] != NULL) { #ifndef COMPLEX e = fabs(eval_error(form[i][j], ord[i][j], sln[i], sln[j], rsln[i], rsln[j], rmi, rmj, rrmi, rrmj)); t = fabs(eval_norm(form[i][j], ord[i][j], rsln[i], rsln[j], rrmi, rrmj)); #else e = std::abs(eval_error(form[i][j], ord[i][j], sln[i], sln[j], rsln[i], rsln[j], rmi, rmj, rrmi, rrmj)); t = std::abs(eval_norm(form[i][j], ord[i][j], rsln[i], rsln[j], rrmi, rrmj)); #endif norms[i] += t; total_norm += t; total_error += e; errors[i][ee[i]->id] += e; } } } } trav.finish(); Element* e; k = 0; for (i = 0; i < num; i++) for_all_active_elements(e, meshes[i]) { esort[k][0] = e->id; esort[k++][1] = i; errors[i][e->id] /= norms[i]; }
double KellyTypeAdapt::calc_err_internal(Hermes::vector<Solution *> slns, Hermes::vector<double>* component_errors, unsigned int error_flags) { int n = slns.size(); error_if (n != this->num, "Wrong number of solutions."); TimePeriod tmr; for (int i = 0; i < n; i++) { this->sln[i] = slns[i]; sln[i]->set_quad_2d(&g_quad_2d_std); } have_coarse_solutions = true; WeakForm::Stage stage; num_act_elems = 0; for (int i = 0; i < num; i++) { stage.meshes.push_back(sln[i]->get_mesh()); stage.fns.push_back(sln[i]); num_act_elems += stage.meshes[i]->get_num_active_elements(); int max = stage.meshes[i]->get_max_element_id(); if (errors[i] != NULL) delete [] errors[i]; errors[i] = new double[max]; memset(errors[i], 0.0, sizeof(double) * max); } /* for (unsigned int i = 0; i < error_estimators_vol.size(); i++) trset.insert(error_estimators_vol[i].ext.begin(), error_estimators_vol[i].ext.end()); for (unsigned int i = 0; i < error_estimators_surf.size(); i++) trset.insert(error_estimators_surf[i].ext.begin(), error_estimators_surf[i].ext.end()); */ double total_norm = 0.0; bool calc_norm = false; if ((error_flags & HERMES_ELEMENT_ERROR_MASK) == HERMES_ELEMENT_ERROR_REL || (error_flags & HERMES_TOTAL_ERROR_MASK) == HERMES_TOTAL_ERROR_REL) calc_norm = true; double *norms = NULL; if (calc_norm) { norms = new double[num]; memset(norms, 0.0, num * sizeof(double)); } double *errors_components = new double[num]; memset(errors_components, 0.0, num * sizeof(double)); this->errors_squared_sum = 0.0; double total_error = 0.0; bool bnd[4]; // FIXME: magic number - maximal possible number of element surfaces SurfPos surf_pos[4]; Element **ee; Traverse trav; // Reset the e->visited status of each element of each mesh (most likely it will be set to true from // the latest assembling procedure). if (ignore_visited_segments) { for (int i = 0; i < num; i++) { Element* e; for_all_active_elements(e, stage.meshes[i]) e->visited = false; } } //WARNING: AD HOC debugging parameter. bool multimesh = false; // Begin the multimesh traversal. trav.begin(num, &(stage.meshes.front()), &(stage.fns.front())); while ((ee = trav.get_next_state(bnd, surf_pos)) != NULL) { // Go through all solution components. for (int i = 0; i < num; i++) { if (ee[i] == NULL) continue; // Set maximum integration order for use in integrals, see limit_order() update_limit_table(ee[i]->get_mode()); RefMap *rm = sln[i]->get_refmap(); double err = 0.0; // Go through all volumetric error estimators. for (unsigned int iest = 0; iest < error_estimators_vol.size(); iest++) { // Skip current error estimator if it is assigned to a different component or geometric area // different from that of the current active element. if (error_estimators_vol[iest]->i != i) continue; /* if (error_estimators_vol[iest].area != ee[i]->marker) continue; */ else if (error_estimators_vol[iest]->area != HERMES_ANY) continue; err += eval_volumetric_estimator(error_estimators_vol[iest], rm); } // Go through all surface error estimators (includes both interface and boundary est's). for (unsigned int iest = 0; iest < error_estimators_surf.size(); iest++) { if (error_estimators_surf[iest]->i != i) continue; for (int isurf = 0; isurf < ee[i]->get_num_surf(); isurf++) { /* if (error_estimators_surf[iest].area > 0 && error_estimators_surf[iest].area != surf_pos[isurf].marker) continue; */ if (bnd[isurf]) // Boundary { if (error_estimators_surf[iest]->area == H2D_DG_INNER_EDGE) continue; /* if (boundary_markers_conversion.get_internal_marker(error_estimators_surf[iest].area) < 0 && error_estimators_surf[iest].area != HERMES_ANY) continue; */ err += eval_boundary_estimator(error_estimators_surf[iest], rm, surf_pos); } else // Interface { if (error_estimators_surf[iest]->area != H2D_DG_INNER_EDGE) continue; /* BEGIN COPY FROM DISCRETE_PROBLEM.CPP */ // 5 is for bits per page in the array. LightArray<NeighborSearch*> neighbor_searches(5); unsigned int num_neighbors = 0; DiscreteProblem::NeighborNode* root; int ns_index; dp.min_dg_mesh_seq = 0; for(int j = 0; j < num; j++) if(stage.meshes[j]->get_seq() < dp.min_dg_mesh_seq || j == 0) dp.min_dg_mesh_seq = stage.meshes[j]->get_seq(); ns_index = stage.meshes[i]->get_seq() - dp.min_dg_mesh_seq; // = 0 for single mesh // Determine the minimum mesh seq in this stage. if (multimesh) { // Initialize the NeighborSearches. dp.init_neighbors(neighbor_searches, stage, isurf); // Create a multimesh tree; root = new DiscreteProblem::NeighborNode(NULL, 0); dp.build_multimesh_tree(root, neighbor_searches); // Update all NeighborSearches according to the multimesh tree. // After this, all NeighborSearches in neighbor_searches should have the same count // of neighbors and proper set of transformations // for the central and the neighbor element(s) alike. // Also check that every NeighborSearch has the same number of neighbor elements. for(unsigned int j = 0; j < neighbor_searches.get_size(); j++) if(neighbor_searches.present(j)) { NeighborSearch* ns = neighbor_searches.get(j); dp.update_neighbor_search(ns, root); if(num_neighbors == 0) num_neighbors = ns->n_neighbors; if(ns->n_neighbors != num_neighbors) error("Num_neighbors of different NeighborSearches not matching in KellyTypeAdapt::calc_err_internal."); } } else { NeighborSearch *ns = new NeighborSearch(ee[i], stage.meshes[i]); ns->original_central_el_transform = stage.fns[i]->get_transform(); ns->set_active_edge(isurf); ns->clear_initial_sub_idx(); num_neighbors = ns->n_neighbors; neighbor_searches.add(ns, ns_index); } // Go through all segments of the currently processed interface (segmentation is caused // by hanging nodes on the other side of the interface). for (unsigned int neighbor = 0; neighbor < num_neighbors; neighbor++) { if (ignore_visited_segments) { bool processed = true; for(unsigned int j = 0; j < neighbor_searches.get_size(); j++) if(neighbor_searches.present(j)) if(!neighbor_searches.get(j)->neighbors.at(neighbor)->visited) { processed = false; break; } if (processed) continue; } // Set the active segment in all NeighborSearches for(unsigned int j = 0; j < neighbor_searches.get_size(); j++) if(neighbor_searches.present(j)) { neighbor_searches.get(j)->active_segment = neighbor; neighbor_searches.get(j)->neighb_el = neighbor_searches.get(j)->neighbors[neighbor]; neighbor_searches.get(j)->neighbor_edge = neighbor_searches.get(j)->neighbor_edges[neighbor]; } // Push all the necessary transformations to all functions of this stage. // The important thing is that the transformations to the current subelement are already there. // Also store the current neighbor element and neighbor edge in neighb_el, neighbor_edge. if (multimesh) { for(unsigned int fns_i = 0; fns_i < stage.fns.size(); fns_i++) for(unsigned int trf_i = 0; trf_i < neighbor_searches.get(stage.meshes[fns_i]->get_seq() - dp.min_dg_mesh_seq)->central_n_trans[neighbor]; trf_i++) stage.fns[fns_i]->push_transform(neighbor_searches.get(stage.meshes[fns_i]->get_seq() - dp.min_dg_mesh_seq)->central_transformations[neighbor][trf_i]); } else { // Push the transformations only to the solution on the current mesh for(unsigned int trf_i = 0; trf_i < neighbor_searches.get(ns_index)->central_n_trans[neighbor]; trf_i++) stage.fns[i]->push_transform(neighbor_searches.get(ns_index)->central_transformations[neighbor][trf_i]); } /* END COPY FROM DISCRETE_PROBLEM.CPP */ rm->force_transform(this->sln[i]->get_transform(), this->sln[i]->get_ctm()); // The estimate is multiplied by 0.5 in order to distribute the error equally onto // the two neighboring elements. double central_err = 0.5 * eval_interface_estimator(error_estimators_surf[iest], rm, surf_pos, neighbor_searches, ns_index); double neighb_err = central_err; // Scale the error estimate by the scaling function dependent on the element diameter // (use the central element's diameter). if (use_aposteriori_interface_scaling && interface_scaling_fns[i]) central_err *= interface_scaling_fns[i](ee[i]->get_diameter()); // In the case this edge will be ignored when calculating the error for the element on // the other side, add the now computed error to that element as well. if (ignore_visited_segments) { Element *neighb = neighbor_searches.get(i)->neighb_el; // Scale the error estimate by the scaling function dependent on the element diameter // (use the diameter of the element on the other side). if (use_aposteriori_interface_scaling && interface_scaling_fns[i]) neighb_err *= interface_scaling_fns[i](neighb->get_diameter()); errors_components[i] += central_err + neighb_err; total_error += central_err + neighb_err; errors[i][ee[i]->id] += central_err; errors[i][neighb->id] += neighb_err; } else err += central_err; /* BEGIN COPY FROM DISCRETE_PROBLEM.CPP */ // Clear the transformations from the RefMaps and all functions. if (multimesh) for(unsigned int fns_i = 0; fns_i < stage.fns.size(); fns_i++) stage.fns[fns_i]->set_transform(neighbor_searches.get(stage.meshes[fns_i]->get_seq() - dp.min_dg_mesh_seq)->original_central_el_transform); else stage.fns[i]->set_transform(neighbor_searches.get(ns_index)->original_central_el_transform); rm->set_transform(neighbor_searches.get(ns_index)->original_central_el_transform); /* END COPY FROM DISCRETE_PROBLEM.CPP */ } /* BEGIN COPY FROM DISCRETE_PROBLEM.CPP */ if (multimesh) // Delete the multimesh tree; delete root; // Delete the neighbor_searches array. for(unsigned int j = 0; j < neighbor_searches.get_size(); j++) if(neighbor_searches.present(j)) delete neighbor_searches.get(j); /* END COPY FROM DISCRETE_PROBLEM.CPP */ } } } if (calc_norm) { double nrm = eval_solution_norm(error_form[i][i], rm, sln[i]); norms[i] += nrm; total_norm += nrm; } errors_components[i] += err; total_error += err; errors[i][ee[i]->id] += err; ee[i]->visited = true; } } trav.finish(); // Store the calculation for each solution component separately. if(component_errors != NULL) { component_errors->clear(); for (int i = 0; i < num; i++) { if((error_flags & HERMES_TOTAL_ERROR_MASK) == HERMES_TOTAL_ERROR_ABS) component_errors->push_back(sqrt(errors_components[i])); else if ((error_flags & HERMES_TOTAL_ERROR_MASK) == HERMES_TOTAL_ERROR_REL) component_errors->push_back(sqrt(errors_components[i]/norms[i])); else { error("Unknown total error type (0x%x).", error_flags & HERMES_TOTAL_ERROR_MASK); return -1.0; } } } tmr.tick(); error_time = tmr.accumulated(); // Make the error relative if needed. if ((error_flags & HERMES_ELEMENT_ERROR_MASK) == HERMES_ELEMENT_ERROR_REL) { for (int i = 0; i < num; i++) { Element* e; for_all_active_elements(e, stage.meshes[i]) errors[i][e->id] /= norms[i]; } } this->errors_squared_sum = total_error; // Element error mask is used here, because this variable is used in the adapt() // function, where the processed error (sum of errors of processed element errors) // is matched to this variable. if ((error_flags & HERMES_TOTAL_ERROR_MASK) == HERMES_ELEMENT_ERROR_REL) errors_squared_sum /= total_norm; // Prepare an ordered list of elements according to an error. fill_regular_queue(&(stage.meshes.front())); have_errors = true; if (calc_norm) delete [] norms; delete [] errors_components; // Return error value. if ((error_flags & HERMES_TOTAL_ERROR_MASK) == HERMES_TOTAL_ERROR_ABS) return sqrt(total_error); else if ((error_flags & HERMES_TOTAL_ERROR_MASK) == HERMES_TOTAL_ERROR_REL) return sqrt(total_error / total_norm); else { error("Unknown total error type (0x%x).", error_flags & HERMES_TOTAL_ERROR_MASK); return -1.0; } }
void FeProblem::assemble(_Vector *rhs, _Matrix *jac, Tuple<Solution*> u_ext) { // Sanity checks. if (!have_spaces) error("You have to call set_spaces() before calling assemble()."); for (int i=0; i<this->wf->neq; i++) { if (this->spaces[i] == NULL) error("A space is NULL in assemble()."); } int k, m, n, marker; AUTOLA_CL(AsmList, al, wf->neq); AsmList *am, *an; bool bnd[4]; AUTOLA_OR(bool, nat, wf->neq); AUTOLA_OR(bool, isempty, wf->neq); EdgePos ep[4]; reset_warn_order(); // create slave pss's for test functions, init quadrature points AUTOLA_OR(PrecalcShapeset*, spss, wf->neq); PrecalcShapeset *fu, *fv; AUTOLA_CL(RefMap, refmap, wf->neq); for (int i = 0; i < wf->neq; i++) { spss[i] = new PrecalcShapeset(pss[i]); pss [i]->set_quad_2d(&g_quad_2d_std); spss[i]->set_quad_2d(&g_quad_2d_std); refmap[i].set_quad_2d(&g_quad_2d_std); } // initialize buffer buffer = NULL; mat_size = 0; get_matrix_buffer(9); // obtain a list of assembling stages std::vector<WeakForm::Stage> stages; wf->get_stages(spaces, NULL, stages, jac == NULL); // Loop through all assembling stages -- the purpose of this is increased performance // in multi-mesh calculations, where, e.g., only the right hand side uses two meshes. // In such a case, the matrix forms are assembled over one mesh, and only the rhs // traverses through the union mesh. On the other hand, if you don't use multi-mesh // at all, there will always be only one stage in which all forms are assembled as usual. Traverse trav; for (unsigned ss = 0; ss < stages.size(); ss++) { WeakForm::Stage* s = &stages[ss]; for (unsigned i = 0; i < s->idx.size(); i++) s->fns[i] = pss[s->idx[i]]; for (unsigned i = 0; i < s->ext.size(); i++) s->ext[i]->set_quad_2d(&g_quad_2d_std); trav.begin(s->meshes.size(), &(s->meshes.front()), &(s->fns.front())); // assemble one stage Element** e; while ((e = trav.get_next_state(bnd, ep)) != NULL) { // find a non-NULL e[i] Element* e0; for (unsigned int i = 0; i < s->idx.size(); i++) if ((e0 = e[i]) != NULL) break; if (e0 == NULL) continue; // set maximum integration order for use in integrals, see limit_order() update_limit_table(e0->get_mode()); // obtain assembly lists for the element at all spaces, set appropriate mode for each pss memset(isempty, 0, sizeof(bool) * wf->neq); for (unsigned int i = 0; i < s->idx.size(); i++) { int j = s->idx[i]; if (e[i] == NULL) { isempty[j] = true; continue; } spaces[j]->get_element_assembly_list(e[i], al+j); spss[j]->set_active_element(e[i]); spss[j]->set_master_transform(); refmap[j].set_active_element(e[i]); refmap[j].force_transform(pss[j]->get_transform(), pss[j]->get_ctm()); u_ext[j]->set_active_element(e[i]); u_ext[j]->force_transform(pss[j]->get_transform(), pss[j]->get_ctm()); } marker = e0->marker; init_cache(); //// assemble volume matrix forms ////////////////////////////////////// if (jac != NULL) { for (unsigned ww = 0; ww < s->mfvol.size(); ww++) { WeakForm::MatrixFormVol* mfv = s->mfvol[ww]; if (isempty[mfv->i] || isempty[mfv->j]) continue; if (mfv->area != H2D_ANY && !wf->is_in_area(marker, mfv->area)) continue; m = mfv->i; fv = spss[m]; am = &al[m]; n = mfv->j; fu = pss[n]; an = &al[n]; bool tra = (m != n) && (mfv->sym != 0); bool sym = (m == n) && (mfv->sym == 1); // assemble the local stiffness matrix for the form mfv scalar bi, **mat = get_matrix_buffer(std::max(am->cnt, an->cnt)); for (int i = 0; i < am->cnt; i++) { if (!tra && (k = am->dof[i]) < 0) continue; fv->set_active_shape(am->idx[i]); if (!sym) // unsymmetric block { for (int j = 0; j < an->cnt; j++) { fu->set_active_shape(an->idx[j]); bi = eval_form(mfv, u_ext, fu, fv, refmap+n, refmap+m) * an->coef[j] * am->coef[i]; if (an->dof[j] >= 0) mat[i][j] = bi; } } else // symmetric block { for (int j = 0; j < an->cnt; j++) { if (j < i && an->dof[j] >= 0) continue; fu->set_active_shape(an->idx[j]); bi = eval_form(mfv, u_ext, fu, fv, refmap+n, refmap+m) * an->coef[j] * am->coef[i]; if (an->dof[j] >= 0) mat[i][j] = mat[j][i] = bi; } } } // insert the local stiffness matrix into the global one jac->add(am->cnt, an->cnt, mat, am->dof, an->dof); // insert also the off-diagonal (anti-)symmetric block, if required if (tra) { if (mfv->sym < 0) chsgn(mat, am->cnt, an->cnt); transpose(mat, am->cnt, an->cnt); jac->add(am->cnt, an->cnt, mat, am->dof, an->dof); } } } //// assemble volume linear forms //////////////////////////////////////// if (rhs != NULL) { for (unsigned int ww = 0; ww < s->vfvol.size(); ww++) { WeakForm::VectorFormVol* vfv = s->vfvol[ww]; if (isempty[vfv->i]) continue; if (vfv->area != H2D_ANY && !wf->is_in_area(marker, vfv->area)) continue; m = vfv->i; fv = spss[m]; am = &al[m]; for (int i = 0; i < am->cnt; i++) { if (am->dof[i] < 0) continue; fv->set_active_shape(am->idx[i]); rhs->add(am->dof[i], eval_form(vfv, u_ext, fv, refmap + m) * am->coef[i]); } } } // assemble surface integrals now: loop through boundary edges of the element for (unsigned int edge = 0; edge < e0->nvert; edge++) { if (!bnd[edge]) continue; marker = ep[edge].marker; // obtain the list of shape functions which are nonzero on this edge for (unsigned int i = 0; i < s->idx.size(); i++) { if (e[i] == NULL) continue; int j = s->idx[i]; if ((nat[j] = (spaces[j]->bc_type_callback(marker) == BC_NATURAL))) spaces[j]->get_edge_assembly_list(e[i], edge, al + j); } // assemble surface matrix forms /////////////////////////////////// if (jac != NULL) { for (unsigned int ww = 0; ww < s->mfsurf.size(); ww++) { WeakForm::MatrixFormSurf* mfs = s->mfsurf[ww]; if (isempty[mfs->i] || isempty[mfs->j]) continue; if (mfs->area != H2D_ANY && !wf->is_in_area(marker, mfs->area)) continue; m = mfs->i; fv = spss[m]; am = &al[m]; n = mfs->j; fu = pss[n]; an = &al[n]; if (!nat[m] || !nat[n]) continue; ep[edge].base = trav.get_base(); ep[edge].space_v = spaces[m]; ep[edge].space_u = spaces[n]; scalar bi, **mat = get_matrix_buffer(std::max(am->cnt, an->cnt)); for (int i = 0; i < am->cnt; i++) { if ((k = am->dof[i]) < 0) continue; fv->set_active_shape(am->idx[i]); for (int j = 0; j < an->cnt; j++) { fu->set_active_shape(an->idx[j]); bi = eval_form(mfs, u_ext, fu, fv, refmap+n, refmap+m, ep+edge) * an->coef[j] * am->coef[i]; if (an->dof[j] >= 0) mat[i][j] = bi; } } jac->add(am->cnt, an->cnt, mat, am->dof, an->dof); } } // assemble surface linear forms ///////////////////////////////////// if (rhs != NULL) { for (unsigned ww = 0; ww < s->vfsurf.size(); ww++) { WeakForm::VectorFormSurf* vfs = s->vfsurf[ww]; if (isempty[vfs->i]) continue; if (vfs->area != H2D_ANY && !wf->is_in_area(marker, vfs->area)) continue; m = vfs->i; fv = spss[m]; am = &al[m]; if (!nat[m]) continue; ep[edge].base = trav.get_base(); ep[edge].space_v = spaces[m]; for (int i = 0; i < am->cnt; i++) { if (am->dof[i] < 0) continue; fv->set_active_shape(am->idx[i]); rhs->add(am->dof[i], eval_form(vfs, u_ext, fv, refmap+m, ep+edge) * am->coef[i]); } } } } delete_cache(); } trav.finish(); } for (int i = 0; i < wf->neq; i++) delete spss[i]; delete [] buffer; buffer = NULL; mat_size = 0; }
void FeProblem::create(SparseMatrix* mat) { assert(mat != NULL); if (is_up_to_date()) { verbose("Reusing matrix sparse structure."); mat->zero(); return; } // spaces have changed: create the matrix from scratch mat->free(); int ndof = get_num_dofs(); mat->prealloc(ndof); AUTOLA_CL(AsmList, al, wf->neq); AUTOLA_OR(Mesh*, meshes, wf->neq); bool **blocks = wf->get_blocks(); // init multi-mesh traversal for (int i = 0; i < wf->neq; i++) meshes[i] = spaces[i]->get_mesh(); Traverse trav; trav.begin(wf->neq, meshes); // loop through all elements Element **e; while ((e = trav.get_next_state(NULL, NULL)) != NULL) { // obtain assembly lists for the element at all spaces for (int i = 0; i < wf->neq; i++) // TODO: do not get the assembly list again if the element was not changed if (e[i] != NULL) spaces[i]->get_element_assembly_list(e[i], al + i); // go through all equation-blocks of the local stiffness matrix for (int m = 0; m < wf->neq; m++) for (int n = 0; n < wf->neq; n++) if (blocks[m][n] && e[m] != NULL && e[n] != NULL) { AsmList *am = al + m; AsmList *an = al + n; // pretend assembling of the element stiffness matrix // register nonzero elements for (int i = 0; i < am->cnt; i++) if (am->dof[i] >= 0) for (int j = 0; j < an->cnt; j++) if (an->dof[j] >= 0) mat->pre_add_ij(am->dof[i], an->dof[j]); } } trav.finish(); delete [] blocks; mat->alloc(); // save space seq numbers and weakform seq number, so we can detect their changes for (int i = 0; i < wf->neq; i++) sp_seq[i] = spaces[i]->get_seq(); wf_seq = wf->get_seq(); struct_changed = true; have_matrix = true; }
void DiscreteProblem::assemble_matrix_and_rhs(bool rhsonly) { int i, j, k, l, m, n; bool bnd[4], nat[neq]; EdgePos ep[4]; if (!ndofs) return; warned_order = false; if (!rhsonly) { alloc_matrix_values(); if (!quiet) { verbose("Assembling stiffness matrix..."); begin_time(); } } else { memset(RHS, 0, sizeof(scalar) * ndofs); if (!quiet) { verbose("Assembling RHS..."); begin_time(); } } // create slave pss's for test functions, init quadrature points PrecalcShapeset* spss[neq]; PrecalcShapeset *fu, *fv; for (i = 0; i < neq; i++) { spss[i] = new PrecalcShapeset(pss[i]); pss [i]->set_quad_2d(&g_quad_2d_std); spss[i]->set_quad_2d(&g_quad_2d_std); } // initialize buffer buffer = NULL; mat_size = 0; get_matrix_buffer(9); // initialize assembly lists, refmap AsmList al[neq], *am, *an; RefMap* refmap = new RefMap[neq]; for (i = 0; i < neq; i++) refmap[i].set_quad_2d(&g_quad_2d_std); for (i = 0; i < num_extern; i++) extern_fns[i]->set_quad_2d(&g_quad_2d_std); // init multi-mesh traversal int nm = neq + num_extern; Mesh* meshes[nm]; Transformable* fn[nm]; for (i = 0; i < neq; i++) meshes[i] = spaces[i]->get_mesh(); memcpy(fn, pss, neq * sizeof(Transformable*)); for (i = 0; i < num_extern; i++) { meshes[neq+i] = extern_fns[i]->get_mesh(); fn[neq+i] = extern_fns[i]; } // todo: kdyz maji nektere slozky stejnou sit, at sdili i refmapy // - ale to bysme potrebovali slave RefMap // loop through all elements Element** e; Traverse trav; trav.begin(nm, meshes, fn); while ((e = trav.get_next_state(bnd, ep)) != NULL) { // set maximum integration order for use in integrals, see limit_order() update_limit_table(e[0]->get_mode()); // obtain assembly lists for the element at all spaces, set appropriate mode for each pss for (i = 0; i < neq; i++) { spaces[i]->get_element_assembly_list(e[i], al + i); // todo: neziskavat znova, pokud se element nezmenil if (is_equi) for (j = 0; j < al[i].cnt; j++) if (al[i].dof[j] >= 0) al[i].coef[j] /= equi[al[i].dof[j]]; spss[i]->set_active_element(e[i]); spss[i]->set_master_transform(); refmap[i].set_active_element(e[i]); refmap[i].force_transform(pss[i]->get_transform(), pss[i]->get_ctm()); } // go through all equation-blocks of the element stiffness matrix, assemble volume integrals for (m = 0, am = al; m < neq; m++, am++) { fv = spss[m]; if (!rhsonly) { for (n = 0, an = al; n < neq; n++, an++) { fu = pss[n]; BiForm* bf = biform[m] + n; if (!bf->sym && !bf->unsym) continue; if (bf->unsym == BF_SYM || bf->unsym == BF_ANTISYM) continue; bool tra = (biform[n][m].unsym == BF_SYM || biform[n][m].unsym == BF_ANTISYM); // assemble the (m,n)-block of the stiffness matrix scalar sy, un, **mat = get_matrix_buffer(std::max(am->cnt, an->cnt)); for (i = 0; i < am->cnt; i++) { if (!tra && (k = am->dof[i]) < 0) continue; fv->set_active_shape(am->idx[i]); // unsymmetric block if (!bf->sym) { for (j = 0; j < an->cnt; j++) { fu->set_active_shape(an->idx[j]); un = bf->unsym(fu, fv, refmap+n, refmap+m) * an->coef[j] * am->coef[i]; if (an->dof[j] < 0) Dir[k] -= un; else mat[i][j] = un; } } // symmetric block else { for (j = 0; j < an->cnt; j++) { scalar coef = an->coef[j] * am->coef[i]; if (an->dof[j] < 0) { fu->set_active_shape(an->idx[j]); un = bf->unsym ? bf->unsym(fu, fv, refmap+n, refmap+m) * coef : 0.0; sy = bf->sym(fu, fv, refmap+n, refmap+m) * coef; Dir[k] -= (un + sy); } if (j >= i) { fu->set_active_shape(an->idx[j]); un = bf->unsym ? bf->unsym(fu, fv, refmap+n, refmap+m) * coef : 0.0; mat[j][i] = sy = bf->sym (fu, fv, refmap+n, refmap+m) * coef; mat[i][j] = (un + sy); } else if (bf->unsym) { fu->set_active_shape(an->idx[j]); mat[i][j] += bf->unsym(fu, fv, refmap+n, refmap+m) * coef; } } } } // insert the local stiffness matrix into the global one insert_matrix(mat, am->dof, an->dof, am->cnt, an->cnt); // insert also the off-diagonal (anti-)symmetric block, if required if (tra) { if (biform[n][m].unsym == BF_ANTISYM) chsgn(mat, am->cnt, an->cnt); transpose(mat, am->cnt, an->cnt); insert_matrix(mat, an->dof, am->dof, an->cnt, am->cnt); // we also need to take care of the RHS... for (j = 0; j < am->cnt; j++) if (am->dof[j] < 0) for (i = 0; i < an->cnt; i++) if (an->dof[i] >= 0) Dir[an->dof[i]] -= mat[i][j]; } } } // assemble rhs (linear form) if (!liform[m].lf) continue; for (i = 0; i < am->cnt; i++) { if (am->dof[i] < 0) continue; fv->set_active_shape(am->idx[i]); RHS[am->dof[i]] += liform[m].lf(fv, refmap+m) * am->coef[i]; } } // assemble surface integrals now: loop through boundary edges of the element if (rhsonly) continue; // fixme for (int edge = 0; edge < e[0]->nvert; edge++) { if (!bnd[edge]) continue; // obtain the list of shape functions which are nonzero on this edge for (i = 0; i < neq; i++) if ((nat[i] = (spaces[i]->bc_type_callback(ep[edge].marker) == BC_NATURAL))) spaces[i]->get_edge_assembly_list(e[i], edge, al + i); // loop through the equation-blocks for (m = 0, am = al; m < neq; m++, am++) { if (!nat[m]) continue; fv = spss[m]; ep[edge].base = trav.get_base(); ep[edge].space_v = spaces[m]; for (n = 0, an = al; n < neq; n++, an++) { if (!nat[n]) continue; BiForm* bf = biform[m] + n; if (!bf->surf) continue; fu = pss[n]; ep[edge].space_u = spaces[n]; // assemble the surface part of the bilinear form scalar bi, **mat = get_matrix_buffer(std::max(am->cnt, an->cnt)); for (i = 0; i < am->cnt; i++) { if ((k = am->dof[i]) < 0) continue; fv->set_active_shape(am->idx[i]); for (j = 0; j < an->cnt; j++) { fu->set_active_shape(an->idx[j]); bi = bf->surf(fu, fv, refmap+n, refmap+m, ep+edge) * an->coef[j] * am->coef[i]; if (an->dof[j] >= 0) mat[i][j] = bi; else Dir[k] -= bi; } } insert_matrix(mat, am->dof, an->dof, am->cnt, an->cnt); } // assemble the surface part of the linear form if (!liform[m].surf) continue; for (i = 0; i < am->cnt; i++) { if (am->dof[i] < 0) continue; fv->set_active_shape(am->idx[i]); RHS[am->dof[i]] += liform[m].surf(fv, refmap+m, ep+edge) * am->coef[i]; } } } } trav.finish(); for (i = 0; i < ndofs; i++) RHS[i] += Dir[i]; if (!quiet) verbose(" (time: %g sec)", end_time()); for (i = 0; i < neq; i++) delete spss[i]; delete [] buffer; delete [] refmap; }
void Vectorizer::process_solution(MeshFunction* xsln, int xitem, MeshFunction* ysln, int yitem, double eps) { // sanity check if (xsln == NULL || ysln == NULL) error("One of the solutions is NULL in Vectorizer:process_solution()."); lock_data(); TimePeriod cpu_time; // initialization this->xsln = xsln; this->ysln = ysln; this->xitem = xitem; this->yitem = yitem; this->eps = eps; nv = nt = ne = nd = 0; del_slot = -1; Mesh* meshes[2] = { xsln->get_mesh(), ysln->get_mesh() }; if (meshes[0] == NULL || meshes[1] == NULL) { error("One of the meshes is NULL in Vectorizer:process_solution()."); } Transformable* fns[2] = { xsln, ysln }; Traverse trav; // estimate the required number of vertices and triangles // (based on the assumption that the linear mesh will be // about four-times finer than the original mesh). int nn = meshes[0]->get_num_elements() + meshes[1]->get_num_elements(); int ev = std::max(32 * nn, 10000); int et = std::max(64 * nn, 20000); int ee = std::max(24 * nn, 7500); int ed = ee; lin_init_array(verts, double4, cv, ev); lin_init_array(tris, int3, ct, et); lin_init_array(edges, int3, ce, ee); lin_init_array(dashes, int2, cd, ed); info = (int4*) malloc(sizeof(int4) * cv); // initialize the hash table int size = 0x1000; while (size*2 < cv) size *= 2; hash_table = (int*) malloc(sizeof(int) * size); memset(hash_table, 0xff, sizeof(int) * size); mask = size-1; // select the linearization quadrature Quad2D *old_quad_x, *old_quad_y; old_quad_x = xsln->get_quad_2d(); old_quad_y = ysln->get_quad_2d(); xsln->set_quad_2d((Quad2D*) &quad_lin); ysln->set_quad_2d((Quad2D*) &quad_lin); if (!xitem) error("Parameter 'xitem' cannot be zero."); if (!yitem) error("Parameter 'yitem' cannot be zero."); get_gv_a_b(xitem, xia, xib); get_gv_a_b(yitem, yia, yib); if (xib >= 6) error("Invalid value of paremeter 'xitem'."); if (yib >= 6) error("Invalid value of paremeter 'yitem'."); max = 1e-10; trav.begin(2, meshes, fns); Element** e; while ((e = trav.get_next_state(NULL, NULL)) != NULL) { xsln->set_quad_order(0, xitem); ysln->set_quad_order(0, yitem); scalar* xval = xsln->get_values(xia, xib); scalar* yval = ysln->get_values(yia, yib); for (unsigned int i = 0; i < e[0]->nvert; i++) { double fx = getvalx(i); double fy = getvaly(i); if (fabs(sqrt(fx*fx + fy*fy)) > max) max = fabs(sqrt(fx*fx + fy*fy)); } } trav.finish(); trav.begin(2, meshes, fns); // process all elements of the mesh while ((e = trav.get_next_state(NULL, NULL)) != NULL) { xsln->set_quad_order(0, xitem); ysln->set_quad_order(0, yitem); scalar* xval = xsln->get_values(xia, xib); scalar* yval = ysln->get_values(yia, yib); double* x = xsln->get_refmap()->get_phys_x(0); double* y = ysln->get_refmap()->get_phys_y(0); int iv[4]; for (unsigned int i = 0; i < e[0]->nvert; i++) { double fx = getvalx(i); double fy = getvaly(i); iv[i] = create_vertex(x[i], y[i], fx, fy); } // we won't bother calculating physical coordinates from the refmap if this is not a curved element curved = (e[0]->cm != NULL); // recur to sub-elements if (e[0]->is_triangle()) process_triangle(iv[0], iv[1], iv[2], 0, NULL, NULL, NULL, NULL, NULL); else process_quad(iv[0], iv[1], iv[2], iv[3], 0, NULL, NULL, NULL, NULL, NULL); // process edges and dashes (bold line for edge in both meshes, dashed line for edge in one of the meshes) Trf* xctm = xsln->get_ctm(); Trf* yctm = ysln->get_ctm(); double r[4] = { -1.0, 1.0, 1.0, -1.0 }; double ref[4][2] = { {-1.0,-1.0}, {1.0,-1.0}, {1.0,1.0}, {-1.0,1.0} }; for (unsigned int i = 0; i < e[0]->nvert; i++) { bool bold = false; double px = ref[i][0]; double py = ref[i][1]; // for odd edges (1, 3) we check x coordinate after ctm transformation, if it's the same (1 or -1) in both meshes => bold if (i & 1) { if ((xctm->m[0]*px + xctm->t[0] == r[i]) && (yctm->m[0]*px + yctm->t[0] == r[i])) bold = true; } // for even edges (0, 4) we check y coordinate after ctm transformation, if it's the same (-1 or 1) in both meshes => bold else { if ((xctm->m[1]*py + xctm->t[1] == r[i]) && (yctm->m[1]*py + yctm->t[1] == r[i])) bold = true; } int j = e[0]->next_vert(i); // we draw a line only if both edges lies on the boundary or if the line is from left top to right bottom if (((e[0]->en[i]->bnd) && (e[1]->en[i]->bnd)) || (verts[iv[i]][1] < verts[iv[j]][1]) || (verts[iv[i]][1] == verts[iv[j]][1] && verts[iv[i]][0] < verts[iv[j]][0])) { if (bold) process_edge(iv[i], iv[j], e[0]->en[i]->marker); else process_dash(iv[i], iv[j]); } } } trav.finish(); find_min_max(); verbose("Vectorizer created %d verts and %d tris in %0.3g s", nv, nt, cpu_time.tick().last()); //if (verbose_mode) print_hash_stats(); unlock_data(); // select old quadratrues xsln->set_quad_2d(old_quad_x); ysln->set_quad_2d(old_quad_y); // clean up ::free(hash_table); ::free(info); }