598 *
return -Utilities::fixed_power<2, double>(this->
get_time()) + 6;
608 * Then, we handle the right boundary condition.
612 *
class RightBoundaryValues :
public Function<dim>
615 * RightBoundaryValues(
const double strike_price,
const double interest_rate);
618 *
const unsigned int component = 0)
const override;
621 *
const double strike_price;
622 *
const double interest_rate;
627 * RightBoundaryValues<dim>::RightBoundaryValues(
const double strike_price,
628 *
const double interest_rate)
629 * : strike_price(strike_price)
630 * , interest_rate(interest_rate)
635 *
double RightBoundaryValues<dim>::value(
const Point<dim> & p,
636 *
const unsigned int component)
const
639 *
return -Utilities::fixed_power<2, double>(p(component)) -
640 * Utilities::fixed_power<2, double>(this->
get_time()) + 6;
642 *
return (p(component) - strike_price) *
651 * Finally, we handle the right hand side.
655 *
class RightHandSide :
public Function<dim>
658 * RightHandSide(
const double asset_volatility,
const double interest_rate);
661 *
const unsigned int component = 0)
const override;
664 *
const double asset_volatility;
665 *
const double interest_rate;
670 * RightHandSide<dim>::RightHandSide(
const double asset_volatility,
671 *
const double interest_rate)
672 * : asset_volatility(asset_volatility)
673 * , interest_rate(interest_rate)
678 *
double RightHandSide<dim>::value(
const Point<dim> & p,
679 *
const unsigned int component)
const
683 * Utilities::fixed_power<2, double>(asset_volatility * p(component)) -
684 * 2 * interest_rate * Utilities::fixed_power<2, double>(p(component)) -
686 * (-Utilities::fixed_power<2, double>(p(component)) -
687 * Utilities::fixed_power<2, double>(this->
get_time()) + 6);
700 * <a name=
"ThecodeBlackScholescodeClass"></a>
701 * <h3>The <code>BlackScholes</code> Class</h3>
705 * The next piece is the declaration of the main
class of this program. This
706 * is very similar to the @ref step_26
"step-26" tutorial, with some modifications. New
707 * matrices had to be added to calculate the A and B matrices, as well as the
708 * @f$V_{diff}@f$ vector mentioned in the introduction. We also define the
709 * parameters used in the problem.
713 * - <code>maximum_stock_price</code>: The imposed upper bound on the spatial
714 * domain. This is the maximum allowed stock price.
715 * - <code>maturity_time</code>: The upper bound on the time domain. This is
716 * when the option expires.\n
717 * - <code>asset_volatility</code>: The volatility of the stock price.\n
718 * - <code>interest_rate</code>: The risk
free interest rate.\n
719 * - <code>strike_price</code>: The agreed upon price that the buyer will
720 * have the option of purchasing the stocks at the expiration time.
724 * Some slight differences between
this program and @ref step_26
"step-26" are the creation
725 * of the <code>a_matrix</code> and the <code>b_matrix</code>, which is
726 * described in the introduction. We then also need to store the current time,
727 * the size of the time step, and the number of the current time step.
728 * Next, we will store the output into a <code>
DataOutStack</code>
729 * variable because we will be layering the solution at each time on top of
730 * one another to create the solution manifold. Then, we have a variable that
731 * stores the current cycle and number of cycles that we will
run when
732 * calculating the solution. The cycle is one full solution calculation given
733 * a mesh. We
refine the mesh once in between each cycle to exhibit the
734 * convergence properties of our program. Finally, we store the convergence
735 * data into a convergence table.
739 * As far as member
functions are concerned, we have a function that
740 * calculates the convergence information
for each cycle, called
741 * <code>process_solution</code>. This is just like what is done in @ref step_7
"step-7".
753 *
void setup_system();
754 *
void solve_time_step();
755 *
void refine_grid();
756 *
void process_solution();
757 *
void add_results_for_output();
758 *
void write_convergence_table();
760 *
const double maximum_stock_price;
761 *
const double maturity_time;
762 *
const double asset_volatility;
763 *
const double interest_rate;
764 *
const double strike_price;
785 *
const double theta;
786 *
const unsigned int n_cycles;
787 *
const unsigned int n_time_steps;
790 * std::vector<std::string> solution_names;
798 * <a name=
"ThecodeBlackScholescodeImplementation"></a>
799 * <h3>The <code>BlackScholes</code> Implementation</h3>
803 * Now, we get to the implementation of the main
class. We will
set the
values
804 *
for the various parameters used in the problem. These were chosen because
805 * they are fairly normal
values for these parameters. Although the stock
806 * price has no upper bound in reality (it is in fact infinite), we impose
807 * an upper bound that is twice the strike price. This is a somewhat arbitrary
808 * choice to be twice the strike price, but it is large enough to see the
809 * interesting parts of the solution.
813 * BlackScholes<dim>::BlackScholes()
814 * : maximum_stock_price(1.)
815 * , maturity_time(1.)
816 * , asset_volatility(.2)
817 * , interest_rate(0.05)
818 * , strike_price(0.5)
824 * , n_time_steps(5000)
826 *
Assert(dim == 1, ExcNotImplemented());
832 * <a name=
"codeBlackScholessetup_systemcode"></a>
833 * <h4><code>BlackScholes::setup_system</code></h4>
837 * The next function sets up the
DoFHandler object, computes
838 * the constraints, and sets the linear algebra objects to their correct
839 * sizes. We also compute the @ref GlossMassMatrix
"mass matrix" here by calling a function from the
840 * library. We will compute the other 3 matrices next, because these need to
841 * be computed
'by hand'.
845 * Note, that the time step is initialized here because the maturity time was
846 * needed to compute the time step.
850 *
void BlackScholes<dim>::setup_system()
854 * time_step = maturity_time / n_time_steps;
856 * constraints.clear();
858 * constraints.close();
864 * sparsity_pattern.copy_from(dsp);
867 * laplace_matrix.reinit(sparsity_pattern);
868 * a_matrix.reinit(sparsity_pattern);
869 * b_matrix.reinit(sparsity_pattern);
870 * system_matrix.reinit(sparsity_pattern);
878 * Below is the code to create the Laplace
matrix with non-
constant
879 * coefficients. This corresponds to the
matrix D in the introduction. This
880 * non-
constant coefficient is represented in the
881 * <code>current_coefficient</code> variable.
884 *
const unsigned int dofs_per_cell = fe.dofs_per_cell;
888 * quadrature_formula,
891 * std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
892 *
for (
const auto &cell : dof_handler.active_cell_iterators())
895 * fe_values.reinit(cell);
896 *
for (
const unsigned int q_index : fe_values.quadrature_point_indices())
898 * const double current_coefficient =
899 * fe_values.quadrature_point(q_index).square();
900 *
for (
const unsigned int i : fe_values.dof_indices())
902 * for (const unsigned
int j : fe_values.dof_indices())
904 * (current_coefficient *
905 * fe_values.shape_grad(i, q_index) *
906 * fe_values.shape_grad(j, q_index) *
907 * fe_values.JxW(q_index));
910 * cell->get_dof_indices(local_dof_indices);
911 *
for (
const unsigned int i : fe_values.dof_indices())
913 * for (const unsigned
int j : fe_values.dof_indices())
914 * laplace_matrix.add(local_dof_indices[i],
915 * local_dof_indices[j],
922 * Now we will create the A
matrix. Below is the code to create the
matrix A
923 * as discussed in the introduction. The non
constant coefficient is again
924 * represented in the <code>current_coefficient</code> variable.
927 *
for (
const auto &cell : dof_handler.active_cell_iterators())
930 * fe_values.reinit(cell);
931 *
for (
const unsigned int q_index : fe_values.quadrature_point_indices())
933 * const
Tensor<1, dim> current_coefficient =
934 * fe_values.quadrature_point(q_index);
935 *
for (
const unsigned int i : fe_values.dof_indices())
937 * for (const unsigned
int j : fe_values.dof_indices())
940 * (current_coefficient *
941 * fe_values.shape_grad(i, q_index) *
942 * fe_values.shape_value(j, q_index) *
943 * fe_values.JxW(q_index));
947 * cell->get_dof_indices(local_dof_indices);
948 *
for (
const unsigned int i : fe_values.dof_indices())
950 * for (const unsigned
int j : fe_values.dof_indices())
951 * a_matrix.add(local_dof_indices[i],
952 * local_dof_indices[j],
959 * Finally we will create the
matrix B. Below is the code to create the
960 *
matrix B as discussed in the introduction. The non
constant coefficient
961 * is again represented in the <code>current_coefficient</code> variable.
964 *
for (
const auto &cell : dof_handler.active_cell_iterators())
967 * fe_values.reinit(cell);
968 *
for (
const unsigned int q_index : fe_values.quadrature_point_indices())
970 * const
Tensor<1, dim> current_coefficient =
971 * fe_values.quadrature_point(q_index);
972 *
for (
const unsigned int i : fe_values.dof_indices())
974 * for (const unsigned
int j : fe_values.dof_indices())
976 * (current_coefficient *
977 * fe_values.shape_value(i, q_index) *
978 * fe_values.shape_grad(j, q_index) *
979 * fe_values.JxW(q_index));
982 * cell->get_dof_indices(local_dof_indices);
983 *
for (
const unsigned int i : fe_values.dof_indices())
985 * for (const unsigned
int j : fe_values.dof_indices())
986 * b_matrix.add(local_dof_indices[i],
987 * local_dof_indices[j],
992 * solution.reinit(dof_handler.n_dofs());
993 * system_rhs.reinit(dof_handler.n_dofs());
999 * <a name=
"codeBlackScholessolve_time_stepcode"></a>
1000 * <h4><code>BlackScholes::solve_time_step</code></h4>
1004 * The next function is the one that solves the actual linear system
for a
1005 * single time step. The only interesting thing here is that the matrices
1010 *
template <
int dim>
1011 *
void BlackScholes<dim>::solve_time_step()
1016 * preconditioner.
initialize(system_matrix, 1.0);
1017 * cg.solve(system_matrix, solution, system_rhs, preconditioner);
1018 * constraints.distribute(solution);
1024 * <a name=
"codeBlackScholesadd_results_for_outputcode"></a>
1025 * <h4><code>BlackScholes::add_results_for_output</code></h4>
1029 * This is simply the function to stitch the solution pieces together. For
1030 *
this, we create a
new layer at each time, and then add the solution vector
1031 *
for that timestep. The function then stitches
this together with the old
1032 * solutions
using 'build_patches
'.
1035 * template <int dim>
1036 * void BlackScholes<dim>::add_results_for_output()
1038 * data_out_stack.new_parameter_value(time, time_step);
1039 * data_out_stack.attach_dof_handler(dof_handler);
1040 * data_out_stack.add_data_vector(solution, solution_names);
1041 * data_out_stack.build_patches(2);
1042 * data_out_stack.finish_parameter_value();
1048 * <a name="codeBlackScholesrefine_gridcode"></a>
1049 * <h4><code>BlackScholes::refine_grid</code></h4>
1053 * It is somewhat unnecessary to have a function for the global refinement
1054 * that we do. The reason for the function is to allow for the possibility of
1055 * an adaptive refinement later.
1058 * template <int dim>
1059 * void BlackScholes<dim>::refine_grid()
1061 * triangulation.refine_global(1);
1067 * <a name="codeBlackScholesprocess_solutioncode"></a>
1068 * <h4><code>BlackScholes::process_solution</code></h4>
1072 * This is where we calculate the convergence and error data to evaluate the
1073 * effectiveness of the program. Here, we calculate the @f$L^2@f$, @f$H^1@f$ and
1074 * @f$L^{\infty}@f$ norms.
1077 * template <int dim>
1078 * void BlackScholes<dim>::process_solution()
1080 * Solution<dim> sol(maturity_time);
1081 * sol.set_time(time);
1082 * Vector<float> difference_per_cell(triangulation.n_active_cells());
1083 * VectorTools::integrate_difference(dof_handler,
1086 * difference_per_cell,
1087 * QGauss<dim>(fe.degree + 1),
1088 * VectorTools::L2_norm);
1089 * const double L2_error =
1090 * VectorTools::compute_global_error(triangulation,
1091 * difference_per_cell,
1092 * VectorTools::L2_norm);
1093 * VectorTools::integrate_difference(dof_handler,
1096 * difference_per_cell,
1097 * QGauss<dim>(fe.degree + 1),
1098 * VectorTools::H1_seminorm);
1099 * const double H1_error =
1100 * VectorTools::compute_global_error(triangulation,
1101 * difference_per_cell,
1102 * VectorTools::H1_seminorm);
1103 * const QTrapezoid<1> q_trapezoid;
1104 * const QIterated<dim> q_iterated(q_trapezoid, fe.degree * 2 + 1);
1105 * VectorTools::integrate_difference(dof_handler,
1108 * difference_per_cell,
1110 * VectorTools::Linfty_norm);
1111 * const double Linfty_error =
1112 * VectorTools::compute_global_error(triangulation,
1113 * difference_per_cell,
1114 * VectorTools::Linfty_norm);
1115 * const unsigned int n_active_cells = triangulation.n_active_cells();
1116 * const unsigned int n_dofs = dof_handler.n_dofs();
1117 * convergence_table.add_value("cells", n_active_cells);
1118 * convergence_table.add_value("dofs", n_dofs);
1119 * convergence_table.add_value("L2", L2_error);
1120 * convergence_table.add_value("H1", H1_error);
1121 * convergence_table.add_value("Linfty", Linfty_error);
1127 * <a name="codeBlackScholeswrite_convergence_tablecode"></a>
1128 * <h4><code>BlackScholes::write_convergence_table</code> </h4>
1132 * This next part is building the convergence and error tables. By this, we
1133 * need to set the settings for how to output the data that was calculated
1134 * during <code>BlackScholes::process_solution</code>. First, we will create
1135 * the headings and set up the cells properly. During this, we will also
1136 * prescribe the precision of our results. Then we will write the calculated
1137 * errors based on the @f$L^2@f$, @f$H^1@f$, and @f$L^{\infty}@f$ norms to the console and
1138 * to the error LaTeX file.
1141 * template <int dim>
1142 * void BlackScholes<dim>::write_convergence_table()
1144 * convergence_table.set_precision("L2", 3);
1145 * convergence_table.set_precision("H1", 3);
1146 * convergence_table.set_precision("Linfty", 3);
1147 * convergence_table.set_scientific("L2", true);
1148 * convergence_table.set_scientific("H1", true);
1149 * convergence_table.set_scientific("Linfty", true);
1150 * convergence_table.set_tex_caption("cells", "\\# cells");
1151 * convergence_table.set_tex_caption("dofs", "\\# dofs");
1152 * convergence_table.set_tex_caption("L2", "@fL^2@f-error");
1153 * convergence_table.set_tex_caption("H1", "@fH^1@f-error");
1154 * convergence_table.set_tex_caption("Linfty", "@fL^\\infty@f-error");
1155 * convergence_table.set_tex_format("cells", "r");
1156 * convergence_table.set_tex_format("dofs", "r");
1157 * std::cout << std::endl;
1158 * convergence_table.write_text(std::cout);
1159 * std::string error_filename = "error";
1160 * error_filename += "-global";
1161 * error_filename += ".tex";
1162 * std::ofstream error_table_file(error_filename);
1163 * convergence_table.write_tex(error_table_file);
1167 * Next, we will make the convergence table. We will again write this to
1168 * the console and to the convergence LaTeX file.
1171 * convergence_table.add_column_to_supercolumn("cells", "n cells");
1172 * std::vector<std::string> new_order;
1173 * new_order.emplace_back("n cells");
1174 * new_order.emplace_back("H1");
1175 * new_order.emplace_back("L2");
1176 * convergence_table.set_column_order(new_order);
1177 * convergence_table.evaluate_convergence_rates(
1178 * "L2", ConvergenceTable::reduction_rate);
1179 * convergence_table.evaluate_convergence_rates(
1180 * "L2", ConvergenceTable::reduction_rate_log2);
1181 * convergence_table.evaluate_convergence_rates(
1182 * "H1", ConvergenceTable::reduction_rate);
1183 * convergence_table.evaluate_convergence_rates(
1184 * "H1", ConvergenceTable::reduction_rate_log2);
1185 * std::cout << std::endl;
1186 * convergence_table.write_text(std::cout);
1187 * std::string conv_filename = "convergence";
1188 * conv_filename += "-global";
1189 * switch (fe.degree)
1192 * conv_filename += "-q1";
1195 * conv_filename += "-q2";
1198 * Assert(false, ExcNotImplemented());
1200 * conv_filename += ".tex";
1201 * std::ofstream table_file(conv_filename);
1202 * convergence_table.write_tex(table_file);
1208 * <a name="codeBlackScholesruncode"></a>
1209 * <h4><code>BlackScholes::run</code></h4>
1213 * Now we get to the main driver of the program. This is where we do all the
1214 * work of looping through the time steps and calculating the solution vector
1215 * each time. Here at the top, we set the initial refinement value and then
1216 * create a mesh. Then we refine this mesh once. Next, we set up the
1217 * data_out_stack object to store our solution. Finally, we start a for loop
1218 * to loop through the cycles. This lets us recalculate a solution for each
1219 * successive mesh refinement. At the beginning of each iteration, we need to
1220 * reset the time and time step number. We introduce an if statement to
1221 * accomplish this because we don't want to
do this on the
first iteration.
1224 *
template <
int dim>
1225 *
void BlackScholes<dim>::run()
1230 * solution_names.emplace_back(
"u");
1231 * data_out_stack.declare_data_vector(solution_names,
1237 *
for (
unsigned int cycle = 0; cycle < n_cycles; ++cycle)
1247 * std::cout << std::endl
1248 * <<
"===========================================" << std::endl
1249 * <<
"Cycle " << cycle <<
':' << std::endl
1250 * <<
"Number of active cells: "
1252 * <<
"Number of degrees of freedom: " << dof_handler.n_dofs()
1257 * InitialConditions<dim>(strike_price),
1260 *
if (cycle == (n_cycles - 1))
1262 * add_results_for_output();
1267 * Next, we
run the main
loop which runs until we exceed the maturity
1268 * time. We
first compute the right hand side of the equation, which is
1269 * described in the introduction. Recall that it contains the term
1270 * @f$\left[-\frac{1}{4}k_n\sigma^2\mathbf{D}-k_nr\mathbf{M}+k_n\sigma^2
1271 * \mathbf{B}-k_nr\mathbf{A}+\mathbf{M}\right]V^{n-1}@f$. We put these
1272 * terms into the variable system_rhs, with the help of a temporary
1276 * vmult_result.reinit(dof_handler.n_dofs());
1277 * forcing_terms.reinit(dof_handler.n_dofs());
1278 *
for (
unsigned int timestep_number = 0; timestep_number < n_time_steps;
1279 * ++timestep_number)
1281 * time += time_step;
1283 *
if (timestep_number % 1000 == 0)
1284 * std::cout <<
"Time step " << timestep_number <<
" at t=" << time
1289 * laplace_matrix.vmult(vmult_result, solution);
1291 * (-1) * (1 - theta) * time_step *
1292 * Utilities::fixed_power<2, double>(asset_volatility) * 0.5,
1296 * system_rhs.add((-1) * (1 - theta) * time_step * interest_rate * 2,
1299 * a_matrix.vmult(vmult_result, solution);
1300 * system_rhs.add((-1) * time_step * interest_rate, vmult_result);
1302 * b_matrix.vmult(vmult_result, solution);
1304 * (-1) * Utilities::fixed_power<2, double>(asset_volatility) *
1310 * The
second piece is to compute the contributions of the source
1311 * terms. This corresponds to the term @f$-k_n\left[\frac{1}{2}
F^{n-1}
1312 * +\frac{1}{2}
F^n\right]@f$. The following code calls
1314 * where we
set the time of the right hand side (source) function
1315 * before we evaluate it. The result of
this all ends up in the
1316 * forcing_terms variable:
1319 * RightHandSide<dim> rhs_function(asset_volatility, interest_rate);
1320 * rhs_function.set_time(time);
1325 * forcing_terms *= time_step * theta;
1326 * system_rhs -= forcing_terms;
1328 * rhs_function.set_time(time - time_step);
1333 * forcing_terms *= time_step * (1 - theta);
1334 * system_rhs -= forcing_terms;
1338 * Next, we add the forcing terms to the ones that come from the
1339 * time stepping, and also build the
matrix @f$\left[\mathbf{M}+
1340 * \frac{1}{4}k_n\sigma^2\mathbf{D}+k_nr\mathbf{M}\right]@f$ that we
1341 * have to
invert in each time step. The
final piece of these
1342 * operations is to eliminate hanging node constrained degrees of
1343 * freedom from the linear system:
1346 * system_matrix.copy_from(mass_matrix);
1347 * system_matrix.add(
1348 * (theta)*time_step *
1349 * Utilities::fixed_power<2, double>(asset_volatility) * 0.5,
1351 * system_matrix.add((time_step)*interest_rate * theta * (1 + 1),
1354 * constraints.condense(system_matrix, system_rhs);
1358 * There is one more operation we need to
do before we can solve it:
1359 * boundary
values. To
this end, we create a boundary
value object,
1360 *
set the proper time to the one of the current time step, and
1361 * evaluate it as we have done many times before. The result is used
1362 * to also
set the correct boundary
values in the linear system:
1366 * RightBoundaryValues<dim> right_boundary_function(strike_price,
1368 * LeftBoundaryValues<dim> left_boundary_function;
1369 * right_boundary_function.set_time(time);
1370 * left_boundary_function.set_time(time);
1371 * std::map<types::global_dof_index, double> boundary_values;
1374 * left_boundary_function,
1378 * right_boundary_function,
1388 * With
this out of the way, all we have to
do is solve the system,
1389 * generate graphical data on the last cycle, and create the
1390 * convergence table data.
1393 * solve_time_step();
1395 *
if (cycle == (n_cycles - 1))
1397 * add_results_for_output();
1401 * process_solution();
1405 *
const std::string filename =
"solution.vtk";
1406 * std::ofstream output(filename);
1407 * data_out_stack.write_vtk(output);
1410 * write_convergence_table();
1419 * <a name=
"ThecodemaincodeFunction"></a>
1420 * <h3>The <code>main</code>
Function</h3>
1424 * Having made it
this far, there is, again,
nothing much to discuss
for the
1425 * main function of
this program: it looks like all such
functions since @ref step_6
"step-6".
1432 *
using namespace BlackScholesSolver;
1434 * BlackScholes<1> black_scholes_solver;
1435 * black_scholes_solver.run();
1437 *
catch (std::exception &exc)
1439 * std::cerr << std::endl
1441 * <<
"----------------------------------------------------"
1443 * std::cerr <<
"Exception on processing: " << std::endl
1444 * << exc.what() << std::endl
1445 * <<
"Aborting!" << std::endl
1446 * <<
"----------------------------------------------------"
1452 * std::cerr << std::endl
1454 * <<
"----------------------------------------------------"
1456 * std::cerr <<
"Unknown exception!" << std::endl
1457 * <<
"Aborting!" << std::endl
1458 * <<
"----------------------------------------------------"
1465<a name=
"Results"></a><h1>Results</h1>
1469Below is the output of the program:
1471===========================================
1472Number of active cells: 1
1473Number of degrees of freedom: 2
1475Time step 0 at t=0.0002
1479Number of active cells: 128
1480Number of degrees of freedom: 129
1482Time step 0 at t=0.0002
1483Time step 1000 at t=0.2002
1484Time step 2000 at t=0.4002
1485Time step 3000 at t=0.6002
1486Time step 4000 at t=0.8002
1488cells dofs
L2 H1 Linfty
1489 1 2 1.667e-01 5.774e-01 2.222e-01
1490 2 3 3.906e-02 2.889e-01 5.380e-02
1491 4 5 9.679e-03 1.444e-01 1.357e-02
1492 8 9 2.405e-03 7.218e-02 3.419e-03
1493 16 17 5.967e-04 3.609e-02 8.597e-04
1494 32 33 1.457e-04 1.804e-02 2.155e-04
1495 64 65 3.307e-05 9.022e-03 5.388e-05
1496 128 129 5.016e-06 4.511e-03 1.342e-05
1499 1 5.774e-01 - - 1.667e-01 - -
1500 2 2.889e-01 2.00 1.00 3.906e-02 4.27 2.09
1501 4 1.444e-01 2.00 1.00 9.679e-03 4.04 2.01
1502 8 7.218e-02 2.00 1.00 2.405e-03 4.02 2.01
1503 16 3.609e-02 2.00 1.00 5.967e-04 4.03 2.01
1504 32 1.804e-02 2.00 1.00 1.457e-04 4.10 2.03
1505 64 9.022e-03 2.00 1.00 3.307e-05 4.41 2.14
1506 128 4.511e-03 2.00 1.00 5.016e-06 6.59 2.72
1509What is more interesting is the output of the convergence tables. They are
1510outputted into the console, as well into a LaTeX file. The convergence tables
1511are shown above. Here, you can see that the solution has a convergence rate
1512of @f$\mathcal{O}(h)@f$ with respect to the @f$H^1@f$-norm, and the solution has a convergence rate
1513of @f$\mathcal{O}(h^2)@f$ with respect to the @f$L^2@f$-
norm.
1516Below is the visualization of the solution.
1518<div style=
"text-align:center;">
1519 <img src=
"https://www.dealii.org/images/steps/developer/step-78.mms-solution.png"
1520 alt=
"Solution of the MMS problem.">
1524<a name=
"PlainProg"></a>
1525<h1> The plain program</h1>
1526@include
"step-78.cc"
void distribute_dofs(const FiniteElement< dim, spacedim > &fe)
virtual RangeNumberType value(const Point< dim > &p, const unsigned int component=0) const
void initialize(const MatrixType &A, const AdditionalData ¶meters=AdditionalData())
__global__ void set(Number *val, const Number s, const size_type N)
#define Assert(cond, exc)
void loop(ITERATOR begin, std_cxx20::type_identity_t< ITERATOR > end, DOFINFO &dinfo, INFOBOX &info, const std::function< void(DOFINFO &, typename INFOBOX::CellInfo &)> &cell_worker, const std::function< void(DOFINFO &, typename INFOBOX::CellInfo &)> &boundary_worker, const std::function< void(DOFINFO &, DOFINFO &, typename INFOBOX::CellInfo &, typename INFOBOX::CellInfo &)> &face_worker, ASSEMBLER &assembler, const LoopControl &lctrl=LoopControl())
void make_hanging_node_constraints(const DoFHandler< dim, spacedim > &dof_handler, AffineConstraints< number > &constraints)
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints=AffineConstraints< number >(), const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
@ update_values
Shape function values.
@ update_JxW_values
Transformed quadrature weights.
@ update_gradients
Shape function gradients.
@ update_quadrature_points
Transformed quadrature points.
void hyper_cube(Triangulation< dim, spacedim > &tria, const double left=0., const double right=1., const bool colorize=false)
void refine(Triangulation< dim, spacedim > &tria, const Vector< Number > &criteria, const double threshold, const unsigned int max_to_mark=numbers::invalid_unsigned_int)
@ matrix
Contents is actually a matrix.
@ symmetric
Matrix is symmetric.
void cell_matrix(FullMatrix< double > &M, const FEValuesBase< dim > &fe, const FEValuesBase< dim > &fetest, const ArrayView< const std::vector< double > > &velocity, const double factor=1.)
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim > > > &Du)
void L2(Vector< number > &result, const FEValuesBase< dim > &fe, const std::vector< double > &input, const double factor=1.)
void mass_matrix(FullMatrix< double > &M, const FEValuesBase< dim > &fe, const double factor=1.)
void create_mass_matrix(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof, const Quadrature< dim > &q, SparseMatrixType &matrix, const Function< spacedim, typename SparseMatrixType::value_type > *const a=nullptr, const AffineConstraints< typename SparseMatrixType::value_type > &constraints=AffineConstraints< typename SparseMatrixType::value_type >())
Tensor< 2, dim, Number > F(const Tensor< 2, dim, Number > &Grad_u)
VectorType::value_type * end(VectorType &V)
void run(const Iterator &begin, const std_cxx20::type_identity_t< Iterator > &end, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length, const unsigned int chunk_size)
int(&) functions(const void *v1, const void *v2)
::VectorizedArray< Number, width > exp(const ::VectorizedArray< Number, width > &)
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation
DEAL_II_HOST constexpr SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &)