diff --git a/include/contacts/contact.h b/include/contacts/contact.h index 98e6b1f69..8058132af 100644 --- a/include/contacts/contact.h +++ b/include/contacts/contact.h @@ -20,6 +20,10 @@ class Contact { //! Compute contact forces virtual inline void compute_contact_forces(){}; + //! Compute contact forces + //! \param[in] dt Analysis time step + virtual inline void compute_contact_forces(double dt){}; + protected: //! Mesh object std::shared_ptr> mesh_; diff --git a/include/contacts/contact_friction.h b/include/contacts/contact_friction.h index 5c9bf6cec..12735f218 100644 --- a/include/contacts/contact_friction.h +++ b/include/contacts/contact_friction.h @@ -15,10 +15,14 @@ class ContactFriction : public Contact { ContactFriction(const std::shared_ptr>& mesh); //! Intialize - virtual inline void initialise() override; + void initialise() override; //! Compute contact forces - virtual inline void compute_contact_forces() override; + void compute_contact_forces() override; + + //! Compute contact forces + //! \param[in] dt Analysis time step + void compute_contact_forces(double dt) override; protected: //! Mesh object diff --git a/include/contacts/contact_friction.tcc b/include/contacts/contact_friction.tcc index 6097c7829..cef5ba5de 100644 --- a/include/contacts/contact_friction.tcc +++ b/include/contacts/contact_friction.tcc @@ -6,7 +6,7 @@ mpm::ContactFriction::ContactFriction( //! Initialize nodal properties template -inline void mpm::ContactFriction::initialise() { +void mpm::ContactFriction::initialise() { // Initialise nodal properties mesh_->initialise_nodal_properties(); @@ -18,7 +18,42 @@ inline void mpm::ContactFriction::initialise() { //! Compute contact forces template -inline void mpm::ContactFriction::compute_contact_forces() { +void mpm::ContactFriction::compute_contact_forces() { + + // Map multimaterial properties from particles to nodes + mesh_->iterate_over_particles(std::bind( + &mpm::ParticleBase::map_multimaterial_mass_momentum_to_nodes, + std::placeholders::_1)); + + // Map multimaterial displacements from particles to nodes + mesh_->iterate_over_particles(std::bind( + &mpm::ParticleBase::map_multimaterial_displacements_to_nodes, + std::placeholders::_1)); + + // Map multimaterial domain gradients from particles to nodes + mesh_->iterate_over_particles(std::bind( + &mpm::ParticleBase::map_multimaterial_domain_gradients_to_nodes, + std::placeholders::_1)); + + // Compute multimaterial change in momentum + mesh_->iterate_over_nodes( + std::bind(&mpm::NodeBase::compute_multimaterial_change_in_momentum, + std::placeholders::_1)); + + // Compute multimaterial separation vector + mesh_->iterate_over_nodes( + std::bind(&mpm::NodeBase::compute_multimaterial_separation_vector, + std::placeholders::_1)); + + // Compute multimaterial normal unit vector + mesh_->iterate_over_nodes( + std::bind(&mpm::NodeBase::compute_multimaterial_normal_unit_vector, + std::placeholders::_1)); +} + +//! Compute contact forces +template +void mpm::ContactFriction::compute_contact_forces(double dt) { // Map multimaterial properties from particles to nodes mesh_->iterate_over_particles(std::bind( diff --git a/include/contacts/contact_levelset.h b/include/contacts/contact_levelset.h new file mode 100644 index 000000000..8205d6c26 --- /dev/null +++ b/include/contacts/contact_levelset.h @@ -0,0 +1,38 @@ +#ifndef MPM_CONTACT_LEVELSET_H_ +#define MPM_CONTACT_LEVELSET_H_ + +#include "contact.h" +#include "mesh_levelset.h" +#include "particle_levelset.h" + +namespace mpm { + +//! ContactLevelset class +//! \brief ContactLevelset base class +//! \tparam Tdim Dimension +template +class ContactLevelset : public Contact { + public: + //! Default constructor with mesh class + ContactLevelset(const std::shared_ptr>& mesh); + + //! Intialize + void initialise() override; + + //! Compute contact forces + //! \param[in] dt Analysis time step + void compute_contact_forces(double dt) override; + + //! Mesh levelset object + std::shared_ptr> mesh_levelset_; + + protected: + //! Mesh object + using mpm::Contact::mesh_; + +}; // ContactLevelset class +} // namespace mpm + +#include "contact_levelset.tcc" + +#endif // MPM_CONTACT_LEVELSET_H_ diff --git a/include/contacts/contact_levelset.tcc b/include/contacts/contact_levelset.tcc new file mode 100644 index 000000000..023f53bc6 --- /dev/null +++ b/include/contacts/contact_levelset.tcc @@ -0,0 +1,25 @@ +//! Constructor of contact with mesh +template +mpm::ContactLevelset::ContactLevelset( + const std::shared_ptr>& mesh) + : mpm::Contact(mesh) { + // Assign mesh + mesh_ = mesh; +} + +//! Initialize nodal properties +template +void mpm::ContactLevelset::initialise() { + // Initialise nodal properties + mesh_->initialise_nodal_properties(); +} + +//! Compute contact forces +template +void mpm::ContactLevelset::compute_contact_forces(double dt) { + + // Compute and map contact forces to nodes + mesh_->iterate_over_particles( + std::bind(&mpm::ParticleBase::map_particle_contact_force_to_nodes, + std::placeholders::_1, dt)); +} diff --git a/include/io/io_mesh.h b/include/io/io_mesh.h index d7e6da99a..0e50ce94e 100644 --- a/include/io/io_mesh.h +++ b/include/io/io_mesh.h @@ -64,11 +64,11 @@ class IOMesh { virtual std::vector> read_particles_stresses( const std::string& particles_stresses) = 0; - //! Read particle scalar properties + //! Read scalar properties for particles or nodes //! \param[in] scalar_file file name with particle scalar properties - //! \retval Vector of particles scalar properties - virtual std::vector> - read_particles_scalar_properties(const std::string& scalar_file) = 0; + //! \retval Vector of scalar properties for particles or nodes + virtual std::vector> read_scalar_properties( + const std::string& scalar_file) = 0; //! Read pressure constraints file //! \param[in] pressure_constraints_files file name with pressure constraints @@ -123,6 +123,12 @@ class IOMesh { read_adhesion_constraints( const std::string& adhesion_constraints_file) = 0; + //! Read levelset file + //! \param[in] levelset_input_file file name with levelset values + virtual std::vector< + std::tuple> + read_levelset_input(const std::string& levelset_input_file) = 0; + //! Read forces file //! \param[in] forces_file file name with nodal concentrated force virtual std::vector> read_forces( diff --git a/include/io/io_mesh_ascii.h b/include/io/io_mesh_ascii.h index 9ce82ad82..88fc7ace2 100644 --- a/include/io/io_mesh_ascii.h +++ b/include/io/io_mesh_ascii.h @@ -51,10 +51,10 @@ class IOMeshAscii : public IOMesh { std::vector> read_particles_stresses( const std::string& particles_stresses) override; - //! Read particle scalar properties - //! \param[in] scalar_file file name with particle scalar properties - //! \retval Vector of particles scalar properties - std::vector> read_particles_scalar_properties( + //! Read scalar properties for particles or nodes + //! \param[in] scalar_file file name with particle or node scalar properties + //! \retval Vector of scalar properties for particles or nodes + std::vector> read_scalar_properties( const std::string& scalar_file) override; //! Read pressure constraints file @@ -110,6 +110,11 @@ class IOMeshAscii : public IOMesh { read_adhesion_constraints( const std::string& adhesion_constraints_file) override; + //! Read levelset file + //! \param[in] levelset_input_file file name with levelset values + std::vector> + read_levelset_input(const std::string& levelset_input_file) override; + //! Read traction file //! \param[in] forces_file file name with nodal concentrated force std::vector> read_forces( diff --git a/include/io/io_mesh_ascii.tcc b/include/io/io_mesh_ascii.tcc index 303c632ed..6b2f47707 100644 --- a/include/io/io_mesh_ascii.tcc +++ b/include/io/io_mesh_ascii.tcc @@ -254,13 +254,13 @@ std::vector> return stresses; } -//! Return particles scalar properties +//! Return scalar properties for particles or nodes template std::vector> - mpm::IOMeshAscii::read_particles_scalar_properties( + mpm::IOMeshAscii::read_scalar_properties( const std::string& scalar_file) { - // Particles scalar properties + // Scalar properties for particles or nodes std::vector> scalar_properties; // input file stream @@ -293,7 +293,7 @@ std::vector> } file.close(); } catch (std::exception& exception) { - console_->error("Read particle {} #{}: {}\n", __FILE__, __LINE__, + console_->error("Read particle/node {} #{}: {}\n", __FILE__, __LINE__, exception.what()); file.close(); } @@ -641,7 +641,7 @@ std::vector> return constraints; } -//! Return friction constraints of particles +//! Return friction constraints template std::vector> mpm::IOMeshAscii::read_friction_constraints( @@ -748,6 +748,64 @@ std::vector> return constraints; } +//! Return nodal levelset information +template +std::vector> + mpm::IOMeshAscii::read_levelset_input( + const std::string& levelset_input_file) { + + // Nodal levelset information + std::vector> + levelset_inputs; + levelset_inputs.clear(); + + // input file stream + std::fstream file; + file.open(levelset_input_file.c_str(), std::ios::in); + + try { + if (file.is_open() && file.good()) { + // Line + std::string line; + while (std::getline(file, line)) { + boost::algorithm::trim(line); + std::istringstream istream(line); + // ignore comment lines (# or !) or blank lines + if ((line.find('#') == std::string::npos) && + (line.find('!') == std::string::npos) && (line != "")) { + // ID + mpm::Index id; + // Levelset value + double levelset; + // Friction + double levelset_mu; + // Adhesion coefficient + double levelset_alpha; + // Barrier stiffness + double barrier_stiffness; + // Slip threshold + double slip_threshold; + while (istream.good()) { + // Read stream + istream >> id >> levelset >> levelset_mu >> levelset_alpha >> + barrier_stiffness >> slip_threshold; + levelset_inputs.emplace_back( + std::make_tuple(id, levelset, levelset_mu, levelset_alpha, + barrier_stiffness, slip_threshold)); + } + } + } + } else { + throw std::runtime_error("File not open or not good!"); + } + file.close(); + } catch (std::exception& exception) { + console_->error("Read levelset inputs: {}", exception.what()); + file.close(); + } + return levelset_inputs; +} + //! Return particles force template std::vector> diff --git a/include/mesh/mesh.h b/include/mesh/mesh.h index 59c315d24..36dcd7115 100644 --- a/include/mesh/mesh.h +++ b/include/mesh/mesh.h @@ -164,7 +164,7 @@ class Mesh { //! \param[in] cells Node ids of cells //! \param[in] check_duplicates Parameter to check duplicates //! \retval status Create cells status - bool create_cells(mpm::Index gnid, + bool create_cells(mpm::Index gcid, const std::shared_ptr>& element, const std::vector>& cells, bool check_duplicates = true); @@ -263,7 +263,7 @@ class Mesh { //! Locate particles in a cell //! Iterate over all cells in a mesh to find the cell in which particles //! are located. - //! \retval particles Particles which cannot be located in the mesh + //! \retval particles which cannot be located in the mesh std::vector>> locate_particles_mesh(); //! Iterate over particles @@ -475,7 +475,7 @@ class Mesh { //! Create map of vector of particles in sets //! \param[in] map of particles ids in sets //! \param[in] check_duplicates Parameter to check duplicates - //! \retval status Status of create particle sets + //! \retval status Status of create particle sets bool create_particle_sets( const tsl::robin_map>& particle_sets, bool check_duplicates); @@ -518,11 +518,34 @@ class Mesh { void inject_particles(double current_time); // Create the nodal properties' map - void create_nodal_properties(); + virtual void create_nodal_properties(); // Initialise the nodal properties' map void initialise_nodal_properties(); + /** + * \defgroup Levelset Functions + */ + /**@{*/ + + //! Assign nodal levelset values + //! \ingroup Levelset + //! \param[in] levelset Levelset value at the particle + //! \param[in] levelset_mu Levelset friction + //! \param[in] levelset_alpha Levelset adhesion coefficient + //! \param[in] barrier_stiffness Barrier stiffness + //! \param[in] slip_threshold Slip threshold + virtual bool assign_nodal_levelset_values( + const std::vector>& levelset_input_file) { + throw std::runtime_error( + "Calling the base class function (assign_nodal_levelset_values) in " + "Mesh:: illegal operation!"); + return false; + }; + + /**@}*/ + /** * \defgroup MultiPhase Functions dealing with multi-phase MPM */ @@ -673,10 +696,11 @@ class Mesh { const Json& generator, unsigned pset_id); // Locate a particle in mesh cells + //! \param[in] particle of interest bool locate_particle_cells( const std::shared_ptr>& particle); - private: + protected: //! mesh id unsigned id_{std::numeric_limits::max()}; //! Isoparametric mesh diff --git a/include/mesh/mesh_levelset.h b/include/mesh/mesh_levelset.h new file mode 100644 index 000000000..8bd65ea5f --- /dev/null +++ b/include/mesh/mesh_levelset.h @@ -0,0 +1,79 @@ +#ifndef MPM_MESH_LEVELSET_H_ +#define MPM_MESH_LEVELSET_H_ + +#include + +#include "logger.h" +#include "mesh.h" + +namespace mpm { + +//! Levelset subclass +//! \brief subclass that stores the information about levelset mesh +//! \tparam Tdim Dimension +template +class MeshLevelset : public Mesh { + + public: + //! Define a vector of size dimension + using VectorDim = Eigen::Matrix; + + // Construct a mesh with a global unique id + //! \param[in] id Global mesh id + //! \param[in] isoparametric Mesh is isoparametric + MeshLevelset(unsigned id, bool isoparametric) + : mpm::Mesh(id, isoparametric), + id_{id}, + isoparametric_{isoparametric} { + // Check if the dimension is between 1 & 3 + static_assert((Tdim >= 1 && Tdim <= 3), "Invalid global dimension"); + //! Logger + std::string logger = "meshlevelset::" + std::to_string(id); + console_ = std::make_unique(logger, mpm::stdout_sink); + } + + //! Default destructor + ~MeshLevelset() = default; + + //! Delete copy constructor + MeshLevelset(const MeshLevelset&) = delete; + + //! Delete assignement operator + MeshLevelset& operator=(const MeshLevelset&) = delete; + + //! Assign mesh levelset values to nodes + //! \param[in] levelset Levelset value at the particle + //! \param[in] levelset_mu Levelset friction + //! \param[in] levelset_alpha Levelset adhesion coefficient + //! \param[in] barrier_stiffness Barrier stiffness + //! \param[in] slip_threshold Slip threshold + bool assign_nodal_levelset_values( + const std::vector>& levelset_input_file) override; + + // Create the nodal properties' map + void create_nodal_properties() override; + + private: + //! mesh id + unsigned id_{std::numeric_limits::max()}; + //! Isoparametric mesh + bool isoparametric_{true}; + //! Logger + std::unique_ptr console_; + //! Vector of nodes + using mpm::Mesh::nodes_; + //! Map of nodes for fast retrieval + using mpm::Mesh::map_nodes_; + //! Materials + using mpm::Mesh::materials_; + //! Nodal property pool + using mpm::Mesh::nodal_properties_; + +}; // MeshLevelset class + +} // namespace mpm + +#include "mesh_levelset.tcc" + +#endif // MPM_MESH_LEVELSET_H_ \ No newline at end of file diff --git a/include/mesh/mesh_levelset.tcc b/include/mesh/mesh_levelset.tcc new file mode 100644 index 000000000..ec018c624 --- /dev/null +++ b/include/mesh/mesh_levelset.tcc @@ -0,0 +1,92 @@ +//! Assign mesh levelset values to nodes +template +bool mpm::MeshLevelset::assign_nodal_levelset_values( + const std::vector>& levelset_input_file) { + bool status = true; + try { + if (!nodes_.size()) + throw std::runtime_error( + "No nodes have been assigned in mesh, cannot assign levelset values"); + + for (const auto& levelset_info : levelset_input_file) { + // Node id + mpm::Index nid = std::get<0>(levelset_info); + // Levelset + double levelset = std::get<1>(levelset_info); + // Levelset friction + double levelset_mu = std::get<2>(levelset_info); + // Levelset adhesion coefficient + double levelset_alpha = std::get<3>(levelset_info); + // Barrier stiffness + double barrier_stiffness = std::get<4>(levelset_info); + // Slip threshold + double slip_threshold = std::get<5>(levelset_info); + + if (map_nodes_.find(nid) != map_nodes_.end()) + status = map_nodes_[nid]->assign_levelset( + levelset, levelset_mu, levelset_alpha, barrier_stiffness, + slip_threshold); + + if (!status) + throw std::runtime_error("Cannot assign invalid nodal levelset values"); + } + } catch (std::exception& exception) { + console_->error("{} #{}: {}\n", __FILE__, __LINE__, exception.what()); + status = false; + } + return status; +} + +// Create the nodal properties' map +template +void mpm::MeshLevelset::create_nodal_properties() { + // Initialise the shared pointer to nodal properties + nodal_properties_ = std::make_shared(); + + // Check if nodes_ and materials_is empty (if empty throw runtime error) + if (nodes_.size() != 0 && materials_.size() != 0) { + // Compute number of rows in nodal properties for vector entities + const unsigned nrows = nodes_.size() * Tdim; + // Create pool data for each property in the nodal properties struct + // object. Properties must be named in the plural form + nodal_properties_->create_property("masses", nodes_.size(), + materials_.size()); + nodal_properties_->create_property("momenta", nrows, materials_.size()); + nodal_properties_->create_property("change_in_momenta", nrows, + materials_.size()); + nodal_properties_->create_property("displacements", nrows, + materials_.size()); + nodal_properties_->create_property("separation_vectors", nrows, + materials_.size()); + nodal_properties_->create_property("domain_gradients", nrows, + materials_.size()); + nodal_properties_->create_property("normal_unit_vectors", nrows, + materials_.size()); + nodal_properties_->create_property("wave_velocities", nrows, + materials_.size()); + nodal_properties_->create_property("density", nodes_.size(), + materials_.size()); + // levelset properties + nodal_properties_->create_property("levelsets", nodes_.size(), + materials_.size()); + nodal_properties_->create_property("levelset_mus", nodes_.size(), + materials_.size()); + nodal_properties_->create_property("levelset_alphas", nodes_.size(), + materials_.size()); + nodal_properties_->create_property("barrier_stiffnesses", nodes_.size(), + materials_.size()); + nodal_properties_->create_property("slip_thresholds", nodes_.size(), + materials_.size()); + nodal_properties_->create_property("levelset_mp_radii", nodes_.size(), + materials_.size()); + + // Iterate over all nodes to initialise the property handle in each node + // and assign its node id as the prop id in the nodal property data pool + for (auto nitr = nodes_.cbegin(); nitr != nodes_.cend(); ++nitr) + (*nitr)->initialise_property_handle((*nitr)->id(), nodal_properties_); + } else { + throw std::runtime_error( + "Number of nodes or number of materials is zero (levelset)"); + } +} \ No newline at end of file diff --git a/include/nodes/node.h b/include/nodes/node.h index aa182e563..bc9769c33 100644 --- a/include/nodes/node.h +++ b/include/nodes/node.h @@ -332,11 +332,11 @@ class Node : public NodeBase { */ /**@{*/ //! Initialise nodal properties for implicit solver - //! \ingroup Impolicit + //! \ingroup Implicit void initialise_implicit() noexcept override; //! Initialise nodal forces - //! \ingroup Impolicit + //! \ingroup Implicit void initialise_force() noexcept override; //! Update inertia at the nodes diff --git a/include/nodes/node_base.h b/include/nodes/node_base.h index af4763e16..e078e4f8d 100644 --- a/include/nodes/node_base.h +++ b/include/nodes/node_base.h @@ -549,6 +549,68 @@ class NodeBase { /**@}*/ + /** + * \defgroup Levelset Functions + */ + /**@{*/ + + // Assign levelset values to nodes + //! \param[in] levelset Levelset value at the particle + //! \param[in] levelset_mu Levelset friction + //! \param[in] levelset_alpha Levelset adhesion coefficient + //! \param[in] barrier_stiffness Barrier stiffness + //! \param[in] slip_threshold Slip threshold + virtual bool assign_levelset(double levelset, double levelset_mu, + double levelset_alpha, double barrier_stiffness, + double slip_threshold) { + throw std::runtime_error( + "Calling the base class function (assign_levelset) in NodeBase:: " + "illegal operation!"); + return false; + }; + + //! Return levelset value + virtual double levelset() const { + throw std::runtime_error( + "Calling the base class function (levelset) in NodeBase:: illegal " + "operation!"); + return 0.; + } + + //! Return levelset friction + virtual double levelset_mu() const { + throw std::runtime_error( + "Calling the base class function (levelset_mu) in NodeBase:: illegal " + "operation!"); + return 0.; + } + + //! Return levelset adhesion coefficient + virtual double levelset_alpha() const { + throw std::runtime_error( + "Calling the base class function (levelset_alpha) in NodeBase:: " + "illegal operation!"); + return 0.; + } + + //! Return barrier stiffness + virtual double barrier_stiffness() const { + throw std::runtime_error( + "Calling the base class function (barrier_stiffness) in NodeBase:: " + "illegal operation!"); + return 0.; + } + + //! Return slip threshold + virtual double slip_threshold() const { + throw std::runtime_error( + "Calling the base class function (slip_threshold) in NodeBase:: " + "illegal operation!"); + return 0.; + } + + /**@}*/ + }; // NodeBase class } // namespace mpm diff --git a/include/nodes/node_levelset.h b/include/nodes/node_levelset.h new file mode 100644 index 000000000..68399514c --- /dev/null +++ b/include/nodes/node_levelset.h @@ -0,0 +1,84 @@ +#ifndef MPM_NODE_LEVELSET_H_ +#define MPM_NODE_LEVELSET_H_ + +#include "node.h" +#include "node_base.h" + +namespace mpm { + +//! Levelset subclass +//! \brief subclass that stores the information about levelset node +//! \tparam Tdim Dimension +template +class NodeLevelset : public Node { + + public: + //! Define a vector of size dimension + using VectorDim = Eigen::Matrix; + + //! Constructor with id, coordinates and dof + //! \param[in] id Node id + //! \param[in] coord coordinates of the node + NodeLevelset(Index id, const VectorDim& coord) + : mpm::Node(id, coord) { + console_ = + std::make_unique("NodeLevelset", mpm::stdout_sink); + }; + + //! Virtual destructor + ~NodeLevelset() override{}; + + //! Delete copy constructor + NodeLevelset(const NodeLevelset&) = delete; + + //! Delete assignement operator + NodeLevelset& operator=(const NodeLevelset&) = delete; + + // Assign levelset values to nodes + //! \param[in] levelset Levelset value at the particle + //! \param[in] levelset_mu Levelset friction + //! \param[in] levelset_alpha Levelset adhesion coefficient + //! \param[in] barrier_stiffness Barrier stiffness + //! \param[in] slip_threshold Slip threshold + bool assign_levelset(double levelset, double levelset_mu, + double levelset_alpha, double barrier_stiffness, + double slip_threshold) override; + + //! Return levelset value + inline double levelset() const override { return levelset_; } + + //! Return levelset friction + inline double levelset_mu() const override { return levelset_mu_; } + + //! Return levelset adhesion coefficient + inline double levelset_alpha() const override { return levelset_alpha_; } + + //! Return barrier stiffness + inline double barrier_stiffness() const override { + return barrier_stiffness_; + } + + //! Return slip threshold + inline double slip_threshold() const override { return slip_threshold_; } + + private: + //! Logger + std::unique_ptr console_; + //! Levelset value + double levelset_; + //! Levelset friction + double levelset_mu_; + //! Levelset adhesion coefficient + double levelset_alpha_; + //! Barrier stiffness + double barrier_stiffness_; + //! Slip threshold + double slip_threshold_; + +}; // NodeLevelset class + +} // namespace mpm + +#include "node_levelset.tcc" + +#endif // MPM_NODE_LEVELSET_H_ \ No newline at end of file diff --git a/include/nodes/node_levelset.tcc b/include/nodes/node_levelset.tcc new file mode 100644 index 000000000..56b66a612 --- /dev/null +++ b/include/nodes/node_levelset.tcc @@ -0,0 +1,26 @@ +// Assign levelset values to the nodes +template +bool mpm::NodeLevelset::assign_levelset( + double levelset, double levelset_mu, double levelset_alpha, + double barrier_stiffness, double slip_threshold) { + bool status = true; + try { + if ((levelset_mu < 0.) || (levelset_alpha < 0.) || (slip_threshold < 0.)) + throw std::runtime_error( + "Levelset mu, alpha, and slip threshold cannot be negative"); + if ((barrier_stiffness <= 0.)) + throw std::runtime_error("Barrier stiffness must be greater than zero"); + + // Set variables + this->levelset_ = levelset; + this->levelset_mu_ = levelset_mu; + this->levelset_alpha_ = levelset_alpha; + this->barrier_stiffness_ = barrier_stiffness; + this->slip_threshold_ = slip_threshold; + + } catch (std::exception& exception) { + console_->error("{} #{}: {}\n", __FILE__, __LINE__, exception.what()); + status = false; + } + return status; +} \ No newline at end of file diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index ac589445a..88ef1c5d3 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -433,6 +433,15 @@ class ParticleBase { //! \ingroup AdvancedMapping virtual Eigen::MatrixXd mapping_matrix() const = 0; + //! Levelset functions-------------------------------------------------------- + //! Assign nodal levelset value to particles + //! \param[in] dt Analysis time step + virtual void map_particle_contact_force_to_nodes(double dt) { + throw std::runtime_error( + "Calling the base class function (map_particle_contact_force_to_nodes) " + "in ParticleBase:: illegal operation!"); + }; + //! Navier-Stokes functions---------------------------------- //! Assigning beta parameter to particle //! \param[in] parameter parameter determining type of projection diff --git a/include/particles/particle_levelset.h b/include/particles/particle_levelset.h new file mode 100644 index 000000000..11b37b859 --- /dev/null +++ b/include/particles/particle_levelset.h @@ -0,0 +1,109 @@ +#ifndef MPM_PARTICLE_LEVELSET_H_ +#define MPM_PARTICLE_LEVELSET_H_ + +#include "logger.h" +#include "math_utility.h" +#include "particle.h" + +#include + +namespace mpm { + +//! Levelset subclass +//! \brief subclass that stores the information about levelset particle +//! \tparam Tdim Dimension +template +class ParticleLevelset : public Particle { + + public: + //! Define a vector of size dimension + using VectorDim = Eigen::Matrix; + + //! Construct a levelset particle with id and coordinates + //! \param[in] id Particle id + //! \param[in] coord Coordinates of the particles + ParticleLevelset(Index id, const VectorDim& coord); + + //! Construct a levelset particle with id, coordinates and status + //! \param[in] id Particle id + //! \param[in] coord coordinates of the particle + //! \param[in] status Particle status (active / inactive) + ParticleLevelset(Index id, const VectorDim& coord, bool status); + + //! Destructor + ~ParticleLevelset() override{}; + + //! Delete copy constructor + ParticleLevelset(const ParticleLevelset&) = delete; + + //! Delete assignment operator + ParticleLevelset& operator=(const ParticleLevelset&) = delete; + + //! Assign nodal Levelset value to particles + //! \param[in] dt Analysis time step + void map_particle_contact_force_to_nodes(double dt) override; + + //! Return the approximate particle diameter + double diameter() const override; + + private: + //! Compute Levelset contact force + //! \param[in] levelset Levelset value at the particle + //! \param[in] levelset_normal Normal vector towards the levelset + //! \param[in] levelset_mu Levelset friction + //! \param[in] levelset_alpha Levelset adhesion coefficient + //! \param[in] barrier_stiffness Barrier stiffness + //! \param[in] slip_threshold Slip threshold + //! \param[in] mp_radius mp radius of influence for contact + //! \param[in] contact_vel Contact velocity from nodes (PIC) + //! \param[in] dt Analysis time step + VectorDim compute_levelset_contact_force( + double levelset, const VectorDim& levelset_normal, double levelset_mu, + double levelset_alpha, double barrier_stiffness, double slip_threshold, + const double mp_radius, const VectorDim& contact_vel, double dt) noexcept; + + private: + //! Logger + std::unique_ptr console_; + //! coupling force + VectorDim couple_force_{VectorDim::Zero()}; + //! levelset value + double levelset{0.}; + //! levelset friction + double levelset_mu{0.}; + //! levelset adhesion coefficient + double levelset_alpha{0.}; + //! barrier stiffness + double barrier_stiffness{0.}; + //! slip threshold + double slip_threshold{0.}; + //! cumulative slip magnitude + double cumulative_slip_mag{0.}; // LEDT check not reseting each step + //! contact velocity + VectorDim contact_vel{VectorDim::Zero()}; + //! Nodes + using Particle::nodes_; + //! Cell + using Particle::cell_; + //! Shape functions + using Particle::shapefn_; + //! dN/dX + using Particle::dn_dx_; + //! Velocity + using Particle::velocity_; + //! Volume + using Particle::volume_; + //! Mass + using Particle::mass_; + //! Size of particle + using Particle::size_; + //! particleBase id + using Particle::id_; + +}; // Particle_Levelset class + +} // namespace mpm + +#include "particle_levelset.tcc" + +#endif // MPM_PARTICLE_LEVELSET_H__ \ No newline at end of file diff --git a/include/particles/particle_levelset.tcc b/include/particles/particle_levelset.tcc new file mode 100644 index 000000000..461600bd2 --- /dev/null +++ b/include/particles/particle_levelset.tcc @@ -0,0 +1,165 @@ +//! Construct a particle with id and coordinates +template +mpm::ParticleLevelset::ParticleLevelset(Index id, const VectorDim& coord) + : mpm::Particle(id, coord) { + this->initialise(); + // Clear cell ptr + cell_ = nullptr; + // Nodes + nodes_.clear(); + // Set material containers + this->initialise_material(1); + // Logger + std::string logger = + "particlelevelset" + std::to_string(Tdim) + "d::" + std::to_string(id); + console_ = std::make_unique(logger, mpm::stdout_sink); +} + +//! Construct a particle with id, coordinates and status +template +mpm::ParticleLevelset::ParticleLevelset(Index id, const VectorDim& coord, + bool status) + : mpm::Particle(id, coord, status) { + this->initialise(); + // Clear cell ptr + cell_ = nullptr; + // Nodes + nodes_.clear(); + // Set material containers + this->initialise_material(1); + //! Logger + std::string logger = + "particlelevelset" + std::to_string(Tdim) + "d::" + std::to_string(id); + console_ = std::make_unique(logger, mpm::stdout_sink); +} + +//! Return the approximate particle diameter +template +double mpm::ParticleLevelset::diameter() const { + double diameter = 0.; + if (Tdim == 2) diameter = 2.0 * std::sqrt(volume_ / M_PI); // radial + if (Tdim == 3) diameter = 2.0 * std::cbrt(volume_ * 0.75 / M_PI); // radial + return diameter; +} + +//! Map levelset contact force +template +void mpm::ParticleLevelset::map_particle_contact_force_to_nodes( + double dt) { + // Compute levelset values at particle + double levelset = 0; + double levelset_mu = 0; + double levelset_alpha = 0; + double barrier_stiffness = 0; + double slip_threshold = 0; + VectorDim levelset_gradient = VectorDim::Zero(); + VectorDim contact_vel = VectorDim::Zero(); + + for (unsigned i = 0; i < nodes_.size(); i++) { + // Map levelset and compute gradient + levelset += shapefn_[i] * nodes_[i]->levelset(); + levelset_gradient += dn_dx_.row(i).transpose() * nodes_[i]->levelset(); + + // Map other input variables + levelset_mu += shapefn_[i] * nodes_[i]->levelset_mu(); + levelset_alpha += shapefn_[i] * nodes_[i]->levelset_alpha(); + barrier_stiffness += shapefn_[i] * nodes_[i]->barrier_stiffness(); + slip_threshold += shapefn_[i] * nodes_[i]->slip_threshold(); + + // Map contact velocity from the nodes (PIC velocity) + contact_vel += shapefn_[i] * nodes_[i]->velocity(mpm::ParticlePhase::Solid); + } + + // Compute normals // LEDT check this once separate meshes + VectorDim levelset_normal = levelset_gradient.normalized(); + + // Approximate radius of influence + double mp_radius = 0.5 * diameter(); // radial + + // Compute contact force in particle + VectorDim force = compute_levelset_contact_force( + levelset, levelset_normal, levelset_mu, levelset_alpha, barrier_stiffness, + slip_threshold, mp_radius, contact_vel, dt); + + // Compute nodal contact force + for (unsigned i = 0; i < nodes_.size(); ++i) { + nodes_[i]->update_external_force(true, mpm::ParticlePhase::Solid, + (shapefn_[i] * force)); + } +} + +//! Compute levelset contact force +template +typename mpm::ParticleLevelset::VectorDim + mpm::ParticleLevelset::compute_levelset_contact_force( + double levelset, const VectorDim& levelset_normal, double levelset_mu, + double levelset_alpha, double barrier_stiffness, double slip_threshold, + const double mp_radius, const VectorDim& contact_vel, + double dt) noexcept { + // Coupling force zero by default + VectorDim couple_force_ = VectorDim::Zero(); + + // Computer error minimum value + if (levelset < std::numeric_limits::epsilon()) + levelset = std::numeric_limits::epsilon(); + + // Contact only if mp within contact zone + if ((levelset < mp_radius) && (levelset > 0.)) { + + // Calculate normal coupling force magnitude + double couple_normal_mag = + barrier_stiffness * (levelset - mp_radius) * + (2 * log(levelset / mp_radius) - (mp_radius / levelset) + 1); + double normal_force = couple_normal_mag; + + // Apply normal couple only if mp moving towards boundary + if ((contact_vel.dot(levelset_normal)) > 0) couple_normal_mag = 0; + + // Calculate normal coupling force + VectorDim couple_force_normal = couple_normal_mag * levelset_normal; + + // Calculate levelset tangential unit vector + VectorDim levelset_tangent = VectorDim::Zero(); + double vel_n = contact_vel.dot(levelset_normal); + VectorDim tangent_calc = contact_vel - (vel_n * levelset_normal); + if (tangent_calc.norm() > std::numeric_limits::epsilon()) + levelset_tangent = tangent_calc.normalized(); + + // Apply friction smoothing function, if applicable + double friction_smoothing = 1.0; + if (slip_threshold > 0.) { + // Calculate cumulative slip magnitude // LEDT check: abs val? per-MP? + cumulative_slip_mag += dt * contact_vel.dot(levelset_tangent); + // Calculate friction smoothing + if (abs(cumulative_slip_mag) < slip_threshold) { + friction_smoothing = + -(std::pow(cumulative_slip_mag, 2) / std::pow(slip_threshold, 2)) + + 2 * abs(cumulative_slip_mag) / slip_threshold; + } + } + + // Calculate friction tangential coupling force magnitude + double tangent_friction = friction_smoothing * levelset_mu * normal_force; + + // Calculate adhesion tangential coupling force magnitude + double contact_area = volume_ / size_[0]; + double tangent_adhesion = levelset_alpha * contact_area; + + // Calculate tangential coupling force magntiude + double couple_tangent_mag = tangent_friction + tangent_adhesion; + + // Calculate tangential contact force magnitude + double contact_tangent_mag = + (mass_ * contact_vel / dt).dot(levelset_tangent); + + // Couple must not exceed cancellation of contact tangential force + couple_tangent_mag = std::min(couple_tangent_mag, contact_tangent_mag); + + // Calculate tangential coupling force vector + VectorDim couple_force_tangent = -levelset_tangent * couple_tangent_mag; + + // Calculate total coupling force vector + couple_force_ = couple_force_normal + couple_force_tangent; + } + return couple_force_; +} diff --git a/include/solvers/mpm_base.h b/include/solvers/mpm_base.h index e72acc2f1..17a88f479 100644 --- a/include/solvers/mpm_base.h +++ b/include/solvers/mpm_base.h @@ -23,6 +23,7 @@ #include "constraints.h" #include "contact.h" #include "contact_friction.h" +#include "contact_levelset.h" #include "mpm.h" #include "mpm_scheme.h" #include "mpm_scheme_musl.h" @@ -220,6 +221,24 @@ class MPMBase : public MPM { //! Initialise particle types void initialise_particle_types(); + /** + * \defgroup Interface Functions (includes multimaterial and levelset) + */ + /**@{*/ + //! \ingroup Interface + //! Return if interface and levelset are active + //! \retval levelset status of mesh + bool is_levelset(); + + //! \ingroup Interface + //! Nodal levelset inputs + //! \param[in] mesh_prop Mesh properties + //! \param[in] mesh_io Mesh IO handle + void interface_inputs(const Json& mesh_prop, + const std::shared_ptr>& mesh_io); + + /**@}*/ + /** * \defgroup Implicit Functions dealing with implicit MPM */ @@ -260,8 +279,6 @@ class MPMBase : public MPM { std::string stress_update_{"usf"}; //! Stress update scheme std::shared_ptr> mpm_scheme_{nullptr}; - //! Interface scheme - std::shared_ptr> contact_{nullptr}; //! Velocity update method mpm::VelocityUpdate velocity_update_{mpm::VelocityUpdate::FLIP}; //! FLIP-PIC blending ratio @@ -295,6 +312,18 @@ class MPMBase : public MPM { //! Boolean to update deformation gradient bool update_defgrad_{false}; + /** + * \defgroup Interface Variables (includes multimaterial and levelset) + * @{ + */ + //! Interface scheme + std::shared_ptr> contact_{nullptr}; + //! Interface bool + bool interface_{false}; + //! Interface type + std::string interface_type_{"none"}; + /**@}*/ + /** * \defgroup Nonlocal Variables for nonlocal MPM * @{ diff --git a/include/solvers/mpm_base.tcc b/include/solvers/mpm_base.tcc index 2292e1b0f..c73efd9f5 100644 --- a/include/solvers/mpm_base.tcc +++ b/include/solvers/mpm_base.tcc @@ -13,12 +13,17 @@ mpm::MPMBase::MPMBase(const std::shared_ptr& io) : mpm::MPM(io) { // Set mesh as isoparametric bool isoparametric = is_isoparametric(); - mesh_ = std::make_shared>(id, isoparametric); + // Construct mesh, use levelset mesh if levelset active + if (is_levelset()) { + mesh_ = std::make_shared>(id, isoparametric); + } else { + mesh_ = std::make_shared>(id, isoparametric); + } // Create constraints constraints_ = std::make_shared>(mesh_); - // Empty all materials + // Empty all materSials materials_.clear(); // Variable list @@ -291,6 +296,9 @@ void mpm::MPMBase::initialise_mesh() { // Read and assign absorbing constraintes this->nodal_absorbing_constraints(mesh_props, mesh_io); + // Read and assign interface (includes multimaterial and levelset) + this->interface_inputs(mesh_props, mesh_io); + // Initialise cell auto cells_begin = std::chrono::steady_clock::now(); // Shape function name @@ -763,6 +771,34 @@ bool mpm::MPMBase::is_isoparametric() { return isoparametric; } +//! Return if interface and levelset are active +template +bool mpm::MPMBase::is_levelset() { + bool levelset_active = true; + + try { + const auto mesh_props = io_->json_object("mesh"); + if (mesh_props.find("interface") != mesh_props.end()) { + this->interface_ = true; + this->interface_type_ = + mesh_props["interface"]["interface_type"].template get(); + if (interface_type_ != "levelset") { + levelset_active = false; + throw std::runtime_error("Interface type is not levelset in JSON"); + } + } else { + levelset_active = false; + throw std::runtime_error("Interface is not found in JSON"); + } + + } catch (std::exception& exception) { + console_->warn("{} #{}: Levelset interface is not active in mesh {}", + __FILE__, __LINE__, exception.what()); + } + + return levelset_active; +} + //! Initialise loads template void mpm::MPMBase::initialise_loads() { @@ -1239,6 +1275,50 @@ void mpm::MPMBase::nodal_adhesional_constraints( } } +// Interface inputs (includes multimaterial and levelset) +template +void mpm::MPMBase::interface_inputs( + const Json& mesh_props, const std::shared_ptr>& mesh_io) { + try { + // Read and assign interface inputs + if (mesh_props.find("interface") != mesh_props.end()) { + this->interface_ = true; + // Get interface type + this->interface_type_ = + mesh_props["interface"]["interface_type"].template get(); + if (interface_type_ == "levelset") { + // Check if levelset inputs are specified in a file + if (mesh_props["interface"].find("location") != + mesh_props["interface"].end()) { + // Retrieve the file name + std::string levelset_input_file = + mesh_props["interface"]["location"].template get(); + // Retrieve levelset info from file + bool levelset_info = + mesh_->assign_nodal_levelset_values(mesh_io->read_levelset_input( + io_->file_name(levelset_input_file))); + if (!levelset_info) { + throw std::runtime_error( + "Levelset inputs are not properly assigned"); + } + } else { + throw std::runtime_error("Levelset inputs JSON not found"); + } + } else if (interface_type_ == "multimaterial") { + throw std::runtime_error( + "Multimaterial interface inputs not supported"); + } else { + throw std::runtime_error("Interface type not supported"); + } + } else { + throw std::runtime_error("Interface inputs JSON not found"); + } + } catch (std::exception& exception) { + console_->warn("#{}: Interface conditions are undefined {} ", __LINE__, + exception.what()); + } +} + // Nodal pressure constraints template void mpm::MPMBase::nodal_pressure_constraints( @@ -1551,7 +1631,7 @@ void mpm::MPMBase::particles_pore_pressures( if (!io_->file_name(fparticles_pore_pressures).empty()) { // Read and assign particles pore pressures if (!mesh_->assign_particles_pore_pressures( - particle_io->read_particles_scalar_properties( + particle_io->read_scalar_properties( io_->file_name(fparticles_pore_pressures)))) throw std::runtime_error( "Particles pore pressures are not properly assigned"); diff --git a/include/solvers/mpm_explicit.h b/include/solvers/mpm_explicit.h index efd2768eb..49645d41a 100644 --- a/include/solvers/mpm_explicit.h +++ b/include/solvers/mpm_explicit.h @@ -7,6 +7,8 @@ #include "mpm_base.h" +#include + namespace mpm { //! MPMExplicit class @@ -82,12 +84,19 @@ class MPMExplicit : public MPMBase { //! Update deformation gradient using mpm::MPMBase::update_defgrad_; + /** + * \defgroup Interface Variables (includes multimaterial and levelset) + * @{ + */ + //! Interface boolean + using mpm::MPMBase::interface_; + //! Interface type + using mpm::MPMBase::interface_type_; + /**@}*/ + private: //! Pressure smoothing bool pressure_smoothing_{false}; - //! Interface - bool interface_{false}; - }; // MPMExplicit class } // namespace mpm diff --git a/include/solvers/mpm_explicit.tcc b/include/solvers/mpm_explicit.tcc index 956604451..08ba16422 100644 --- a/include/solvers/mpm_explicit.tcc +++ b/include/solvers/mpm_explicit.tcc @@ -12,9 +12,13 @@ mpm::MPMExplicit::MPMExplicit(const std::shared_ptr& io) else mpm_scheme_ = std::make_shared>(mesh_, dt_); - //! Interface scheme if (this->interface_) - contact_ = std::make_shared>(mesh_); + if (this->interface_type_ == "multimaterial") + contact_ = std::make_shared>(mesh_); + else if (this->interface_type_ == "levelset") + contact_ = std::make_shared>(mesh_); + else // default is "none" + contact_ = std::make_shared>(mesh_); else contact_ = std::make_shared>(mesh_); } @@ -55,9 +59,6 @@ bool mpm::MPMExplicit::solve() { // Pressure smoothing pressure_smoothing_ = io_->analysis_bool("pressure_smoothing"); - // Interface - interface_ = io_->analysis_bool("interface"); - // Initialise material this->initialise_materials(); @@ -127,14 +128,14 @@ bool mpm::MPMExplicit::solve() { // Initialise nodes, cells and shape functions mpm_scheme_->initialise(); - // Initialise nodal properties and append material ids to node - contact_->initialise(); + // Initialise nodal properties + contact_->initialise(); // LEDT check // Mass momentum and compute velocity at nodes mpm_scheme_->compute_nodal_kinematics(velocity_update_, phase); - // Map material properties to nodes - contact_->compute_contact_forces(); + // Contact forces at nodes + contact_->compute_contact_forces(dt_); // Update stress first mpm_scheme_->precompute_stress_strain(phase, pressure_smoothing_, diff --git a/src/node.cc b/src/node.cc index ea93ebb86..dd0e82b98 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1,6 +1,7 @@ #include "node.h" #include "factory.h" #include "node_base.h" +#include "node_levelset.h" // Node2D (2 DoF, 1 Phase) static Register, mpm::Node<2, 2, 1>, mpm::Index, @@ -20,4 +21,14 @@ static Register, mpm::Node<2, 2, 2>, mpm::Index, // Node3D (3 DoF, 2 Phase) static Register, mpm::Node<3, 3, 2>, mpm::Index, const Eigen::Matrix&> - node3d2phase("N3D2P"); \ No newline at end of file + node3d2phase("N3D2P"); + +// Node2D (2 DoF, 1 Phase) +static Register, mpm::NodeLevelset<2, 2, 1>, mpm::Index, + const Eigen::Matrix&> + node2dlevelset("N2DLS"); + +// Node3D (3 DoF, 1 Phase) +static Register, mpm::NodeLevelset<3, 3, 1>, mpm::Index, + const Eigen::Matrix&> + node3dlevelset("N3DLS"); \ No newline at end of file diff --git a/src/particle.cc b/src/particle.cc index 3b2878735..49005b100 100644 --- a/src/particle.cc +++ b/src/particle.cc @@ -4,6 +4,7 @@ #include "particle_bbar.h" #include "particle_finite_strain.h" #include "particle_fluid.h" +#include "particle_levelset.h" #include "particle_twophase.h" namespace mpm { @@ -11,11 +12,11 @@ namespace mpm { std::map ParticleType = { {"P2D", 0}, {"P3D", 1}, {"P2DFLUID", 2}, {"P3DFLUID", 3}, {"P2D2PHASE", 4}, {"P3D2PHASE", 5}, {"P2DBBAR", 6}, {"P3DBBAR", 7}, - {"P2DFS", 8}, {"P3DFS", 9}}; + {"P2DFS", 8}, {"P3DFS", 9}, {"P2DLS", 10}, {"P3DLS", 11}}; std::map ParticleTypeName = { {0, "P2D"}, {1, "P3D"}, {2, "P2DFLUID"}, {3, "P3DFLUID"}, {4, "P2D2PHASE"}, {5, "P3D2PHASE"}, {6, "P2DBBAR"}, {7, "P3DBBAR"}, - {8, "P2DFS"}, {9, "P3DFS"}}; + {8, "P2DFS"}, {9, "P3DFS"}, {10, "P2DLS"}, {11, "P3DLS"}}; std::map ParticlePODTypeName = { {"P2D", "particles"}, {"P3D", "particles"}, @@ -26,7 +27,9 @@ std::map ParticlePODTypeName = { {"P2DBBAR", "particles"}, {"P3DBBAR", "particles"}, {"P2DFS", "particles"}, - {"P3DFS", "particles"}}; + {"P3DFS", "particles"}, + {"P2DLS", "particles"}, + {"P3DLS", "particles"}}; } // namespace mpm // Particle2D (2 Dim) @@ -77,4 +80,14 @@ static Register, mpm::ParticleFiniteStrain<2>, mpm::Index, // Particle3D with finite strain formulation (3 Dim) static Register, mpm::ParticleFiniteStrain<3>, mpm::Index, const Eigen::Matrix&> - particle3dfinitestrain("P3DFS"); \ No newline at end of file + particle3dfinitestrain("P3DFS"); + +// Particle2D with Levelset (2 Dim) +static Register, mpm::ParticleLevelset<2>, mpm::Index, + const Eigen::Matrix&> + particle2dlevelset("P2DLS"); + +// Particle3D with Levelset (3 Dim) +static Register, mpm::ParticleLevelset<3>, mpm::Index, + const Eigen::Matrix&> + particle3dlevelset("P3DLS"); \ No newline at end of file diff --git a/tests/io/io_mesh_ascii_test.cc b/tests/io/io_mesh_ascii_test.cc index 58356a53c..dc9c44d6a 100644 --- a/tests/io/io_mesh_ascii_test.cc +++ b/tests/io/io_mesh_ascii_test.cc @@ -843,14 +843,14 @@ TEST_CASE("IOMeshAscii is checked for 2D", "[IOMesh][IOMeshAscii][2D]") { auto io_mesh = std::make_unique>(); // Try to read particles scalar properties from a non-existant file - auto read_particles_scalars = io_mesh->read_particles_scalar_properties( - "particles-scalar-missing.txt"); + auto read_particles_scalars = + io_mesh->read_scalar_properties("particles-scalar-missing.txt"); // Check number of particles scalar properties REQUIRE(read_particles_scalars.size() == 0); // Check particles scalar properties read_particles_scalars = - io_mesh->read_particles_scalar_properties("particles-scalars-2d.txt"); + io_mesh->read_scalar_properties("particles-scalars-2d.txt"); // Check number of particles REQUIRE(read_particles_scalars.size() == particles_scalars.size()); @@ -1792,14 +1792,14 @@ TEST_CASE("IOMeshAscii is checked for 3D", "[IOMesh][IOMeshAscii][3D]") { auto io_mesh = std::make_unique>(); // Try to read particles scalar properties from a non-existant file - auto read_particles_scalars = io_mesh->read_particles_scalar_properties( - "particles-scalar-missing.txt"); + auto read_particles_scalars = + io_mesh->read_scalar_properties("particles-scalar-missing.txt"); // Check number of particles scalar properties REQUIRE(read_particles_scalars.size() == 0); // Check particles scalar properties read_particles_scalars = - io_mesh->read_particles_scalar_properties("particles-scalars-3d.txt"); + io_mesh->read_scalar_properties("particles-scalars-3d.txt"); // Check number of particles REQUIRE(read_particles_scalars.size() == particles_scalars.size());