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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GeoMechanicsApplication] Add a minimal 3+3 line interface element #12674

Merged
merged 70 commits into from
Sep 19, 2024

Conversation

rfaasse
Copy link
Contributor

@rfaasse rfaasse commented Sep 16, 2024

📝 Description
This PR adds a minimal implementation for a line interface element (which could be 2+2 or 3+3 depending on the geometry passed on during construction). The functionality (e.g. for the LHS/RHS calculations and the other functions) is unit tested.

rfaasse and others added 30 commits August 27, 2024 14:38
…geometry is the same as the underlying mid geometry and changed the tests accordingly
Moved the application-wide tolerance values for testing to a dedicated namespace.
Don't increment a local variable at function call time. Increment first, then pass the updated value to the function.
To better reflect what the test case actually covers.
So far we expect a zero vector (this will soon change).

Also renamed a local variable.
So far, the function under test doesn't calculate anything. It simply returns a default-constructed vector.
The calculation follows the formulation found in any textbook about the finite element method
rfaasse and others added 5 commits September 16, 2024 10:54
…AIN` and `CAUCHY_STRESS_VECTOR` might be overwritten by the base class
…-interface-element' into geo/12649-add-a-minimal-3_3-line-interface-element
…-interface-element' into geo/12649-add-a-minimal-3_3-line-interface-element
Comment on lines 210 to 214
KRATOS_EXPECT_EQ(degrees_of_freedom.size(), 8);
const std::vector<double> expected_dof_values = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0};
for (std::size_t i = 0; i < degrees_of_freedom.size(); i++) {
KRATOS_EXPECT_DOUBLE_EQ(degrees_of_freedom[i]->GetSolutionStepValue(), expected_dof_values[i]);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

As discussed before, perhaps we'd better check the degrees of freedom themselves, rather than their associated values (since that should be covered by the unit tests for the degrees of freedom class itself).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


// Assert
Vector expected_cauchy_stress{2};
expected_cauchy_stress <<= 10.0, 2.0;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the traction vector using the local axis system. Is that we intend?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is intended, but let's discuss with @WPK4FEM before merging

Copy link
Contributor

Choose a reason for hiding this comment

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

For me the tractions ( I also think its clearer to call the thing traction in the test, even if we are forced to use functions for strain and Cauchy stress ) are clearer in the local axes than in a global system.

For our continuum elements the local and the global axes system coincide, for interface elements that would be very uncomfortable. Please keep the interface element integration point results local.

@rfaasse rfaasse added the GeoMechanics Issues related to the GeoMechanicsApplication label Sep 16, 2024
@rfaasse rfaasse marked this pull request as ready for review September 16, 2024 15:20
Copy link
Contributor

@mnabideltares mnabideltares left a comment

Choose a reason for hiding this comment

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

@rfaasse and @avdg81
Thank you for adding interface element. Here some of my observations:


using Element::GeometryType;
using Element::PropertiesType;
using BaseType = Element;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why to not remove "using BaseType = Element" and use "Element" everywhere in the function, to be more clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Initially we did it to be able to insert a class into the hierarchy more easily. In that case some of these Element:: should remain as they are, and some should be changed to a new base class. However, we had some discussion about it before already if we really need it and after your comment decided to indeed remove BaseType, good suggestion!

using Element::PropertiesType;
using BaseType = Element;

LineInterfaceElement() = default;
Copy link
Contributor

Choose a reason for hiding this comment

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

As this is a peurly header file, preferly "default" to be in the cpp, to be consistent with the other constructors. Also to be more clear in reading the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The argument here is that (since it's default only, this doesn't hold when there is a real implementation), in this way we don't have to go to the source file just to see it's a default constructor, but I think it's a matter of preference.

const Geometry<GeometricalObject::NodeType>::Pointer& rGeometry,
const Properties::Pointer& rProperties)
: Element(NewId, rGeometry, rProperties),
mIntegrationScheme(std::make_unique<LobattoIntegrationScheme>(GetGeometry().PointsNumber() / 2)),
Copy link
Contributor

Choose a reason for hiding this comment

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

Here, the integration scheme is defined based on the number of the points. For now, it is enough, but for further development, it is not flexible. In other elements, we define "GetIntegrationMethod" to be able define the scheme based on the desired order. For example, consider different order element. However, this further investigation/definition can be in a different PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem with GetIntegrationMethod is that the enum it returns doesn't have entries for Lobatto integration, which is why we cannot directly use it.

void LineInterfaceElement::CalculateLeftHandSide(MatrixType& rLeftHandSideMatrix, const ProcessInfo&)
{
// Currently, the left-hand side matrix only includes the stiffness matrix. In the future, it
// will also include water pressure contributions and coupling terms.
Copy link
Contributor

Choose a reason for hiding this comment

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

It is for my curiousity. Do you then set the pressure as zero? As far as I know, there is no solver for U available in Geo, and our solvers are Pw and U-Pw (and T)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We'll have to double check if this method works when looking at integration tests (first indication is it does, but we'll have to see if that holds), but we're just not giving Pw as degrees of freedom in these interfaces, meaning the interfaces shouldn't change water pressure at all. It basically works as a 'diff order' element, but the pressure geometry is set to empty.

{
// Currently, the right-hand side only includes the internal force vector. In the future, it
// will also include water pressure contributions and coupling terms.
const auto local_b_matrices = CalculateLocalBMatricesAtIntegrationPoints();
Copy link
Contributor

Choose a reason for hiding this comment

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

Calculation of B-Matrix is done twice, once for left hand, then for right hand, by means of "CalculateLocalBMatricesAtIntegrationPoints()". It is more efficient to calculate it once for the left hand side, then use it for the right hand side.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since we're using the CalculateRightHandSide function from the Element interface, we cannot change the signature of the function. I agree we do double work now, so we could look into caching certain lists if we see it's a performance bottleneck. For now, I think it would add complexity to the element that might have limited value until we solve more prominent performance problems.

const auto local_b_matrices = CalculateLocalBMatricesAtIntegrationPoints();
const auto relative_displacements = CalculateRelativeDisplacementsAtIntegrationPoints(local_b_matrices);
const auto tractions = CalculateTractionsAtIntegrationPoints(relative_displacements);
const auto integration_coefficients = CalculateIntegrationCoefficients();
Copy link
Contributor

Choose a reason for hiding this comment

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

"CalculateIntegrationCoefficients();" is done for left as well as, right hand side. Keep the values in an array reuse them

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Valid point, but as per my previous comment, I'd rather choose readability for now and optimize performance later.

Comment on lines 162 to 174
Element::Pointer LineInterfaceElement::Create(IndexType NewId,
const NodesArrayType& rNodes,
PropertiesType::Pointer pProperties) const
{
return Create(NewId, this->GetGeometry().Create(rNodes), pProperties);
}

Element::Pointer LineInterfaceElement::Create(IndexType NewId,
GeometryType::Pointer pGeometry,
PropertiesType::Pointer pProperties) const
{
return make_intrusive<LineInterfaceElement>(NewId, pGeometry, pProperties);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Preferly Creat to be after the constructors to be consistent with the other element classes design

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, moved

Copy link
Contributor

@WPK4FEM WPK4FEM left a comment

Choose a reason for hiding this comment

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

Dear Anne and Richard,
Thank you for the element setup. Not much to complain about from my side.
Wijtze Pieter

Comment on lines 212 to 213
// For interface elements, the shape function gradients are not defined, since these are
// non-continuum elements. Therefore, we pass an empty matrix.
Copy link
Contributor

Choose a reason for hiding this comment

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

Shape function gradients are always defined, they are not used for interface elements.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adjusted the comment, thanks for the clarification!

Copy link
Contributor Author

@rfaasse rfaasse left a comment

Choose a reason for hiding this comment

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

Thank you for the thorough reviews! I think I processed/answered to all of them, please let me know if you think any other changes are necessary

const Geometry<GeometricalObject::NodeType>::Pointer& rGeometry,
const Properties::Pointer& rProperties)
: Element(NewId, rGeometry, rProperties),
mIntegrationScheme(std::make_unique<LobattoIntegrationScheme>(GetGeometry().PointsNumber() / 2)),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The problem with GetIntegrationMethod is that the enum it returns doesn't have entries for Lobatto integration, which is why we cannot directly use it.

void LineInterfaceElement::CalculateLeftHandSide(MatrixType& rLeftHandSideMatrix, const ProcessInfo&)
{
// Currently, the left-hand side matrix only includes the stiffness matrix. In the future, it
// will also include water pressure contributions and coupling terms.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We'll have to double check if this method works when looking at integration tests (first indication is it does, but we'll have to see if that holds), but we're just not giving Pw as degrees of freedom in these interfaces, meaning the interfaces shouldn't change water pressure at all. It basically works as a 'diff order' element, but the pressure geometry is set to empty.

{
// Currently, the right-hand side only includes the internal force vector. In the future, it
// will also include water pressure contributions and coupling terms.
const auto local_b_matrices = CalculateLocalBMatricesAtIntegrationPoints();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since we're using the CalculateRightHandSide function from the Element interface, we cannot change the signature of the function. I agree we do double work now, so we could look into caching certain lists if we see it's a performance bottleneck. For now, I think it would add complexity to the element that might have limited value until we solve more prominent performance problems.


using Element::GeometryType;
using Element::PropertiesType;
using BaseType = Element;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Initially we did it to be able to insert a class into the hierarchy more easily. In that case some of these Element:: should remain as they are, and some should be changed to a new base class. However, we had some discussion about it before already if we really need it and after your comment decided to indeed remove BaseType, good suggestion!

using Element::PropertiesType;
using BaseType = Element;

LineInterfaceElement() = default;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The argument here is that (since it's default only, this doesn't hold when there is a real implementation), in this way we don't have to go to the source file just to see it's a default constructor, but I think it's a matter of preference.

void Initialize(const ProcessInfo& rCurrentProcessInfo) override;

Element::Pointer Create(IndexType NewId, const NodesArrayType& rNodes, PropertiesType::Pointer pProperties) const override;
Element::Pointer Create(IndexType NewId, GeometryType::Pointer pGeom, PropertiesType::Pointer pProperties) const override;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! Added a check that calls the base class check, verifies that the constitutive laws are there and calls check on them.

Copy link
Contributor

@WPK4FEM WPK4FEM left a comment

Choose a reason for hiding this comment

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

Dear Anne and Richard,
To me this looks quite good to go, I don't find things that are really worth putting substantial effort in first anymore.
Thank you, Wijtze Pieter

Comment on lines +535 to +536
expected_right_hand_side <<= 0.33333333, 1.66666667, 0.33333333, 1.66666667, 1.33333333,
6.66666667, -0.33333333, -1.66666667, -0.33333333, -1.66666667, -1.33333333, -6.66666667;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we use this or write 1/3, 5/3, 1/3, 5/3, 4/3, 19/3, -1/3 et.c

Similarly maybe at line 429, 463 and the similar things where square roots of 3 are involved

Guess this is a matter of preference.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For this set of 'n/3' I completely agree, if I remember correctly the 429 and 463 examples, the analytical solution was not a lot more clear than just the numbers generated by Annes python 'check' script.

I'll wait for the sonarcloud results here: if I need to make changes because of that, I'll change this line, otherwise I'd propose to merge this and avoid another pipeline wait.

Copy link
Contributor

@mnabideltares mnabideltares left a comment

Choose a reason for hiding this comment

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

It looks OK to me.

@rfaasse rfaasse merged commit 1677ea8 into master Sep 19, 2024
9 of 11 checks passed
@rfaasse rfaasse deleted the geo/12649-add-a-minimal-3_3-line-interface-element branch September 19, 2024 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
GeoMechanics Issues related to the GeoMechanicsApplication
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[GeoMechanicsApplication] Add a minimal 3+3 line interface element
4 participants