379 *
#include <deal.II/base/utilities.h>
380 *
#include <deal.II/base/quadrature_lib.h>
381 *
#include <deal.II/base/function.h>
382 *
#include <deal.II/base/logstream.h>
383 *
#include <deal.II/lac/vector.h>
384 *
#include <deal.II/lac/full_matrix.h>
385 *
#include <deal.II/lac/dynamic_sparsity_pattern.h>
386 *
#include <deal.II/lac/sparse_matrix.h>
387 *
#include <deal.II/lac/solver_cg.h>
388 *
#include <deal.II/lac/precondition.h>
389 *
#include <deal.II/lac/affine_constraints.h>
390 *
#include <deal.II/grid/tria.h>
391 *
#include <deal.II/grid/grid_generator.h>
392 *
#include <deal.II/grid/grid_refinement.h>
393 *
#include <deal.II/grid/grid_out.h>
394 *
#include <deal.II/dofs/dof_handler.h>
395 *
#include <deal.II/dofs/dof_tools.h>
396 *
#include <deal.II/fe/fe_q.h>
397 *
#include <deal.II/fe/fe_system.h>
398 *
#include <deal.II/fe/fe_values.h>
399 *
#include <deal.II/numerics/data_out.h>
400 *
#include <deal.II/numerics/vector_tools.h>
401 *
#include <deal.II/numerics/error_estimator.h>
402 *
#include <deal.II/numerics/solution_transfer.h>
403 *
#include <deal.II/numerics/matrix_tools.h>
404 *
#include <deal.II/lac/sparse_direct.h>
405 *
#include <deal.II/base/timer.h>
407 *
#include <deal.II/grid/manifold_lib.h>
408 *
#include <deal.II/grid/grid_tools.h>
410 *
#include <
boost/math/special_functions/ellint_1.hpp>
413 *
#include <iostream>
416 *
namespace SwiftHohenbergSolver
424 * This
enum defines the five mesh
types implemented
425 * in
this program and allows the user to pass which
426 * mesh is desired to the solver at runtime. This is
427 * useful
for looping over different meshes.
430 *
enum MeshType {HYPERCUBE, CYLINDER, SPHERE, TORUS, SINUSOID};
435 * This
enum defines the three
initial conditions used
436 * by the program. This allows
for the solver
class to
437 * use a
template argument to determine the desired
438 *
initial condition, which is helpful
for setting up
439 * loops to solve with a variety of different conditions
442 *
enum InitialConditionType {HOTSPOT, PSUEDORANDOM, RANDOM};
449 * This function warps points on a cylindrical mesh by cosine wave along the central axis.
450 * We use
this function to generate the
"sinusoid" mesh, which is the surface of revolution
451 * bounded by the cosine wave. spacedim is the dimension of the embedding space, which is
452 * where the input
point lives. p is the input
point to be translated. The
return is a translated
453 *
point in the same dimensional space. This is the
new point on the mesh.
456 *
template<
int spacedim>
461 * Currently
this only works
for a 3-dimensional embedding space
462 * because we are explicitly referencing the x, y, and z coordinates
465 *
Assert(spacedim == 3, ExcNotImplemented());
469 * Returns a
point where the x-coordinate is unchanged but the y and z coordinates are adjusted
470 * by a
cos wave of period 20, amplitude .5, and vertical
shift 1
481 * This is the
class that holds all the important variables
for the solver, as well as the important
482 * member
functions. This
class is based off the HeatEquation
class from @ref step_26
"step-26", so we won
't go into
483 * full detail on all the features, but we will highlight what has been changed for this problem. dim
484 * is the intrinsic dimension of the manifold we are solving on. spacedim is the dimension of the embedding
485 * space. MESH determines what manifold we are solving on ICTYPE determines what initial condition we use
488 * template <int dim, int spacedim, MeshType MESH, InitialConditionType ICTYPE>
494 * Default constructor, initializes all variables and objects with default values
502 * Overloaded constructor, allows user to pass values for important constants. degree is the degree
503 * of finite element used, time_step_denominator determines what size timestep we use. The timestep
504 * is 1/time_step_denominator. ref_num is the number of times the mesh will be globally refined.
505 * r_constant is a constant for the linear component, default 0.5, g1_constant is a constant for the
506 * quadratic component, default 0.5. output_file_name is self explanatory, default "solution-" end_time
507 * determines when the solver stops, default 0.5, should be ~100 to see equilibrium solutions
510 * SHEquation(const unsigned int degree
511 * , double time_step_denominator
512 * , unsigned int ref_num
513 * , double r_constant = 0.5
514 * , double g1_constant = 0.5
515 * , std::string output_file_name = "solution-"
516 * , double end_time = 0.5);
520 * void setup_system();
521 * void solve_time_step();
522 * void output_results() const;
525 * This function calls a different grid generation function depending on the template argument MESH.
526 * Allows the solver object to generate different mesh types based on the template parameter.
533 * Generates a cylindrical mesh with radius 6 and width 6*pi by first creating a volumetric cylinder,
534 * extracting the boundary, and redefining the mesh as a cylinder, then refining the mesh refinement_number times
537 * void make_cylinder();
540 * Uses the same process as creating a cylinder, but then also warps the boundary of the
541 * cylinder by the function (1 + 0.5*cos(pi*x/10))
544 * void make_sinusoid();
547 * Generates a spherical mesh of radius 6*pi using GridGenerator and refines it refinement_number times.
550 * void make_sphere();
553 * Generates a torus mesh with inner radius 4 and outer radius 9 using GridGenerator and refines it
554 * refinement_number times.
560 * Generates a hypercube mesh with sidelength 12*pi using GridGenerator and refines it refinement_number times.
563 * void make_hypercube();
568 * The degree of finite element to be used, default 1
571 * const unsigned int degree;
575 * Object holding the mesh
578 * Triangulation<dim, spacedim> triangulation;
581 * Object describing the finite element vectors at each node (I believe this gives a basis for the
582 * finite elements at each node)
585 * FESystem<dim, spacedim> fe;
588 * Object which understands which finite elements are at each node
591 * DoFHandler<dim, spacedim> dof_handler;
595 * Describes the sparsity of the system matrix, allows for more efficient storage
598 * SparsityPattern sparsity_pattern;
602 * Object holding the system matrix, stored as a sparse matrix
605 * SparseMatrix<double> system_matrix;
609 * Vector of coefficients for the solution in the current timestep. We solve for this in each timestep
612 * Vector<double> solution;
615 * Stores the solution from the previous timestep. Used to compute non-linear terms
618 * Vector<double> old_solution;
621 * Stores the coefficients of the right hand side function(in terms of the finite elements). Is the
622 * RHS for the linear system
625 * Vector<double> system_rhs;
629 * Stores the current time, in the units of the problem
635 * The amount time is increased each iteration/ the denominator of the discretized time derivative
641 * Counts the number of iterations that have elapsed
644 * unsigned int timestep_number;
647 * Used to compute the time_step: time_step = 1/timestep_denominator
650 * unsigned int timestep_denominator;
653 * Determines how much to globally refine each mesh
656 * unsigned int refinement_number;
660 * Coefficient of the linear term in the SH equation. This is often taken to be constant and g_1 allowed to vary
666 * Coefficient of the quadratic term in the SH equation. Determines whether hexagonal lattices can form
672 * A control parameter for the cubic term. Can be useful for testing, in this code we let k=1 in all cases
679 * Name used to create output file. Should not include extension
682 * const std::string output_file_name;
686 * Determines when the solver terminates, endtime of ~100 are useful to see equilibrium results
689 * const double end_time;
695 * The function which applies zero Dirichlet boundary conditions, and is not being used by the solver
696 * currently. Leaving the code in case this is ever needed. spacedim is the dimension of the points
697 * which the function takes as input
700 * template <int spacedim>
701 * class BoundaryValues : public Function<spacedim>
705 * : Function<spacedim>(2)
708 * virtual double value(const Point<spacedim> & p,
709 * const unsigned int component = 0) const override;
716 * Returns 0 for all points. This is the output for the boundary spacedim is the dimension of
717 * points that are input, p is the input point, component determines whether we are solving
718 * for u or v, which determines which part of the system we are solving. Returns 0, which is
719 * the boundary value for all points
722 * template <int spacedim>
723 * double BoundaryValues<spacedim>::value(const Point<spacedim> & p,
724 * const unsigned int component) const
727 * AssertIndexRange(component, 2);
734 * This class holds the initial condition function we will use for the solver.
735 * Note that this class takes both MeshType and InitialConditionType as parameters.
736 * This class is capable of producing several different initial conditions without
737 * having to change the code each time, which makes it useful for running longer
738 * experiments without having to stop the code each time. The downside of this is
739 * the code is that the class is rather large, and functions have to be defined
740 * multiple times to be compatible with the different configurations of MESH and
741 * ICTYPE. Because of this, our implementation is not a good solution if more than
742 * a few variations of mesh and initial conditions need to be used. spacedim is the
743 * dimension of the input points. MESH is the type of mesh to apply initial conditions
744 * to, of type MeshType ICTYPE is the type of initial condition to apply, of type
745 * InitialConditionType
748 * template<int spacedim, MeshType MESH, InitialConditionType ICTYPE>
749 * class InitialCondition : public Function<spacedim>
754 * The value of the parameter r, used to determine a bound for the magnitude of the initial conditions
760 * A center point, used to determine the location of the hot spot for the HotSpot initial condition
763 * Point<spacedim> center;
766 * Radius of the hot spot
772 * Stores the randomly generated coefficients for planar sine waves along the x-axis, used for psuedorandom initial conditions
775 * double x_sin_coefficients[10];
778 * Stores the randomly generated coefficients for planar sine waves along the y-axis, used for psuedorandom initial conditions
781 * double y_sin_coefficients[10];
786 * The default constructor for the class. Initializes a function of 2 parameters and sets r and radius
787 * to default values. The constructor also loops through the coefficient arrays and stores the random
788 * coefficients for the psuedorandom initial condition.
792 * : Function<spacedim>(2),
796 * for(int i = 0; i < 10; ++i){
797 * x_sin_coefficients[i] = 2*std::sqrt(r)*(std::rand()%1001)/1000 - std::sqrt(r);
798 * y_sin_coefficients[i] = 2*std::sqrt(r)*(std::rand()%1001)/1000 - std::sqrt(r);
804 * An overloaded constructor, takes r and radius as parameters and uses these for initialization.
805 * Also loops through the coefficient arrays and stores the random coefficients for the psuedorandom
806 * initial condition. r is the value of the r parameter in the SH equation. radius is the radius of
810 * InitialCondition(const double r,
811 * const double radius)
812 * : Function<spacedim>(2),
816 * for(int i = 0; i < 10; ++i){
817 * x_sin_coefficients[i] = 2*std::sqrt(r)*(std::rand()%1001)/1000 - std::sqrt(r);
818 * y_sin_coefficients[i] = 2*std::sqrt(r)*(std::rand()%1001)/1000 - std::sqrt(r);
824 * The return value of the initial condition function. This function is highly overloaded to account for a variety
825 * of different initial condition and mesh configurations, based on the template parameter given.
826 * Note that each initial condition sets the v component to 1e18. The v initial condition should not effect our solutions,
827 * and this is a good way to make any bugs causing v's
initial condition to affect the solution easy to detect
828 * The RANDOM
initial condition type does not change from mesh to mesh, it just returns a
random number between -
sqrt(r) and
sqrt(r)
829 * The HOTSPOT
initial condition changes the center depending on the input mesh type so that the hotspot is on the surface of the mesh
830 * The PSEUDORANDOM
initial condition generates a function by summing up 10 sine waves in the x and y directions, with periods chosen so
831 * that the smallest period wave can still be resolved by a mesh with global refinement 5 or higher. On the plane, the
value at each
point
832 * is the product of the x sine
sum and the y sine
sum evaluated at the
point. On the
cylinder and Sinusoid, the x
component is still used
833 *
for the x sine
sum, but we use ((arctan(y, z) - pi)/pi)*6*pi
for the y sine
sum. This wraps the psuedorandom function around the
cylinder
834 * so that we can compare it to the same
initial conditions on the plane. This function will
run for the
torus and sphere, but it has not been
835 * implemented to be comparable to the plane.
843 * Places a small hot spot in the center of the plane on the u solution, and
set v to a large number.
844 * p is the input
point.
component determines whether the input is
for u or v. The function returns
849 *
double InitialCondition<2, HYPERCUBE, HOTSPOT>::value(
851 *
const unsigned int component)
const
853 *
if(component == 0){
854 *
if(p.square() <=
radius){
874 *
double InitialCondition<3, CYLINDER, HOTSPOT>::value(
876 *
const unsigned int component)
const
878 *
if(component == 0){
880 *
const Point<3> compare(p - center);
881 *
if(compare.square() <=
radius){
895 * Places the hot spot on the
outside of the sphere, along the
positive x axis
896 * p is the input
point.
897 *
component determines whether the input is
for u or v.
902 *
double InitialCondition<3, SPHERE, HOTSPOT>::value(
904 *
const unsigned int component)
const
906 *
if(component == 0){
907 *
const Point<3> center(18.41988074, 0, 0);
908 *
const Point<3> compare(p - center);
909 *
if(compare.square() <=
radius){
923 * Places the hot spot on the
outside of the
torus, along the x axis.
924 * p is the input
point.
925 *
component determines whether the input is
for u or v.
930 *
double InitialCondition<3, TORUS, HOTSPOT>::value(
932 *
const unsigned int component)
const
934 *
if(component == 0){
936 *
const Point<3> compare(p - center);
937 *
if(compare.square() <=
radius){
951 * Places the hot spot in the center of the sinusoid, on the
positive z side.
952 * p is the input
point.
953 *
component determines whether the input is
for u or v.
958 *
double InitialCondition<3, SINUSOID, HOTSPOT>::value(
960 *
const unsigned int component)
const
962 *
if(component == 0){
964 *
const Point<3> compare(p - center);
965 *
if(compare.square() <=
radius){
979 * Returns the
value of the psuedorandom function at the input
point, as described above.
980 * p is the input
point.
981 *
component determines whether the input is
for u or v.
986 *
double InitialCondition<2, HYPERCUBE, PSUEDORANDOM>::value(
988 *
const unsigned int component)
const
990 *
if(component == 0){
993 *
for(
int i=0; i < 10; ++i){
994 *
x_val += x_sin_coefficients[i]*
std::sin(2*3.141592653*p(0)/((i+1)*1.178097245));
995 *
y_val += y_sin_coefficients[i]*
std::sin(2*3.141592653*p(1)/((i+1)*1.178097245));
998 *
return x_val*y_val;
1007 * Returns the
value of the psuedorandom function at the input
point, as described above.
1008 * p is the input
point.
1009 *
component determines whether the input is
for u or v.
1014 *
double InitialCondition<3, CYLINDER, PSUEDORANDOM>::value(
1016 *
const unsigned int component)
const
1018 *
if(component == 0){
1021 *
double width = ((std::atan2(p(1),p(2)) - 3.1415926)/3.1415926)*18.84955592;
1022 *
for(
int i=0; i < 10; ++i){
1023 *
x_val += x_sin_coefficients[i]*
std::sin(2*3.141592653*p(0)/((i+1)*1.178097245));
1024 *
w_val += y_sin_coefficients[i]*
std::sin(2*3.141592653*width/((i+1)*1.178097245));
1027 *
return x_val*w_val;
1036 * NOTE: Not particularly useful at the moment. Returns the
value of the psuedorandom function
1037 * at the input
point, as described above.
1038 * p is the input
point.
1039 *
component determines whether the input is
for u or v.
1044 *
double InitialCondition<3, SPHERE, PSUEDORANDOM>::value(
1046 *
const unsigned int component)
const
1048 *
if(component == 0){
1051 *
for(
int i=0; i < 10; ++i){
1052 *
x_val += x_sin_coefficients[i]*
std::sin(2*3.141592653*p(0)/((i+1)*1.178097245));
1053 *
y_val += y_sin_coefficients[i]*
std::sin(2*3.141592653*p(1)/((i+1)*1.178097245));
1056 *
return x_val*y_val;
1065 * NOTE: Not particularly useful at the moment. Returns the
value of the psuedorandom function
1066 * at the input
point, as described above.
1067 * p is the input
point.
1068 *
component determines whether the input is
for u or v.
1073 *
double InitialCondition<3, TORUS, PSUEDORANDOM>::value(
1075 *
const unsigned int component)
const
1077 *
if(component == 0){
1080 *
for(
int i=0; i < 10; ++i){
1081 *
x_val += x_sin_coefficients[i]*
std::sin(2*3.141592653*p(0)/((i+1)*1.178097245));
1082 *
z_val += y_sin_coefficients[i]*
std::sin(2*3.141592653*p(2)/((i+1)*1.178097245));
1085 *
return x_val*z_val;
1094 * Returns the
value of the psuedorandom function at the input
point, as described above.
1095 * p is the input
point.
1096 *
component determines whether the input is
for u or v.
1101 *
double InitialCondition<3, SINUSOID, PSUEDORANDOM>::value(
1103 *
const unsigned int component)
const
1105 *
if(component == 0){
1108 *
double width = ((std::atan2(p(1),p(2)) - 3.1415926)/3.1415926)*18.84955592;
1109 *
for(
int i=0; i < 10; ++i){
1110 *
x_val += x_sin_coefficients[i]*
std::sin(2*3.141592653*p(0)/((i+1)*1.178097245));
1111 *
w_val += y_sin_coefficients[i]*
std::sin(2*3.141592653*width/((i+1)*1.178097245));
1114 *
return x_val*w_val;
1127 *
double InitialCondition<2, HYPERCUBE, RANDOM>::value(
1129 *
const unsigned int component)
const
1131 *
if(component == 0){
1145 *
double InitialCondition<3, CYLINDER, RANDOM>::value(
1147 *
const unsigned int component)
const
1149 *
if(component == 0){
1163 *
double InitialCondition<3, SPHERE, RANDOM>::value(
1165 *
const unsigned int component)
const
1167 *
if(component == 0){
1181 *
double InitialCondition<3, TORUS, RANDOM>::value(
1183 *
const unsigned int component)
const
1185 *
if(component == 0){
1199 *
double InitialCondition<3, SINUSOID, RANDOM>::value(
1201 *
const unsigned int component)
const
1203 *
if(component == 0){
1211 *
template <
int dim,
int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1212 *
SHEquation<dim, spacedim, MESH, ICTYPE>::SHEquation()
1215 *
, dof_handler(triangulation)
1216 *
, time_step(1. / 1500)
1217 *
, timestep_denominator(1500)
1218 *
, refinement_number(4)
1222 *
, output_file_name(
"solution-")
1226 *
template <
int dim,
int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1227 *
SHEquation<dim, spacedim, MESH, ICTYPE>::SHEquation(
const unsigned int degree,
1228 *
double time_step_denominator,
1229 *
unsigned int ref_num,
1230 *
double r_constant,
1231 *
double g1_constant,
1232 *
std::string output_file_name,
1236 *
, dof_handler(triangulation)
1237 *
, time_step(1. / time_step_denominator)
1238 *
, timestep_denominator(time_step_denominator)
1239 *
, refinement_number(ref_num)
1243 *
, output_file_name(output_file_name)
1244 *
, end_time(end_time)
1249 * Distributes the finite element vectors to each DoF, creates the system
matrix, solution,
1250 * old_solution, and system_rhs vectors,
1251 * and outputs the number of DoF
's to the console.
1252 * dim is the dimension of the manifold.
1253 * spacedim is the dimension of the ambient space.
1254 * MESH is the type of mesh being used, doesn't change how
this function works.
1255 * ICTYPE is the type of
initial condition used, doesn
't change how this function works.
1258 * template <int dim, int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1259 * void SHEquation<dim, spacedim, MESH, ICTYPE>::setup_system()
1261 * dof_handler.distribute_dofs(fe);
1265 * Counts the DoF's
for outputting to consolse
1268 *
const std::vector<types::global_dof_index> dofs_per_component =
1270 *
const unsigned int n_u = dofs_per_component[0],
1271 *
n_v = dofs_per_component[1];
1273 *
std::cout <<
"Number of active cells: " << triangulation.n_active_cells()
1275 *
<<
"Total number of cells: " << triangulation.n_cells()
1277 *
<<
"Number of degrees of freedom: " << dof_handler.n_dofs()
1278 *
<<
" (" << n_u <<
'+' << n_v <<
')' << std::endl;
1284 *
sparsity_pattern.copy_from(dsp);
1286 *
system_matrix.reinit(sparsity_pattern);
1288 *
solution.reinit(dof_handler.n_dofs());
1289 *
old_solution.reinit(dof_handler.n_dofs());
1290 *
system_rhs.reinit(dof_handler.n_dofs());
1296 * Uses a direct solver to
invert the system
matrix, then multiplies the RHS vector by the inverted
matrix to get the solution.
1297 * Also includes a timer feature, which is currently commented out, but can be helpful to compute how
long a
run will take.
1298 * dim is the dimension of the manifold.
1299 * spacedim is the dimension of the ambient space.
1300 * MESH is the type of mesh being used, doesn
't change how this function works.
1301 * ICTYPE is the type of initial condition used, doesn't change how
this function works.
1304 *
template <
int dim,
int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1305 *
void SHEquation<dim, spacedim, MESH, ICTYPE>::solve_time_step()
1309 * std::cout <<
"Solving linear system" << std::endl;
1320 *
direct_solver.vmult(solution, system_rhs);
1325 * std::cout <<
"done (" << timer.
cpu_time() <<
" s)" << std::endl;
1334 * Converts the solution vector into a .vtu file and labels the outputs as u and v.
1335 * dim is the dimension of the manifold.
1336 * spacedim is the dimension of the ambient space.
1337 * MESH is the type of mesh being used, doesn
't change how this function works.
1338 * ICTYPE is the type of initial condition used, doesn't change how
this function works.
1341 *
template <
int dim,
int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1342 *
void SHEquation<dim, spacedim, MESH, ICTYPE>::output_results() const
1344 *
std::vector<std::string> solution_names(1,
"u");
1345 *
solution_names.emplace_back(
"v");
1346 *
std::vector<DataComponentInterpretation::DataComponentInterpretation>
1358 *
data_out.build_patches(degree + 1);
1362 * Takes the output_file_name
string and appends timestep_number with up to three leading 0
's
1365 * const std::string filename =
1366 * output_file_name + Utilities::int_to_string(timestep_number, 3) + ".vtu";
1368 * std::ofstream output(filename);
1369 * data_out.write_vtu(output);
1374 * Below are all the different template cases for the make_grid() function
1378 * void SHEquation<2, 2, HYPERCUBE, HOTSPOT>::make_grid()
1384 * void SHEquation<2, 3, CYLINDER, HOTSPOT>::make_grid()
1390 * void SHEquation<2, 3, SPHERE, HOTSPOT>::make_grid()
1396 * void SHEquation<2, 3, TORUS, HOTSPOT>::make_grid()
1402 * void SHEquation<2, 3, SINUSOID, HOTSPOT>::make_grid()
1408 * void SHEquation<2, 2, HYPERCUBE, PSUEDORANDOM>::make_grid()
1414 * void SHEquation<2, 3, CYLINDER, PSUEDORANDOM>::make_grid()
1420 * void SHEquation<2, 3, SPHERE, PSUEDORANDOM>::make_grid()
1426 * void SHEquation<2, 3, TORUS, PSUEDORANDOM>::make_grid()
1432 * void SHEquation<2, 3, SINUSOID, PSUEDORANDOM>::make_grid()
1438 * void SHEquation<2, 2, HYPERCUBE, RANDOM>::make_grid()
1444 * void SHEquation<2, 3, CYLINDER, RANDOM>::make_grid()
1450 * void SHEquation<2, 3, SPHERE, RANDOM>::make_grid()
1456 * void SHEquation<2, 3, TORUS, RANDOM>::make_grid()
1462 * void SHEquation<2, 3, SINUSOID, RANDOM>::make_grid()
1470 * Runs the solver. First it creates the mesh and sets up the system, then constructs the system matrix, and finally loops over time to create
1471 * the RHS vector and solve the system at each step.
1472 * dim is the dimension of the manifold.
1473 * spacedim is the dimension of the ambient space.
1474 * MESH is the type of mesh being used.
1475 * ICTYPE is the type of initial condition used, doesn't change how
this function works.
1478 *
template <
int dim,
int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1479 *
void SHEquation<dim, spacedim, MESH, ICTYPE>::run()
1487 * Counts total time elapsed
1493 * Counts number of iterations
1496 *
timestep_number = 0;
1500 * Sets the
random seed so runs are repeatable, remove
for varying
random initial conditions
1505 *
InitialCondition<spacedim, MESH, ICTYPE> initial_conditions(r, 0.5);
1509 * Applies the
initial conditions to the old_solution
1513 *
initial_conditions,
1515 *
solution = old_solution;
1526 * Sets up the quadrature formula and
FEValues object
1529 *
const QGauss<dim> quadrature_formula(degree + 2);
1535 *
const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
1542 * The vector which stores the global indices that each local
index connects to
1545 *
std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
1549 * Extracts the finite elements associated to u and v
1557 * Loops over the cells to create the system
matrix. We
do this only once because the timestep is
constant
1560 *
for(
const auto &cell : dof_handler.active_cell_iterators()){
1564 *
fe_values.reinit(cell);
1566 *
cell->get_dof_indices(local_dof_indices);
1568 *
for(
const unsigned int q_index : fe_values.quadrature_point_indices()){
1570 *
for(
const unsigned int i : fe_values.dof_indices()){
1573 * These are the ith finite elements associated to u and v
1576 *
const double phi_i_u = fe_values[u].value(i, q_index);
1578 *
const double phi_i_v = fe_values[v].value(i, q_index);
1581 *
for(
const unsigned int j : fe_values.dof_indices())
1585 * These are the jth finite elements associated to u and v
1588 *
const double phi_j_u = fe_values[u].
value(j, q_index);
1590 *
const double phi_j_v = fe_values[v].value(j, q_index);
1595 * This formula comes from expanding the PDE system
1598 *
cell_matrix(i, j) += (phi_i_u*phi_j_u - time_step*r*phi_i_u*phi_j_u
1599 *
+ time_step*phi_i_u*phi_j_v - time_step*grad_phi_i_u*grad_phi_j_v
1600 *
+ phi_i_v*phi_j_u - grad_phi_i_v*grad_phi_j_u
1601 *
- phi_i_v*phi_j_v)*fe_values.JxW(q_index);
1608 * Loops over the dof indices to fill the entries of the system_matrix with the local
data
1611 *
for(
unsigned int i : fe_values.dof_indices()){
1612 *
for(
unsigned int j : fe_values.dof_indices()){
1613 *
system_matrix.add(local_dof_indices[i],
1614 *
local_dof_indices[j],
1622 * Loops over time, incrementing by timestep, to create the RHS, solve the linear system, then output the result
1625 *
while (time <= end_time)
1629 * Increments time and timestep_number
1632 *
time += time_step;
1633 *
++timestep_number;
1637 * Outputs to console the number of iterations and current time. Currently outputs once every
"second"
1640 *
if(timestep_number%timestep_denominator == 0){
1641 *
std::cout <<
"Time step " << timestep_number <<
" at t=" << time
1647 * Resets the system_rhs vector. THIS IS VERY IMPORTANT TO ENSURE THE SYSTEM IS SOLVED CORRECTLY AT EACH TIMESTEP
1654 * Loops over cells, then quadrature points, then dof indices to construct the RHS
1657 *
for(
const auto &cell : dof_handler.active_cell_iterators()){
1660 * Resets the cell_rhs. THIS IS ALSO VERY IMPORTANT TO ENSURE THE SYSTEM IS SOLVED CORRECTLY
1667 * Resets the
FEValues object to only the current cell
1670 *
fe_values.
reinit(cell);
1672 *
cell->get_dof_indices(local_dof_indices);
1676 * Loop over the quadrature points
1679 *
for(
const unsigned int q_index : fe_values.quadrature_point_indices()){
1682 * Stores the
value of the previous solution at the quadrature
point
1689 * Loops over the dof indices to get the
value of Un1
1692 *
for(
const unsigned int i : fe_values.dof_indices()){
1693 *
Un1 += old_solution(local_dof_indices[i])*fe_values[u].value(i, q_index);
1698 * Loops over the dof indices,
using Un1 to construct the RHS
for the current timestep.
1699 * Un1 is used to account
for the nonlinear terms in the SH equation
1702 *
for(
const unsigned int i : fe_values.dof_indices()){
1703 *
cell_rhs(i) += (Un1 + time_step*g1*
std::pow(Un1, 2) - time_step*k*
std::pow(Un1, 3))
1704 *
*fe_values[u].
value(i, q_index)*fe_values.JxW(q_index);
1710 * Loops over the dof indices to store the local
data in the global RHS vector
1713 *
for(
unsigned int i : fe_values.dof_indices()){
1714 *
system_rhs(local_dof_indices[i]) += cell_rhs(i);
1721 * This is where Dirichlet conditions are applied, or Neumann conditions
if the code is commented out
1740 *
solve_time_step();
1744 * Outputs the solution at regular intervals, currently once every
"second" The SH equation evolves slowly in time, so
this saves disk space
1747 *
if(timestep_number%timestep_denominator == 0){
1751 *
old_solution = solution;
1755 *
template <
int dim,
int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1756 *
void SHEquation<dim, spacedim, MESH, ICTYPE>::make_cylinder()
1768 * Extracts the boundary mesh with ID 0, which happens to be the tube part of the
cylinder
1775 * The manifold information is lost upon boundary extraction. This sets the mesh boundary type to be a
cylinder again
1779 *
triangulation.set_all_manifold_ids(0);
1780 *
triangulation.set_manifold(0, boundary);
1782 *
triangulation.refine_global(refinement_number);
1785 *
template <
int dim,
int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1786 *
void SHEquation<dim, spacedim, MESH, ICTYPE>::make_sinusoid()
1790 * Same process as above
1799 *
triangulation.set_all_manifold_ids(0);
1800 *
triangulation.set_manifold(0, boundary);
1802 *
triangulation.refine_global(refinement_number);
1806 * We warp the mesh after refinement to avoid a jagged mesh. We can
't tell the code that the boundary
1807 * should be a perfect sine wave, so we only warp after the
1808 * mesh is fine enough to resolve this
1811 * GridTools::transform(transform_function<spacedim>, triangulation);
1814 * template <int dim, int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1815 * void SHEquation<dim, spacedim, MESH, ICTYPE>::make_sphere()
1817 * GridGenerator::hyper_sphere(triangulation, Point<3>(0, 0, 0), 18.41988074);
1818 * triangulation.refine_global(refinement_number);
1821 * template <int dim, int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1822 * void SHEquation<dim, spacedim, MESH, ICTYPE>::make_torus()
1824 * GridGenerator::torus(triangulation, 9., 4.);
1825 * triangulation.refine_global(refinement_number);
1827 * template <int dim, int spacedim, MeshType MESH, InitialConditionType ICTYPE>
1828 * void SHEquation<dim, spacedim, MESH, ICTYPE>::make_hypercube()
1830 * GridGenerator::hyper_cube(triangulation, -18.84955592, 18.84955592);
1831 * triangulation.refine_global(refinement_number);
1833 * } // namespace SwiftHohenbergSolver
1839 * using namespace SwiftHohenbergSolver;
1843 * An array of mesh types. We iterate over this to allow for longer runs without having to stop the code
1846 * MeshType mesh_types[5] = {HYPERCUBE, CYLINDER, SPHERE, TORUS, SINUSOID};
1849 * An array of initial condition types. We iterate this as well, for the same reason
1852 * InitialConditionType ic_types[3] = {HOTSPOT, PSUEDORANDOM, RANDOM};
1856 * Controls how long the code runs
1859 * const double end_time = 100.;
1863 * The number of times we refine the hypercube mesh
1866 * const unsigned int ref_num = 6;
1870 * The timestep will be 1/timestep_denominator
1873 * const unsigned int timestep_denominator = 25;
1877 * Loops over mesh types, then initial condition types, then loops over values of g_1
1880 * for(const auto MESH : mesh_types){
1881 * for(const auto ICTYPE: ic_types){
1882 * for(int i = 0; i < 8; ++i){
1885 * The value of g_1 passed to the solver object
1888 * const double g_constant = 0.2*i;
1892 * Used to distinguish the start of each run
1895 * std::cout<< std::endl << std::endl;
1900 * Switch statement that determines what template parameters are used by the solver object.
1901 * Template parameters must be known at compile time, so we cannot
1902 * pass this as a variable unfortunately. In each case, we create a filename string
1903 * (named appropriately for the particular case), output to the console what
1904 * we are running, create the solver object, and call run(). Note that for the cylinder, sphere,
1905 * and sinusoid we decrease the refinement number by 1. This keeps
1906 * the number of dofs used in these cases comparable to the number of dofs on the 2D hypercube
1907 * (otherwise the number of dofs is much larger). For the torus, we
1908 * decrease the refinement number by 2.
1917 * std::string filename = "HYPERCUBE-HOTSPOT-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
1918 * std::cout << "Running: " << filename << std::endl << std::endl;
1920 * SHEquation<2, 2, HYPERCUBE, HOTSPOT> heat_equation_solver(1, timestep_denominator,
1921 * ref_num, 0.3, g_constant,
1922 * filename, end_time);
1923 * heat_equation_solver.run();
1927 * case PSUEDORANDOM:
1929 * std::string filename = "HYPERCUBE-PSUEDORANDOM-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
1930 * std::cout << "Running: " << filename << std::endl << std::endl;
1932 * SHEquation<2, 2, HYPERCUBE, PSUEDORANDOM> heat_equation_solver(1, timestep_denominator,
1933 * ref_num, 0.3, g_constant,
1934 * filename, end_time);
1935 * heat_equation_solver.run();
1941 * std::string filename = "HYPERCUBE-RANDOM-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
1942 * std::cout << "Running: " << filename << std::endl << std::endl;
1944 * SHEquation<2, 2, HYPERCUBE, RANDOM> heat_equation_solver(1, timestep_denominator,
1945 * ref_num, 0.3, g_constant,
1946 * filename, end_time);
1947 * heat_equation_solver.run();
1956 * std::string filename = "CYLINDER-HOTSPOT-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
1957 * std::cout << "Running: " << filename << std::endl << std::endl;
1959 * SHEquation<2, 3, CYLINDER, HOTSPOT> heat_equation_solver(1, timestep_denominator,
1960 * ref_num-1, 0.3, g_constant,
1961 * filename, end_time);
1962 * heat_equation_solver.run();
1966 * case PSUEDORANDOM:
1968 * std::string filename = "CYLINDER-PSUEDORANDOM-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
1969 * std::cout << "Running: " << filename << std::endl << std::endl;
1971 * SHEquation<2, 3, CYLINDER, PSUEDORANDOM> heat_equation_solver(1, timestep_denominator,
1972 * ref_num-1, 0.3, g_constant,
1973 * filename, end_time);
1974 * heat_equation_solver.run();
1980 * std::string filename = "CYLINDER-RANDOM-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
1981 * std::cout << "Running: " << filename << std::endl << std::endl;
1983 * SHEquation<2, 3, CYLINDER, RANDOM> heat_equation_solver(1, timestep_denominator,
1984 * ref_num-1, 0.3, g_constant,
1985 * filename, end_time);
1986 * heat_equation_solver.run();
1995 * std::string filename = "SPHERE-HOTSPOT-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
1996 * std::cout << "Running: " << filename << std::endl << std::endl;
1998 * SHEquation<2, 3, SPHERE, HOTSPOT> heat_equation_solver(1, timestep_denominator,
1999 * ref_num-1, 0.3, g_constant,
2000 * filename, end_time);
2001 * heat_equation_solver.run();
2005 * case PSUEDORANDOM:
2007 * std::string filename = "SPHERE-PSUEDORANDOM-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
2008 * std::cout << "Running: " << filename << std::endl << std::endl;
2010 * SHEquation<2, 3, SPHERE, PSUEDORANDOM> heat_equation_solver(1, timestep_denominator,
2011 * ref_num-1, 0.3, g_constant,
2012 * filename, end_time);
2013 * heat_equation_solver.run();
2019 * std::string filename = "SPHERE-RANDOM-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
2020 * std::cout << "Running: " << filename << std::endl << std::endl;
2022 * SHEquation<2, 3, SPHERE, RANDOM> heat_equation_solver(1, timestep_denominator,
2023 * ref_num-1, 0.3, g_constant,
2024 * filename, end_time);
2025 * heat_equation_solver.run();
2034 * std::string filename = "TORUS-HOTSPOT-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
2035 * std::cout << "Running: " << filename << std::endl << std::endl;
2037 * SHEquation<2, 3, TORUS, HOTSPOT> heat_equation_solver(1, timestep_denominator,
2038 * ref_num-2, 0.3, g_constant,
2039 * filename, end_time);
2040 * heat_equation_solver.run();
2044 * case PSUEDORANDOM:
2046 * std::string filename = "TORUS-PSUEDORANDOM-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
2047 * std::cout << "Running: " << filename << std::endl << std::endl;
2049 * SHEquation<2, 3, TORUS, PSUEDORANDOM> heat_equation_solver(1, timestep_denominator,
2050 * ref_num-2, 0.3, g_constant,
2051 * filename, end_time);
2052 * heat_equation_solver.run();
2058 * std::string filename = "TORUS-RANDOM-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
2059 * std::cout << "Running: " << filename << std::endl << std::endl;
2061 * SHEquation<2, 3, TORUS, RANDOM> heat_equation_solver(1, timestep_denominator,
2062 * ref_num-2, 0.3, g_constant,
2063 * filename, end_time);
2064 * heat_equation_solver.run();
2073 * std::string filename = "SINUSOID-HOTSPOT-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
2074 * std::cout << "Running: " << filename << std::endl << std::endl;
2076 * SHEquation<2, 3, SINUSOID, HOTSPOT> heat_equation_solver(1, timestep_denominator,
2077 * ref_num-1, 0.3, g_constant,
2078 * filename, end_time);
2079 * heat_equation_solver.run();
2083 * case PSUEDORANDOM:
2085 * std::string filename = "SINUSOID-PSUEDORANDOM-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
2086 * std::cout << "Running: " << filename << std::endl << std::endl;
2088 * SHEquation<2, 3, SINUSOID, PSUEDORANDOM> heat_equation_solver(1, timestep_denominator,
2089 * ref_num-1, 0.3, g_constant,
2090 * filename, end_time);
2091 * heat_equation_solver.run();
2097 * std::string filename = "SINUSOID-RANDOM-G1-0.2x" + Utilities::int_to_string(i, 1) + "-";
2098 * std::cout << "Running: " << filename << std::endl << std::endl;
2100 * SHEquation<2, 3, SINUSOID, RANDOM> heat_equation_solver(1, timestep_denominator,
2101 * ref_num-1, 0.3, g_constant,
2102 * filename, end_time);
2103 * heat_equation_solver.run();
2112 * catch (std::exception &exc)
2114 * std::cout << "An error occurred" << std::endl;
2115 * std::cerr << std::endl
2117 * << "----------------------------------------------------"
2119 * std::cerr << "Exception on processing: " << std::endl
2120 * << exc.what() << std::endl
2121 * << "Aborting!" << std::endl
2122 * << "----------------------------------------------------"
2129 * std::cout << "Error occurred, made it past first catch" << std::endl;
2130 * std::cerr << std::endl
2132 * << "----------------------------------------------------"
2134 * std::cerr << "Unknown exception!" << std::endl
2135 * << "Aborting!" << std::endl
2136 * << "----------------------------------------------------"
* x_component_mask set(0, true)
* * * struct InterferenceTaperTransform *
void add_data_vector(const VectorType &data, const std::vector< std::string > &names, const DataVectorType type=type_automatic, const std::vector< DataComponentInterpretation::DataComponentInterpretation > &data_component_interpretation={})
void reinit(const TriaIterator< DoFCellAccessor< dim, spacedim, level_dof_access > > &cell)
void initialize(const SparsityPattern &sparsity_pattern)
#define Assert(cond, exc)
void make_sparsity_pattern(const DoFHandler< dim, spacedim > &dof_handler, SparsityPatternBase &sparsity_pattern, const AffineConstraints< number > &constraints={}, 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.
std::vector< index_type > data
void random(DoFHandler< dim, spacedim > &dof_handler)
void cylinder(Triangulation< dim > &tria, const double radius=1., const double half_length=1.)
return_type extract_boundary_mesh(const MeshType< dim, spacedim > &volume_mesh, MeshType< dim - 1, spacedim > &surface_mesh, const std::set< types::boundary_id > &boundary_ids=std::set< types::boundary_id >())
void torus(Triangulation< dim, spacedim > &tria, const double centerline_radius, const double inner_radius, const unsigned int n_cells_toroidal=6, const double phi=2.0 *numbers::PI)
@ matrix
Contents is actually a matrix.
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.)
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
* * * ScaleZFunction< dim, Number, components >::ScaleZFunction * component(component)
* * * * std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters const
T sum(const T &t, const MPI_Comm mpi_communicator)
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 > cos(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
constexpr SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &)