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

[Solver] Two-Phase One-Point Explicit #680

Open
wants to merge 211 commits into
base: develop
Choose a base branch
from

Conversation

tianchiTJ
Copy link
Contributor

@tianchiTJ tianchiTJ commented Jul 29, 2020

Describe the PR
This PR is to implement the two-phase one-layer explicit solver for fully saturated soil simulation. One particle includes both solid phase and liquid phase, which are defined by the corresponding volume fractions (n_s and n_w). The theory of porous media is adopted to set up the governing equations (momentum balance and mass balance). The pore pressure is updated based on the strain of the solid skeleton, and a weakly compressible fluid assumption is assumed to solve the fluid and mixture balance equations. All implementations work coherently with the current MPI and OpenMP parallelization features.

New implementation
TwoPhase Particle

  1. New class ParticleTwoPhase derived from Particle, which include all necessary functions for two-phase computation and new variables for two-phase (etc. pore pressure, porosity)
  2. New hdf5 structure for ParticleTwoPhase (also includes refactoring of base hdf5 class)

MPMExplicitTwoPhase

  1. New class MPMExplicitTwoPhase derived from MPMBase, which includes the main loop for two-phase computation.
  2. New input functions in MPMBase to read the new types of input files for two-phase.

TwoPhase functions for nodes

  1. New functions for the two-phase are implemented in the current Node class directly (see node_multiphase.tcc)

Free surface detection functions in mesh

  1. New functions to detect the changing surface are implemented in the Mesh class (see mesh_multiphase.tcc)

Unit tests and benchmarks
All new implementations are validated by corresponding test functions, and the unit-test for two-phase computation is also included. Related 2D and 3D one-dimensional consolidation tests can be found here: cb-geo/mpm-benchmarks#19.

tianchiTJ and others added 30 commits June 28, 2020 22:08
Copy link
Contributor

@kks32 kks32 left a comment

Choose a reason for hiding this comment

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

Partial review: Could we move the fluid function implementations in mesh to a separate "mesh_fluid.tcc" file so it's easier to manage?


//! Map cell volume to the nodes
//! \param[in] phase to map volume
void map_cell_volume_to_nodes(unsigned phase);
Copy link
Contributor

Choose a reason for hiding this comment

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

Would this affect GIMP and other non-local algorithms?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm... That's a good point, but I don't think the free-surface detection can be used for non-local MPM yet in general, so I am expecting this function won't work well too for non-local MPM.

include/cell.tcc Outdated Show resolved Hide resolved
include/node.tcc Show resolved Hide resolved
include/node.tcc Outdated Show resolved Hide resolved
include/node_base.h Outdated Show resolved Hide resolved
@bodhinandach
Copy link
Contributor

@kks32 I take some of the functions out to mesh_multiphase.tcc as you suggested. Will move the other functions related to two-phase out in the semi-implicit and 2P2P too

Copy link
Contributor

@kks32 kks32 left a comment

Choose a reason for hiding this comment

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

Partial review: Review contains comments for mesh amd mesh_mutliphase files.

CMakeLists.txt Show resolved Hide resolved
//! Map cell volume to nodes
template <unsigned Tdim>
void mpm::Cell<Tdim>::map_cell_volume_to_nodes(unsigned phase) {
if (this->status()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we doing this only for cells with particles? The function looks generic so adding this for only cells with particles looks strange. Could we instead pass it as a function argument?

Copy link
Contributor

Choose a reason for hiding this comment

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

We only do this for cells with particles as otherwise, the nodes_->update_volume() will be called as many numbers as the MPI thread. Therefore, without checking the status(), we won't have the free_surface detection working in parallel.

include/mesh.tcc Outdated Show resolved Hide resolved
include/mesh_multiphase.tcc Outdated Show resolved Hide resolved
include/mesh_multiphase.tcc Show resolved Hide resolved

//! Check internal cell
for (const auto neighbour_cell_id : (*citr)->neighbours()) {
#if USE_MPI
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not use the same functions? solving_status?

Copy link
Contributor

Choose a reason for hiding this comment

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

In that case, we need to assign solving_status for a single MPI threat too, which is just a copy of status

// Assign free surface nodes
if (!common_node_id.empty()) {
for (const auto common_id : common_node_id) {
map_nodes_[common_id]->assign_free_surface(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs to be reduced across MPI tasks

Copy link
Contributor

@bodhinandach bodhinandach Nov 17, 2020

Choose a reason for hiding this comment

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

Good point! I think, since we did the solving_status all_reduce prior to this call, we don't have to do so.

include/mesh_multiphase.tcc Outdated Show resolved Hide resolved
bool status = true;

try {
if (!particles_.size())
Copy link
Contributor

Choose a reason for hiding this comment

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

Use assert to check this condition

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, we just followed the format of other assign_particles_xxx functions. If we change this one, will that be better to change all other functions in mesh.tcc?

mpm::ParticlePhase::Liquid);
}
} catch (std::exception& exception) {
console_->error("{} #{}: {}\n", __FILE__, __LINE__, exception.what());
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we OK if this fails and we can continue the calculation with default values?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that would be better if we stop the simulation.

Copy link
Contributor

@kks32 kks32 left a comment

Choose a reason for hiding this comment

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

Could you make sure all return functions are marked const

  • Partial review

include/node.h Outdated
@@ -245,6 +260,21 @@ class Node : public NodeBase<Tdim> {
//! Set ghost id
void ghost_id(Index gid) override { ghost_id_ = gid; }

//! Return real density at a given node for a given phase
Copy link
Contributor

Choose a reason for hiding this comment

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

What's a real density? is it the interpolated density of the particles?

Copy link
Contributor

Choose a reason for hiding this comment

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

Please make sure the return statements are all const

Copy link
Contributor

@bodhinandach bodhinandach Nov 17, 2020

Choose a reason for hiding this comment

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

Yes, it is the interpolated particle density, but not from mass_density. It is computed by the division of mass_/volume_ inside nodes. Those two variables are the interpolated variables from particles. I think I will change the detail to "approximated" density instead of "real" density.


// Apply velocity constraints, which also sets acceleration to 0,
// when velocity is set.
this->apply_velocity_constraints();
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this apply velocity constraints to both solid and fluid phases?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, according to this:

mpm/include/node.tcc

Lines 362 to 367 in 689d9fb

// Direction value in the constraint (0, Dim * Nphases)
const unsigned dir = constraint.first;
// Direction: dir % Tdim (modulus)
const auto direction = static_cast<unsigned>(dir % Tdim);
// Phase: Integer value of division (dir / Tdim)
const auto phase = static_cast<unsigned>(dir / Tdim);

include/particles/particle.h Show resolved Hide resolved
include/particles/particle.h Outdated Show resolved Hide resolved
const mpm::dense_map& state_vars,
const std::shared_ptr<mpm::Material<Tdim>>& material,
unsigned phase = mpm::ParticlePhase::Solid) override;
PODParticle& particle,
Copy link
Contributor

Choose a reason for hiding this comment

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

could we pass it as a const&?

Copy link
Contributor

Choose a reason for hiding this comment

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

I checked this one, it is not possible as we reinterpret_cast it inside the derived function:

bool mpm::TwoPhaseParticle<Tdim>::initialise_particle(PODParticle& particle) {
// Initialise solid phase
bool status = mpm::Particle<Tdim>::initialise_particle(particle);
auto twophase_particle = reinterpret_cast<PODParticleTwoPhase*>(&particle);

template <unsigned Tdim>
bool mpm::TwoPhaseParticle<Tdim>::initialise_particle(
PODParticle& particle,
const std::vector<std::shared_ptr<mpm::Material<Tdim>>>& materials) {
auto twophase_particle = reinterpret_cast<PODParticleTwoPhase*>(&particle);
bool status = this->initialise_particle(*twophase_particle);

include/particles/particle.h Show resolved Hide resolved
//! Free surface
bool free_surface_{false};
//! Free surface
Eigen::Matrix<double, Tdim, 1> normal_;
Copy link
Contributor

Choose a reason for hiding this comment

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

Specifically a free_surface_normal_?

Copy link
Contributor

Choose a reason for hiding this comment

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

Do I need to change the name of the assign and return functions too?

@@ -284,6 +290,16 @@ bool mpm::Particle<Tdim>::assign_material_state_vars(
return status;
}

//! Assign a state variable
template <unsigned Tdim>
void mpm::Particle<Tdim>::assign_state_variable(const std::string& var,
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need this function to assign a specific state variable?

We should check this in assert, and go ahead without throwing an error. No reason to throw, if we can't recover.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we need this to assign_pressure or pore_pressure, just to make it generalize as when we call pressure(), we actually calling state_variable("pressure").

I agree with you to make it in assert instead of throwing an error.

//! Update porosity
//! \param[in] dt Analysis time step
virtual void update_porosity(double dt) {
throw std::runtime_error(
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we have to do this? I would keep this pure virtual and throw error elsewhere?

Copy link
Contributor

@bodhinandach bodhinandach Nov 17, 2020

Choose a reason for hiding this comment

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

That means we need to populate the particle.h with all these functions. Are you okay with that?

// Pore pressure constraint phase indice
unsigned constraint_phase = constraints["phase_id"];

if (constraint_phase >= Tnphases)
Copy link
Contributor

Choose a reason for hiding this comment

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

We can't check phases in MPM class, we should check it in the nodes

Copy link
Contributor

Choose a reason for hiding this comment

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

Super cool. I removed this check. It will be automatically called as the constraints_->assign_nodal_pressure_constraints() function is called. In short, they will reach this assertion.

mpm/include/node.tcc

Lines 178 to 186 in 689d9fb

//! Assign pressure constraint
template <unsigned Tdim, unsigned Tdof, unsigned Tnphases>
bool mpm::Node<Tdim, Tdof, Tnphases>::assign_pressure_constraint(
const unsigned phase, const double pressure,
const std::shared_ptr<FunctionBase>& function) {
bool status = true;
try {
// Constrain directions can take values between 0 and Tnphases
assert(phase < Tnphases * 2);

src/particle.cc Outdated Show resolved Hide resolved
@bodhinandach
Copy link
Contributor

@kks32 Thanks for your time reviewing this PR. It looks much better now. I partially modified the PR according to your comments. Can you spend some time to check it again?

@bodhinandach
Copy link
Contributor

ping @kks32, any further issues with this branch. Other than dynamic load balancing and resume feature in MPI, I am good.

@bodhinandach
Copy link
Contributor

@kks32 Some missing features which are not available in MPI while running 2Phase:

  1. compute_free_surface_by_geometry: Please use compute_free_surface_by_density instead. You can change the setting below from the input json:
"free_surface_detection": {
	"type": "density",
	"volume_tolerance": 0.25
}
  1. dynamic_load_balancing and resume functions are not working properly in 3D (It was checked to be OK for 2D). Problem was observed to be occurring in the halo exchange function for shared nodes between MPI rank.
    Error in halo exchange:
    image
    NOTE: For instance, at node 1050 (a shared node), solid and liquid mass are expected to be 0.01456 and 0.0024, respectively (they are not properly summed). Thus, producing this:
    image
    Error in dynamic load balancing at step 10:
    image

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

Successfully merging this pull request may close these issues.

Create two phase solver for saturated soil with explicit method
5 participants