void ISOP2P1::boundaryValueStokes(Vector<double> &x) { /// 各空间自由度. unsigned int n_dof_v = fem_space_v.n_dof(); unsigned int n_dof_p = fem_space_p.n_dof(); unsigned int n_total_dof_v = 2 * n_dof_v; const std::size_t * rowstart = sp_stokes.get_rowstart_indices(); const unsigned int * colnum = sp_stokes.get_column_numbers(); std::cout << "n_dof_v: " << n_dof_v << ", n_dof_p: " << n_dof_p << std::endl; std::cout << "n_A: " << sp_stokes.n_rows() << ", m_A: " << sp_stokes.n_cols() << std::endl; /// 遍历全部维度的速度节点. for (unsigned int i = 0; i < n_total_dof_v; ++i) { /// 边界标志. int bm = -1; /// 判断一下是 x 方向还是 y 方向. 分别读取标志. if (i < n_dof_v) bm = fem_space_v.dofInfo(i).boundary_mark; else bm = fem_space_v.dofInfo(i - n_dof_v).boundary_mark; if (bm == 0) continue; /// 对 Dirichelet 边界根据边界分别赋值. 注意同时还要区别 x 和 /// y 方向. // /// 障碍流边界条件. // if (bm < 8 && bm > 0 && bm != 6) // x(i) = 0.0; // else if (bm == 8 || bm == 9) // if (i < n_dof_v) // { // PoiseuilleVx poiseuille_vx(0.0, 2.0); // x(i) = poiseuille_vx.value(fem_space_v.dofInfo(i).interp_point); // } // else // { // PoiseuilleVy poiseuille_vy; // x(i) = poiseuille_vy.value(fem_space_v.dofInfo(i - n_dof_v).interp_point); // } /// 方腔流边界条件. if (bm == 1 || bm == 2 || bm == 3 || bm == 4) { if (i < n_dof_v) { DiVx real_vx(viscosity, t + dt); x(i) = real_vx.value(fem_space_v.dofInfo(i).interp_point); } else { DiVy real_vy(viscosity, t + dt); x(i) = real_vy.value(fem_space_v.dofInfo(i - n_dof_v).interp_point); } } // else if (bm == 2 || bm == 3) // if (i < n_dof_v) // { // PoiseuilleVx poiseuille_vx(0.0, 1.0); // x(i) = poiseuille_vx.value(fem_space_v.dofInfo(i).interp_point); // } // else // { // PoiseuilleVy poiseuille_vy; // x(i) = poiseuille_vy.value(fem_space_v.dofInfo(i - n_dof_v).interp_point); // } // else if (bm == 11) // if (i < n_dof_v) // { // RealVx real_vx; // x(i) = real_vx.value(fem_space_v.dofInfo(i).interp_point); // } // else // { // RealVy real_vy; // x(i) = real_vy.value(fem_space_v.dofInfo(i - n_dof_v).interp_point); // } /// 右端项这样改, 如果该行和列其余元素均为零, 则在迭代中确 /// 保该数值解和边界一致. /// 方腔流边界条件. if (bm == 1 || bm == 2 || bm == 3 || bm == 4 || bm == 5 || bm == 11) // /// 障碍流边界条件. // if (bm < 10 && bm > 0 && bm != 6) { rhs(i) = matrix.diag_element(i) * x(i); /// 遍历 i 行. for (unsigned int j = rowstart[i] + 1; j < rowstart[i + 1]; ++j) { /// 第 j 个元素消成零(不是第 j 列!). 注意避开了对角元. matrix.global_entry(j) -= matrix.global_entry(j); /// 第 j 个元素是第 k 列. unsigned int k = colnum[j]; /// 看看第 k 行的 i 列是否为零元. const unsigned int *p = std::find(&colnum[rowstart[k] + 1], &colnum[rowstart[k + 1]], i); /// 如果是非零元. 则需要将这一项移动到右端项. 因为第 i 个未知量已知. if (p != &colnum[rowstart[k + 1]]) { /// 计算 k 行 i 列的存储位置. unsigned int l = p - &colnum[rowstart[0]]; /// 移动到右端项. 等价于 r(k) = r(k) - x(i) * A(k, i). rhs(k) -= matrix.global_entry(l) * x(i); /// 移完此项自然是零. matrix.global_entry(l) -= matrix.global_entry(l); } } } } std::cout << "boundary values for Stokes OK!" << std::endl; };
void ISOP2P1::updateSolution() { // /// 更新插值点. fem_space_p.updateDofInterpPoint(); fem_space_v.updateDofInterpPoint(); /// 备份数值解. FEMFunction<double, DIM> _u_h(v_h[0]); FEMFunction<double, DIM> _v_h(v_h[1]); FEMFunction<double, DIM> _p_h(p_h); const double& msl = moveStepLength(); /// 因为有限元空间插值点变化, 重新构造矩阵. buildMatrixStruct(); buildMatrix(); /// 因为网格移动量为小量, 因此时间步长可以相对取的大些. double _dt = 1.0; 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; int n_total_dof_v = 2 * n_dof_v; /// 一步Euler. for (int m = 1; m > 0; --m) { /// 系数矩阵直接使用 Stokes 矩阵结构. SparseMatrix<double> mat_moving; mat_moving.reinit(sp_stokes); /// (0, 0) for (int i = 0; i < sp_vxvx.n_nonzero_elements(); ++i) mat_moving.global_entry(index_vxvx[i]) = mat_v_mass.global_entry(i); /// (1, 1) 这两个对角块仅有质量块. for (int i = 0; i < sp_vyvy.n_nonzero_elements(); ++i) mat_moving.global_entry(index_vyvy[i]) = mat_v_mass.global_entry(i); /// (0, 2) 这个不是方阵. 在矩阵结构定义的时候已经直接排除了对角元优 /// 先. for (int i = 0; i < sp_pvx.n_nonzero_elements(); ++i) mat_moving.global_entry(index_pvx[i]) = (1.0 / m) * mat_pvx_divT.global_entry(i); /// (1, 2) for (int i = 0; i < sp_pvy.n_nonzero_elements(); ++i) mat_moving.global_entry(index_pvy[i]) = (1.0 / m) * mat_pvy_divT.global_entry(i); /// (2, 0) for (int i = 0; i < sp_vxp.n_nonzero_elements(); ++i) mat_moving.global_entry(index_vxp[i]) = (1.0 / m) * mat_vxp_div.global_entry(i); /// (2, 1) 这四块直接复制散度矩阵. for (int i = 0; i < sp_vyp.n_nonzero_elements(); ++i) mat_moving.global_entry(index_vyp[i]) = (1.0 / m) * mat_vyp_div.global_entry(i); /// 问题的右端项. rhs.reinit(n_total_dof); FEMSpace<double, DIM>::ElementIterator the_element_v = fem_space_v.beginElement(); FEMSpace<double, DIM>::ElementIterator end_element_v = fem_space_v.endElement(); /// 遍历速度单元, 拼装相关系数矩阵和右端项. for (the_element_v = fem_space_v.beginElement(); the_element_v != end_element_v; ++the_element_v) { /// 当前单元信息. double volume = the_element_v->templateElement().volume(); /// 积分精度至少为2. const QuadratureInfo<DIM>& quad_info = the_element_v->findQuadratureInfo(3); std::vector<double> jacobian = the_element_v->local_to_global_jacobian(quad_info.quadraturePoint()); int n_quadrature_point = quad_info.n_quadraturePoint(); std::vector<Point<DIM> > q_point = the_element_v->local_to_global(quad_info.quadraturePoint()); /// 速度单元信息. std::vector<std::vector<double> > basis_value_v = the_element_v->basis_function_value(q_point); std::vector<double> vx_value = _u_h.value(q_point, *the_element_v); std::vector<double> vy_value = _v_h.value(q_point, *the_element_v); std::vector<std::vector<double> > vx_gradient = v_h[0].gradient(q_point, *the_element_v); std::vector<std::vector<double> > vy_gradient = v_h[1].gradient(q_point, *the_element_v); const std::vector<int>& element_dof_v = the_element_v->dof(); int n_element_dof_v = the_element_v->n_dof(); /// 速度积分点上的移动方向, 注意是速度单元的积分点, 在压力单元上的移动方向. /// 所以下面一行程序, 仔细算过, 没有问题. std::vector<std::vector<double> > move_vector = moveDirection(q_point, index_v2p[the_element_v->index()]); for (int l = 0; l < n_quadrature_point; ++l) { double Jxw = quad_info.weight(l) * jacobian[l] * volume; for (int i = 0; i < n_element_dof_v; ++i) { double rhs_cont = (vx_value[l] + (1.0 / m) * msl * innerProduct(move_vector[l], vx_gradient[l])) * basis_value_v[i][l]; rhs_cont *= Jxw; rhs(element_dof_v[i]) += rhs_cont; rhs_cont = (vy_value[l] + (1.0 / m) * msl * innerProduct(move_vector[l], vy_gradient[l])) * basis_value_v[i][l]; rhs_cont *= Jxw; rhs(n_dof_v + element_dof_v[i]) += rhs_cont; } } } /// 构建系数矩阵和右端项. Vector<double> x(n_total_dof); PoiseuilleVx real_vx (-1.0, 1.0); PoiseuilleVy real_vy; // Operator::L2Project(real_vx, v_h[0], Operator::LOCAL_LEAST_SQUARE, 3); // Operator::L2Project(real_vy, v_h[1], Operator::LOCAL_LEAST_SQUARE, 3); // /// 边界处理. const std::size_t * rowstart = sp_stokes.get_rowstart_indices(); const unsigned int * colnum = sp_stokes.get_column_numbers(); /// 遍历全部维度的速度节点. for (unsigned int i = 0; i < n_total_dof_v; ++i) { /// 边界标志. int bm = -1; /// 判断一下是 x 方向还是 y 方向. 分别读取标志. if (i < n_dof_v) bm = fem_space_v.dofInfo(i).boundary_mark; else bm = fem_space_v.dofInfo(i - n_dof_v).boundary_mark; if (bm == 0) continue; // /// 方腔流边界条件. // if (bm == 1 || bm == 2 || bm == 4) // x(i) = 0.0; // if (bm == 3) // if (i < n_dof_v) // { // Regularized regularize; // x(i) = regularize.value(fem_space_v.dofInfo(i).interp_point); // } // else // x(i) = 0.0; if (bm == 1 || bm == 2 || bm == 4 || bm == 5) if (i < n_dof_v) x(i) = scale * real_vx.value(fem_space_v.dofInfo(i).interp_point); else x(i) = scale * real_vy.value(fem_space_v.dofInfo(i - n_dof_v).interp_point); /// 右端项这样改, 如果该行和列其余元素均为零, 则在迭代中确 /// 保该数值解和边界一致. if (bm == 1 || bm == 2 || bm == 4 || bm == 5) { rhs(i) = mat_moving.diag_element(i) * x(i); /// 遍历 i 行. for (unsigned int j = rowstart[i] + 1; j < rowstart[i + 1]; ++j) { /// 第 j 个元素消成零(不是第 j 列!). 注意避开了对角元. mat_moving.global_entry(j) -= mat_moving.global_entry(j); /// 第 j 个元素是第 k 列. unsigned int k = colnum[j]; /// 看看第 k 行的 i 列是否为零元. const unsigned int *p = std::find(&colnum[rowstart[k] + 1], &colnum[rowstart[k + 1]], i); /// 如果是非零元. 则需要将这一项移动到右端项. 因为第 i 个未知量已知. if (p != &colnum[rowstart[k + 1]]) { /// 计算 k 行 i 列的存储位置. unsigned int l = p - &colnum[rowstart[0]]; /// 移动到右端项. 等价于 r(k) = r(k) - x(i) * A(k, i). rhs(k) -= mat_moving.global_entry(l) * x(i); /// 移完此项自然是零. mat_moving.global_entry(l) -= mat_moving.global_entry(l); } } } } std::cout << "boundary values for updateSolution OK!" << std::endl; clock_t t_cost = clock(); /// 预处理矩阵. /// 不完全LU分解. dealii::SparseILU <double> preconditioner; preconditioner.initialize(mat_moving); /// 矩阵求解. dealii::SolverControl solver_control (4000000, l_tol, check); SolverMinRes<Vector<double> > minres (solver_control); minres.solve (mat_moving, x, rhs, PreconditionIdentity()); 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); /// debug std::ofstream mat_deb; // rowstart = sp_pvx.get_rowstart_indices(); // colnum = sp_pvx.get_column_numbers(); mat_deb.open("mat.m", std::ofstream::out); mat_deb.setf(std::ios::fixed); mat_deb.precision(20); for (int i = 0; i < n_total_dof; ++i) { for (int j = rowstart[i]; j < rowstart[i + 1]; ++j) { mat_deb << "A(" << i + 1 << ", " << colnum[j] + 1 << ")=" << mat_moving.global_entry(j) << ";" << std::endl; } mat_deb << "x(" << i + 1<< ") = " << x(i) << ";" << std::endl; mat_deb << "rhs(" << i + 1 << ") = " << rhs(i) << ";" << std::endl; } // for(int i = 0; i < n_dof_p; ++i) // mat_deb << "x(" << i + 1<< ") = " << p_h(i) << ";" << std::endl; mat_deb.close(); std::cout << "mat output" << std::endl; Vector<double> res(n_total_dof); mat_moving.vmult(res, x); res *= -1; res += rhs; std::cout << "res_l2norm =" << res.l2_norm() << std::endl; double error; error = Functional::L2Error(v_h[0], real_vx, 3); std::cout << "|| u - u_h ||_L2 = " << error << std::endl; error = Functional::H1SemiError(v_h[0], real_vx, 3); std::cout << "|| u - u_h ||_H1 = " << error << std::endl; /// debug // // /// 输出一下. outputTecplot("NS_Euler"); getchar(); } };