//----------------------------------------------------------------------------- void DofMap::set(GenericVector& x, double value) const { std::vector<double> _value; std::vector<std::vector<dolfin::la_index> >::const_iterator cell_dofs; for (cell_dofs = _dofmap.begin(); cell_dofs != _dofmap.end(); ++cell_dofs) { _value.resize(cell_dofs->size(), value); x.set(_value.data(), cell_dofs->size(), cell_dofs->data()); } x.apply("add"); }
//----------------------------------------------------------------------------- void DofMap::set(GenericVector& x, double value) const { dolfin_assert(_dofmap.size() % _cell_dimension == 0); const std::size_t num_cells = _dofmap.size() / _cell_dimension; std::vector<double> _value(_cell_dimension, value); for (std::size_t i = 0; i < num_cells; ++i) { const ArrayView<const la_index> dofs = cell_dofs(i); x.set_local(_value.data(), dofs.size(), dofs.data()); } x.apply("insert"); }
//----------------------------------------------------------------------------- void PointSource::apply(GenericVector& b) { dolfin_assert(_V); log(PROGRESS, "Applying point source to right-hand side vector."); // Find the cell containing the point (may be more than one cell but // we only care about the first). Well-defined if the basis // functions are continuous but may give unexpected results for DG. dolfin_assert(_V->mesh()); const Mesh& mesh = *_V->mesh(); std::shared_ptr<BoundingBoxTree> tree = mesh.bounding_box_tree(); const unsigned int cell_index = tree->compute_first_entity_collision(_p); // Check that we found the point on at least one processor int num_found = 0; if (cell_index == std::numeric_limits<unsigned int>::max()) num_found = MPI::sum(mesh.mpi_comm(), 0); else num_found = MPI::sum(mesh.mpi_comm(), 1); if (MPI::rank(mesh.mpi_comm()) == 0 && num_found == 0) { dolfin_error("PointSource.cpp", "apply point source to vector", "The point is outside of the domain (%s)", _p.str().c_str()); } // Return if point not found if (cell_index == std::numeric_limits<unsigned int>::max()) { b.apply("add"); return; } // Create cell const Cell cell(mesh, static_cast<std::size_t>(cell_index)); // Vertex coordinates const std::size_t gdim = mesh.geometry().dim(); const std::size_t num_vertices = cell.num_entities(0); std::vector<double> vertex_coordinates(gdim* num_vertices); const unsigned int* vertices = cell.entities(0); for (std::size_t i = 0; i < num_vertices; i++) for (std::size_t j = 0; j < gdim; j++) vertex_coordinates[i*gdim + j] = mesh.geometry().x(vertices[i])[j]; // Evaluate all basis functions at the point() dolfin_assert(_V->element()); dolfin_assert(_V->element()->value_rank() == 0); std::vector<double> values(_V->element()->space_dimension()); const int cell_orientation = 0; _V->element()->evaluate_basis_all(values.data(), _p.coordinates(), vertex_coordinates.data(), cell_orientation); // Scale by magnitude for (std::size_t i = 0; i < _V->element()->space_dimension(); i++) values[i] *= _magnitude; // Compute local-to-global mapping dolfin_assert(_V->dofmap()); const std::vector<dolfin::la_index>& dofs = _V->dofmap()->cell_dofs(cell.index()); // Add values to vector dolfin_assert(_V->element()->space_dimension() == _V->dofmap()->cell_dimension(cell.index())); b.add(values.data(), _V->element()->space_dimension(), dofs.data()); b.apply("add"); }
//----------------------------------------------------------------------------- void FunctionSpace::interpolate(GenericVector& expansion_coefficients, const GenericFunction& v) const { dolfin_assert(_mesh); dolfin_assert(_element); dolfin_assert(_dofmap); // Check that function ranks match if (_element->value_rank() != v.value_rank()) { dolfin_error("FunctionSpace.cpp", "interpolate function into function space", "Rank of function (%d) does not match rank of function space (%d)", v.value_rank(), element()->value_rank()); } // Check that function dims match for (std::size_t i = 0; i < _element->value_rank(); ++i) { if (_element->value_dimension(i) != v.value_dimension(i)) { dolfin_error("FunctionSpace.cpp", "interpolate function into function space", "Dimension %d of function (%d) does not match dimension %d of function space (%d)", i, v.value_dimension(i), i, element()->value_dimension(i)); } } // Initialize vector of expansion coefficients if (expansion_coefficients.size() != _dofmap->global_dimension()) { dolfin_error("FunctionSpace.cpp", "interpolate function into function space", "Wrong size of vector"); } expansion_coefficients.zero(); // Initialize local arrays std::vector<double> cell_coefficients(_dofmap->max_element_dofs()); // Iterate over mesh and interpolate on each cell ufc::cell ufc_cell; std::vector<double> vertex_coordinates; for (CellIterator cell(*_mesh); !cell.end(); ++cell) { // Update to current cell cell->get_vertex_coordinates(vertex_coordinates); cell->get_cell_data(ufc_cell); // Restrict function to cell v.restrict(cell_coefficients.data(), *_element, *cell, vertex_coordinates.data(), ufc_cell); // Tabulate dofs const ArrayView<const dolfin::la_index> cell_dofs = _dofmap->cell_dofs(cell->index()); // Copy dofs to vector expansion_coefficients.set_local(cell_coefficients.data(), _dofmap->num_element_dofs(cell->index()), cell_dofs.data()); } // Finalise changes expansion_coefficients.apply("insert"); }
//----------------------------------------------------------------------------- std::size_t MUMPSLUSolver::solve(GenericVector& x, const GenericVector& b) { dolfin_assert(_matA); DMUMPS_STRUC_C data; data.comm_fortran = -987654; // Initialise data.job = -1; // Host participates in solve data.par = 1; // Output related parameters //data.ICNTL(1) = 6; // error messages //data.ICNTL(2) = 0; //data.ICNTL(3) = 6; // Global information //data.ICNTL(3) = 6; // Global information if (parameters["verbose"]) data.ICNTL(4) = 2; else data.ICNTL(4) = 1; // Matrix symmetry (0=non-symmetric, 2=symmetric positive defn, 2=symmetric) data.sym = 0; if (parameters["symmetric"]) data.sym = 2; // Initialise MUMPS dmumps_c(&data); // Related to use of ScaLAPACK (+/-. Negative is faster?) //data.ICNTL(13) = -1; // Solve transpose (1: A x = b, otherwise A^T x = b) data.ICNTL(9) = 1; // FIXME (20=default) data.ICNTL(14) = 20; // Reordering (7=automatic) data.ICNTL(7) = 7; // Control solution vector (0=solution on root, 1=solution distributed) data.ICNTL(21) = 1; // Distributed matrix data.ICNTL(18) = 3; // Parallel/serial analysis (0=auto, 1=serial, 2=parallel) if (MPI::size(_matA->mpi_comm()) > 1) data.ICNTL(28) = 2; else data.ICNTL(28) = 0; // Parallel graph partitioning library (0=auto, 1=pt-scotch, 2=parmetis) data.ICNTL(29) = 0; // Global size dolfin_assert(_matA->size(0) == _matA->size(1)); data.n = _matA->size(0); if (!_matA->base_one()) dolfin_error("MUMPSLUSolver.cpp", "initialize solver", "MUMPS requires a CoordinateMatrix with Fortran-style " "base 1 indexing"); // Get matrix coordinate and value data const std::vector<std::size_t>& rows = _matA->rows(); const std::vector<std::size_t>& cols = _matA->columns(); const std::vector<double>& vals = _matA->values(); // Number of non-zero entries on this process data.nz_loc = rows.size(); // Pass matrix data to MUMPS. Trust MUMPS not to change it data.irn_loc = const_cast<int*>(reinterpret_cast<const int*>(rows.data())); data.jcn_loc = const_cast<int*>(reinterpret_cast<const int*>(cols.data())); data.a_loc = const_cast<double*>(vals.data()); // Analyse and factorize data.job = 4; dmumps_c(&data); if (data.INFOG(1) < 0) dolfin_error("MUMPSLUSolver.cpp", "compute matrix factors", "MUMPS reported an error during the analysis and " "factorisation"); cout << "Factorisation finished" << endl; // Gather RHS on root process and attach std::vector<double> _b; b.gather_on_zero(_b); data.rhs = _b.data(); // Scaling strategy (77 is default) data.ICNTL(8) = 77; // Get size of local solution vector x and create objects to hold solution const std::size_t local_x_size = data.INFO(23); std::vector<int> x_local_indices(local_x_size); std::vector<double> x_local_vals(local_x_size); // Attach solution data to MUMPS object data.lsol_loc = local_x_size; data.sol_loc = x_local_vals.data(); data.isol_loc = x_local_indices.data(); // Solve problem data.job = 3; dmumps_c(&data); if (data.INFOG(1) < 0) dolfin_error("MUMPSLUSolver.cpp", "compute matrix factors", "MUMPS reported an error during the solve"); // Shift indices by -1 for (std::size_t i = 0; i < local_x_size ; ++i) x_local_indices[i]--; // Set x values #if defined(PETSC_USE_64BIT_INDICES) // Cast indices to 64 bit std::vector<dolfin::la_index> _x_local_indices(x_local_indices.begin(), x_local_indices.end()); x.set_local(x_local_vals.data(), x_local_indices.size(), _x_local_indices.data()); #else x.set_local(x_local_vals.data(), x_local_indices.size(), x_local_indices.data()); #endif x.apply("insert"); // Clean up data.job = -2; dmumps_c(&data); return 1; }
//----------------------------------------------------------------------------- void NewDirichletBC::apply(GenericMatrix& A, GenericVector& b, const GenericVector* x, const DofMap& dof_map, const ufc::form& form) { bool reassemble = dolfin_get("PDE reassemble matrix"); std::cout << "newdirichlet: " << reassemble << std::endl; // FIXME: How do we reuse the dof map for u? if (method == topological) message("Applying Dirichlet boundary conditions to linear system."); /* else if (method == geometrical) message("Applying Dirichlet boundary conditions to linear system (geometrical approach)."); else message("Applying Dirichlet boundary conditions to linear system (pointwise approach)."); */ // Make sure we have the facet - cell connectivity const uint D = _mesh.topology().dim(); if (method == topological) _mesh.init(D - 1, D); // Create local data for application of boundary conditions BoundaryCondition::LocalData data(form, _mesh, dof_map, sub_system); // A map to hold the mapping from boundary dofs to boundary values std::map<uint, real> boundary_values; if (method == pointwise) { Progress p("Applying Dirichlet boundary conditions", _mesh.size(D)); for (CellIterator cell(_mesh); !cell.end(); ++cell) { computeBCPointwise(boundary_values, *cell, data); p++; } } else { // Iterate over the facets of the mesh Progress p("Applying Dirichlet boundary conditions", _mesh.size(D - 1)); for (FacetIterator facet(_mesh); !facet.end(); ++facet) { // Skip facets not inside the sub domain if ((*sub_domains)(*facet) != sub_domain) { p++; continue; } // Chose strategy if (method == topological) computeBCTopological(boundary_values, *facet, data); else computeBCGeometrical(boundary_values, *facet, data); // Update process p++; } } // Copy boundary value data to arrays uint* dofs = new uint[boundary_values.size()]; real* values = new real[boundary_values.size()]; std::map<uint, real>::const_iterator boundary_value; uint i = 0; for (boundary_value = boundary_values.begin(); boundary_value != boundary_values.end(); ++boundary_value) { dofs[i] = boundary_value->first; values[i++] = boundary_value->second; } // Modify boundary values for nonlinear problems if (x) { real* x_values = new real[boundary_values.size()]; x->get(x_values, boundary_values.size(), dofs); for (uint i = 0; i < boundary_values.size(); i++) values[i] -= x_values[i]; delete[] x_values; } // Modify RHS vector (b[i] = value) b.set(values, boundary_values.size(), dofs); if(reassemble) { // Modify linear system (A_ii = 1) A.ident(boundary_values.size(), dofs); } // Clear temporary arrays delete [] dofs; delete [] values; // Finalise changes to b b.apply(); }
//---------------------------------------------------------------------------- void LocalSolver::solve(GenericVector& x, const Form& a, const Form& L, bool symmetric) const { UFC ufc_a(a); UFC ufc_L(L); // Set timer Timer timer("Local solver"); // Extract mesh const Mesh& mesh = a.mesh(); // Form ranks const std::size_t rank_a = ufc_a.form.rank(); const std::size_t rank_L = ufc_L.form.rank(); // Check form ranks dolfin_assert(rank_a == 2); dolfin_assert(rank_L == 1); // Collect pointers to dof maps std::shared_ptr<const GenericDofMap> dofmap_a0 = a.function_space(0)->dofmap(); std::shared_ptr<const GenericDofMap> dofmap_a1 = a.function_space(1)->dofmap(); std::shared_ptr<const GenericDofMap> dofmap_L = a.function_space(0)->dofmap(); dolfin_assert(dofmap_a0); dolfin_assert(dofmap_a1); dolfin_assert(dofmap_L); // Initialise vector if (x.empty()) { std::pair<std::size_t, std::size_t> local_range = dofmap_L->ownership_range(); x.init(mesh.mpi_comm(), local_range); } // Cell integrals ufc::cell_integral* integral_a = ufc_a.default_cell_integral.get(); ufc::cell_integral* integral_L = ufc_L.default_cell_integral.get(); // Eigen data structures Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> A; Eigen::VectorXd b, x_local; // Assemble over cells Progress p("Performing local (cell-wise) solve", mesh.num_cells()); ufc::cell ufc_cell; std::vector<double> vertex_coordinates; for (CellIterator cell(mesh); !cell.end(); ++cell) { // Update to current cell cell->get_vertex_coordinates(vertex_coordinates); cell->get_cell_data(ufc_cell); ufc_a.update(*cell, vertex_coordinates, ufc_cell, integral_a->enabled_coefficients()); ufc_L.update(*cell, vertex_coordinates, ufc_cell, integral_L->enabled_coefficients()); // Get local-to-global dof maps for cell const std::vector<dolfin::la_index>& dofs_a0 = dofmap_a0->cell_dofs(cell->index()); const std::vector<dolfin::la_index>& dofs_a1 = dofmap_a1->cell_dofs(cell->index()); const std::vector<dolfin::la_index>& dofs_L = dofmap_L->cell_dofs(cell->index()); // Check that local problem is square and a and L match dolfin_assert(dofs_a0.size() == dofs_a1.size()); dolfin_assert(dofs_a1.size() == dofs_L.size()); // Resize A and b A.resize(dofs_a0.size(), dofs_a1.size()); b.resize(dofs_L.size()); // Tabulate A and b on cell integral_a->tabulate_tensor(A.data(), ufc_a.w(), vertex_coordinates.data(), ufc_cell.orientation); integral_L->tabulate_tensor(b.data(), ufc_L.w(), vertex_coordinates.data(), ufc_cell.orientation); // Solve local problem x_local = A.partialPivLu().solve(b); // Set solution in global vector x.set(x_local.data(), dofs_a0.size(), dofs_a0.data()); p++; } // Finalise vector x.apply("insert"); }
//----------------------------------------------------------------------------- void DirichletBC::zero_columns(GenericMatrix& A, GenericVector& b, double diag_val) const { // Check arguments check_arguments(&A, &b, NULL, 1); // A map to hold the mapping from boundary dofs to boundary values Map bv_map; get_boundary_values(bv_map); // Create lookup table of dofs //const std::size_t nrows = A.size(0); // should be equal to b.size() const std::size_t ncols = A.size(1); // should be equal to max possible dof+1 std::pair<std::size_t, std::size_t> rows = A.local_range(0); std::vector<char> is_bc_dof(ncols); std::vector<double> bc_dof_val(ncols); for (Map::const_iterator bv = bv_map.begin(); bv != bv_map.end(); ++bv) { is_bc_dof[bv->first] = 1; bc_dof_val[bv->first] = bv->second; } // Scan through all columns of all rows, setting to zero if // is_bc_dof[column]. At the same time, we collect corrections to // the RHS std::vector<std::size_t> cols; std::vector<double> vals; std::vector<double> b_vals; std::vector<dolfin::la_index> b_rows; for (std::size_t row = rows.first; row < rows.second; row++) { // If diag_val is nonzero, the matrix is a diagonal block // (nrows==ncols), and we can set the whole BC row if (diag_val != 0.0 && is_bc_dof[row]) { A.getrow(row, cols, vals); for (std::size_t j = 0; j < cols.size(); j++) vals[j] = (cols[j] == row)*diag_val; A.setrow(row, cols, vals); A.apply("insert"); b.setitem(row, bc_dof_val[row]*diag_val); } else // Otherwise, we scan the row for BC columns { A.getrow(row, cols, vals); bool row_changed = false; for (std::size_t j = 0; j < cols.size(); j++) { const std::size_t col = cols[j]; // Skip columns that aren't BC, and entries that are zero if (!is_bc_dof[col] || vals[j] == 0.0) continue; // We're going to change the row, so make room for it if (!row_changed) { row_changed = true; b_rows.push_back(row); b_vals.push_back(0.0); } b_vals.back() -= bc_dof_val[col]*vals[j]; vals[j] = 0.0; } if (row_changed) { A.setrow(row, cols, vals); A.apply("insert"); } } } b.add_local(&b_vals.front(), b_rows.size(), &b_rows.front()); b.apply("add"); }
//---------------------------------------------------------------------------- void LocalSolver::solve_local(GenericVector& x, const GenericVector& b, const GenericDofMap& dofmap_b) const { dolfin_assert(_a); dolfin_assert(_a->rank() == 2); // Extract mesh dolfin_assert(_a->function_space(0)->mesh()); const Mesh& mesh = *_a->function_space(0)->mesh(); // Check whether to use cache for factorizations const bool use_cache = _cholesky_cache.empty() and _lu_cache.empty() ? false : true; // Create UFC object UFC ufc_a(*_a); // Get cell integral std::shared_ptr<ufc::cell_integral> integral_a = ufc_a.default_cell_integral; dolfin_assert(integral_a); // Get dofmaps std::array<std::shared_ptr<const GenericDofMap>, 2> dofmaps_a = {{_a->function_space(0)->dofmap(), _a->function_space(1)->dofmap()}}; dolfin_assert(dofmaps_a[0] and dofmaps_a[1]); // Check dimensions dolfin_assert(dofmaps_a[0]->global_dimension() == dofmaps_a[1]->global_dimension()); dolfin_assert(dofmaps_a[0]->global_dimension() == dofmap_b.global_dimension()); // Eigen data structures for local tensors Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> A_e; Eigen::VectorXd b_e, x_e; // Eigen factorizations Eigen::PartialPivLU<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> lu; Eigen::LLT<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> cholesky; // Assemble LHS over cells and solve Progress p("Performing local (cell-wise) solve", mesh.num_cells()); ufc::cell ufc_cell; std::vector<double> vertex_coordinates; for (CellIterator cell(mesh); !cell.end(); ++cell) { // Get cell dofmaps const ArrayView<const dolfin::la_index> dofs_L = dofmap_b.cell_dofs(cell->index()); const ArrayView<const dolfin::la_index> dofs_a0 = dofmaps_a[0]->cell_dofs(cell->index()); // Check dimensions dolfin_assert(dofs_L.size() == dofs_a0.size()); // Copy global RHS data into local RHS b_e.resize(dofs_L.size()); b.get_local(b_e.data(), dofs_L.size(), dofs_L.data()); // Solve local problem if (!use_cache) { // Update to current cell cell->get_vertex_coordinates(vertex_coordinates); cell->get_cell_data(ufc_cell); // Update LHS UFC object ufc_a.update(*cell, vertex_coordinates, ufc_cell, integral_a->enabled_coefficients()); // Resize A_e and tabulate on for cell const std::size_t dim = dofmaps_a[0]->num_element_dofs(cell->index()); dolfin_assert(dim == dofmaps_a[1]->num_element_dofs(cell->index())); A_e.resize(dim, dim); integral_a->tabulate_tensor(A_e.data(), ufc_a.w(), vertex_coordinates.data(), ufc_cell.orientation); // Solve local problem if (_solver_type == SolverType::Cholesky) { cholesky.compute(A_e); x_e = cholesky.solve(b_e); } else { lu.compute(A_e); x_e = lu.solve(b_e); } } else { if (_solver_type == SolverType::Cholesky) x_e = _cholesky_cache[cell->index()].solve(b_e); else x_e = _lu_cache[cell->index()].solve(b_e); } // Set solution in global vector x.set_local(x_e.data(), dofs_a0.size(), dofs_a0.data()); p++; } // Finalise vector x.apply("insert"); }