This example solves an axial bar extension problem.
Initialization
Initialize libMesh library.
libMesh::LibMeshInit init(argc, argv);
MAST::Examples::GetPotWrapper
input(argc, argv, "input");
Initialize Mesh and System
Create Mesh object on default MPI communicator and generate a line mesh (5 elements, 10 units long).
Note that in libMesh, all meshes are parallel by default in the sense that the equations on the mesh are solved in parallel by PETSc.
A "ReplicatedMesh" is one where all MPI processes have the full mesh in memory, as compared to a "DistributedMesh" where the mesh is
"chunked" up and distributed across processes, each having their own piece.
libMesh::ReplicatedMesh mesh(init.comm());
libMesh::MeshTools::Generation::build_line(mesh, 20, 0.0, 10.0);
mesh.print_info();
mesh.boundary_info->print_info();
Create EquationSystems object, which is a container for multiple systems of equations that are defined on a given mesh.
libMesh::EquationSystems equation_systems(mesh);
Add system of type MAST::NonlinearSystem (which wraps libMesh::NonlinearImplicitSystem) to the EquationSystems container.
Create a finite element type for the system. Here we use first order Lagrangian-type finite elements.
libMesh::FEType fetype(libMesh::FIRST, libMesh::LAGRANGE);
Initialize the system to the correct set of variables for a conduction analysis. In libMesh this is analogous to adding variables (each with specific finite element type/order to the system for a particular system of equations.
Initialize a new conduction discipline using equation_systems.
Create and add boundary conditions to the conduction system. This Dirichlet BC fixes the temperature of left end of the bar to a value of 10K. This requires creation of a FieldFunction that provides the value of the constrained variable at given spatial location and time.
public:
virtual ~TempValue() {}
v.setZero(1);
v(0) = 10;
}
};
TempValue tval;
Conduction system has one variable, Temperature, which is constrained on the left boundary.
dirichlet_bc.
init(0, conduction_system.vars(),
The object takes a pointer to this function to set the value of temperatue on the left boundary
tell the object that a system variable order is to be used and that the sytsem has 1 variable in it.
libMesh::SYSTEM_VARIABLE_ORDER, 1);
Tell the discipline that the left boundary is constrained by this object.
discipline.add_dirichlet_bc(0, dirichlet_bc);
discipline.init_system_dirichlet_bc(system);
Initialize the equation system since we now know the size of our system matrices (based on mesh, element type, variables in the conduction_system) as well as the setup of dirichlet boundary conditions. This initialization process is basically a pre-processing step to preallocate storage and spread it across processors.
equation_systems.init();
equation_systems.print_info();
Initialize Parameters and Property Cards
Create parameters.
MAST::Parameter thickness_y(
"thy", input(
"thy",
"section y-thickness", 0.06));
MAST::Parameter thickness_z(
"thz", input(
"thz",
"section z-thickness", 0.02));
Create ConstantFieldFunctions used to spread parameters throughout the model.
Initialize load.
right_end_flux.add(q_f);
discipline.add_side_load(1, right_end_flux);
Create the material property card ("card" is NASTRAN lingo) and the relevant parameters to it. An isotropic material needs thermal conductivity (k), density (rho) and thermal capacitance (cp) to describe its behavior.
Create the section property card. Attach all property values.
Specify a section orientation point and add it to the section.
Attach material to the card.
Initialize the section and specify the subdomain in the mesh that it applies to.
discipline.set_property_for_subdomain(0, section);
Computation
transient solution
compute_transient_solution(discipline, conduction_system, input);
transient solution sensitivity with respect to section y-thickness
compute_transient_sensitivity(discipline, conduction_system, input, thickness_y);
return 0;
}
Transient analysis
MAST::Examples::GetPotWrapper& input) {
std::string
output_name = input("output_file_root", "prefix of output file names", "output"),
transient_output_name = output_name + "_transient.exo";
libMesh::EquationSystems& eq_sys = sys.get_equation_systems();
create the nonlinear assembly object
MAST::FirstOrderNewmarkTransientSolver solver;
solver.set_discipline_and_system(discipline, sys_init);
solver.set_elem_operation_object(elem_ops);
initialize the solution to zero, or to something that the user may have provided
sys.get_dof_map().enforce_constraints_exactly(sys, sys.solution.get());
sys.update();
file to write the solution for visualization
libMesh::ExodusII_IO transient_output(sys.get_mesh());
unsigned int
t_step = 0,
n_steps = input("n_transient_steps", "number of transient time-steps", 100);
solver.dt = input("dt", "time-step size", 1.e+3);
solver.beta = input("beta", "Newmark solver beta parameter ", 0.5);
ask the solver to update the initial condition for d2(X)/dt2 This is recommended only for the initial time step, since the time integration scheme updates the velocity and acceleration at each subsequent iterate
solver.solve_highest_derivative_and_advance_time_step(assembly);
loop over time steps
while (t_step < n_steps) {
libMesh::out
<< "Time step: " << t_step
<< " : t = " << sys.time
<< " : dt = " << solver.dt
<< " : xdot-L2 = " << solver.velocity().l2_norm()
<< std::endl;
write the time-step
transient_output.write_timestep(transient_output_name,
eq_sys,
t_step+1,
sys.time);
std::ostringstream oss_sol;
oss_sol << output_name << "_sol_t_" << t_step;
solve for the time-step
solver.solve(assembly);
solver.advance_time_step();
update time step
Transient sensitivity analysis
MAST::Examples::GetPotWrapper& input,
std::string
output_name = input("output_file_root", "prefix of output file names", "output"),
transient_output_name = output_name +
"_transient_sensitivity_" + p.
name() +
".exo",
nonlinear_sol_dir = input("nonlinear_sol_dir", "directory containing the location of nonlinear solutions", "data");
libMesh::EquationSystems& eq_sys = sys.get_equation_systems();
create the nonlinear assembly object
MAST::FirstOrderNewmarkTransientSolver solver;
solver.set_discipline_and_system(discipline, sys_init);
solver.set_elem_operation_object(elem_ops);
initial condition for solution is read from disk and initial condition for sensitivity of temperature is also assumed to be zero.
std::ostringstream oss;
oss << output_name << "_sol_t_" << 0;
sys.
read_in_vector(*sys.solution, nonlinear_sol_dir, oss.str(),
true);
sys.update();
file to write the solution for visualization
libMesh::ExodusII_IO transient_output(sys.get_mesh());
unsigned int
t_step = 0,
n_steps = input("n_transient_steps", "number of transient time-steps", 100);
solver.dt = input("dt", "time-step size", 1.e+3);
sys.time = 0.;
solver.beta = input("beta", "Newmark solver beta parameter ", 0.5);
ask the solver to update the initial condition for d2(X)/dt2 This is recommended only for the initial time step, since the time integration scheme updates the velocity and acceleration at each subsequent iterate
solver.solve_highest_derivative_and_advance_time_step(assembly, false);
solver.solve_highest_derivative_and_advance_time_step_with_sensitivity(assembly, p);
loop over time steps
while (t_step < n_steps-1) {
libMesh::out
<< "Time step: " << t_step
<< " : t = " << sys.time
<< " : xdot-L2 = " << solver.velocity_sensitivity().l2_norm()
<< std::endl;
write the time-step
sys.solution->swap(solver.solution_sensitivity());
transient_output.write_timestep(transient_output_name,
eq_sys,
t_step+1,
sys.time);
sys.solution->swap(solver.solution_sensitivity());
std::ostringstream oss_sol;
oss_sol << output_name << "_sol_t_" << t_step+1;
sys.
read_in_vector(*sys.solution, nonlinear_sol_dir, oss_sol.str(),
true);
solver.update_velocity(solver.velocity(), *sys.solution);
sys.update();
solve for the sensitivity time-step
solver.sensitivity_solve(assembly, p);
solver.advance_time_step(false);
solver.advance_time_step_with_sensitivity();
update time step counter