Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

A Robin boundary condition for structure module #555

Open
wants to merge 19 commits into
base: master
Choose a base branch
from

Conversation

richardschu
Copy link
Contributor

This PR introduces a general BC of the form
traction = P*N = - k * displacement - c * velocity - exterior_pressure.
For this we need several adaptations:

  1. boundary_mass_operator to shift the discretized remainder of the velocity term to the rhs (similar to the weak damping or general time integration terms, but now with a mass term on the boundary only). This will also come in handy, if we want to project values on the boundary.
  2. changes in operator_base such that CG discretizations involve some boundary integrals if evaluate_boundary_integrals() == true in the fundamental apply, apply_add, evaluate, evaluate_add ... operations and the remaining MF loops used in MG setup, preconditioner update etc. (building the system matrix or its inverse diagonal)
  3. consider the new boundary integral in the linear and nonlinear operators in the structure module
  4. scaling factors and updates of the new BoundaryMassOperator in structure/operator.cpp and the according interface.
  5. tests for the new functionality

I have this now in a single PR to thoroughly check if the behavior is as desired (see the tests) and the changes in OperatorBase actually work as expected to get good Multigrid convergence. Also, with this you can see the intended use (and extensibility towards Robin coupling conditions in FSI). If need be, I can split into several parts:

  1. changes in OperatorBase
  2. add exterior pressure (Robin condition part 1)
  3. add spring (Robin condition part 1)
  4. add BoundaryMassOperator
  5. add dashpot (use BoundaryMassOperator and temporal scaling)

Whoever is reviewing this (probably @nfehn 馃), let's have an in-person discussion first!

Comment on lines +81 to +82
std::map<dealii::types::boundary_id, std::pair<std::array<bool, 2>, std::array<double, 3>>>
robin_k_c_p_param;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This design makes the code very hard to read in my opinion, because in the implementation (SpatialOperator classes), one only sees .first, .second, etc., without concrete names.

Here, the data structure should look like

std::map<dealii::types::boundary_id, ExaDG::RobinParameters> robin_bc;

and inside RobinParameters variables like .stiffness, etc. with readable names. Instead of robin_k_c_p_param (where it is hard to guess the meaning), we should follow the naming robin_bc for consistency with other boundary types.

Comment on lines +294 to +299
// integrate standard (stored) traction or exterior pressure on Robin boundaries
if(boundary_type == BoundaryType::Neumann or boundary_type == BoundaryType::NeumannCached or
boundary_type == BoundaryType::RobinSpringDashpotPressure)
{
tensor F = get_F<dim, Number>(integrator.get_gradient(q));
vector N = integrator.get_normal_vector(q);
// da/dA * n = det F F^{-T} * N := n_star
// -> da/dA = n_star.norm()
vector n_star = determinant(F) * transpose(invert(F)) * N;
// t_0 = da/dA * t
traction *= n_star.norm();
if(operator_type == OperatorType::inhomogeneous or operator_type == OperatorType::full)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the many if-statements introduced in this function, I am not sure whether we have a good design right now for the new Robin-functionality in the present PR. Note that the free function calculate_neumann_value() already contains if-else-statements. I am also not convinced right now that the way OperatorBase is extended is a good design.

traction *= n_star.norm();
if(operator_type == OperatorType::inhomogeneous or operator_type == OperatorType::full)
{
traction -= calculate_neumann_value<dim, Number>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like that a minus sign is introduced here, and that the sign is also changed below in function submit_value().

(As a sidenote, the present change turns an assignment into an addition/subtraction, so why didn't you actually write traction = - xyz; to introduce a minus sign compared to the previous version? However, we should keep the previous version in my opinion. Robin is a new boundary type, that is optional.)


if(this->operator_data.pull_back_traction)
// integrate standard (stored) traction or exterior pressure on Robin boundaries
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment reads like Robin is "the only" boundary type. I think it holds for the present PR as a whole: We should aim at a modular code where Robin is one specific feature and where it would be very easy in the future to remove this feature again (with "easy" I mean minimal code changes). Even if we will never remove it again, this way of thinking might help for the design.

Comment on lines +144 to +150
if(param.problem_type == ProblemType::Unsteady)
{
// The boundary mass operator is only required, if we have damping terms from the Robin
// condition, i.e., in the unsteady case.
matrix_free_data.append_mapping_flags(
BoundaryMassOperator<dim, Number, dim /* n_components */>::get_mapping_flags());
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to this comment https://github.com/exadg/exadg/pull/555/files#r1457203165: I think we do not always have Robin type BCs, so I would expect an if-statement here like if(robin){...}?

Comment on lines +316 to +358
// check boundary ID in robin_k_c_p_param to add boundary mass integrals from Robin boundaries
// on BoundaryType::NeumannCached or BoundaryType::RobinSpringDashpotPressure
if(boundary_type == BoundaryType::NeumannCached or
boundary_type == BoundaryType::RobinSpringDashpotPressure)
{
if(operator_type == OperatorType::homogeneous or operator_type == OperatorType::full)
{
auto const it = this->operator_data.bc->robin_k_c_p_param.find(boundary_id);

if(it != this->operator_data.bc->robin_k_c_p_param.end())
{
bool const normal_projection_displacement = it->second.first[0];
double const coefficient_displacement = it->second.second[0];

if(normal_projection_displacement)
{
vector const N = integrator.get_normal_vector(q);
traction += N * (coefficient_displacement * (N * integrator.get_value(q)));
}
else
{
traction += coefficient_displacement * integrator.get_value(q);
}

if(this->operator_data.unsteady)
{
bool const normal_projection_velocity = it->second.first[1];
double const coefficient_velocity = it->second.second[1];

if(normal_projection_velocity)
{
vector const N = integrator.get_normal_vector(q);
traction += N * (coefficient_velocity * this->scaling_factor_mass_boundary *
(N * integrator.get_value(q)));
}
else
{
traction +=
coefficient_velocity * this->scaling_factor_mass_boundary * integrator.get_value(q);
}
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code looks like a duplication of the one above in linear_operator.cpp. Is there any difference between these two version?

In the previous code version, one could say we had a function call calculate_neumann_value() in both cases (linear operator and nonlinear operator), but here we have a sequence of instructions and calculations that are duplicated, which should definitely be avoided.

dealii::AlignedVector<dealii::VectorizedArray<Number>> local_diag(dofs_per_cell);

for(auto face = range.first; face < range.second; ++face)
if(is_dg)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a mixture of different styles. In other places, we use functions face_loop_empty() so that we see on the top level (matrix_free->loop()/cell_loop()) which type of loops we have. I think we need a consistent way, otherwise we can here and there introduce if-statements "for safety". So I think we should not write if(is_dg) here.

@@ -823,10 +861,10 @@ OperatorBase<dim, Number, n_components>::internal_calculate_system_matrix(
SparseMatrix & system_matrix) const
{
// assemble matrix locally on each process
if(evaluate_face_integrals() and is_dg)
if(evaluate_face_integrals())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this logic irritating or not consistent with other parts of OperatorBase where we pass empty functions to matrix_free->loop(). I think we should distinguish between dg and cg on the top level, and inside each variant introduce another if depending on whether we have face loops or not. If we enter a face_loop, we will then also do the face integrals. See also comment https://github.com/exadg/exadg/pull/555/files#r1457261288

@nfehn
Copy link
Member

nfehn commented Jan 18, 2024

I think it makes sense to split this PR into parts as you suggest above.

The starting point are probably changes made to OperatorBase. If I understand the present PR correctly, the main change is essentially about allowing - for continuous Galerkin discretizations - homogeneous boundary face integrals (while so far only boundary face integrals of inhomogeneous type are possible for continuous Galerkin, which stem from usual Neumann BCs)?

From the code review I did so far, I see mainly three PRs (1. OperatorBase, 2. introduction of BoundaryMassOperator, 3. all the rest; but we should do them one after the other and then the picture might also become clearer).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants