I have solve a thermoelastic problem using block matrices and block vectors and a same dofhandler for the displacement and temperature fields.
Fully coupled thermal-stress analysis is needed when the stress analysis is dependent on the temperature distribution and the temperature distribution depends on the stress solution. For example, metalworking problems may include significant heating due to inelastic deformation of the material which, in turn, changes the material properties. Some problems require a fully coupled analysis in the sense that the mechanical and thermal solutions evolve simultaneously, but with a weak coupling between the two solutions. In other words, the components in the off-diagonal submatrices are small compared to the components in the diagonal submatrices. For the thermoelastic problem the off-diagonal terms are zero and solution is straight and stable. You can solve the block systems simultaneously or using staggered solution technique.
Setup system as follow:
dof_handler.distribute_dofs (fe);
DoFRenumbering::Cuthill_McKee(dof_handler);
BlockDynamicSparsityPattern dsp(2, 2);
std::vector<unsigned int> block_component(3, 0);
block_component[dim] = 1; // temperature
DoFRenumbering::component_wise(dof_handler, block_component);
DoFTools::count_dofs_per_block(dof_handler, dofs_per_block,block_component);
const types::global_dof_index n_dofs_u = dofs_per_block[0];
// determine number of temperature DOFs if requested.
types::global_dof_index n_dofs_temp;
n_dofs_temp = dofs_per_block[1];
dsp.block(0, 0).reinit(n_dofs_u, n_dofs_u);
dsp.block(0, 1).reinit(n_dofs_u, n_dofs_temp);
dsp.block(1, 0).reinit(n_dofs_temp, n_dofs_u);
dsp.block(1, 1).reinit(n_dofs_temp, n_dofs_temp);
dsp.collect_sizes();
...
use the following approach for system matrix and RHS:
for (unsigned int q=0; q<n_q_points; ++q)
{
// extract temperature gradient and value
for (unsigned int k = 0; k < dofs_per_cell; ++k){
temp_grads_N[k] = fe_values[temp_fe].gradient (k, q);
temp_N[k] = fe_values[temp_fe].value (k, q);
}
for (unsigned int i=0; i<dofs_per_cell; ++i)
for (unsigned int j=0; j<dofs_per_cell; ++j)
{
const unsigned int comp_j = fe.system_to_component_index(j).first;
if (comp_j < dim){
cell_matrix(i,j) += determine block(0,0)
}else if (comp_j == dim){
// temperature
cell_matrix(i,j) += determine block(1,1)
}
}
}
cell->get_dof_indices (local_dof_indices);
for (unsigned int i=0; i<dofs_per_cell; ++i)
{
for (unsigned int j=0; j<dofs_per_cell; ++j)
system_matrix.add (local_dof_indices[i],local_dof_indices[j],
cell_matrix(i,j));
}
for rhs:
loop over cells:
PointHistory<dim> *local_quadrature_points_data
= reinterpret_cast<PointHistory<dim>*>(cell->user_pointer());
// extract nodal total and incremental displacement
// will be used for strain and delta_strain calculation
fe_values[u_fe].get_function_gradients (solution_delta, displacement_increment_grads);
fe_values[u_fe].get_function_gradients (solution, displacement_grads);
// extract temperature values and gradients
if(parameters.is_temperature){
fe_values[temp_fe].get_function_values (solution, temp_value);
fe_values[temp_fe].get_function_values (solution_n, temp_n_value);
fe_values[temp_fe].get_function_gradients (solution, temp_grads);
}
// Then we can loop over all degrees of freedom on this cell and
// compute local contributions to the right hand side:
for (unsigned int q=0; q<n_q_points; ++q)
{
for (unsigned int i=0; i<dofs_per_cell; ++i){
const unsigned int comp_i = fe.system_to_component_index(i).first;
if ((comp_i < dim) ){
cell_rhs(i) +=
}else if (comp_i == dim)
{
// temperature
cell_rhs(i) +=
}
}
}
cell->get_dof_indices (local_dof_indices);
for (unsigned int i=0; i<dofs_per_cell; ++i)
system_rhs(local_dof_indices[i]) += cell_rhs(i);