void ISOP2P1::solveStokes() { buildStokesSys(); std::cout << "Stokes system builded." << std::endl; int n_dof_v = fem_space_v.n_dof(); int n_dof_p = fem_space_p.n_dof(); int n_total_dof = 2 * n_dof_v + n_dof_p; /// 构建系数矩阵和右端项. /// 这个存放整体的数值解. 没有分割成 u_h[0], u_h[1] 和 p_h. Vector<double> x(n_total_dof); /// 将数值解合并一个向量便于边界处理. for (int i = 0; i < n_dof_v; ++i) { x(i) = v_h[0](i); x(n_dof_v + i) = v_h[1](i); } for (int i = 0; i < n_dof_p; ++i) x(2 * n_dof_v + i) = p_h(i); rhs.reinit(n_total_dof); /// 边界条件一起处理了. 修改了matrix, rhs和x. boundaryValueStokes(x, t); std::cout << "Stokes boundary applied." << std::endl; // /// 矩阵求解. // dealii::SolverControl solver_control (400000, l_tol * rhs.l2_norm(), 1); // /// 不完全LU分解. // dealii::SparseILU<double> preconditioner; // preconditioner.initialize(matrix); // /// 求解Stokes方程, MinRes要比GMRES求解器要快很多, // /// 当矩阵规模稍微大点的时候,GMRES会出现不收敛的情况. // SolverMinRes<Vector<double> > minres (solver_control); // SolverGMRES<Vector<double> >::AdditionalData para(1000, false, true); // /// 不用para算不动, 但是均匀网格下可以不用para,算其它的例子也不用para,是不是跟计算区域有关系? // SolverGMRES<Vector<double> > gmres(solver_control, para); // // /// 移动网格和时间发展中,这个预处理失效. // // StokesPreconditioner preconditioner; // // /// 预处理矩阵. // // SparseMatrix<double> matrix_vxvx(sp_vxvx); // // SparseMatrix<double> matrix_vyvy(sp_vyvy); // // /// 这里从 Stokes 取是因为加了边界条件. // // for (int i = 0; i < sp_vxvx.n_nonzero_elements(); ++i) // // matrix_vxvx.global_entry(i) = matrix.global_entry(index_vxvx[i]); // // for (int i = 0; i < sp_vyvy.n_nonzero_elements(); ++i) // // matrix_vyvy.global_entry(i) = matrix.global_entry(index_vyvy[i]); // // preconditioner.initialize(mat_v_stiff, mat_v_stiff, mat_p_mass); // clock_t t_cost = clock(); // minres.solve (matrix, x, rhs, PreconditionIdentity()); // // gmres.solve(matrix, x, rhs, preconditioner); // t_cost = clock() - t_cost; // std::cout << "time cost: " << (((float)t_cost) / CLOCKS_PER_SEC) << std::endl; // /// 将整体数值解分割成速度和压力. // for (int i = 0; i < n_dof_v; ++i) // { // v_h[0](i) = x(i); // v_h[1](i) = x(i + n_dof_v); // } // for (int i = 0; i < n_dof_p; ++i) // p_h(i) = x(i + 2 * n_dof_v); // /// 计算误差, t为时间参数. // computeError(t); // /// debug, 计算惨量的L2 norm。 // Vector<double> res(n_total_dof); // matrix.vmult(res, x); // res *= -1; // res += rhs; // std::cout << "res_l2norm =" << res.l2_norm() << std::endl; /// 矩阵求解. SparseMatrix<double> mat_BTx(sp_pvx); SparseMatrix<double> mat_BTy(sp_pvy); SparseMatrix<double> mat_Bx(sp_vxp); SparseMatrix<double> mat_By(sp_vyp); SparseMatrix<double> mat_Ax(sp_vxvx); SparseMatrix<double> mat_Ay(sp_vyvy); for (int i = 0; i < sp_vxvx.n_nonzero_elements(); ++i) mat_Ax.global_entry(i) = matrix.global_entry(index_vxvx[i]); for (int i = 0; i < sp_vyvy.n_nonzero_elements(); ++i) mat_Ay.global_entry(i) = matrix.global_entry(index_vyvy[i]); for (int i = 0; i < sp_pvx.n_nonzero_elements(); ++i) mat_BTx.global_entry(i) = matrix.global_entry(index_pvx[i]); for (int i = 0; i < sp_pvy.n_nonzero_elements(); ++i) mat_BTy.global_entry(i) = matrix.global_entry(index_pvy[i]); for (int i = 0; i < sp_vxp.n_nonzero_elements(); ++i) mat_Bx.global_entry(i) = matrix.global_entry(index_vxp[i]); for (int i = 0; i < sp_vyp.n_nonzero_elements(); ++i) mat_By.global_entry(i) = matrix.global_entry(index_vyp[i]); /// alp对AMGSolver的初始化影响比较大, 如果取得很小,初始化很快. double alp = dt * viscosity; AMGSolver solverQ(mat_Ax, 1.0e-12, 3, 100, 0.382, alp); // AMGSolver solverQ(mat_Ax); InverseMatrix AInv(mat_Ax, solverQ); /// 这里没有对速度质量阵进行边界条件处理. InverseMatrix QInv(mat_v_mass, solverQ); SchurComplement schur_complement(mat_BTx, mat_BTy, mat_Bx, mat_By, mat_v_mass, QInv, QInv); AMGSolver solverP(mat_p_mass); ApproxSchurComplement asc(mat_p_mass, solverQ); LSCPreconditioner lsc_preconditioner(mat_BTx, mat_BTy, mat_Bx, mat_By, mat_Ax, mat_Ax, mat_v_mass, schur_complement, asc, QInv, AInv, AInv); /// 矩阵求解. dealii::SolverControl solver_control (400000, l_Euler_tol * rhs.l2_norm(), 0); SolverMinRes<Vector<double> > minres(solver_control); minres.solve(matrix, x, rhs, lsc_preconditioner); /// 将整体数值解分割成速度和压力. for (int i = 0; i < n_dof_v; ++i) { v_h[0](i) = x(i); v_h[1](i) = x(i + n_dof_v); } for (int i = 0; i < n_dof_p; ++i) p_h(i) = x(i + 2 * n_dof_v); /// 计算误差, t为时间参数. computeError(t); // /// 矩阵求解. // SparseMatrix<double> mat_BTx(sp_pvx); // SparseMatrix<double> mat_BTy(sp_pvy); // SparseMatrix<double> mat_Bx(sp_vxp); // SparseMatrix<double> mat_By(sp_vyp); // SparseMatrix<double> mat_Ax(sp_vxvx); // SparseMatrix<double> mat_Ay(sp_vyvy); // for (int i = 0; i < sp_vxvx.n_nonzero_elements(); ++i) // mat_Ax.global_entry(i) = matrix.global_entry(index_vxvx[i]); // for (int i = 0; i < sp_vyvy.n_nonzero_elements(); ++i) // mat_Ay.global_entry(i) = matrix.global_entry(index_vyvy[i]); // for (int i = 0; i < sp_pvx.n_nonzero_elements(); ++i) // mat_BTx.global_entry(i) = matrix.global_entry(index_pvx[i]); // for (int i = 0; i < sp_pvy.n_nonzero_elements(); ++i) // mat_BTy.global_entry(i) = matrix.global_entry(index_pvy[i]); // for (int i = 0; i < sp_vxp.n_nonzero_elements(); ++i) // mat_Bx.global_entry(i) = matrix.global_entry(index_vxp[i]); // for (int i = 0; i < sp_vyp.n_nonzero_elements(); ++i) // mat_By.global_entry(i) = matrix.global_entry(index_vyp[i]); // Vector<double> tmp1(n_dof_v); // Vector<double> tmp2(n_dof_v); // Vector<double> rhs_vx(n_dof_v); // Vector<double> rhs_vy(n_dof_v); // Vector<double> rhs_p(n_dof_p); // for (int i = 0; i < n_dof_v; ++i) // { // rhs_vx(i) = rhs(i); // v_h[0](i) = x(i); // rhs_vy(i) = rhs(n_dof_v + i); // v_h[1](i) = x(n_dof_v + i); // } // for (int i = 0; i < n_dof_p; ++i) // { // rhs_p(i) = rhs(2 * n_dof_v + i); // p_h(i) = x(2 * n_dof_v + i); // } // Vector<double> schur_rhs (n_dof_p); // AMGSolver solverQ(mat_Ax); // InverseMatrix M(mat_Ax, solverQ); // M.vmult (tmp1, rhs_vx); // M.vmult (tmp2, rhs_vy); // mat_Bx.vmult(schur_rhs, tmp1); // mat_By.vmult_add(schur_rhs, tmp2); // schur_rhs -= rhs_p; // SchurComplement schur_complement(mat_BTx, mat_BTy, mat_Bx, mat_By, mat_v_mass, M, M, dt); // SolverControl solver_control_cg (n_dof_p * 2, // 1e-12*schur_rhs.l2_norm()); // SolverCG<> cg (solver_control_cg); // AMGSolver AQ(mat_p_mass); // ApproxSchurComplement asc(mat_p_mass, AQ); // cg.solve (schur_complement, p_h, schur_rhs, asc); // // cg.solve (schur_complement, p_h, schur_rhs, PreconditionIdentity()); // std::cout << solver_control_cg.last_step() // << " CG Schur complement iterations to obtain convergence." // << std::endl; // mat_BTx.vmult(tmp1, *dynamic_cast<const Vector<double>* >(&p_h)); // mat_BTy.vmult(tmp2, *dynamic_cast<const Vector<double>* >(&p_h)); // tmp1 *= -1; // tmp2 *= -1; // tmp1 += rhs_vx; // tmp2 += rhs_vy; // M.vmult(v_h[0], tmp1); // M.vmult(v_h[1], tmp2); // std::cout << "Stokes system solved." << std::endl; // /// 计算误差, t为时间参数. // computeError(t); // /// debug, 计算惨量的L2 norm。 // Vector<double> res(n_total_dof); // matrix.vmult(res, x); // res *= -1; // res += rhs; // std::cout << "res_l2norm =" << res.l2_norm() << std::endl; };
std::tuple<bool, CMatrix2D const, std::vector<double> > LinearSolver::GaussElim(CMatrix2D const & A, std::vector<double> const & f) { // Solve the equation A x = f using Gauss' elimination method with // row pivoting. // Description of algorithm: First, search for the largest element // in all following row and use it as the pivot element. This is to // reduce round-off errors. The matrix ACopy will be transformed // into the identity matrix, while the identity matrix, AInv, will // be transformed into the inverse. // A x = Id y is successively transformed into A^{-1} A x = A^{-1} Id f, // where ACopy = Id and A^{-1} = AInv. boost::uint64_t max_col = A.getCols(); boost::uint64_t max_row = A.getRows(); bool success = false; CMatrix2D ACopy(A); CMatrix2D AInv(CMatrix2D::identity(max_col)); std::vector<double> rhs(f); bool assert_cond = max_col == max_row; BOOST_ASSERT_MSG(assert_cond, "LinearSolver::GaussElim: Matrix must be quadratic"); if (!assert_cond) throw std::out_of_range("LinearSolver::GaussElim: Matrix must be quadratic"); // Create row-row map (because of pivoting) std::vector<int> row_row_map(max_row); for (int i = 0; i < max_row; ++i) row_row_map[i] = i; for (int col = 0; col < max_col; ++col) { // Find pivot element row index int pivot_index = findPivotIndex(ACopy, col, row_row_map); // Arrange rows due to row pivoting if (col != row_row_map[pivot_index]) { row_row_map[pivot_index] = col; row_row_map[row_row_map[col]] = pivot_index; } BOOST_ASSERT_MSG(row_row_map[pivot_index] == col, "LinearSolver::GaussElim: Pivoting error"); double pivot_element = ACopy(pivot_index, col); // Matrix is singular if (pivot_element == 0.0) return std::make_tuple(false, AInv, rhs); // Divide pivot row by pivot element to set element to 1 for (int i = 0; i < max_col; ++i) { // ACopy(pivot_index, i) = 0 for i < col if (i >= col) { double& val1 = ACopy(pivot_index, i); val1 /= pivot_element; } double& val2 = AInv(pivot_index, i); val2 /= pivot_element; } // Do same transformation on the rhs rhs[pivot_index] /= pivot_element; // Add pivot row to all other rows to reduce column col // to the corresponding identity matrix column. for (int current_row = 0; current_row < max_row; ++current_row) { if (current_row == pivot_index) continue; double val = - ACopy(current_row, col) / ACopy(pivot_index, col); for (int j = 0; j < max_col; ++j) { ACopy(current_row, j) += val * ACopy(pivot_index, j); AInv(current_row, j) += val * AInv(pivot_index, j); } // Do same transformation on the rhs rhs[current_row] += val * rhs[pivot_index]; } } // Rearrange the rows in AInv due to pivoting for (int i = 0; i < max_row; ++i) { int row = row_row_map[i]; if (row == i) continue; // Swap rows for (int j = 0; j < max_col; ++j) { double tmp = AInv(i, j); AInv(i, j) = AInv(row, j); AInv(row, j) = tmp; tmp = ACopy(i, j); ACopy(i, j) = ACopy(row, j); ACopy(row, j) = tmp; } // Swap rows on the rhs double tmp = rhs[i]; rhs[i] = rhs[row]; rhs[row] = tmp; row_row_map[i] = row_row_map[row]; row_row_map[row] = row; } return std::make_tuple(success, AInv, rhs); }
void magnet::math::Spline::generate() { if (size() < 2) throw std::runtime_error("Spline requires at least 2 points"); // If any spline points are at the same x location, we have to // just slightly seperate them { bool testPassed(false); while (!testPassed) { testPassed = true; std::sort(base::begin(), base::end()); for (auto iPtr = base::begin(); iPtr != base::end() - 1; ++iPtr) if (iPtr->first == (iPtr + 1)->first) { if ((iPtr + 1)->first != 0) (iPtr + 1)->first += (iPtr + 1)->first * std::numeric_limits <double>::epsilon() * 10; else (iPtr + 1)->first = std::numeric_limits <double>::epsilon() * 10; testPassed = false; break; } } } const size_t e = size() - 1; switch (_type) { case LINEAR: { _data.resize(e); for (size_t i(0); i < e; ++i) { _data[i].x = x(i); _data[i].a = 0; _data[i].b = 0; _data[i].c = (y(i + 1) - y(i)) / (x(i + 1) - x(i)); _data[i].d = y(i); } break; } case CUBIC: { ublas::matrix<double> A(size(), size()); for (size_t yv(0); yv <= e; ++yv) for (size_t xv(0); xv <= e; ++xv) A(xv, yv) = 0; for (size_t i(1); i < e; ++i) { A(i - 1, i) = h(i - 1); A(i, i) = 2 * (h(i - 1) + h(i)); A(i + 1, i) = h(i); } ublas::vector<double> C(size()); for (size_t xv(0); xv <= e; ++xv) C(xv) = 0; for (size_t i(1); i < e; ++i) C(i) = 6 * ((y(i + 1) - y(i)) / h(i) - (y(i) - y(i - 1)) / h(i - 1)); // Boundary conditions switch (_BCLow) { case FIXED_1ST_DERIV_BC: C(0) = 6 * ((y(1) - y(0)) / h(0) - _BCLowVal); A(0, 0) = 2 * h(0); A(1, 0) = h(0); break; case FIXED_2ND_DERIV_BC: C(0) = _BCLowVal; A(0, 0) = 1; break; case PARABOLIC_RUNOUT_BC: C(0) = 0; A(0, 0) = 1; A(1, 0) = -1; break; } switch (_BCHigh) { case FIXED_1ST_DERIV_BC: C(e) = 6 * (_BCHighVal - (y(e) - y(e - 1)) / h(e - 1)); A(e, e) = 2 * h(e - 1); A(e - 1, e) = h(e - 1); break; case FIXED_2ND_DERIV_BC: C(e) = _BCHighVal; A(e, e) = 1; break; case PARABOLIC_RUNOUT_BC: C(e) = 0; A(e, e) = 1; A(e - 1, e) = -1; break; } ublas::matrix<double> AInv(size(), size()); InvertMatrix(A, AInv); _ddy = ublas::prod(C, AInv); _data.resize(size() - 1); for (size_t i(0); i < e; ++i) { _data[i].x = x(i); _data[i].a = (_ddy(i + 1) - _ddy(i)) / (6 * h(i)); _data[i].b = _ddy(i) / 2; _data[i].c = (y(i + 1) - y(i)) / h(i) - _ddy(i + 1) * h(i) / 6 - _ddy(i) * h(i) / 3; _data[i].d = y(i); } } } _valid = true; }