From f3f6fb29e812f4ed26f9ac51774371a3ac9492d1 Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Mon, 29 Jun 2020 12:43:10 -0500 Subject: [PATCH 01/55] :computer: Refactor particle to handle scalar and vector properties --- include/node.h | 34 ++++++++++++ include/node.tcc | 48 +++++++++++++++++ include/node_base.h | 30 +++++++++++ include/particles/particle.h | 9 ++-- include/particles/particle.tcc | 3 ++ include/particles/particle_base.h | 55 ++++++++++++++++++++ include/particles/particle_base.tcc | 81 +++++++++++++++++++++++++++++ 7 files changed, 257 insertions(+), 3 deletions(-) diff --git a/include/node.h b/include/node.h index c1e17e531..019f663d1 100644 --- a/include/node.h +++ b/include/node.h @@ -65,6 +65,33 @@ class Node : public NodeBase { //! Return status bool status() const override { return status_; } + //! Update scalar property at the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Property value from the particles in a cell + void update_scalar_property(mpm::properties::Scalar property, bool update, + unsigned phase, double value) noexcept override; + + //! Return property at a given node for a given phase + //! \param[in] phase Index corresponding to the phase + double scalar_property(mpm::properties::Scalar property, + unsigned phase) const override; + + //! Update vector property at the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Property value from the particles in a cell + virtual void update_vector_property( + mpm::properties::Vector property, bool update, unsigned phase, + const Eigen::Matrix& value) noexcept override; + + //! Return property at a given node for a given phase + //! \param[in] phase Index corresponding to the phase + virtual Eigen::Matrix vector_property( + mpm::properties::Vector property, unsigned phase) const override; + //! Update mass at the nodes from particle //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase @@ -275,6 +302,13 @@ class Node : public NodeBase { unsigned dof_{std::numeric_limits::max()}; //! Status bool status_{false}; + //! Scalar properties + tsl::ordered_map> + scalar_properties_; + //! Vector properties + tsl::ordered_map> + vector_properties_; //! Mass Eigen::Matrix mass_; //! Volume diff --git a/include/node.tcc b/include/node.tcc index 8c4d4b542..e4ab6700e 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -17,6 +17,10 @@ mpm::Node::Node( // Clear any velocity constraints velocity_constraints_.clear(); concentrated_force_.setZero(); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::Mass, + Eigen::Matrix::Zero())); + this->initialise(); } @@ -34,6 +38,7 @@ void mpm::Node::initialise() noexcept { acceleration_.setZero(); status_ = false; material_ids_.clear(); + scalar_properties_.at(mpm::properties::Scalar::Mass).setZero(); } //! Initialise shared pointer to nodal properties pool @@ -46,6 +51,49 @@ void mpm::Node::initialise_property_handle( this->prop_id_ = prop_id; } +//! Update scalar property at the nodes from particle +template +void mpm::Node::update_scalar_property( + mpm::properties::Scalar property, bool update, unsigned phase, + double value) noexcept { + // Decide to update or assign + const double factor = (update == true) ? 1. : 0.; + + // Update/assign mass + std::lock_guard guard(node_mutex_); + scalar_properties_.at(property)[phase] = + (scalar_properties_.at(property)[phase] * factor) + value; +} + +//! Update scalar property at the nodes from particle +template +double mpm::Node::scalar_property( + mpm::properties::Scalar property, unsigned phase) const { + return scalar_properties_.at(property)[phase]; +} + +//! Update vector property at the nodes from particle +template +void mpm::Node::update_vector_property( + mpm::properties::Vector property, bool update, unsigned phase, + const Eigen::Matrix& value) noexcept { + // Decide to update or assign + const double factor = (update == true) ? 1. : 0.; + + // Update/assign mass + std::lock_guard guard(node_mutex_); + Eigen::Matrix vecvalue = + vector_properties_.at(property).col(phase); + vector_properties_.at(property).col(phase) = (vecvalue * factor) + value; +} + +//! Update vector property at the nodes from particle +template +Eigen::Matrix mpm::Node::vector_property( + mpm::properties::Vector property, unsigned phase) const { + return vector_properties_.at(property).col(phase); +} + //! Update mass at the nodes from particle template void mpm::Node::update_mass(bool update, unsigned phase, diff --git a/include/node_base.h b/include/node_base.h index 81dfc62f4..636234703 100644 --- a/include/node_base.h +++ b/include/node_base.h @@ -10,9 +10,11 @@ #include #include +#include #include "data_types.h" #include "function_base.h" +#include "mpm_properties.h" #include "nodal_properties.h" namespace mpm { @@ -70,6 +72,34 @@ class NodeBase { //! Return status virtual bool status() const = 0; + //! Update scalar property at the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Property value from the particles in a cell + virtual void update_scalar_property(mpm::properties::Scalar property, + bool update, unsigned phase, + double value) noexcept = 0; + + //! Return property at a given node for a given phase + //! \param[in] phase Index corresponding to the phase + virtual double scalar_property(mpm::properties::Scalar property, + unsigned phase) const = 0; + + //! Update vector property at the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Property value from the particles in a cell + virtual void update_vector_property( + mpm::properties::Vector property, bool update, unsigned phase, + const Eigen::Matrix& value) noexcept = 0; + + //! Return property at a given node for a given phase + //! \param[in] phase Index corresponding to the phase + virtual Eigen::Matrix vector_property( + mpm::properties::Vector property, unsigned phase) const = 0; + //! Update mass at the nodes from particle //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase diff --git a/include/particles/particle.h b/include/particles/particle.h index 4d7a4b918..542764001 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -131,7 +131,6 @@ class Particle : public ParticleBase { //! Update volume based on centre volumetric strain rate void update_volume() noexcept override; - //! Return mass density //! \param[in] phase Index corresponding to the phase double mass_density() const override { return mass_density_; } @@ -307,6 +306,12 @@ class Particle : public ParticleBase { using ParticleBase::state_variables_; //! Neighbour particles using ParticleBase::neighbours_; + //! Scalar properties + using ParticleBase::scalar_properties_; + //! Vector properties + using ParticleBase::vector_properties_; + //! Shape functions + using ParticleBase::shapefn_; //! Volumetric mass density (mass / volume) double mass_density_{0.}; //! Mass @@ -339,8 +344,6 @@ class Particle : public ParticleBase { bool set_traction_{false}; //! Surface Traction (given as a stress; force/area) Eigen::Matrix traction_; - //! Shape functions - Eigen::VectorXd shapefn_; //! dN/dX Eigen::MatrixXd dn_dx_; //! dN/dX at cell centroid diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index b35f2c1a2..2115d59f4 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -9,6 +9,9 @@ mpm::Particle::Particle(Index id, const VectorDim& coord) nodes_.clear(); // Set material pointer to null material_ = nullptr; + // Scalar properties + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::Mass, double(0.))); // Logger std::string logger = "particle" + std::to_string(Tdim) + "d::" + std::to_string(id); diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index ab60f8fb5..32afefd8b 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -130,6 +130,54 @@ class ParticleBase { //! Update volume based on centre volumetric strain rate virtual void update_volume() noexcept = 0; + //! Update scalar property at the particle + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] value Property value from the particles in a cell + void update_scalar_property(mpm::properties::Scalar property, bool update, + double value) noexcept; + + //! Return property + //! \param[in] phase Index corresponding to the phase + double scalar_property(mpm::properties::Scalar property) const; + + //! Map scalar property to the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + void map_scalar_property_nodes(mpm::properties::Scalar property, bool update, + unsigned phase) noexcept; + + //! Return property at a given node for a given phase + //! \param[in] phase Index corresponding to the phase + double interpolate_scalar_property_nodes(mpm::properties::Scalar property, + unsigned phase) const; + + //! Update vector property at the particle + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] value Property value from the particles in a cell + void update_vector_property( + mpm::properties::Vector property, bool update, + const Eigen::Matrix& value) noexcept; + + //! Return property + //! \param[in] phase Index corresponding to the phase + Eigen::Matrix vector_property( + mpm::properties::Vector property) const; + + //! Map vector property to the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + void map_vector_property_nodes(mpm::properties::Vector property, bool update, + unsigned phase) noexcept; + + //! Return property at a given node for a given phase + //! \param[in] phase Index corresponding to the phase + double interpolate_vector_property_nodes(mpm::properties::Vector property, + unsigned phase) const; + //! Return mass density virtual double mass_density() const = 0; @@ -279,6 +327,13 @@ class ParticleBase { mpm::dense_map state_variables_; //! Vector of particle neighbour ids std::vector neighbours_; + //! Shape functions + Eigen::VectorXd shapefn_; + //! Scalar properties + tsl::ordered_map scalar_properties_; + //! Vector properties + tsl::ordered_map> + vector_properties_; }; // ParticleBase class } // namespace mpm diff --git a/include/particles/particle_base.tcc b/include/particles/particle_base.tcc index a75e8ca0b..58f52f426 100644 --- a/include/particles/particle_base.tcc +++ b/include/particles/particle_base.tcc @@ -15,3 +15,84 @@ mpm::ParticleBase::ParticleBase(Index id, const VectorDim& coord, : mpm::ParticleBase::ParticleBase(id, coord) { status_ = status; } + +//! Update scalar property at particle +template +void mpm::ParticleBase::update_scalar_property( + mpm::properties::Scalar property, bool update, double value) noexcept { + // Decide to update or assign + const double factor = (update == true) ? 1. : 0.; + scalar_properties_.at(property) = + scalar_properties_.at(property) * factor + value; +} + +//! Update scalar property at particle +template +double mpm::ParticleBase::scalar_property( + mpm::properties::Scalar property) const { + return scalar_properties_.at(property); +} + +//! Map scalar property to nodes +template +void mpm::ParticleBase::map_scalar_property_nodes( + mpm::properties::Scalar property, bool update, unsigned phase) noexcept { + // Check if particle property is set + assert(scalar_properties_.at(property) != std::numeric_limits::max()); + + // Map scalar property to nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + nodes_[i]->update_scalar_property( + property, update, phase, scalar_properties_.at(property) * shapefn_[i]); +} + +//! Interpolate scalar property from nodes +template +double mpm::ParticleBase::interpolate_scalar_property_nodes( + mpm::properties::Scalar property, unsigned phase) const { + double value = 0.; + // Interpolate scalar property from nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + value += nodes_[i]->scalar_property(property, phase) * shapefn_[i]; + return value; +} + +//! Update vector property at particle +template +void mpm::ParticleBase::update_vector_property( + mpm::properties::Vector property, bool update, + const Eigen::Matrix& value) noexcept { + // Decide to update or assign + const double factor = (update == true) ? 1. : 0.; + vector_properties_.at(property) = + vector_properties_.at(property) * factor + value; +} + +//! Update vector property at particle +template +Eigen::Matrix mpm::ParticleBase::vector_property( + mpm::properties::Vector property) const { + return vector_properties_.at(property); +} + +//! Map vector property to nodes +template +void mpm::ParticleBase::map_vector_property_nodes( + mpm::properties::Vector property, bool update, unsigned phase) noexcept { + // TODO: Check assert needed? + // Map vector property to nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + nodes_[i]->update_vector_property( + property, update, phase, vector_properties_.at(property) * shapefn_[i]); +} + +//! Interpolate vector property from nodes +template +double mpm::ParticleBase::interpolate_vector_property_nodes( + mpm::properties::Vector property, unsigned phase) const { + Eigen::Matrix value = Eigen::Matrix::Zero(); + // Interpolate vector property from nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + value += nodes_[i]->vector_property(property, phase) * shapefn_[i]; + return value; +} From ad09dfaa2cb1e614062692d429e08d12f0bf099c Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Mon, 29 Jun 2020 12:47:31 -0500 Subject: [PATCH 02/55] :dart: Add testing to particle refactor of scalar properties --- external/tsl/ordered_hash.h | 1617 +++++++++++++++++++++++++++++++++++ external/tsl/ordered_map.h | 833 ++++++++++++++++++ external/tsl/ordered_set.h | 688 +++++++++++++++ include/mpm_properties.h | 13 + tests/particle_test.cc | 24 + 5 files changed, 3175 insertions(+) create mode 100644 external/tsl/ordered_hash.h create mode 100644 external/tsl/ordered_map.h create mode 100644 external/tsl/ordered_set.h create mode 100644 include/mpm_properties.h diff --git a/external/tsl/ordered_hash.h b/external/tsl/ordered_hash.h new file mode 100644 index 000000000..46d9b4877 --- /dev/null +++ b/external/tsl/ordered_hash.h @@ -0,0 +1,1617 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ORDERED_HASH_H +#define TSL_ORDERED_HASH_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * Macros for compatibility with GCC 4.8 + */ +#if (defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 9)) +# define TSL_OH_NO_CONTAINER_ERASE_CONST_ITERATOR +# define TSL_OH_NO_CONTAINER_EMPLACE_CONST_ITERATOR +#endif + +/** + * Only activate tsl_oh_assert if TSL_DEBUG is defined. + * This way we avoid the performance hit when NDEBUG is not defined with assert as tsl_oh_assert is used a lot + * (people usually compile with "-O3" and not "-O3 -DNDEBUG"). + */ +#ifdef TSL_DEBUG +# define tsl_oh_assert(expr) assert(expr) +#else +# define tsl_oh_assert(expr) (static_cast(0)) +#endif + +/** + * If exceptions are enabled, throw the exception passed in parameter, otherwise call std::terminate. + */ +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (defined (_MSC_VER) && defined (_CPPUNWIND))) && !defined(TSL_NO_EXCEPTIONS) +# define TSL_OH_THROW_OR_TERMINATE(ex, msg) throw ex(msg) +#else +# define TSL_OH_NO_EXCEPTIONS +# ifdef NDEBUG +# define TSL_OH_THROW_OR_TERMINATE(ex, msg) std::terminate() +# else +# include +# define TSL_OH_THROW_OR_TERMINATE(ex, msg) do { std::cerr << msg << std::endl; std::terminate(); } while(0) +# endif +#endif + + +namespace tsl { + +namespace detail_ordered_hash { + +template +struct make_void { + using type = void; +}; + +template +struct has_is_transparent: std::false_type { +}; + +template +struct has_is_transparent::type>: std::true_type { +}; + + +template +struct is_vector: std::false_type { +}; + +template +struct is_vector>::value + >::type>: std::true_type { +}; + +// Only available in C++17, we need to be compatible with C++11 +template +const T& clamp( const T& v, const T& lo, const T& hi) { + return std::min(hi, std::max(lo, v)); +} + +template +static T numeric_cast(U value, const char* error_message = "numeric_cast() failed.") { + T ret = static_cast(value); + if(static_cast(ret) != value) { + TSL_OH_THROW_OR_TERMINATE(std::runtime_error, error_message); + } + + const bool is_same_signedness = (std::is_unsigned::value && std::is_unsigned::value) || + (std::is_signed::value && std::is_signed::value); + if(!is_same_signedness && (ret < T{}) != (value < U{})) { + TSL_OH_THROW_OR_TERMINATE(std::runtime_error, error_message); + } + + return ret; +} + + +/** + * Fixed size type used to represent size_type values on serialization. Need to be big enough + * to represent a std::size_t on 32 and 64 bits platforms, and must be the same size on both platforms. + */ +using slz_size_type = std::uint64_t; +static_assert(std::numeric_limits::max() >= std::numeric_limits::max(), + "slz_size_type must be >= std::size_t"); + +template +static T deserialize_value(Deserializer& deserializer) { + // MSVC < 2017 is not conformant, circumvent the problem by removing the template keyword +#if defined (_MSC_VER) && _MSC_VER < 1910 + return deserializer.Deserializer::operator()(); +#else + return deserializer.Deserializer::template operator()(); +#endif +} + + +/** + * Each bucket entry stores an index which is the index in m_values corresponding to the bucket's value + * and a hash (which may be truncated to 32 bits depending on IndexType) corresponding to the hash of the value. + * + * The size of IndexType limits the size of the hash table to std::numeric_limits::max() - 1 elements (-1 due to + * a reserved value used to mark a bucket as empty). + */ +template +class bucket_entry { + static_assert(std::is_unsigned::value, "IndexType must be an unsigned value."); + static_assert(std::numeric_limits::max() <= std::numeric_limits::max(), + "std::numeric_limits::max() must be <= std::numeric_limits::max()."); + +public: + using index_type = IndexType; + using truncated_hash_type = typename std::conditional::max() <= + std::numeric_limits::max(), + std::uint_least32_t, + std::size_t>::type; + + bucket_entry() noexcept: m_index(EMPTY_MARKER_INDEX), m_hash(0) { + } + + bool empty() const noexcept { + return m_index == EMPTY_MARKER_INDEX; + } + + void clear() noexcept { + m_index = EMPTY_MARKER_INDEX; + } + + index_type index() const noexcept { + tsl_oh_assert(!empty()); + return m_index; + } + + index_type& index_ref() noexcept { + tsl_oh_assert(!empty()); + return m_index; + } + + void set_index(index_type index) noexcept { + tsl_oh_assert(index <= max_size()); + + m_index = index; + } + + truncated_hash_type truncated_hash() const noexcept { + tsl_oh_assert(!empty()); + return m_hash; + } + + truncated_hash_type& truncated_hash_ref() noexcept { + tsl_oh_assert(!empty()); + return m_hash; + } + + void set_hash(std::size_t hash) noexcept { + m_hash = truncate_hash(hash); + } + + template + void serialize(Serializer& serializer) const { + const slz_size_type index = m_index; + serializer(index); + + const slz_size_type hash = m_hash; + serializer(hash); + } + + template + static bucket_entry deserialize(Deserializer& deserializer) { + const slz_size_type index = deserialize_value(deserializer); + const slz_size_type hash = deserialize_value(deserializer); + + bucket_entry bentry; + bentry.m_index = numeric_cast(index, "Deserialized index is too big."); + bentry.m_hash = numeric_cast(hash, "Deserialized hash is too big."); + + return bentry; + } + + + + static truncated_hash_type truncate_hash(std::size_t hash) noexcept { + return truncated_hash_type(hash); + } + + static std::size_t max_size() noexcept { + return static_cast(std::numeric_limits::max()) - NB_RESERVED_INDEXES; + } + +private: + static const index_type EMPTY_MARKER_INDEX = std::numeric_limits::max(); + static const std::size_t NB_RESERVED_INDEXES = 1; + + index_type m_index; + truncated_hash_type m_hash; +}; + + + +/** + * Internal common class used by ordered_map and ordered_set. + * + * ValueType is what will be stored by ordered_hash (usually std::pair for map and Key for set). + * + * KeySelect should be a FunctionObject which takes a ValueType in parameter and return a reference to the key. + * + * ValueSelect should be a FunctionObject which takes a ValueType in parameter and return a reference to the value. + * ValueSelect should be void if there is no value (in set for example). + * + * ValueTypeContainer is the container which will be used to store ValueType values. + * Usually a std::deque or std::vector. + * + * + * + * The ordered_hash structure is a hash table which preserves the order of insertion of the elements. + * To do so, it stores the values in the ValueTypeContainer (m_values) using emplace_back at each + * insertion of a new element. Another structure (m_buckets of type std::vector) will + * serve as buckets array for the hash table part. Each bucket stores an index which corresponds to + * the index in m_values where the bucket's value is and the (truncated) hash of this value. An index + * is used instead of a pointer to the value to reduce the size of each bucket entry. + * + * To resolve collisions in the buckets array, the structures use robin hood linear probing with + * backward shift deletion. + */ +template +class ordered_hash: private Hash, private KeyEqual { +private: + template + using has_mapped_type = typename std::integral_constant::value>; + + static_assert(std::is_same::value, + "ValueTypeContainer::value_type != ValueType. " + "Check that the ValueTypeContainer has 'Key' as type for a set or 'std::pair' as type for a map."); + + static_assert(std::is_same::value, + "ValueTypeContainer::allocator_type != Allocator. " + "Check that the allocator for ValueTypeContainer is the same as Allocator."); + + static_assert(std::is_same::value, + "Allocator::value_type != ValueType. " + "Check that the allocator has 'Key' as type for a set or 'std::pair' as type for a map."); + + +public: + template + class ordered_iterator; + + using key_type = typename KeySelect::key_type; + using value_type = ValueType; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using hasher = Hash; + using key_equal = KeyEqual; + using allocator_type = Allocator; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = ordered_iterator; + using const_iterator = ordered_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + using values_container_type = ValueTypeContainer; + +public: + template + class ordered_iterator { + friend class ordered_hash; + + private: + using iterator = typename std::conditional::type; + + + ordered_iterator(iterator it) noexcept: m_iterator(it) { + } + + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = const typename ordered_hash::value_type; + using difference_type = typename iterator::difference_type; + using reference = value_type&; + using pointer = value_type*; + + + ordered_iterator() noexcept { + } + + // Copy constructor from iterator to const_iterator. + template::type* = nullptr> + ordered_iterator(const ordered_iterator& other) noexcept: m_iterator(other.m_iterator) { + } + + ordered_iterator(const ordered_iterator& other) = default; + ordered_iterator(ordered_iterator&& other) = default; + ordered_iterator& operator=(const ordered_iterator& other) = default; + ordered_iterator& operator=(ordered_iterator&& other) = default; + + const typename ordered_hash::key_type& key() const { + return KeySelect()(*m_iterator); + } + + template::value && IsConst>::type* = nullptr> + const typename U::value_type& value() const { + return U()(*m_iterator); + } + + template::value && !IsConst>::type* = nullptr> + typename U::value_type& value() { + return U()(*m_iterator); + } + + reference operator*() const { return *m_iterator; } + pointer operator->() const { return m_iterator.operator->(); } + + ordered_iterator& operator++() { ++m_iterator; return *this; } + ordered_iterator& operator--() { --m_iterator; return *this; } + + ordered_iterator operator++(int) { ordered_iterator tmp(*this); ++(*this); return tmp; } + ordered_iterator operator--(int) { ordered_iterator tmp(*this); --(*this); return tmp; } + + reference operator[](difference_type n) const { return m_iterator[n]; } + + ordered_iterator& operator+=(difference_type n) { m_iterator += n; return *this; } + ordered_iterator& operator-=(difference_type n) { m_iterator -= n; return *this; } + + ordered_iterator operator+(difference_type n) { ordered_iterator tmp(*this); tmp += n; return tmp; } + ordered_iterator operator-(difference_type n) { ordered_iterator tmp(*this); tmp -= n; return tmp; } + + friend bool operator==(const ordered_iterator& lhs, const ordered_iterator& rhs) { + return lhs.m_iterator == rhs.m_iterator; + } + + friend bool operator!=(const ordered_iterator& lhs, const ordered_iterator& rhs) { + return lhs.m_iterator != rhs.m_iterator; + } + + friend bool operator<(const ordered_iterator& lhs, const ordered_iterator& rhs) { + return lhs.m_iterator < rhs.m_iterator; + } + + friend bool operator>(const ordered_iterator& lhs, const ordered_iterator& rhs) { + return lhs.m_iterator > rhs.m_iterator; + } + + friend bool operator<=(const ordered_iterator& lhs, const ordered_iterator& rhs) { + return lhs.m_iterator <= rhs.m_iterator; + } + + friend bool operator>=(const ordered_iterator& lhs, const ordered_iterator& rhs) { + return lhs.m_iterator >= rhs.m_iterator; + } + + friend ordered_iterator operator+(difference_type n, const ordered_iterator& it) { + return n + it.m_iterator; + } + + friend difference_type operator-(const ordered_iterator& lhs, const ordered_iterator& rhs) { + return lhs.m_iterator - rhs.m_iterator; + } + + private: + iterator m_iterator; + }; + + +private: + using bucket_entry = tsl::detail_ordered_hash::bucket_entry; + + using buckets_container_allocator = typename + std::allocator_traits::template rebind_alloc; + + using buckets_container_type = std::vector; + + + using truncated_hash_type = typename bucket_entry::truncated_hash_type; + using index_type = typename bucket_entry::index_type; + +public: + ordered_hash(size_type bucket_count, + const Hash& hash, + const KeyEqual& equal, + const Allocator& alloc, + float max_load_factor): Hash(hash), + KeyEqual(equal), + m_buckets_data(alloc), + m_buckets(static_empty_bucket_ptr()), + m_hash_mask(0), + m_values(alloc), + m_grow_on_next_insert(false) + { + if(bucket_count > max_bucket_count()) { + TSL_OH_THROW_OR_TERMINATE(std::length_error, "The map exceeds its maximum size."); + } + + if(bucket_count > 0) { + bucket_count = round_up_to_power_of_two(bucket_count); + + m_buckets_data.resize(bucket_count); + m_buckets = m_buckets_data.data(), + m_hash_mask = bucket_count - 1; + } + + this->max_load_factor(max_load_factor); + } + + ordered_hash(const ordered_hash& other): Hash(other), + KeyEqual(other), + m_buckets_data(other.m_buckets_data), + m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr(): + m_buckets_data.data()), + m_hash_mask(other.m_hash_mask), + m_values(other.m_values), + m_load_threshold(other.m_load_threshold), + m_max_load_factor(other.m_max_load_factor), + m_grow_on_next_insert(other.m_grow_on_next_insert) + { + } + + ordered_hash(ordered_hash&& other) noexcept(std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value && + std::is_nothrow_move_constructible::value) + : Hash(std::move(static_cast(other))), + KeyEqual(std::move(static_cast(other))), + m_buckets_data(std::move(other.m_buckets_data)), + m_buckets(m_buckets_data.empty()?static_empty_bucket_ptr(): + m_buckets_data.data()), + m_hash_mask(other.m_hash_mask), + m_values(std::move(other.m_values)), + m_load_threshold(other.m_load_threshold), + m_max_load_factor(other.m_max_load_factor), + m_grow_on_next_insert(other.m_grow_on_next_insert) + { + other.m_buckets_data.clear(); + other.m_buckets = static_empty_bucket_ptr(); + other.m_hash_mask = 0; + other.m_values.clear(); + other.m_load_threshold = 0; + other.m_grow_on_next_insert = false; + } + + ordered_hash& operator=(const ordered_hash& other) { + if(&other != this) { + Hash::operator=(other); + KeyEqual::operator=(other); + + m_buckets_data = other.m_buckets_data; + m_buckets = m_buckets_data.empty()?static_empty_bucket_ptr(): + m_buckets_data.data(); + + m_hash_mask = other.m_hash_mask; + m_values = other.m_values; + m_load_threshold = other.m_load_threshold; + m_max_load_factor = other.m_max_load_factor; + m_grow_on_next_insert = other.m_grow_on_next_insert; + } + + return *this; + } + + ordered_hash& operator=(ordered_hash&& other) { + other.swap(*this); + other.clear(); + + return *this; + } + + allocator_type get_allocator() const { + return m_values.get_allocator(); + } + + + /* + * Iterators + */ + iterator begin() noexcept { + return iterator(m_values.begin()); + } + + const_iterator begin() const noexcept { + return cbegin(); + } + + const_iterator cbegin() const noexcept { + return const_iterator(m_values.cbegin()); + } + + iterator end() noexcept { + return iterator(m_values.end()); + } + + const_iterator end() const noexcept { + return cend(); + } + + const_iterator cend() const noexcept { + return const_iterator(m_values.cend()); + } + + + reverse_iterator rbegin() noexcept { + return reverse_iterator(m_values.end()); + } + + const_reverse_iterator rbegin() const noexcept { + return rcbegin(); + } + + const_reverse_iterator rcbegin() const noexcept { + return const_reverse_iterator(m_values.cend()); + } + + reverse_iterator rend() noexcept { + return reverse_iterator(m_values.begin()); + } + + const_reverse_iterator rend() const noexcept { + return rcend(); + } + + const_reverse_iterator rcend() const noexcept { + return const_reverse_iterator(m_values.cbegin()); + } + + + /* + * Capacity + */ + bool empty() const noexcept { + return m_values.empty(); + } + + size_type size() const noexcept { + return m_values.size(); + } + + size_type max_size() const noexcept { + return std::min(bucket_entry::max_size(), m_values.max_size()); + } + + + /* + * Modifiers + */ + void clear() noexcept { + for(auto& bucket: m_buckets_data) { + bucket.clear(); + } + + m_values.clear(); + m_grow_on_next_insert = false; + } + + template + std::pair insert(P&& value) { + return insert_impl(KeySelect()(value), std::forward

(value)); + } + + template + iterator insert_hint(const_iterator hint, P&& value) { + if(hint != cend() && compare_keys(KeySelect()(*hint), KeySelect()(value))) { + return mutable_iterator(hint); + } + + return insert(std::forward

(value)).first; + } + + template + void insert(InputIt first, InputIt last) { + if(std::is_base_of::iterator_category>::value) + { + const auto nb_elements_insert = std::distance(first, last); + const size_type nb_free_buckets = m_load_threshold - size(); + tsl_oh_assert(m_load_threshold >= size()); + + if(nb_elements_insert > 0 && nb_free_buckets < size_type(nb_elements_insert)) { + reserve(size() + size_type(nb_elements_insert)); + } + } + + for(; first != last; ++first) { + insert(*first); + } + } + + + + template + std::pair insert_or_assign(K&& key, M&& value) { + auto it = try_emplace(std::forward(key), std::forward(value)); + if(!it.second) { + it.first.value() = std::forward(value); + } + + return it; + } + + template + iterator insert_or_assign(const_iterator hint, K&& key, M&& obj) { + if(hint != cend() && compare_keys(KeySelect()(*hint), key)) { + auto it = mutable_iterator(hint); + it.value() = std::forward(obj); + + return it; + } + + return insert_or_assign(std::forward(key), std::forward(obj)).first; + } + + + + template + std::pair emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert_hint(hint, value_type(std::forward(args)...)); + } + + + + template + std::pair try_emplace(K&& key, Args&&... value_args) { + return insert_impl(key, std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(value_args)...)); + } + + template + iterator try_emplace_hint(const_iterator hint, K&& key, Args&&... args) { + if(hint != cend() && compare_keys(KeySelect()(*hint), key)) { + return mutable_iterator(hint); + } + + return try_emplace(std::forward(key), std::forward(args)...).first; + } + + + + /** + * Here to avoid `template size_type erase(const K& key)` being used when + * we use an `iterator` instead of a `const_iterator`. + */ + iterator erase(iterator pos) { + return erase(const_iterator(pos)); + } + + iterator erase(const_iterator pos) { + tsl_oh_assert(pos != cend()); + + const std::size_t index_erase = iterator_to_index(pos); + + auto it_bucket = find_key(pos.key(), hash_key(pos.key())); + tsl_oh_assert(it_bucket != m_buckets_data.end()); + + erase_value_from_bucket(it_bucket); + + /* + * One element was removed from m_values, due to the left shift the next element + * is now at the position of the previous element (or end if none). + */ + return begin() + index_erase; + } + + iterator erase(const_iterator first, const_iterator last) { + if(first == last) { + return mutable_iterator(first); + } + + tsl_oh_assert(std::distance(first, last) > 0); + const std::size_t start_index = iterator_to_index(first); + const std::size_t nb_values = std::size_t(std::distance(first, last)); + const std::size_t end_index = start_index + nb_values; + + // Delete all values +#ifdef TSL_OH_NO_CONTAINER_ERASE_CONST_ITERATOR + auto next_it = m_values.erase(mutable_iterator(first).m_iterator, mutable_iterator(last).m_iterator); +#else + auto next_it = m_values.erase(first.m_iterator, last.m_iterator); +#endif + + /* + * Mark the buckets corresponding to the values as empty and do a backward shift. + * + * Also, the erase operation on m_values has shifted all the values on the right of last.m_iterator. + * Adapt the indexes for these values. + */ + std::size_t ibucket = 0; + while(ibucket < m_buckets_data.size()) { + if(m_buckets[ibucket].empty()) { + ibucket++; + } + else if(m_buckets[ibucket].index() >= start_index && m_buckets[ibucket].index() < end_index) { + m_buckets[ibucket].clear(); + backward_shift(ibucket); + // Don't increment ibucket, backward_shift may have replaced current bucket. + } + else if(m_buckets[ibucket].index() >= end_index) { + m_buckets[ibucket].set_index(index_type(m_buckets[ibucket].index() - nb_values)); + ibucket++; + } + else { + ibucket++; + } + } + + return iterator(next_it); + } + + + template + size_type erase(const K& key) { + return erase(key, hash_key(key)); + } + + template + size_type erase(const K& key, std::size_t hash) { + return erase_impl(key, hash); + } + + void swap(ordered_hash& other) { + using std::swap; + + swap(static_cast(*this), static_cast(other)); + swap(static_cast(*this), static_cast(other)); + swap(m_buckets_data, other.m_buckets_data); + swap(m_buckets, other.m_buckets); + swap(m_hash_mask, other.m_hash_mask); + swap(m_values, other.m_values); + swap(m_load_threshold, other.m_load_threshold); + swap(m_max_load_factor, other.m_max_load_factor); + swap(m_grow_on_next_insert, other.m_grow_on_next_insert); + } + + + + + /* + * Lookup + */ + template::value>::type* = nullptr> + typename U::value_type& at(const K& key) { + return at(key, hash_key(key)); + } + + template::value>::type* = nullptr> + typename U::value_type& at(const K& key, std::size_t hash) { + return const_cast(static_cast(this)->at(key, hash)); + } + + template::value>::type* = nullptr> + const typename U::value_type& at(const K& key) const { + return at(key, hash_key(key)); + } + + template::value>::type* = nullptr> + const typename U::value_type& at(const K& key, std::size_t hash) const { + auto it = find(key, hash); + if(it != end()) { + return it.value(); + } + else { + TSL_OH_THROW_OR_TERMINATE(std::out_of_range, "Couldn't find the key."); + } + } + + + template::value>::type* = nullptr> + typename U::value_type& operator[](K&& key) { + return try_emplace(std::forward(key)).first.value(); + } + + + template + size_type count(const K& key) const { + return count(key, hash_key(key)); + } + + template + size_type count(const K& key, std::size_t hash) const { + if(find(key, hash) == cend()) { + return 0; + } + else { + return 1; + } + } + + template + iterator find(const K& key) { + return find(key, hash_key(key)); + } + + template + iterator find(const K& key, std::size_t hash) { + auto it_bucket = find_key(key, hash); + return (it_bucket != m_buckets_data.end())?iterator(m_values.begin() + it_bucket->index()):end(); + } + + template + const_iterator find(const K& key) const { + return find(key, hash_key(key)); + } + + template + const_iterator find(const K& key, std::size_t hash) const { + auto it_bucket = find_key(key, hash); + return (it_bucket != m_buckets_data.cend())?const_iterator(m_values.begin() + it_bucket->index()):end(); + } + + + template + std::pair equal_range(const K& key) { + return equal_range(key, hash_key(key)); + } + + template + std::pair equal_range(const K& key, std::size_t hash) { + iterator it = find(key, hash); + return std::make_pair(it, (it == end())?it:std::next(it)); + } + + template + std::pair equal_range(const K& key) const { + return equal_range(key, hash_key(key)); + } + + template + std::pair equal_range(const K& key, std::size_t hash) const { + const_iterator it = find(key, hash); + return std::make_pair(it, (it == cend())?it:std::next(it)); + } + + + /* + * Bucket interface + */ + size_type bucket_count() const { + return m_buckets_data.size(); + } + + size_type max_bucket_count() const { + return m_buckets_data.max_size(); + } + + /* + * Hash policy + */ + float load_factor() const { + if(bucket_count() == 0) { + return 0; + } + + return float(size())/float(bucket_count()); + } + + float max_load_factor() const { + return m_max_load_factor; + } + + void max_load_factor(float ml) { + m_max_load_factor = clamp(ml, float(MAX_LOAD_FACTOR__MINIMUM), + float(MAX_LOAD_FACTOR__MAXIMUM)); + + m_max_load_factor = ml; + m_load_threshold = size_type(float(bucket_count())*m_max_load_factor); + } + + void rehash(size_type count) { + count = std::max(count, size_type(std::ceil(float(size())/max_load_factor()))); + rehash_impl(count); + } + + void reserve(size_type count) { + reserve_space_for_values(count); + + count = size_type(std::ceil(float(count)/max_load_factor())); + rehash(count); + } + + + /* + * Observers + */ + hasher hash_function() const { + return static_cast(*this); + } + + key_equal key_eq() const { + return static_cast(*this); + } + + + /* + * Other + */ + iterator mutable_iterator(const_iterator pos) { + return iterator(m_values.begin() + iterator_to_index(pos)); + } + + iterator nth(size_type index) { + tsl_oh_assert(index <= size()); + return iterator(m_values.begin() + index); + } + + const_iterator nth(size_type index) const { + tsl_oh_assert(index <= size()); + return const_iterator(m_values.cbegin() + index); + } + + const_reference front() const { + tsl_oh_assert(!empty()); + return m_values.front(); + } + + const_reference back() const { + tsl_oh_assert(!empty()); + return m_values.back(); + } + + const values_container_type& values_container() const noexcept { + return m_values; + } + + template::value>::type* = nullptr> + const typename values_container_type::value_type* data() const noexcept { + return m_values.data(); + } + + template::value>::type* = nullptr> + size_type capacity() const noexcept { + return m_values.capacity(); + } + + void shrink_to_fit() { + m_values.shrink_to_fit(); + } + + + template + std::pair insert_at_position(const_iterator pos, P&& value) { + return insert_at_position_impl(pos.m_iterator, KeySelect()(value), std::forward

(value)); + } + + template + std::pair emplace_at_position(const_iterator pos, Args&&... args) { + return insert_at_position(pos, value_type(std::forward(args)...)); + } + + template + std::pair try_emplace_at_position(const_iterator pos, K&& key, Args&&... value_args) { + return insert_at_position_impl(pos.m_iterator, key, + std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(value_args)...)); + } + + + void pop_back() { + tsl_oh_assert(!empty()); + erase(std::prev(end())); + } + + + /** + * Here to avoid `template size_type unordered_erase(const K& key)` being used when + * we use a iterator instead of a const_iterator. + */ + iterator unordered_erase(iterator pos) { + return unordered_erase(const_iterator(pos)); + } + + iterator unordered_erase(const_iterator pos) { + const std::size_t index_erase = iterator_to_index(pos); + unordered_erase(pos.key()); + + /* + * One element was deleted, index_erase now points to the next element as the elements after + * the deleted value were shifted to the left in m_values (will be end() if we deleted the last element). + */ + return begin() + index_erase; + } + + template + size_type unordered_erase(const K& key) { + return unordered_erase(key, hash_key(key)); + } + + template + size_type unordered_erase(const K& key, std::size_t hash) { + auto it_bucket_key = find_key(key, hash); + if(it_bucket_key == m_buckets_data.end()) { + return 0; + } + + /** + * If we are not erasing the last element in m_values, we swap + * the element we are erasing with the last element. We then would + * just have to do a pop_back() in m_values. + */ + if(!compare_keys(key, KeySelect()(back()))) { + auto it_bucket_last_elem = find_key(KeySelect()(back()), hash_key(KeySelect()(back()))); + tsl_oh_assert(it_bucket_last_elem != m_buckets_data.end()); + tsl_oh_assert(it_bucket_last_elem->index() == m_values.size() - 1); + + using std::swap; + swap(m_values[it_bucket_key->index()], m_values[it_bucket_last_elem->index()]); + swap(it_bucket_key->index_ref(), it_bucket_last_elem->index_ref()); + } + + erase_value_from_bucket(it_bucket_key); + + return 1; + } + + template + void serialize(Serializer& serializer) const { + serialize_impl(serializer); + } + + template + void deserialize(Deserializer& deserializer, bool hash_compatible) { + deserialize_impl(deserializer, hash_compatible); + } + + friend bool operator==(const ordered_hash& lhs, const ordered_hash& rhs) { + return lhs.m_values == rhs.m_values; + } + + friend bool operator!=(const ordered_hash& lhs, const ordered_hash& rhs) { + return lhs.m_values != rhs.m_values; + } + + friend bool operator<(const ordered_hash& lhs, const ordered_hash& rhs) { + return lhs.m_values < rhs.m_values; + } + + friend bool operator<=(const ordered_hash& lhs, const ordered_hash& rhs) { + return lhs.m_values <= rhs.m_values; + } + + friend bool operator>(const ordered_hash& lhs, const ordered_hash& rhs) { + return lhs.m_values > rhs.m_values; + } + + friend bool operator>=(const ordered_hash& lhs, const ordered_hash& rhs) { + return lhs.m_values >= rhs.m_values; + } + + +private: + template + std::size_t hash_key(const K& key) const { + return Hash::operator()(key); + } + + template + bool compare_keys(const K1& key1, const K2& key2) const { + return KeyEqual::operator()(key1, key2); + } + + template + typename buckets_container_type::iterator find_key(const K& key, std::size_t hash) { + auto it = static_cast(this)->find_key(key, hash); + return m_buckets_data.begin() + std::distance(m_buckets_data.cbegin(), it); + } + + /** + * Return bucket which has the key 'key' or m_buckets_data.end() if none. + * + * From the bucket_for_hash, search for the value until we either find an empty bucket + * or a bucket which has a value with a distance from its ideal bucket longer + * than the probe length for the value we are looking for. + */ + template + typename buckets_container_type::const_iterator find_key(const K& key, std::size_t hash) const { + for(std::size_t ibucket = bucket_for_hash(hash), dist_from_ideal_bucket = 0; ; + ibucket = next_bucket(ibucket), dist_from_ideal_bucket++) + { + if(m_buckets[ibucket].empty()) { + return m_buckets_data.end(); + } + else if(m_buckets[ibucket].truncated_hash() == bucket_entry::truncate_hash(hash) && + compare_keys(key, KeySelect()(m_values[m_buckets[ibucket].index()]))) + { + return m_buckets_data.begin() + ibucket; + } + else if(dist_from_ideal_bucket > distance_from_ideal_bucket(ibucket)) { + return m_buckets_data.end(); + } + } + } + + void rehash_impl(size_type bucket_count) { + tsl_oh_assert(bucket_count >= size_type(std::ceil(float(size())/max_load_factor()))); + + if(bucket_count > max_bucket_count()) { + TSL_OH_THROW_OR_TERMINATE(std::length_error, "The map exceeds its maximum size."); + } + + if(bucket_count > 0) { + bucket_count = round_up_to_power_of_two(bucket_count); + } + + if(bucket_count == this->bucket_count()) { + return; + } + + + buckets_container_type old_buckets(bucket_count); + m_buckets_data.swap(old_buckets); + m_buckets = m_buckets_data.empty()?static_empty_bucket_ptr(): + m_buckets_data.data(); + // Everything should be noexcept from here. + + m_hash_mask = (bucket_count > 0)?(bucket_count - 1):0; + this->max_load_factor(m_max_load_factor); + m_grow_on_next_insert = false; + + + + for(const bucket_entry& old_bucket: old_buckets) { + if(old_bucket.empty()) { + continue; + } + + truncated_hash_type insert_hash = old_bucket.truncated_hash(); + index_type insert_index = old_bucket.index(); + + for(std::size_t ibucket = bucket_for_hash(insert_hash), dist_from_ideal_bucket = 0; ; + ibucket = next_bucket(ibucket), dist_from_ideal_bucket++) + { + if(m_buckets[ibucket].empty()) { + m_buckets[ibucket].set_index(insert_index); + m_buckets[ibucket].set_hash(insert_hash); + break; + } + + const std::size_t distance = distance_from_ideal_bucket(ibucket); + if(dist_from_ideal_bucket > distance) { + std::swap(insert_index, m_buckets[ibucket].index_ref()); + std::swap(insert_hash, m_buckets[ibucket].truncated_hash_ref()); + dist_from_ideal_bucket = distance; + } + } + } + } + + template::value>::type* = nullptr> + void reserve_space_for_values(size_type count) { + m_values.reserve(count); + } + + template::value>::type* = nullptr> + void reserve_space_for_values(size_type /*count*/) { + } + + /** + * Swap the empty bucket with the values on its right until we cross another empty bucket + * or if the other bucket has a distance_from_ideal_bucket == 0. + */ + void backward_shift(std::size_t empty_ibucket) noexcept { + tsl_oh_assert(m_buckets[empty_ibucket].empty()); + + std::size_t previous_ibucket = empty_ibucket; + for(std::size_t current_ibucket = next_bucket(previous_ibucket); + !m_buckets[current_ibucket].empty() && distance_from_ideal_bucket(current_ibucket) > 0; + previous_ibucket = current_ibucket, current_ibucket = next_bucket(current_ibucket)) + { + std::swap(m_buckets[current_ibucket], m_buckets[previous_ibucket]); + } + } + + void erase_value_from_bucket(typename buckets_container_type::iterator it_bucket) { + tsl_oh_assert(it_bucket != m_buckets_data.end() && !it_bucket->empty()); + + m_values.erase(m_values.begin() + it_bucket->index()); + + /* + * m_values.erase shifted all the values on the right of the erased value, + * shift the indexes by -1 in the buckets array for these values. + */ + if(it_bucket->index() != m_values.size()) { + shift_indexes_in_buckets(it_bucket->index(), -1); + } + + // Mark the bucket as empty and do a backward shift of the values on the right + it_bucket->clear(); + backward_shift(std::size_t(std::distance(m_buckets_data.begin(), it_bucket))); + } + + /** + * Go through each value from [from_ivalue, m_values.size()) in m_values and for each + * bucket corresponding to the value, shift the index by delta. + * + * delta must be equal to 1 or -1. + */ + void shift_indexes_in_buckets(index_type from_ivalue, int delta) noexcept { + tsl_oh_assert(delta == 1 || delta == -1); + + for(std::size_t ivalue = from_ivalue; ivalue < m_values.size(); ivalue++) { + // All the values in m_values have been shifted by delta. Find the bucket corresponding + // to the value m_values[ivalue] + const index_type old_index = static_cast(ivalue - delta); + + std::size_t ibucket = bucket_for_hash(hash_key(KeySelect()(m_values[ivalue]))); + while(m_buckets[ibucket].index() != old_index) { + ibucket = next_bucket(ibucket); + } + + m_buckets[ibucket].set_index(index_type(ivalue)); + } + } + + template + size_type erase_impl(const K& key, std::size_t hash) { + auto it_bucket = find_key(key, hash); + if(it_bucket != m_buckets_data.end()) { + erase_value_from_bucket(it_bucket); + + return 1; + } + else { + return 0; + } + } + + /** + * Insert the element at the end. + */ + template + std::pair insert_impl(const K& key, Args&&... value_type_args) { + const std::size_t hash = hash_key(key); + + std::size_t ibucket = bucket_for_hash(hash); + std::size_t dist_from_ideal_bucket = 0; + + while(!m_buckets[ibucket].empty() && dist_from_ideal_bucket <= distance_from_ideal_bucket(ibucket)) { + if(m_buckets[ibucket].truncated_hash() == bucket_entry::truncate_hash(hash) && + compare_keys(key, KeySelect()(m_values[m_buckets[ibucket].index()]))) + { + return std::make_pair(begin() + m_buckets[ibucket].index(), false); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + if(size() >= max_size()) { + TSL_OH_THROW_OR_TERMINATE(std::length_error, "We reached the maximum size for the hash table."); + } + + + if(grow_on_high_load()) { + ibucket = bucket_for_hash(hash); + dist_from_ideal_bucket = 0; + } + + + m_values.emplace_back(std::forward(value_type_args)...); + insert_index(ibucket, dist_from_ideal_bucket, + index_type(m_values.size() - 1), bucket_entry::truncate_hash(hash)); + + + return std::make_pair(std::prev(end()), true); + } + + /** + * Insert the element before insert_position. + */ + template + std::pair insert_at_position_impl(typename values_container_type::const_iterator insert_position, + const K& key, Args&&... value_type_args) + { + const std::size_t hash = hash_key(key); + + std::size_t ibucket = bucket_for_hash(hash); + std::size_t dist_from_ideal_bucket = 0; + + while(!m_buckets[ibucket].empty() && dist_from_ideal_bucket <= distance_from_ideal_bucket(ibucket)) { + if(m_buckets[ibucket].truncated_hash() == bucket_entry::truncate_hash(hash) && + compare_keys(key, KeySelect()(m_values[m_buckets[ibucket].index()]))) + { + return std::make_pair(begin() + m_buckets[ibucket].index(), false); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + if(size() >= max_size()) { + TSL_OH_THROW_OR_TERMINATE(std::length_error, "We reached the maximum size for the hash table."); + } + + + if(grow_on_high_load()) { + ibucket = bucket_for_hash(hash); + dist_from_ideal_bucket = 0; + } + + + const index_type index_insert_position = index_type(std::distance(m_values.cbegin(), insert_position)); + +#ifdef TSL_OH_NO_CONTAINER_EMPLACE_CONST_ITERATOR + m_values.emplace(m_values.begin() + std::distance(m_values.cbegin(), insert_position), std::forward(value_type_args)...); +#else + m_values.emplace(insert_position, std::forward(value_type_args)...); +#endif + + insert_index(ibucket, dist_from_ideal_bucket, + index_insert_position, bucket_entry::truncate_hash(hash)); + + /* + * The insertion didn't happend at the end of the m_values container, + * we need to shift the indexes in m_buckets_data. + */ + if(index_insert_position != m_values.size() - 1) { + shift_indexes_in_buckets(index_insert_position + 1, 1); + } + + return std::make_pair(iterator(m_values.begin() + index_insert_position), true); + } + + void insert_index(std::size_t ibucket, std::size_t dist_from_ideal_bucket, + index_type index_insert, truncated_hash_type hash_insert) noexcept + { + while(!m_buckets[ibucket].empty()) { + const std::size_t distance = distance_from_ideal_bucket(ibucket); + if(dist_from_ideal_bucket > distance) { + std::swap(index_insert, m_buckets[ibucket].index_ref()); + std::swap(hash_insert, m_buckets[ibucket].truncated_hash_ref()); + + dist_from_ideal_bucket = distance; + } + + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + + + if(dist_from_ideal_bucket > REHASH_ON_HIGH_NB_PROBES__NPROBES && !m_grow_on_next_insert && + load_factor() >= REHASH_ON_HIGH_NB_PROBES__MIN_LOAD_FACTOR) + { + // We don't want to grow the map now as we need this method to be noexcept. + // Do it on next insert. + m_grow_on_next_insert = true; + } + } + + + m_buckets[ibucket].set_index(index_insert); + m_buckets[ibucket].set_hash(hash_insert); + } + + std::size_t distance_from_ideal_bucket(std::size_t ibucket) const noexcept { + const std::size_t ideal_bucket = bucket_for_hash(m_buckets[ibucket].truncated_hash()); + + if(ibucket >= ideal_bucket) { + return ibucket - ideal_bucket; + } + // If the bucket is smaller than the ideal bucket for the value, there was a wrapping at the end of the + // bucket array due to the modulo. + else { + return (bucket_count() + ibucket) - ideal_bucket; + } + } + + std::size_t next_bucket(std::size_t index) const noexcept { + tsl_oh_assert(index < m_buckets_data.size()); + + index++; + return (index < m_buckets_data.size())?index:0; + } + + std::size_t bucket_for_hash(std::size_t hash) const noexcept { + return hash & m_hash_mask; + } + + std::size_t iterator_to_index(const_iterator it) const noexcept { + const auto dist = std::distance(cbegin(), it); + tsl_oh_assert(dist >= 0); + + return std::size_t(dist); + } + + /** + * Return true if the map has been rehashed. + */ + bool grow_on_high_load() { + if(m_grow_on_next_insert || size() >= m_load_threshold) { + rehash_impl(std::max(size_type(1), bucket_count() * 2)); + m_grow_on_next_insert = false; + + return true; + } + else { + return false; + } + } + + template + void serialize_impl(Serializer& serializer) const { + const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION; + serializer(version); + + const slz_size_type nb_elements = m_values.size(); + serializer(nb_elements); + + const slz_size_type bucket_count = m_buckets_data.size(); + serializer(bucket_count); + + const float max_load_factor = m_max_load_factor; + serializer(max_load_factor); + + + for(const value_type& value: m_values) { + serializer(value); + } + + for(const bucket_entry& bucket: m_buckets_data) { + bucket.serialize(serializer); + } + } + + template + void deserialize_impl(Deserializer& deserializer, bool hash_compatible) { + tsl_oh_assert(m_buckets_data.empty()); // Current hash table must be empty + + const slz_size_type version = deserialize_value(deserializer); + // For now we only have one version of the serialization protocol. + // If it doesn't match there is a problem with the file. + if(version != SERIALIZATION_PROTOCOL_VERSION) { + TSL_OH_THROW_OR_TERMINATE(std::runtime_error, "Can't deserialize the ordered_map/set. " + "The protocol version header is invalid."); + } + + const slz_size_type nb_elements = deserialize_value(deserializer); + const slz_size_type bucket_count_ds = deserialize_value(deserializer); + const float max_load_factor = deserialize_value(deserializer); + + if(max_load_factor < MAX_LOAD_FACTOR__MINIMUM || max_load_factor > MAX_LOAD_FACTOR__MAXIMUM) { + TSL_OH_THROW_OR_TERMINATE(std::runtime_error, "Invalid max_load_factor. Check that the serializer " + "and deserializer support floats correctly as they " + "can be converted implicitly to ints."); + } + + + this->max_load_factor(max_load_factor); + + if(bucket_count_ds == 0) { + tsl_oh_assert(nb_elements == 0); + return; + } + + + if(!hash_compatible) { + reserve(numeric_cast(nb_elements, "Deserialized nb_elements is too big.")); + for(slz_size_type el = 0; el < nb_elements; el++) { + insert(deserialize_value(deserializer)); + } + } + else { + m_buckets_data.reserve(numeric_cast(bucket_count_ds, "Deserialized bucket_count is too big.")); + m_buckets = m_buckets_data.data(), + m_hash_mask = m_buckets_data.capacity() - 1; + + reserve_space_for_values(numeric_cast(nb_elements, "Deserialized nb_elements is too big.")); + for(slz_size_type el = 0; el < nb_elements; el++) { + m_values.push_back(deserialize_value(deserializer)); + } + + for(slz_size_type b = 0; b < bucket_count_ds; b++) { + m_buckets_data.push_back(bucket_entry::deserialize(deserializer)); + } + } + } + + static std::size_t round_up_to_power_of_two(std::size_t value) { + if(is_power_of_two(value)) { + return value; + } + + if(value == 0) { + return 1; + } + + --value; + for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) { + value |= value >> i; + } + + return value + 1; + } + + static constexpr bool is_power_of_two(std::size_t value) { + return value != 0 && (value & (value - 1)) == 0; + } + + +public: + static const size_type DEFAULT_INIT_BUCKETS_SIZE = 0; + static constexpr float DEFAULT_MAX_LOAD_FACTOR = 0.75f; + +private: + static constexpr float MAX_LOAD_FACTOR__MINIMUM = 0.1f; + static constexpr float MAX_LOAD_FACTOR__MAXIMUM = 0.95f; + + static const size_type REHASH_ON_HIGH_NB_PROBES__NPROBES = 128; + static constexpr float REHASH_ON_HIGH_NB_PROBES__MIN_LOAD_FACTOR = 0.15f; + + /** + * Protocol version currenlty used for serialization. + */ + static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1; + + /** + * Return an always valid pointer to an static empty bucket_entry with last_bucket() == true. + */ + bucket_entry* static_empty_bucket_ptr() { + static bucket_entry empty_bucket; + return &empty_bucket; + } + +private: + buckets_container_type m_buckets_data; + + /** + * Points to m_buckets_data.data() if !m_buckets_data.empty() otherwise points to static_empty_bucket_ptr. + * This variable is useful to avoid the cost of checking if m_buckets_data is empty when trying + * to find an element. + * + * TODO Remove m_buckets_data and only use a pointer+size instead of a pointer+vector to save some space in the ordered_hash object. + */ + bucket_entry* m_buckets; + + size_type m_hash_mask; + + values_container_type m_values; + + size_type m_load_threshold; + float m_max_load_factor; + + bool m_grow_on_next_insert; +}; + + +} // end namespace detail_ordered_hash + +} // end namespace tsl + +#endif diff --git a/external/tsl/ordered_map.h b/external/tsl/ordered_map.h new file mode 100644 index 000000000..c8b8b6bb2 --- /dev/null +++ b/external/tsl/ordered_map.h @@ -0,0 +1,833 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ORDERED_MAP_H +#define TSL_ORDERED_MAP_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ordered_hash.h" + + +namespace tsl { + + +/** + * Implementation of an hash map using open addressing with robin hood with backshift delete to resolve collisions. + * + * The particularity of this hash map is that it remembers the order in which the elements were added and + * provide a way to access the structure which stores these values through the 'values_container()' method. + * The used container is defined by ValueTypeContainer, by default a std::deque is used (grows faster) but + * a std::vector may be used. In this case the map provides a 'data()' method which give a direct access + * to the memory used to store the values (which can be useful to communicate with C API's). + * + * The Key and T must be copy constructible and/or move constructible. To use `unordered_erase` they both + * must be swappable. + * + * The behaviour of the hash map is undefined if the destructor of Key or T throws an exception. + * + * By default the maximum size of a map is limited to 2^32 - 1 values, if needed this can be changed through + * the IndexType template parameter. Using an `uint64_t` will raise this limit to 2^64 - 1 values but each + * bucket will use 16 bytes instead of 8 bytes in addition to the space needed to store the values. + * + * Iterators invalidation: + * - clear, operator=, reserve, rehash: always invalidate the iterators (also invalidate end()). + * - insert, emplace, emplace_hint, operator[]: when a std::vector is used as ValueTypeContainer + * and if size() < capacity(), only end(). + * Otherwise all the iterators are invalidated if an insert occurs. + * - erase, unordered_erase: when a std::vector is used as ValueTypeContainer invalidate the iterator of + * the erased element and all the ones after the erased element (including end()). + * Otherwise all the iterators are invalidated if an erase occurs. + */ +template, + class KeyEqual = std::equal_to, + class Allocator = std::allocator>, + class ValueTypeContainer = std::deque, Allocator>, + class IndexType = std::uint_least32_t> +class ordered_map { +private: + template + using has_is_transparent = tsl::detail_ordered_hash::has_is_transparent; + + class KeySelect { + public: + using key_type = Key; + + const key_type& operator()(const std::pair& key_value) const noexcept { + return key_value.first; + } + + key_type& operator()(std::pair& key_value) noexcept { + return key_value.first; + } + }; + + class ValueSelect { + public: + using value_type = T; + + const value_type& operator()(const std::pair& key_value) const noexcept { + return key_value.second; + } + + value_type& operator()(std::pair& key_value) noexcept { + return key_value.second; + } + }; + + using ht = detail_ordered_hash::ordered_hash, KeySelect, ValueSelect, + Hash, KeyEqual, Allocator, ValueTypeContainer, IndexType>; + +public: + using key_type = typename ht::key_type; + using mapped_type = T; + using value_type = typename ht::value_type; + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using allocator_type = typename ht::allocator_type; + using reference = typename ht::reference; + using const_reference = typename ht::const_reference; + using pointer = typename ht::pointer; + using const_pointer = typename ht::const_pointer; + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + using reverse_iterator = typename ht::reverse_iterator; + using const_reverse_iterator = typename ht::const_reverse_iterator; + + using values_container_type = typename ht::values_container_type; + + + /* + * Constructors + */ + ordered_map(): ordered_map(ht::DEFAULT_INIT_BUCKETS_SIZE) { + } + + explicit ordered_map(size_type bucket_count, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): + m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR) + { + } + + ordered_map(size_type bucket_count, + const Allocator& alloc): ordered_map(bucket_count, Hash(), KeyEqual(), alloc) + { + } + + ordered_map(size_type bucket_count, + const Hash& hash, + const Allocator& alloc): ordered_map(bucket_count, hash, KeyEqual(), alloc) + { + } + + explicit ordered_map(const Allocator& alloc): ordered_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) { + } + + template + ordered_map(InputIt first, InputIt last, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): ordered_map(bucket_count, hash, equal, alloc) + { + insert(first, last); + } + + template + ordered_map(InputIt first, InputIt last, + size_type bucket_count, + const Allocator& alloc): ordered_map(first, last, bucket_count, Hash(), KeyEqual(), alloc) + { + } + + template + ordered_map(InputIt first, InputIt last, + size_type bucket_count, + const Hash& hash, + const Allocator& alloc): ordered_map(first, last, bucket_count, hash, KeyEqual(), alloc) + { + } + + ordered_map(std::initializer_list init, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): + ordered_map(init.begin(), init.end(), bucket_count, hash, equal, alloc) + { + } + + ordered_map(std::initializer_list init, + size_type bucket_count, + const Allocator& alloc): + ordered_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc) + { + } + + ordered_map(std::initializer_list init, + size_type bucket_count, + const Hash& hash, + const Allocator& alloc): + ordered_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc) + { + } + + + ordered_map& operator=(std::initializer_list ilist) { + m_ht.clear(); + + m_ht.reserve(ilist.size()); + m_ht.insert(ilist.begin(), ilist.end()); + + return *this; + } + + allocator_type get_allocator() const { return m_ht.get_allocator(); } + + + + /* + * Iterators + */ + iterator begin() noexcept { return m_ht.begin(); } + const_iterator begin() const noexcept { return m_ht.begin(); } + const_iterator cbegin() const noexcept { return m_ht.cbegin(); } + + iterator end() noexcept { return m_ht.end(); } + const_iterator end() const noexcept { return m_ht.end(); } + const_iterator cend() const noexcept { return m_ht.cend(); } + + reverse_iterator rbegin() noexcept { return m_ht.rbegin(); } + const_reverse_iterator rbegin() const noexcept { return m_ht.rbegin(); } + const_reverse_iterator rcbegin() const noexcept { return m_ht.rcbegin(); } + + reverse_iterator rend() noexcept { return m_ht.rend(); } + const_reverse_iterator rend() const noexcept { return m_ht.rend(); } + const_reverse_iterator rcend() const noexcept { return m_ht.rcend(); } + + + /* + * Capacity + */ + bool empty() const noexcept { return m_ht.empty(); } + size_type size() const noexcept { return m_ht.size(); } + size_type max_size() const noexcept { return m_ht.max_size(); } + + /* + * Modifiers + */ + void clear() noexcept { m_ht.clear(); } + + + + std::pair insert(const value_type& value) { return m_ht.insert(value); } + + template::value>::type* = nullptr> + std::pair insert(P&& value) { return m_ht.emplace(std::forward

(value)); } + + std::pair insert(value_type&& value) { return m_ht.insert(std::move(value)); } + + + iterator insert(const_iterator hint, const value_type& value) { + return m_ht.insert_hint(hint, value); + } + + template::value>::type* = nullptr> + iterator insert(const_iterator hint, P&& value) { + return m_ht.emplace_hint(hint, std::forward

(value)); + } + + iterator insert(const_iterator hint, value_type&& value) { + return m_ht.insert_hint(hint, std::move(value)); + } + + + template + void insert(InputIt first, InputIt last) { m_ht.insert(first, last); } + void insert(std::initializer_list ilist) { m_ht.insert(ilist.begin(), ilist.end()); } + + + + + template + std::pair insert_or_assign(const key_type& k, M&& obj) { + return m_ht.insert_or_assign(k, std::forward(obj)); + } + + template + std::pair insert_or_assign(key_type&& k, M&& obj) { + return m_ht.insert_or_assign(std::move(k), std::forward(obj)); + } + + + template + iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) { + return m_ht.insert_or_assign(hint, k, std::forward(obj)); + } + + template + iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) { + return m_ht.insert_or_assign(hint, std::move(k), std::forward(obj)); + } + + /** + * Due to the way elements are stored, emplace will need to move or copy the key-value once. + * The method is equivalent to insert(value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + std::pair emplace(Args&&... args) { return m_ht.emplace(std::forward(args)...); } + + /** + * Due to the way elements are stored, emplace_hint will need to move or copy the key-value once. + * The method is equivalent to insert(hint, value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return m_ht.emplace_hint(hint, std::forward(args)...); + } + + + + + template + std::pair try_emplace(const key_type& k, Args&&... args) { + return m_ht.try_emplace(k, std::forward(args)...); + } + + template + std::pair try_emplace(key_type&& k, Args&&... args) { + return m_ht.try_emplace(std::move(k), std::forward(args)...); + } + + template + iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) { + return m_ht.try_emplace_hint(hint, k, std::forward(args)...); + } + + template + iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) { + return m_ht.try_emplace_hint(hint, std::move(k), std::forward(args)...); + } + + + + + /** + * When erasing an element, the insert order will be preserved and no holes will be present in the container + * returned by 'values_container()'. + * + * The method is in O(n), if the order is not important 'unordered_erase(...)' method is faster with an O(1) + * average complexity. + */ + iterator erase(iterator pos) { return m_ht.erase(pos); } + + /** + * @copydoc erase(iterator pos) + */ + iterator erase(const_iterator pos) { return m_ht.erase(pos); } + + /** + * @copydoc erase(iterator pos) + */ + iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } + + /** + * @copydoc erase(iterator pos) + */ + size_type erase(const key_type& key) { return m_ht.erase(key); } + + /** + * @copydoc erase(iterator pos) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. + */ + size_type erase(const key_type& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + /** + * @copydoc erase(iterator pos) + * + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type erase(const K& key) { return m_ht.erase(key); } + + /** + * @copydoc erase(const key_type& key, std::size_t precalculated_hash) + * + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type erase(const K& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + + + void swap(ordered_map& other) { other.m_ht.swap(m_ht); } + + /* + * Lookup + */ + T& at(const Key& key) { return m_ht.at(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + T& at(const Key& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); } + + + const T& at(const Key& key) const { return m_ht.at(key); } + + /** + * @copydoc at(const Key& key, std::size_t precalculated_hash) + */ + const T& at(const Key& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); } + + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + T& at(const K& key) { return m_ht.at(key); } + + /** + * @copydoc at(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + T& at(const K& key, std::size_t precalculated_hash) { return m_ht.at(key, precalculated_hash); } + + /** + * @copydoc at(const K& key) + */ + template::value>::type* = nullptr> + const T& at(const K& key) const { return m_ht.at(key); } + + /** + * @copydoc at(const K& key, std::size_t precalculated_hash) + */ + template::value>::type* = nullptr> + const T& at(const K& key, std::size_t precalculated_hash) const { return m_ht.at(key, precalculated_hash); } + + + + T& operator[](const Key& key) { return m_ht[key]; } + T& operator[](Key&& key) { return m_ht[std::move(key)]; } + + + + size_type count(const Key& key) const { return m_ht.count(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + size_type count(const Key& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type count(const K& key) const { return m_ht.count(key); } + + /** + * @copydoc count(const K& key) const + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + size_type count(const K& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + + + iterator find(const Key& key) { return m_ht.find(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } + + const_iterator find(const Key& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const Key& key, std::size_t precalculated_hash) + */ + const_iterator find(const Key& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + iterator find(const K& key) { return m_ht.find(key); } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } + + /** + * @copydoc find(const K& key) + */ + template::value>::type* = nullptr> + const_iterator find(const K& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + const_iterator find(const K& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + + + std::pair equal_range(const Key& key) { return m_ht.equal_range(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + std::pair equal_range(const Key& key, std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + std::pair equal_range(const Key& key) const { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const Key& key, std::size_t precalculated_hash) + */ + std::pair equal_range(const Key& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key) { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key, std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * @copydoc equal_range(const K& key) + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key) const { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const K& key, std::size_t precalculated_hash) + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + + + /* + * Bucket interface + */ + size_type bucket_count() const { return m_ht.bucket_count(); } + size_type max_bucket_count() const { return m_ht.max_bucket_count(); } + + + /* + * Hash policy + */ + float load_factor() const { return m_ht.load_factor(); } + float max_load_factor() const { return m_ht.max_load_factor(); } + void max_load_factor(float ml) { m_ht.max_load_factor(ml); } + + void rehash(size_type count) { m_ht.rehash(count); } + void reserve(size_type count) { m_ht.reserve(count); } + + + /* + * Observers + */ + hasher hash_function() const { return m_ht.hash_function(); } + key_equal key_eq() const { return m_ht.key_eq(); } + + + + /* + * Other + */ + + /** + * Convert a const_iterator to an iterator. + */ + iterator mutable_iterator(const_iterator pos) { + return m_ht.mutable_iterator(pos); + } + + /** + * Requires index <= size(). + * + * Return an iterator to the element at index. Return end() if index == size(). + */ + iterator nth(size_type index) { return m_ht.nth(index); } + + /** + * @copydoc nth(size_type index) + */ + const_iterator nth(size_type index) const { return m_ht.nth(index); } + + + /** + * Return const_reference to the first element. Requires the container to not be empty. + */ + const_reference front() const { return m_ht.front(); } + + /** + * Return const_reference to the last element. Requires the container to not be empty. + */ + const_reference back() const { return m_ht.back(); } + + + /** + * Only available if ValueTypeContainer is a std::vector. Same as calling 'values_container().data()'. + */ + template::value>::type* = nullptr> + const typename values_container_type::value_type* data() const noexcept { return m_ht.data(); } + + /** + * Return the container in which the values are stored. The values are in the same order as the insertion order + * and are contiguous in the structure, no holes (size() == values_container().size()). + */ + const values_container_type& values_container() const noexcept { return m_ht.values_container(); } + + template::value>::type* = nullptr> + size_type capacity() const noexcept { return m_ht.capacity(); } + + void shrink_to_fit() { m_ht.shrink_to_fit(); } + + + + /** + * Insert the value before pos shifting all the elements on the right of pos (including pos) one position + * to the right. + * + * Amortized linear time-complexity in the distance between pos and end(). + */ + std::pair insert_at_position(const_iterator pos, const value_type& value) { + return m_ht.insert_at_position(pos, value); + } + + /** + * @copydoc insert_at_position(const_iterator pos, const value_type& value) + */ + std::pair insert_at_position(const_iterator pos, value_type&& value) { + return m_ht.insert_at_position(pos, std::move(value)); + } + + /** + * @copydoc insert_at_position(const_iterator pos, const value_type& value) + * + * Same as insert_at_position(pos, value_type(std::forward(args)...), mainly + * here for coherence. + */ + template + std::pair emplace_at_position(const_iterator pos, Args&&... args) { + return m_ht.emplace_at_position(pos, std::forward(args)...); + } + + /** + * @copydoc insert_at_position(const_iterator pos, const value_type& value) + */ + template + std::pair try_emplace_at_position(const_iterator pos, const key_type& k, Args&&... args) { + return m_ht.try_emplace_at_position(pos, k, std::forward(args)...); + } + + /** + * @copydoc insert_at_position(const_iterator pos, const value_type& value) + */ + template + std::pair try_emplace_at_position(const_iterator pos, key_type&& k, Args&&... args) { + return m_ht.try_emplace_at_position(pos, std::move(k), std::forward(args)...); + } + + + + void pop_back() { m_ht.pop_back(); } + + /** + * Faster erase operation with an O(1) average complexity but it doesn't preserve the insertion order. + * + * If an erasure occurs, the last element of the map will take the place of the erased element. + */ + iterator unordered_erase(iterator pos) { return m_ht.unordered_erase(pos); } + + /** + * @copydoc unordered_erase(iterator pos) + */ + iterator unordered_erase(const_iterator pos) { return m_ht.unordered_erase(pos); } + + /** + * @copydoc unordered_erase(iterator pos) + */ + size_type unordered_erase(const key_type& key) { return m_ht.unordered_erase(key); } + + /** + * @copydoc unordered_erase(iterator pos) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + size_type unordered_erase(const key_type& key, std::size_t precalculated_hash) { + return m_ht.unordered_erase(key, precalculated_hash); + } + + /** + * @copydoc unordered_erase(iterator pos) + * + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type unordered_erase(const K& key) { return m_ht.unordered_erase(key); } + + /** + * @copydoc unordered_erase(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + size_type unordered_erase(const K& key, std::size_t precalculated_hash) { + return m_ht.unordered_erase(key, precalculated_hash); + } + + /** + * Serialize the map through the `serializer` parameter. + * + * The `serializer` parameter must be a function object that supports the following call: + * - `template void operator()(const U& value);` where the types `std::uint64_t`, `float` and `std::pair` must be supported for U. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes + * in the hands of the `Serializer` function object if compatibility is required. + */ + template + void serialize(Serializer& serializer) const { + m_ht.serialize(serializer); + } + + /** + * Deserialize a previously serialized map through the `deserializer` parameter. + * + * The `deserializer` parameter must be a function object that supports the following calls: + * - `template U operator()();` where the types `std::uint64_t`, `float` and `std::pair` must be supported for U. + * + * If the deserialized hash map type is hash compatible with the serialized map, the deserialization process can be + * sped up by setting `hash_compatible` to true. To be hash compatible, the Hash and KeyEqual must behave the same way + * than the ones used on the serialized map. The `std::size_t` must also be of the same size as the one on the platform used + * to serialize the map, the same apply for `IndexType`. If these criteria are not met, the behaviour is undefined with + * `hash_compatible` sets to true. + * + * The behaviour is undefined if the type `Key` and `T` of the `ordered_map` are not the same as the + * types used during serialization. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it + * deserializes in the hands of the `Deserializer` function object if compatibility is required. + */ + template + static ordered_map deserialize(Deserializer& deserializer, bool hash_compatible = false) { + ordered_map map(0); + map.m_ht.deserialize(deserializer, hash_compatible); + + return map; + } + + + + friend bool operator==(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht == rhs.m_ht; } + friend bool operator!=(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht != rhs.m_ht; } + friend bool operator<(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht < rhs.m_ht; } + friend bool operator<=(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht <= rhs.m_ht; } + friend bool operator>(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht > rhs.m_ht; } + friend bool operator>=(const ordered_map& lhs, const ordered_map& rhs) { return lhs.m_ht >= rhs.m_ht; } + + friend void swap(ordered_map& lhs, ordered_map& rhs) { lhs.swap(rhs); } + +private: + ht m_ht; +}; + +} // end namespace tsl + +#endif diff --git a/external/tsl/ordered_set.h b/external/tsl/ordered_set.h new file mode 100644 index 000000000..0932f54b9 --- /dev/null +++ b/external/tsl/ordered_set.h @@ -0,0 +1,688 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ORDERED_SET_H +#define TSL_ORDERED_SET_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ordered_hash.h" + + +namespace tsl { + + +/** + * Implementation of an hash set using open addressing with robin hood with backshift delete to resolve collisions. + * + * The particularity of this hash set is that it remembers the order in which the elements were added and + * provide a way to access the structure which stores these values through the 'values_container()' method. + * The used container is defined by ValueTypeContainer, by default a std::deque is used (grows faster) but + * a std::vector may be used. In this case the set provides a 'data()' method which give a direct access + * to the memory used to store the values (which can be useful to communicate with C API's). + * + * The Key must be copy constructible and/or move constructible. To use `unordered_erase` it also must be swappable. + * + * The behaviour of the hash set is undefined if the destructor of Key throws an exception. + * + * By default the maximum size of a set is limited to 2^32 - 1 values, if needed this can be changed through + * the IndexType template parameter. Using an `uint64_t` will raise this limit to 2^64 - 1 values but each + * bucket will use 16 bytes instead of 8 bytes in addition to the space needed to store the values. + * + * Iterators invalidation: + * - clear, operator=, reserve, rehash: always invalidate the iterators (also invalidate end()). + * - insert, emplace, emplace_hint, operator[]: when a std::vector is used as ValueTypeContainer + * and if size() < capacity(), only end(). + * Otherwise all the iterators are invalidated if an insert occurs. + * - erase, unordered_erase: when a std::vector is used as ValueTypeContainer invalidate the iterator of + * the erased element and all the ones after the erased element (including end()). + * Otherwise all the iterators are invalidated if an erase occurs. + */ +template, + class KeyEqual = std::equal_to, + class Allocator = std::allocator, + class ValueTypeContainer = std::deque, + class IndexType = std::uint_least32_t> +class ordered_set { +private: + template + using has_is_transparent = tsl::detail_ordered_hash::has_is_transparent; + + class KeySelect { + public: + using key_type = Key; + + const key_type& operator()(const Key& key) const noexcept { + return key; + } + + key_type& operator()(Key& key) noexcept { + return key; + } + }; + + using ht = detail_ordered_hash::ordered_hash; + +public: + using key_type = typename ht::key_type; + using value_type = typename ht::value_type; + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using allocator_type = typename ht::allocator_type; + using reference = typename ht::reference; + using const_reference = typename ht::const_reference; + using pointer = typename ht::pointer; + using const_pointer = typename ht::const_pointer; + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + using reverse_iterator = typename ht::reverse_iterator; + using const_reverse_iterator = typename ht::const_reverse_iterator; + + using values_container_type = typename ht::values_container_type; + + + /* + * Constructors + */ + ordered_set(): ordered_set(ht::DEFAULT_INIT_BUCKETS_SIZE) { + } + + explicit ordered_set(size_type bucket_count, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): + m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR) + { + } + + ordered_set(size_type bucket_count, + const Allocator& alloc): ordered_set(bucket_count, Hash(), KeyEqual(), alloc) + { + } + + ordered_set(size_type bucket_count, + const Hash& hash, + const Allocator& alloc): ordered_set(bucket_count, hash, KeyEqual(), alloc) + { + } + + explicit ordered_set(const Allocator& alloc): ordered_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) { + } + + template + ordered_set(InputIt first, InputIt last, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): ordered_set(bucket_count, hash, equal, alloc) + { + insert(first, last); + } + + template + ordered_set(InputIt first, InputIt last, + size_type bucket_count, + const Allocator& alloc): ordered_set(first, last, bucket_count, Hash(), KeyEqual(), alloc) + { + } + + template + ordered_set(InputIt first, InputIt last, + size_type bucket_count, + const Hash& hash, + const Allocator& alloc): ordered_set(first, last, bucket_count, hash, KeyEqual(), alloc) + { + } + + ordered_set(std::initializer_list init, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()): + ordered_set(init.begin(), init.end(), bucket_count, hash, equal, alloc) + { + } + + ordered_set(std::initializer_list init, + size_type bucket_count, + const Allocator& alloc): + ordered_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), alloc) + { + } + + ordered_set(std::initializer_list init, + size_type bucket_count, + const Hash& hash, + const Allocator& alloc): + ordered_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), alloc) + { + } + + + ordered_set& operator=(std::initializer_list ilist) { + m_ht.clear(); + + m_ht.reserve(ilist.size()); + m_ht.insert(ilist.begin(), ilist.end()); + + return *this; + } + + allocator_type get_allocator() const { return m_ht.get_allocator(); } + + + /* + * Iterators + */ + iterator begin() noexcept { return m_ht.begin(); } + const_iterator begin() const noexcept { return m_ht.begin(); } + const_iterator cbegin() const noexcept { return m_ht.cbegin(); } + + iterator end() noexcept { return m_ht.end(); } + const_iterator end() const noexcept { return m_ht.end(); } + const_iterator cend() const noexcept { return m_ht.cend(); } + + reverse_iterator rbegin() noexcept { return m_ht.rbegin(); } + const_reverse_iterator rbegin() const noexcept { return m_ht.rbegin(); } + const_reverse_iterator rcbegin() const noexcept { return m_ht.rcbegin(); } + + reverse_iterator rend() noexcept { return m_ht.rend(); } + const_reverse_iterator rend() const noexcept { return m_ht.rend(); } + const_reverse_iterator rcend() const noexcept { return m_ht.rcend(); } + + + /* + * Capacity + */ + bool empty() const noexcept { return m_ht.empty(); } + size_type size() const noexcept { return m_ht.size(); } + size_type max_size() const noexcept { return m_ht.max_size(); } + + /* + * Modifiers + */ + void clear() noexcept { m_ht.clear(); } + + + + std::pair insert(const value_type& value) { return m_ht.insert(value); } + std::pair insert(value_type&& value) { return m_ht.insert(std::move(value)); } + + iterator insert(const_iterator hint, const value_type& value) { + return m_ht.insert_hint(hint, value); + } + + iterator insert(const_iterator hint, value_type&& value) { + return m_ht.insert_hint(hint, std::move(value)); + } + + template + void insert(InputIt first, InputIt last) { m_ht.insert(first, last); } + void insert(std::initializer_list ilist) { m_ht.insert(ilist.begin(), ilist.end()); } + + + + /** + * Due to the way elements are stored, emplace will need to move or copy the key-value once. + * The method is equivalent to insert(value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + std::pair emplace(Args&&... args) { return m_ht.emplace(std::forward(args)...); } + + /** + * Due to the way elements are stored, emplace_hint will need to move or copy the key-value once. + * The method is equivalent to insert(hint, value_type(std::forward(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return m_ht.emplace_hint(hint, std::forward(args)...); + } + + /** + * When erasing an element, the insert order will be preserved and no holes will be present in the container + * returned by 'values_container()'. + * + * The method is in O(n), if the order is not important 'unordered_erase(...)' method is faster with an O(1) + * average complexity. + */ + iterator erase(iterator pos) { return m_ht.erase(pos); } + + /** + * @copydoc erase(iterator pos) + */ + iterator erase(const_iterator pos) { return m_ht.erase(pos); } + + /** + * @copydoc erase(iterator pos) + */ + iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); } + + /** + * @copydoc erase(iterator pos) + */ + size_type erase(const key_type& key) { return m_ht.erase(key); } + + /** + * @copydoc erase(iterator pos) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash. + */ + size_type erase(const key_type& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + /** + * @copydoc erase(iterator pos) + * + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type erase(const K& key) { return m_ht.erase(key); } + + /** + * @copydoc erase(const key_type& key, std::size_t precalculated_hash) + * + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type erase(const K& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + + + void swap(ordered_set& other) { other.m_ht.swap(m_ht); } + + /* + * Lookup + */ + size_type count(const Key& key) const { return m_ht.count(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + size_type count(const Key& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type count(const K& key) const { return m_ht.count(key); } + + /** + * @copydoc count(const K& key) const + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + size_type count(const K& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + + + + iterator find(const Key& key) { return m_ht.find(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + iterator find(const Key& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } + + const_iterator find(const Key& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const Key& key, std::size_t precalculated_hash) + */ + const_iterator find(const Key& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + iterator find(const K& key) { return m_ht.find(key); } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + iterator find(const K& key, std::size_t precalculated_hash) { return m_ht.find(key, precalculated_hash); } + + /** + * @copydoc find(const K& key) + */ + template::value>::type* = nullptr> + const_iterator find(const K& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + const_iterator find(const K& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + + + std::pair equal_range(const Key& key) { return m_ht.equal_range(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + std::pair equal_range(const Key& key, std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + std::pair equal_range(const Key& key) const { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const Key& key, std::size_t precalculated_hash) + */ + std::pair equal_range(const Key& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key) { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key, std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * @copydoc equal_range(const K& key) + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key) const { return m_ht.equal_range(key); } + + /** + * @copydoc equal_range(const K& key, std::size_t precalculated_hash) + */ + template::value>::type* = nullptr> + std::pair equal_range(const K& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + + /* + * Bucket interface + */ + size_type bucket_count() const { return m_ht.bucket_count(); } + size_type max_bucket_count() const { return m_ht.max_bucket_count(); } + + + /* + * Hash policy + */ + float load_factor() const { return m_ht.load_factor(); } + float max_load_factor() const { return m_ht.max_load_factor(); } + void max_load_factor(float ml) { m_ht.max_load_factor(ml); } + + void rehash(size_type count) { m_ht.rehash(count); } + void reserve(size_type count) { m_ht.reserve(count); } + + + /* + * Observers + */ + hasher hash_function() const { return m_ht.hash_function(); } + key_equal key_eq() const { return m_ht.key_eq(); } + + + /* + * Other + */ + + /** + * Convert a const_iterator to an iterator. + */ + iterator mutable_iterator(const_iterator pos) { + return m_ht.mutable_iterator(pos); + } + + /** + * Requires index <= size(). + * + * Return an iterator to the element at index. Return end() if index == size(). + */ + iterator nth(size_type index) { return m_ht.nth(index); } + + /** + * @copydoc nth(size_type index) + */ + const_iterator nth(size_type index) const { return m_ht.nth(index); } + + + /** + * Return const_reference to the first element. Requires the container to not be empty. + */ + const_reference front() const { return m_ht.front(); } + + /** + * Return const_reference to the last element. Requires the container to not be empty. + */ + const_reference back() const { return m_ht.back(); } + + + /** + * Only available if ValueTypeContainer is a std::vector. Same as calling 'values_container().data()'. + */ + template::value>::type* = nullptr> + const typename values_container_type::value_type* data() const noexcept { return m_ht.data(); } + + /** + * Return the container in which the values are stored. The values are in the same order as the insertion order + * and are contiguous in the structure, no holes (size() == values_container().size()). + */ + const values_container_type& values_container() const noexcept { return m_ht.values_container(); } + + template::value>::type* = nullptr> + size_type capacity() const noexcept { return m_ht.capacity(); } + + void shrink_to_fit() { m_ht.shrink_to_fit(); } + + + + /** + * Insert the value before pos shifting all the elements on the right of pos (including pos) one position + * to the right. + * + * Amortized linear time-complexity in the distance between pos and end(). + */ + std::pair insert_at_position(const_iterator pos, const value_type& value) { + return m_ht.insert_at_position(pos, value); + } + + /** + * @copydoc insert_at_position(const_iterator pos, const value_type& value) + */ + std::pair insert_at_position(const_iterator pos, value_type&& value) { + return m_ht.insert_at_position(pos, std::move(value)); + } + + /** + * @copydoc insert_at_position(const_iterator pos, const value_type& value) + * + * Same as insert_at_position(pos, value_type(std::forward(args)...), mainly + * here for coherence. + */ + template + std::pair emplace_at_position(const_iterator pos, Args&&... args) { + return m_ht.emplace_at_position(pos, std::forward(args)...); + } + + + + void pop_back() { m_ht.pop_back(); } + + /** + * Faster erase operation with an O(1) average complexity but it doesn't preserve the insertion order. + * + * If an erasure occurs, the last element of the map will take the place of the erased element. + */ + iterator unordered_erase(iterator pos) { return m_ht.unordered_erase(pos); } + + /** + * @copydoc unordered_erase(iterator pos) + */ + iterator unordered_erase(const_iterator pos) { return m_ht.unordered_erase(pos); } + + /** + * @copydoc unordered_erase(iterator pos) + */ + size_type unordered_erase(const key_type& key) { return m_ht.unordered_erase(key); } + + /** + * @copydoc unordered_erase(iterator pos) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + size_type unordered_erase(const key_type& key, std::size_t precalculated_hash) { + return m_ht.unordered_erase(key, precalculated_hash); + } + + /** + * @copydoc unordered_erase(iterator pos) + * + * This overload only participates in the overload resolution if the typedef KeyEqual::is_transparent exists. + * If so, K must be hashable and comparable to Key. + */ + template::value>::type* = nullptr> + size_type unordered_erase(const K& key) { return m_ht.unordered_erase(key); } + + /** + * @copydoc unordered_erase(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same + * as hash_function()(key). Useful to speed-up the lookup if you already have the hash. + */ + template::value>::type* = nullptr> + size_type unordered_erase(const K& key, std::size_t precalculated_hash) { + return m_ht.unordered_erase(key, precalculated_hash); + } + + /** + * Serialize the set through the `serializer` parameter. + * + * The `serializer` parameter must be a function object that supports the following call: + * - `void operator()(const U& value);` where the types `std::uint64_t`, `float` and `Key` must be supported for U. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes + * in the hands of the `Serializer` function object if compatibility is required. + */ + template + void serialize(Serializer& serializer) const { + m_ht.serialize(serializer); + } + + /** + * Deserialize a previously serialized set through the `deserializer` parameter. + * + * The `deserializer` parameter must be a function object that supports the following calls: + * - `template U operator()();` where the types `std::uint64_t`, `float` and `Key` must be supported for U. + * + * If the deserialized hash set type is hash compatible with the serialized set, the deserialization process can be + * sped up by setting `hash_compatible` to true. To be hash compatible, the Hash and KeyEqual must behave the same way + * than the ones used on the serialized map. The `std::size_t` must also be of the same size as the one on the platform used + * to serialize the map, the same apply for `IndexType`. If these criteria are not met, the behaviour is undefined with + * `hash_compatible` sets to true. + * + * The behaviour is undefined if the type `Key` of the `ordered_set` is not the same as the + * type used during serialization. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it + * deserializes in the hands of the `Deserializer` function object if compatibility is required. + */ + template + static ordered_set deserialize(Deserializer& deserializer, bool hash_compatible = false) { + ordered_set set(0); + set.m_ht.deserialize(deserializer, hash_compatible); + + return set; + } + + + + friend bool operator==(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht == rhs.m_ht; } + friend bool operator!=(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht != rhs.m_ht; } + friend bool operator<(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht < rhs.m_ht; } + friend bool operator<=(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht <= rhs.m_ht; } + friend bool operator>(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht > rhs.m_ht; } + friend bool operator>=(const ordered_set& lhs, const ordered_set& rhs) { return lhs.m_ht >= rhs.m_ht; } + + friend void swap(ordered_set& lhs, ordered_set& rhs) { lhs.swap(rhs); } + +private: + ht m_ht; +}; + +} // end namespace tsl + +#endif diff --git a/include/mpm_properties.h b/include/mpm_properties.h new file mode 100644 index 000000000..cff0edced --- /dev/null +++ b/include/mpm_properties.h @@ -0,0 +1,13 @@ +#ifndef MPM_PROPERTIES_H_ +#define MPM_PROPERTIES_H_ + +namespace mpm { +namespace properties { +//! Scalar Properties +enum Scalar : unsigned int { Mass }; +//! Vector Properties +enum Vector : unsigned int { Velocity, Displacement, Acceleration }; +} // namespace properties +} // namespace mpm + +#endif // MPM_PROPERTIES_H_ diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 0d2656f54..2d65d097e 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -144,6 +144,18 @@ TEST_CASE("Particle is checked for 1D case", "[particle][1D]") { std::shared_ptr> particle = std::make_shared>(id, coords); + // Check scalar properties + SECTION("Check scalar properties") { + // Check mass + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(0.0).epsilon(Tolerance)); + double mass = 100.5; + particle->update_scalar_property(mpm::properties::Scalar::Mass, true, + mass); + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(100.5).epsilon(Tolerance)); + } + // Check mass REQUIRE(particle->mass() == Approx(0.0).epsilon(Tolerance)); double mass = 100.5; @@ -1222,6 +1234,18 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { std::shared_ptr> particle = std::make_shared>(id, coords); + // Check scalar properties + SECTION("Check scalar properties") { + // Check mass + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(0.0).epsilon(Tolerance)); + double mass = 100.5; + particle->update_scalar_property(mpm::properties::Scalar::Mass, true, + mass); + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(100.5).epsilon(Tolerance)); + } + // Check mass REQUIRE(particle->mass() == Approx(0.0).epsilon(Tolerance)); double mass = 100.5; From e68ab89ced068421a49e758de032eb98a98ed40d Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Sat, 4 Jul 2020 16:16:54 -0500 Subject: [PATCH 03/55] :computer: Compute mass free function --- include/particles/particle.h | 5 +++++ include/particles/particle.tcc | 6 ++++++ include/particles/particle_base.h | 4 ++++ include/particles/particle_functions.tcc | 17 +++++++++++++++++ tests/particle_test.cc | 7 ++++++- 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 include/particles/particle_functions.tcc diff --git a/include/particles/particle.h b/include/particles/particle.h index 542764001..b22aa09f9 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -159,6 +159,10 @@ class Particle : public ParticleBase { bool assign_material( const std::shared_ptr>& material) override; + //! Return material + //! \retval material Pointer to a material + std::shared_ptr> material() const override; + //! Compute strain //! \param[in] dt Analysis time step void compute_strain(double dt) noexcept override; @@ -357,5 +361,6 @@ class Particle : public ParticleBase { } // namespace mpm #include "particle.tcc" +#include "particle_functions.tcc" #endif // MPM_PARTICLE_H__ diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 2115d59f4..d80589759 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -393,6 +393,12 @@ bool mpm::Particle::assign_material( return status; } +// Return material of particle +template +std::shared_ptr> mpm::Particle::material() const { + return material_; +} + // Compute reference location cell to particle template bool mpm::Particle::compute_reference_location() noexcept { diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index 32afefd8b..5e0128abc 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -197,6 +197,10 @@ class ParticleBase { virtual bool assign_material( const std::shared_ptr>& material) = 0; + //! Return material + //! \retval material Pointer to a material + virtual std::shared_ptr> material() const = 0; + //! Return material id unsigned material_id() const { return material_id_; } diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc new file mode 100644 index 000000000..0a5a71f4c --- /dev/null +++ b/include/particles/particle_functions.tcc @@ -0,0 +1,17 @@ +// Compute mass of particle +namespace mpm { +namespace particle { +template +void compute_mass(std::shared_ptr> particle) noexcept { + // Check if particle volume is set and material ptr is valid + assert(particle->volume() != std::numeric_limits::max() && + particle->material() != nullptr); + // Mass = volume of particle * mass_density + auto density = + (particle->material())->template property(std::string("density")); + // Update particle mass + particle->update_scalar_property(mpm::properties::Scalar::Mass, false, + particle->volume() * density); +} +} // namespace particle +} // namespace mpm diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 2d65d097e..0dfe49a8d 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -630,7 +630,8 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // Add particle mpm::Index id = 0; coords << 0.75, 0.75; - auto particle = std::make_shared>(id, coords); + std::shared_ptr> particle = + std::make_shared>(id, coords); // Time-step const double dt = 0.1; @@ -752,6 +753,10 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { REQUIRE_NOTHROW(particle->compute_mass()); // Mass REQUIRE(particle->mass() == Approx(1000.).epsilon(Tolerance)); + // Compute mass function + REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(1000.).epsilon(Tolerance)); // Map particle mass to nodes particle->assign_mass(std::numeric_limits::max()); From 67303d8e9d2bd8736b50fba63e4a16ce9474cf69 Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 8 Jul 2020 16:20:24 -0700 Subject: [PATCH 04/55] :wrench: modify mass, mass_density_volume --- include/particles/particle.h | 22 +++++----- include/particles/particle.tcc | 79 +++++++++++++++++++--------------- tests/particle_test.cc | 12 +++--- 3 files changed, 63 insertions(+), 50 deletions(-) diff --git a/include/particles/particle.h b/include/particles/particle.h index 92f392063..1b692ca97 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -117,7 +117,9 @@ class Particle : public ParticleBase { bool assign_volume(double volume) override; //! Return volume - double volume() const override { return volume_; } + double volume() const override { + return this->scalar_property(mpm::properties::Scalar::Volume); + } //! Return size of particle in natural coordinates VectorDim natural_size() const override { return natural_size_; } @@ -129,7 +131,9 @@ class Particle : public ParticleBase { void update_volume() noexcept override; //! \param[in] phase Index corresponding to the phase - double mass_density() const override { return mass_density_; } + double mass_density() const override { + return this->scalar_property(mpm::properties::Scalar::MassDensity); + } //! Compute mass as volume * density void compute_mass() noexcept override; @@ -149,10 +153,14 @@ class Particle : public ParticleBase { //! Assign nodal mass to particles //! \param[in] mass Mass from the particles in a cell //! \retval status Assignment status - void assign_mass(double mass) override { mass_ = mass; } + void assign_mass(double mass) override { + scalar_properties_.at(mpm::properties::Scalar::Mass) = mass; + } //! Return mass of the particles - double mass() const override { return mass_; } + double mass() const override { + return this->scalar_property(mpm::properties::Scalar::Mass); + } //! Assign material //! \param[in] material Pointer to a material @@ -316,12 +324,6 @@ class Particle : public ParticleBase { using ParticleBase::vector_properties_; //! Shape functions using ParticleBase::shapefn_; - //! Volumetric mass density (mass / volume) - double mass_density_{0.}; - //! Mass - double mass_{0.}; - //! Volume - double volume_{0.}; //! Size of particle Eigen::Matrix size_; //! Size of particle in natural coordinates diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index af5984e9b..5eb9cdf72 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -9,9 +9,6 @@ mpm::Particle::Particle(Index id, const VectorDim& coord) nodes_.clear(); // Set material pointer to null material_ = nullptr; - // Scalar properties - scalar_properties_.emplace( - std::make_pair(mpm::properties::Scalar::Mass, double(0.))); // Logger std::string logger = "particle" + std::to_string(Tdim) + "d::" + std::to_string(id); @@ -39,11 +36,12 @@ bool mpm::Particle::initialise_particle(const HDF5Particle& particle) { // Assign id this->id_ = particle.id; // Mass - this->mass_ = particle.mass; + scalar_properties_.at(mpm::properties::Scalar::Mass) = particle.mass; // Volume - this->volume_ = particle.volume; + scalar_properties_.at(mpm::properties::Scalar::Volume) = particle.volume; // Mass Density - this->mass_density_ = particle.mass / particle.volume; + scalar_properties_.at(mpm::properties::Scalar::MassDensity) = + particle.mass / particle.volume; // Set local size of particle Eigen::Vector3d psize; psize << particle.nsize_x, particle.nsize_y, particle.nsize_z; @@ -228,7 +226,6 @@ template void mpm::Particle::initialise() { displacement_.setZero(); dstrain_.setZero(); - mass_ = 0.; natural_size_.setZero(); set_traction_ = false; size_.setZero(); @@ -237,9 +234,16 @@ void mpm::Particle::initialise() { stress_.setZero(); traction_.setZero(); velocity_.setZero(); - volume_ = std::numeric_limits::max(); volumetric_strain_centroid_ = 0.; + // Initialize scalar properties + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::Mass, double(0.))); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::MassDensity, double(0.))); + scalar_properties_.emplace(std::make_pair( + mpm::properties::Scalar::Volume, std::numeric_limits::max())); + // Initialize vector data properties this->properties_["stresses"] = [&]() { return stress(); }; this->properties_["strains"] = [&]() { return strain(); }; @@ -442,10 +446,10 @@ bool mpm::Particle::assign_volume(double volume) { if (volume <= 0.) throw std::runtime_error("Particle volume cannot be negative"); - this->volume_ = volume; + scalar_properties_.at(mpm::properties::Scalar::Volume) = volume; // Compute size of particle in each direction const double length = - std::pow(this->volume_, static_cast(1. / Tdim)); + std::pow(this->volume(), static_cast(1. / Tdim)); // Set particle size as length on each side this->size_.fill(length); @@ -479,36 +483,42 @@ void mpm::Particle::compute_volume() noexcept { template void mpm::Particle::update_volume() noexcept { // Check if particle has a valid cell ptr and a valid volume - assert(cell_ != nullptr && volume_ != std::numeric_limits::max()); + assert(cell_ != nullptr && + this->volume() != std::numeric_limits::max()); // Compute at centroid // Strain rate for reduced integration - this->volume_ *= (1. + dvolumetric_strain_); - this->mass_density_ = this->mass_density_ / (1. + dvolumetric_strain_); + scalar_properties_.at(mpm::properties::Scalar::Volume) *= + (1. + dvolumetric_strain_); + scalar_properties_.at(mpm::properties::Scalar::MassDensity) = + scalar_properties_.at(mpm::properties::Scalar::MassDensity) / + (1. + dvolumetric_strain_); } // Compute mass of particle template void mpm::Particle::compute_mass() noexcept { // Check if particle volume is set and material ptr is valid - assert(volume_ != std::numeric_limits::max() && material_ != nullptr); + assert(this->volume() != std::numeric_limits::max() && + material_ != nullptr); // Mass = volume of particle * mass_density - this->mass_density_ = + scalar_properties_.at(mpm::properties::Scalar::MassDensity) = material_->template property(std::string("density")); - this->mass_ = volume_ * mass_density_; + scalar_properties_.at(mpm::properties::Scalar::Mass) = + this->volume() * this->mass_density(); } //! Map particle mass and momentum to nodes template void mpm::Particle::map_mass_momentum_to_nodes() noexcept { // Check if particle mass is set - assert(mass_ != std::numeric_limits::max()); + assert(this->mass() != std::numeric_limits::max()); // Map mass and momentum to nodes for (unsigned i = 0; i < nodes_.size(); ++i) { nodes_[i]->update_mass(true, mpm::ParticlePhase::Solid, - mass_ * shapefn_[i]); + this->mass() * shapefn_[i]); nodes_[i]->update_momentum(true, mpm::ParticlePhase::Solid, - mass_ * shapefn_[i] * velocity_); + this->mass() * shapefn_[i] * velocity_); } } @@ -516,14 +526,14 @@ void mpm::Particle::map_mass_momentum_to_nodes() noexcept { template void mpm::Particle::map_multimaterial_mass_momentum_to_nodes() noexcept { // Check if particle mass is set - assert(mass_ != std::numeric_limits::max()); + assert(this->mass() != std::numeric_limits::max()); // Unit 1x1 Eigen matrix to be used with scalar quantities Eigen::Matrix nodal_mass; // Map mass and momentum to nodal property taking into account the material id for (unsigned i = 0; i < nodes_.size(); ++i) { - nodal_mass(0, 0) = mass_ * shapefn_[i]; + nodal_mass(0, 0) = this->mass() * shapefn_[i]; nodes_[i]->update_property(true, "masses", nodal_mass, material_id_, 1); nodes_[i]->update_property(true, "momenta", velocity_ * nodal_mass, material_id_, Tdim); @@ -534,12 +544,12 @@ void mpm::Particle::map_multimaterial_mass_momentum_to_nodes() noexcept { template void mpm::Particle::map_multimaterial_displacements_to_nodes() noexcept { // Check if particle mass is set - assert(mass_ != std::numeric_limits::max()); + assert(this->mass() != std::numeric_limits::max()); // Map displacements to nodal property and divide it by the respective // nodal-material mass for (unsigned i = 0; i < nodes_.size(); ++i) { - const auto& displacement = mass_ * shapefn_[i] * displacement_; + const auto& displacement = this->mass() * shapefn_[i] * displacement_; nodes_[i]->update_property(true, "displacements", displacement, material_id_, Tdim); } @@ -550,13 +560,14 @@ template void mpm::Particle< Tdim>::map_multimaterial_domain_gradients_to_nodes() noexcept { // Check if particle volume is set - assert(volume_ != std::numeric_limits::max()); + assert(this->volume() != std::numeric_limits::max()); // Map domain gradients to nodal property. The domain gradients is defined as // the gradient of the particle volume for (unsigned i = 0; i < nodes_.size(); ++i) { Eigen::Matrix gradient; - for (unsigned j = 0; j < Tdim; ++j) gradient[j] = volume_ * dn_dx_(i, j); + for (unsigned j = 0; j < Tdim; ++j) + gradient[j] = this->volume() * dn_dx_(i, j); nodes_[i]->update_property(true, "domain_gradients", gradient, material_id_, Tdim); } @@ -656,7 +667,7 @@ void mpm::Particle::map_body_force(const VectorDim& pgravity) noexcept { // Compute nodal body forces for (unsigned i = 0; i < nodes_.size(); ++i) nodes_[i]->update_external_force(true, mpm::ParticlePhase::Solid, - (pgravity * mass_ * shapefn_(i))); + (pgravity * this->mass() * shapefn_(i))); } //! Map internal force @@ -666,7 +677,7 @@ inline void mpm::Particle<1>::map_internal_force() noexcept { for (unsigned i = 0; i < nodes_.size(); ++i) { // Compute force: -pstress * volume Eigen::Matrix force; - force[0] = -1. * dn_dx_(i, 0) * volume_ * stress_[0]; + force[0] = -1. * dn_dx_(i, 0) * this->volume() * stress_[0]; nodes_[i]->update_internal_force(true, mpm::ParticlePhase::Solid, force); } @@ -682,7 +693,7 @@ inline void mpm::Particle<2>::map_internal_force() noexcept { force[0] = dn_dx_(i, 0) * stress_[0] + dn_dx_(i, 1) * stress_[3]; force[1] = dn_dx_(i, 1) * stress_[1] + dn_dx_(i, 0) * stress_[3]; - force *= -1. * this->volume_; + force *= -1. * this->volume(); nodes_[i]->update_internal_force(true, mpm::ParticlePhase::Solid, force); } @@ -704,7 +715,7 @@ inline void mpm::Particle<3>::map_internal_force() noexcept { force[2] = dn_dx_(i, 2) * stress_[2] + dn_dx_(i, 1) * stress_[4] + dn_dx_(i, 0) * stress_[5]; - force *= -1. * this->volume_; + force *= -1. * this->volume(); nodes_[i]->update_internal_force(true, mpm::ParticlePhase::Solid, force); } @@ -725,12 +736,12 @@ bool mpm::Particle::assign_traction(unsigned direction, double traction) { bool status = false; try { if (direction >= Tdim || - this->volume_ == std::numeric_limits::max()) { + this->volume() == std::numeric_limits::max()) { throw std::runtime_error( "Particle traction property: volume / direction is invalid"); } // Assign traction - traction_(direction) = traction * this->volume_ / this->size_(direction); + traction_(direction) = traction * this->volume() / this->size_(direction); status = true; this->set_traction_ = true; } catch (std::exception& exception) { @@ -791,17 +802,17 @@ void mpm::Particle::compute_updated_position( template bool mpm::Particle::map_pressure_to_nodes() noexcept { // Mass is initialized - assert(mass_ != std::numeric_limits::max()); + assert(this->mass() != std::numeric_limits::max()); bool status = false; // Check if particle mass is set and state variable pressure is found - if (mass_ != std::numeric_limits::max() && + if (this->mass() != std::numeric_limits::max() && (state_variables_.find("pressure") != state_variables_.end())) { // Map particle pressure to nodes for (unsigned i = 0; i < nodes_.size(); ++i) nodes_[i]->update_mass_pressure( mpm::ParticlePhase::Solid, - shapefn_[i] * mass_ * state_variables_["pressure"]); + shapefn_[i] * this->mass() * state_variables_["pressure"]); status = true; } diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 0dfe49a8d..64f835b80 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -157,10 +157,10 @@ TEST_CASE("Particle is checked for 1D case", "[particle][1D]") { } // Check mass - REQUIRE(particle->mass() == Approx(0.0).epsilon(Tolerance)); - double mass = 100.5; - particle->assign_mass(mass); REQUIRE(particle->mass() == Approx(100.5).epsilon(Tolerance)); + double mass = 201.0; + particle->assign_mass(mass); + REQUIRE(particle->mass() == Approx(201.0).epsilon(Tolerance)); // Check stress Eigen::Matrix stress; @@ -1252,10 +1252,10 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { } // Check mass - REQUIRE(particle->mass() == Approx(0.0).epsilon(Tolerance)); - double mass = 100.5; - particle->assign_mass(mass); REQUIRE(particle->mass() == Approx(100.5).epsilon(Tolerance)); + double mass = 201.0; + particle->assign_mass(mass); + REQUIRE(particle->mass() == Approx(201.0).epsilon(Tolerance)); // Check stress Eigen::Matrix stress; From 0a6ac168b24006f9c35a5c9d21d5d00effca6107 Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 8 Jul 2020 16:34:26 -0700 Subject: [PATCH 05/55] :wrench: modify compute_mass function --- include/mesh.tcc | 2 +- include/particles/particle.h | 3 --- include/particles/particle.tcc | 13 ------------- include/particles/particle_base.h | 3 --- include/particles/particle_functions.tcc | 10 ++++++---- include/solvers/mpm_explicit.tcc | 4 +++- tests/particle_cell_crossing_test.cc | 10 ++++++---- tests/particle_test.cc | 22 ++++++++-------------- 8 files changed, 24 insertions(+), 43 deletions(-) diff --git a/include/mesh.tcc b/include/mesh.tcc index 0379a344b..3f03363c8 100644 --- a/include/mesh.tcc +++ b/include/mesh.tcc @@ -1752,7 +1752,7 @@ void mpm::Mesh::inject_particles(double current_time) { } for (auto particle : injected_particles) { particle->compute_volume(); - particle->compute_mass(); + mpm::particle::compute_mass(particle); } } } diff --git a/include/particles/particle.h b/include/particles/particle.h index 1b692ca97..1dc76319f 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -135,9 +135,6 @@ class Particle : public ParticleBase { return this->scalar_property(mpm::properties::Scalar::MassDensity); } - //! Compute mass as volume * density - void compute_mass() noexcept override; - //! Map particle mass and momentum to nodes void map_mass_momentum_to_nodes() noexcept override; diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 5eb9cdf72..1218cbab2 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -494,19 +494,6 @@ void mpm::Particle::update_volume() noexcept { (1. + dvolumetric_strain_); } -// Compute mass of particle -template -void mpm::Particle::compute_mass() noexcept { - // Check if particle volume is set and material ptr is valid - assert(this->volume() != std::numeric_limits::max() && - material_ != nullptr); - // Mass = volume of particle * mass_density - scalar_properties_.at(mpm::properties::Scalar::MassDensity) = - material_->template property(std::string("density")); - scalar_properties_.at(mpm::properties::Scalar::Mass) = - this->volume() * this->mass_density(); -} - //! Map particle mass and momentum to nodes template void mpm::Particle::map_mass_momentum_to_nodes() noexcept { diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index dcf9ec042..71f6269fb 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -181,9 +181,6 @@ class ParticleBase { //! Return mass density virtual double mass_density() const = 0; - //! Compute mass of particle - virtual void compute_mass() noexcept = 0; - //! Map particle mass and momentum to nodes virtual void map_mass_momentum_to_nodes() noexcept = 0; diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc index 0a5a71f4c..0cd83e5ee 100644 --- a/include/particles/particle_functions.tcc +++ b/include/particles/particle_functions.tcc @@ -7,11 +7,13 @@ void compute_mass(std::shared_ptr> particle) noexcept { assert(particle->volume() != std::numeric_limits::max() && particle->material() != nullptr); // Mass = volume of particle * mass_density - auto density = - (particle->material())->template property(std::string("density")); + particle->update_scalar_property( + mpm::properties::Scalar::MassDensity, false, + particle->material()->template property(std::string("density"))); // Update particle mass - particle->update_scalar_property(mpm::properties::Scalar::Mass, false, - particle->volume() * density); + particle->update_scalar_property( + mpm::properties::Scalar::Mass, false, + particle->volume() * particle->mass_density()); } } // namespace particle } // namespace mpm diff --git a/include/solvers/mpm_explicit.tcc b/include/solvers/mpm_explicit.tcc index 05ddf36c2..b8bddad5b 100644 --- a/include/solvers/mpm_explicit.tcc +++ b/include/solvers/mpm_explicit.tcc @@ -122,7 +122,9 @@ bool mpm::MPMExplicit::solve() { // Compute mass mesh_->iterate_over_particles( - std::bind(&mpm::ParticleBase::compute_mass, std::placeholders::_1)); + [](std::shared_ptr> ptr) { + return mpm::particle::compute_mass(ptr); + }); // Check point resume if (resume) this->checkpoint_resume(); diff --git a/tests/particle_cell_crossing_test.cc b/tests/particle_cell_crossing_test.cc index 05ca405fe..cd084d8f6 100644 --- a/tests/particle_cell_crossing_test.cc +++ b/tests/particle_cell_crossing_test.cc @@ -169,8 +169,9 @@ TEST_CASE("Particle cell crossing is checked for 2D case", &mpm::ParticleBase::compute_volume, std::placeholders::_1)); // Compute mass - mesh->iterate_over_particles( - std::bind(&mpm::ParticleBase::compute_mass, std::placeholders::_1)); + mesh->iterate_over_particles([](std::shared_ptr> ptr) { + return mpm::particle::compute_mass(ptr); + }); // Initialise nodes mesh->iterate_over_nodes( @@ -425,8 +426,9 @@ TEST_CASE("Particle cell crossing is checked for 3D case", &mpm::ParticleBase::compute_volume, std::placeholders::_1)); // Compute mass - mesh->iterate_over_particles( - std::bind(&mpm::ParticleBase::compute_mass, std::placeholders::_1)); + mesh->iterate_over_particles([](std::shared_ptr> ptr) { + return mpm::particle::compute_mass(ptr); + }); // Initialise nodes mesh->iterate_over_nodes( diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 64f835b80..f864a553f 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -734,9 +734,6 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { Factory, unsigned, const Json&>::instance()->create( "LinearElastic2D", std::move(mid), jmaterial); - // Check compute mass before material and volume - // TODO Assert: REQUIRE(particle->compute_mass() == false); - // Test compute stress before material assignment // TODO Assert: REQUIRE(particle->compute_stress() == false); @@ -750,7 +747,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { REQUIRE_NOTHROW(particle->compute_volume()); // Compute mass - REQUIRE_NOTHROW(particle->compute_mass()); + REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); // Mass REQUIRE(particle->mass() == Approx(1000.).epsilon(Tolerance)); // Compute mass function @@ -777,7 +774,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); - REQUIRE_NOTHROW(particle->compute_mass()); + REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); REQUIRE_NOTHROW(particle->map_mass_momentum_to_nodes()); // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); @@ -1073,7 +1070,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { REQUIRE_NOTHROW(particle->compute_volume()); // Compute mass - REQUIRE_NOTHROW(particle->compute_mass()); + REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); // Mass REQUIRE(particle->mass() == Approx(1000.).epsilon(Tolerance)); @@ -1095,7 +1092,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); - REQUIRE_NOTHROW(particle->compute_mass()); + REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); REQUIRE_NOTHROW(particle->map_mass_momentum_to_nodes()); // Check volumetric strain at centroid @@ -2008,9 +2005,6 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { Factory, unsigned, const Json&>::instance()->create( "LinearElastic3D", std::move(mid), jmaterial); - // Check compute mass before material and volume - // TODO Assert: REQUIRE(particle->compute_mass() == false); - // Test compute stress before material assignment // TODO Assert: REQUIRE(particle->compute_stress() == false); @@ -2024,7 +2018,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { REQUIRE_NOTHROW(particle->compute_volume()); // Compute mass - REQUIRE_NOTHROW(particle->compute_mass()); + REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); // Mass REQUIRE(particle->mass() == Approx(8000.).epsilon(Tolerance)); @@ -2047,7 +2041,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); - REQUIRE_NOTHROW(particle->compute_mass()); + REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); REQUIRE_NOTHROW(particle->map_mass_momentum_to_nodes()); // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); @@ -2337,7 +2331,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { REQUIRE_NOTHROW(particle->compute_volume()); // Compute mass - REQUIRE_NOTHROW(particle->compute_mass()); + REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); // Mass REQUIRE(particle->mass() == Approx(8000.).epsilon(Tolerance)); @@ -2359,7 +2353,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); - REQUIRE_NOTHROW(particle->compute_mass()); + REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); REQUIRE_NOTHROW(particle->map_mass_momentum_to_nodes()); // Check volumetric strain at centroid From adfd93f29227f8cecbdbd95dd1279e1b365bedf6 Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 8 Jul 2020 17:02:04 -0700 Subject: [PATCH 06/55] :wrench: refactor particle velocity and displacement --- include/particles/particle.h | 12 +++++------ include/particles/particle.tcc | 38 +++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/include/particles/particle.h b/include/particles/particle.h index 1dc76319f..ad347ba00 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -215,10 +215,14 @@ class Particle : public ParticleBase { bool assign_velocity(const VectorDim& velocity) override; //! Return velocity of the particle - VectorDim velocity() const override { return velocity_; } + VectorDim velocity() const override { + return this->vector_property(mpm::properties::Vector::Velocity); + } //! Return displacement of the particle - VectorDim displacement() const override { return displacement_; } + VectorDim displacement() const override { + return this->vector_property(mpm::properties::Vector::Displacement); + } //! Assign traction to the particle //! \param[in] direction Index corresponding to the direction of traction @@ -337,10 +341,6 @@ class Particle : public ParticleBase { Eigen::Matrix strain_rate_; //! dstrains Eigen::Matrix dstrain_; - //! Velocity - Eigen::Matrix velocity_; - //! Displacement - Eigen::Matrix displacement_; //! Particle velocity constraints std::map particle_velocity_constraints_; //! Set traction diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 1218cbab2..cead6c73a 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -59,13 +59,16 @@ bool mpm::Particle::initialise_particle(const HDF5Particle& particle) { displacement << particle.displacement_x, particle.displacement_y, particle.displacement_z; // Initialise displacement - for (unsigned i = 0; i < Tdim; ++i) this->displacement_(i) = displacement(i); + for (unsigned i = 0; i < Tdim; ++i) + vector_properties_.at(mpm::properties::Vector::Displacement)(i) = + displacement(i); // Velocity Eigen::Vector3d velocity; velocity << particle.velocity_x, particle.velocity_y, particle.velocity_z; // Initialise velocity - for (unsigned i = 0; i < Tdim; ++i) this->velocity_(i) = velocity(i); + for (unsigned i = 0; i < Tdim; ++i) + vector_properties_.at(mpm::properties::Vector::Velocity)(i) = velocity(i); // Stress this->stress_[0] = particle.stress_xx; @@ -143,11 +146,11 @@ mpm::HDF5Particle mpm::Particle::hdf5() const { Eigen::Vector3d displacement; displacement.setZero(); - for (unsigned j = 0; j < Tdim; ++j) displacement[j] = this->displacement_[j]; + for (unsigned j = 0; j < Tdim; ++j) displacement[j] = this->displacement()[j]; Eigen::Vector3d velocity; velocity.setZero(); - for (unsigned j = 0; j < Tdim; ++j) velocity[j] = this->velocity_[j]; + for (unsigned j = 0; j < Tdim; ++j) velocity[j] = this->velocity()[j]; // Particle local size Eigen::Vector3d nsize; @@ -224,7 +227,6 @@ mpm::HDF5Particle mpm::Particle::hdf5() const { // Initialise particle properties template void mpm::Particle::initialise() { - displacement_.setZero(); dstrain_.setZero(); natural_size_.setZero(); set_traction_ = false; @@ -233,7 +235,6 @@ void mpm::Particle::initialise() { strain_.setZero(); stress_.setZero(); traction_.setZero(); - velocity_.setZero(); volumetric_strain_centroid_ = 0.; // Initialize scalar properties @@ -244,6 +245,12 @@ void mpm::Particle::initialise() { scalar_properties_.emplace(std::make_pair( mpm::properties::Scalar::Volume, std::numeric_limits::max())); + // Initialize vector properties + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::Displacement, VectorDim::Zero())); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::Velocity, VectorDim::Zero())); + // Initialize vector data properties this->properties_["stresses"] = [&]() { return stress(); }; this->properties_["strains"] = [&]() { return strain(); }; @@ -505,7 +512,7 @@ void mpm::Particle::map_mass_momentum_to_nodes() noexcept { nodes_[i]->update_mass(true, mpm::ParticlePhase::Solid, this->mass() * shapefn_[i]); nodes_[i]->update_momentum(true, mpm::ParticlePhase::Solid, - this->mass() * shapefn_[i] * velocity_); + this->mass() * shapefn_[i] * this->velocity()); } } @@ -522,7 +529,7 @@ void mpm::Particle::map_multimaterial_mass_momentum_to_nodes() noexcept { for (unsigned i = 0; i < nodes_.size(); ++i) { nodal_mass(0, 0) = this->mass() * shapefn_[i]; nodes_[i]->update_property(true, "masses", nodal_mass, material_id_, 1); - nodes_[i]->update_property(true, "momenta", velocity_ * nodal_mass, + nodes_[i]->update_property(true, "momenta", this->velocity() * nodal_mass, material_id_, Tdim); } } @@ -536,7 +543,8 @@ void mpm::Particle::map_multimaterial_displacements_to_nodes() noexcept { // Map displacements to nodal property and divide it by the respective // nodal-material mass for (unsigned i = 0; i < nodes_.size(); ++i) { - const auto& displacement = this->mass() * shapefn_[i] * displacement_; + const auto& displacement = + this->mass() * shapefn_[i] * this->displacement(); nodes_[i]->update_property(true, "displacements", displacement, material_id_, Tdim); } @@ -713,7 +721,7 @@ template bool mpm::Particle::assign_velocity( const Eigen::Matrix& velocity) { // Assign velocity - velocity_ = velocity; + vector_properties_.at(mpm::properties::Vector::Velocity) = velocity; return true; } @@ -773,16 +781,18 @@ void mpm::Particle::compute_updated_position( shapefn_[i] * nodes_[i]->acceleration(mpm::ParticlePhase::Solid); // Update particle velocity from interpolated nodal acceleration - this->velocity_ += nodal_acceleration * dt; + vector_properties_.at(mpm::properties::Vector::Velocity) += + nodal_acceleration * dt; } // Update particle velocity using interpolated nodal velocity else - this->velocity_ = nodal_velocity; + vector_properties_.at(mpm::properties::Vector::Velocity) = nodal_velocity; // New position current position + velocity * dt this->coordinates_ += nodal_velocity * dt; // Update displacement (displacement is initialized from zero) - this->displacement_ += nodal_velocity * dt; + vector_properties_.at(mpm::properties::Vector::Displacement) += + nodal_velocity * dt; } //! Map particle pressure to nodes @@ -833,7 +843,7 @@ template void mpm::Particle::apply_particle_velocity_constraints(unsigned dir, double velocity) { // Set particle velocity constraint - this->velocity_(dir) = velocity; + vector_properties_.at(mpm::properties::Vector::Velocity)(dir) = velocity; } //! Return particle tensor data From 2b92b3f059041626218e5213501da208f791aea8 Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 8 Jul 2020 18:14:28 -0700 Subject: [PATCH 07/55] :wrench: refactor nodal mass and volume --- include/node.h | 12 ++++++------ include/node.tcc | 50 ++++++++++++++++++++++++------------------------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/include/node.h b/include/node.h index bb875ec36..490386fe5 100644 --- a/include/node.h +++ b/include/node.h @@ -100,7 +100,9 @@ class Node : public NodeBase { //! Return mass at a given node for a given phase //! \param[in] phase Index corresponding to the phase - double mass(unsigned phase) const override { return mass_(phase); } + double mass(unsigned phase) const override { + return this->scalar_property(mpm::properties::Scalar::Mass, phase); + } //! Update volume at the nodes from particle //! \param[in] update A boolean to update (true) or assign (false) @@ -111,7 +113,9 @@ class Node : public NodeBase { //! Return volume at a given node for a given phase //! \param[in] phase Index corresponding to the phase - double volume(unsigned phase) const override { return volume_(phase); } + double volume(unsigned phase) const override { + return this->scalar_property(mpm::properties::Scalar::Volume, phase); + } //! Assign concentrated force to the node //! \param[in] phase Index corresponding to the phase @@ -312,10 +316,6 @@ class Node : public NodeBase { tsl::ordered_map> vector_properties_; - //! Mass - Eigen::Matrix mass_; - //! Volume - Eigen::Matrix volume_; //! External force Eigen::Matrix external_force_; //! Internal force diff --git a/include/node.tcc b/include/node.tcc index 4ed495285..fcce85bb5 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -17,9 +17,14 @@ mpm::Node::Node( // Clear any velocity constraints velocity_constraints_.clear(); concentrated_force_.setZero(); + + // Initialize scalar properties scalar_properties_.emplace( std::make_pair(mpm::properties::Scalar::Mass, Eigen::Matrix::Zero())); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::Volume, + Eigen::Matrix::Zero())); this->initialise(); } @@ -27,8 +32,6 @@ mpm::Node::Node( //! Initialise nodal properties template void mpm::Node::initialise() noexcept { - mass_.setZero(); - volume_.setZero(); external_force_.setZero(); internal_force_.setZero(); pressure_.setZero(); @@ -39,6 +42,7 @@ void mpm::Node::initialise() noexcept { status_ = false; material_ids_.clear(); scalar_properties_.at(mpm::properties::Scalar::Mass).setZero(); + scalar_properties_.at(mpm::properties::Scalar::Volume).setZero(); } //! Initialise shared pointer to nodal properties pool @@ -98,24 +102,16 @@ Eigen::Matrix mpm::Node::vector_property( template void mpm::Node::update_mass(bool update, unsigned phase, double mass) noexcept { - // Decide to update or assign - const double factor = (update == true) ? 1. : 0.; - - // Update/assign mass - std::lock_guard guard(node_mutex_); - mass_(phase) = (mass_(phase) * factor) + mass; + this->update_scalar_property(mpm::properties::Scalar::Mass, update, phase, + mass); } //! Update volume at the nodes from particle template void mpm::Node::update_volume(bool update, unsigned phase, double volume) noexcept { - // Decide to update or assign - const double factor = (update == true) ? 1. : 0.; - - // Update/assign volume - std::lock_guard guard(node_mutex_); - volume_(phase) = volume_(phase) * factor + volume; + this->update_scalar_property(mpm::properties::Scalar::Volume, update, phase, + volume); } // Assign concentrated force to the node @@ -208,9 +204,9 @@ void mpm::Node::update_mass_pressure( const double tolerance = 1.E-16; // Compute pressure from mass*pressure - if (mass_(phase) > tolerance) { + if (this->mass(phase) > tolerance) { std::lock_guard guard(node_mutex_); - pressure_(phase) += mass_pressure / mass_(phase); + pressure_(phase) += mass_pressure / this->mass(phase); } } @@ -229,8 +225,8 @@ template void mpm::Node::compute_velocity() { const double tolerance = 1.E-16; for (unsigned phase = 0; phase < Tnphases; ++phase) { - if (mass_(phase) > tolerance) { - velocity_.col(phase) = momentum_.col(phase) / mass_(phase); + if (this->mass(phase) > tolerance) { + velocity_.col(phase) = momentum_.col(phase) / this->mass(phase); // Check to see if value is below threshold for (unsigned i = 0; i < velocity_.rows(); ++i) @@ -265,11 +261,11 @@ bool mpm::Node::compute_acceleration_velocity( unsigned phase, double dt) noexcept { bool status = false; const double tolerance = 1.0E-15; - if (mass_(phase) > tolerance) { + if (this->mass(phase) > tolerance) { // acceleration = (unbalaced force / mass) this->acceleration_.col(phase) = (this->external_force_.col(phase) + this->internal_force_.col(phase)) / - this->mass_(phase); + this->mass(phase); // Apply friction constraints this->apply_friction_constraints(dt); @@ -298,14 +294,14 @@ bool mpm::Node::compute_acceleration_velocity_cundall( unsigned phase, double dt, double damping_factor) noexcept { bool status = false; const double tolerance = 1.0E-15; - if (mass_(phase) > tolerance) { + if (this->mass(phase) > tolerance) { // acceleration = (unbalaced force / mass) auto unbalanced_force = this->external_force_.col(phase) + this->internal_force_.col(phase); this->acceleration_.col(phase) = (unbalanced_force - damping_factor * unbalanced_force.norm() * this->velocity_.col(phase).cwiseSign()) / - this->mass_(phase); + this->mass(phase); // Apply friction constraints this->apply_friction_constraints(dt); @@ -621,7 +617,9 @@ void mpm::Nodeproperty("masses", prop_id_, *mitr); // displacement of the center of mass - contact_displacement_ += material_displacement / mass_(0, 0); + contact_displacement_ += + material_displacement / + scalar_properties_.at(mpm::properties::Scalar::Mass)(0, 0); // assign nodal-multimaterial displacement by dividing it by this material's // mass property_handle_->assign_property( @@ -639,8 +637,10 @@ void mpm::Nodeupdate_property("separation_vectors", prop_id_, *mitr, separation_vector, Tdim); } From 336cfe6f5f7dd86dcd8f7af28c561a3c204c0033 Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 8 Jul 2020 19:20:08 -0700 Subject: [PATCH 08/55] :wrench: refactor nodal velocity and acceleration --- include/node.h | 8 +--- include/node.tcc | 120 ++++++++++++++++++++++++++++------------------- 2 files changed, 75 insertions(+), 53 deletions(-) diff --git a/include/node.h b/include/node.h index 490386fe5..37a96a30b 100644 --- a/include/node.h +++ b/include/node.h @@ -193,7 +193,7 @@ class Node : public NodeBase { //! Return velocity at a given node for a given phase //! \param[in] phase Index corresponding to the phase VectorDim velocity(unsigned phase) const override { - return velocity_.col(phase); + return this->vector_property(mpm::properties::Vector::Velocity, phase); } //! Update nodal acceleration @@ -206,7 +206,7 @@ class Node : public NodeBase { //! Return acceleration at a given node for a given phase //! \param[in] phase Index corresponding to the phase VectorDim acceleration(unsigned phase) const override { - return acceleration_.col(phase); + return this->vector_property(mpm::properties::Vector::Acceleration, phase); } //! Compute acceleration and velocity @@ -324,12 +324,8 @@ class Node : public NodeBase { Eigen::Matrix pressure_; //! Displacement Eigen::Matrix contact_displacement_; - //! Velocity - Eigen::Matrix velocity_; //! Momentum Eigen::Matrix momentum_; - //! Acceleration - Eigen::Matrix acceleration_; //! Velocity constraints std::map velocity_constraints_; //! Rotation matrix for general velocity constraints diff --git a/include/node.tcc b/include/node.tcc index fcce85bb5..5abb68a09 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -26,6 +26,14 @@ mpm::Node::Node( std::make_pair(mpm::properties::Scalar::Volume, Eigen::Matrix::Zero())); + // Initialize vector properties + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::Velocity, + Eigen::Matrix::Zero())); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::Acceleration, + Eigen::Matrix::Zero())); + this->initialise(); } @@ -36,13 +44,13 @@ void mpm::Node::initialise() noexcept { internal_force_.setZero(); pressure_.setZero(); contact_displacement_.setZero(); - velocity_.setZero(); momentum_.setZero(); - acceleration_.setZero(); status_ = false; material_ids_.clear(); scalar_properties_.at(mpm::properties::Scalar::Mass).setZero(); scalar_properties_.at(mpm::properties::Scalar::Volume).setZero(); + vector_properties_.at(mpm::properties::Vector::Velocity).setZero(); + vector_properties_.at(mpm::properties::Vector::Acceleration).setZero(); } //! Initialise shared pointer to nodal properties pool @@ -226,12 +234,14 @@ void mpm::Node::compute_velocity() { const double tolerance = 1.E-16; for (unsigned phase = 0; phase < Tnphases; ++phase) { if (this->mass(phase) > tolerance) { - velocity_.col(phase) = momentum_.col(phase) / this->mass(phase); + vector_properties_.at(mpm::properties::Vector::Velocity).col(phase) = + momentum_.col(phase) / this->mass(phase); // Check to see if value is below threshold - for (unsigned i = 0; i < velocity_.rows(); ++i) - if (std::abs(velocity_.col(phase)(i)) < 1.E-15) - velocity_.col(phase)(i) = 0.; + for (unsigned i = 0; i < Tdim; ++i) + if (std::abs(this->velocity(phase)(i)) < 1.E-15) + vector_properties_.at(mpm::properties::Vector::Velocity) + .col(phase)(i) = 0.; } } @@ -245,14 +255,8 @@ template void mpm::Node::update_acceleration( bool update, unsigned phase, const Eigen::Matrix& acceleration) noexcept { - assert(phase < Tnphases); - - // Decide to update or assign - const double factor = (update == true) ? 1. : 0.; - - //! Update/assign acceleration - std::lock_guard guard(node_mutex_); - acceleration_.col(phase) = acceleration_.col(phase) * factor + acceleration; + this->update_vector_property(mpm::properties::Vector::Acceleration, update, + phase, acceleration); } //! Compute acceleration and velocity @@ -263,7 +267,7 @@ bool mpm::Node::compute_acceleration_velocity( const double tolerance = 1.0E-15; if (this->mass(phase) > tolerance) { // acceleration = (unbalaced force / mass) - this->acceleration_.col(phase) = + vector_properties_.at(mpm::properties::Vector::Acceleration).col(phase) = (this->external_force_.col(phase) + this->internal_force_.col(phase)) / this->mass(phase); @@ -271,18 +275,21 @@ bool mpm::Node::compute_acceleration_velocity( this->apply_friction_constraints(dt); // Velocity += acceleration * dt - this->velocity_.col(phase) += this->acceleration_.col(phase) * dt; + vector_properties_.at(mpm::properties::Vector::Velocity).col(phase) += + this->acceleration(phase) * dt; // Apply velocity constraints, which also sets acceleration to 0, // when velocity is set. this->apply_velocity_constraints(); // Set a threshold for (unsigned i = 0; i < Tdim; ++i) - if (std::abs(velocity_.col(phase)(i)) < tolerance) - velocity_.col(phase)(i) = 0.; + if (std::abs(this->velocity(phase)(i)) < tolerance) + vector_properties_.at(mpm::properties::Vector::Velocity).col(phase)(i) = + 0.; for (unsigned i = 0; i < Tdim; ++i) - if (std::abs(acceleration_.col(phase)(i)) < tolerance) - acceleration_.col(phase)(i) = 0.; + if (std::abs(this->acceleration(phase)(i)) < tolerance) + vector_properties_.at(mpm::properties::Vector::Acceleration) + .col(phase)(i) = 0.; status = true; } return status; @@ -298,27 +305,30 @@ bool mpm::Node::compute_acceleration_velocity_cundall( // acceleration = (unbalaced force / mass) auto unbalanced_force = this->external_force_.col(phase) + this->internal_force_.col(phase); - this->acceleration_.col(phase) = + vector_properties_.at(mpm::properties::Vector::Acceleration).col(phase) = (unbalanced_force - damping_factor * unbalanced_force.norm() * - this->velocity_.col(phase).cwiseSign()) / + this->velocity(phase).cwiseSign()) / this->mass(phase); // Apply friction constraints this->apply_friction_constraints(dt); // Velocity += acceleration * dt - this->velocity_.col(phase) += this->acceleration_.col(phase) * dt; + vector_properties_.at(mpm::properties::Vector::Velocity).col(phase) += + this->acceleration(phase) * dt; // Apply velocity constraints, which also sets acceleration to 0, // when velocity is set. this->apply_velocity_constraints(); // Set a threshold for (unsigned i = 0; i < Tdim; ++i) - if (std::abs(velocity_.col(phase)(i)) < tolerance) - velocity_.col(phase)(i) = 0.; + if (std::abs(this->velocity(phase)(i)) < tolerance) + vector_properties_.at(mpm::properties::Vector::Velocity).col(phase)(i) = + 0.; for (unsigned i = 0; i < Tdim; ++i) - if (std::abs(acceleration_.col(phase)(i)) < tolerance) - acceleration_.col(phase)(i) = 0.; + if (std::abs(this->acceleration(phase)(i)) < tolerance) + vector_properties_.at(mpm::properties::Vector::Acceleration) + .col(phase)(i) = 0.; status = true; } return status; @@ -359,9 +369,11 @@ void mpm::Node::apply_velocity_constraints() { if (!generic_boundary_constraints_) { // Velocity constraints are applied on Cartesian boundaries - this->velocity_(direction, phase) = constraint.second; + vector_properties_.at(mpm::properties::Vector::Velocity)( + direction, phase) = constraint.second; // Set acceleration to 0 in direction of velocity constraint - this->acceleration_(direction, phase) = 0.; + vector_properties_.at(mpm::properties::Vector::Acceleration)(direction, + phase) = 0.; } else { // Velocity constraints on general boundaries // Compute inverse rotation matrix @@ -369,15 +381,19 @@ void mpm::Node::apply_velocity_constraints() { rotation_matrix_.inverse(); // Transform to local coordinate Eigen::Matrix local_velocity = - inverse_rotation_matrix * this->velocity_; + inverse_rotation_matrix * + vector_properties_.at(mpm::properties::Vector::Velocity); Eigen::Matrix local_acceleration = - inverse_rotation_matrix * this->acceleration_; + inverse_rotation_matrix * + vector_properties_.at(mpm::properties::Vector::Acceleration); // Apply boundary condition in local coordinate local_velocity(direction, phase) = constraint.second; local_acceleration(direction, phase) = 0.; // Transform back to global coordinate - this->velocity_ = rotation_matrix_ * local_velocity; - this->acceleration_ = rotation_matrix_ * local_acceleration; + vector_properties_.at(mpm::properties::Vector::Velocity) = + rotation_matrix_ * local_velocity; + vector_properties_.at(mpm::properties::Vector::Acceleration) = + rotation_matrix_ * local_acceleration; } } } @@ -433,10 +449,13 @@ void mpm::Node::apply_friction_constraints(double dt) { if (!generic_boundary_constraints_) { // Cartesian case // Normal and tangential acceleration - acc_n = this->acceleration_(dir_n, phase); - acc_t = this->acceleration_(dir_t, phase); + acc_n = vector_properties_.at(mpm::properties::Vector::Acceleration)( + dir_n, phase); + acc_t = vector_properties_.at(mpm::properties::Vector::Acceleration)( + dir_t, phase); // Velocity tangential - vel_t = this->velocity_(dir_t, phase); + vel_t = vector_properties_.at(mpm::properties::Vector::Velocity)(dir_t, + phase); } else { // General case, transform to local coordinate // Compute inverse rotation matrix @@ -444,9 +463,11 @@ void mpm::Node::apply_friction_constraints(double dt) { rotation_matrix_.inverse(); // Transform to local coordinate Eigen::Matrix local_acceleration = - inverse_rotation_matrix * this->acceleration_; + inverse_rotation_matrix * + vector_properties_.at(mpm::properties::Vector::Acceleration); Eigen::Matrix local_velocity = - inverse_rotation_matrix * this->velocity_; + inverse_rotation_matrix * + vector_properties_.at(mpm::properties::Vector::Velocity); // Normal and tangential acceleration acc_n = local_acceleration(dir_n, phase); acc_t = local_acceleration(dir_t, phase); @@ -471,7 +492,8 @@ void mpm::Node::apply_friction_constraints(double dt) { if (!generic_boundary_constraints_) { // Cartesian case - this->acceleration_(dir_t, phase) = acc_t; + vector_properties_.at(mpm::properties::Vector::Acceleration)( + dir_t, phase) = acc_t; } else { // Local acceleration in terms of tangential and normal Eigen::Matrix acc; @@ -479,7 +501,8 @@ void mpm::Node::apply_friction_constraints(double dt) { acc(dir_n, phase) = acc_n; // General case, transform to global coordinate - this->acceleration_.col(phase) = rotation_matrix_ * acc.col(phase); + vector_properties_.at(mpm::properties::Vector::Acceleration) + .col(phase) = rotation_matrix_ * acc.col(phase); } } } else if (Tdim == 3) { @@ -497,16 +520,16 @@ void mpm::Node::apply_friction_constraints(double dt) { Eigen::Matrix acc, vel; if (!generic_boundary_constraints_) { // Cartesian case - acc = this->acceleration_.col(phase); - vel = this->velocity_.col(phase); + acc = this->acceleration(phase); + vel = this->velocity(phase); } else { // General case, transform to local coordinate // Compute inverse rotation matrix const Eigen::Matrix inverse_rotation_matrix = rotation_matrix_.inverse(); // Transform to local coordinate - acc = inverse_rotation_matrix * this->acceleration_.col(phase); - vel = inverse_rotation_matrix * this->velocity_.col(phase); + acc = inverse_rotation_matrix * this->acceleration(phase); + vel = inverse_rotation_matrix * this->velocity(phase); } const auto acc_n = acc(dir_n); @@ -546,10 +569,12 @@ void mpm::Node::apply_friction_constraints(double dt) { if (!generic_boundary_constraints_) { // Cartesian case - this->acceleration_.col(phase) = acc; + vector_properties_.at(mpm::properties::Vector::Acceleration) + .col(phase) = acc; } else { // General case, transform to global coordinate - this->acceleration_.col(phase) = rotation_matrix_ * acc; + vector_properties_.at(mpm::properties::Vector::Acceleration) + .col(phase) = rotation_matrix_ * acc; } } } @@ -596,7 +621,8 @@ void mpm::Node momentum = property_handle_->property("momenta", prop_id_, *mitr, Tdim); const Eigen::Matrix change_in_momenta = - velocity_ * mass - momentum; + vector_properties_.at(mpm::properties::Vector::Velocity) * mass - + momentum; property_handle_->update_property("change_in_momenta", prop_id_, *mitr, change_in_momenta, Tdim); } From 91d5c974b355161dcdf4a8601d612dbf9be69d09 Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 8 Jul 2020 20:11:11 -0700 Subject: [PATCH 09/55] :wrench: refactor nodal momentum, external, and internal forces --- include/node.h | 12 +++-------- include/node.tcc | 54 +++++++++++++++++++----------------------------- 2 files changed, 24 insertions(+), 42 deletions(-) diff --git a/include/node.h b/include/node.h index 37a96a30b..d302d3683 100644 --- a/include/node.h +++ b/include/node.h @@ -142,7 +142,7 @@ class Node : public NodeBase { //! Return external force at a given node for a given phase //! \param[in] phase Index corresponding to the phase VectorDim external_force(unsigned phase) const override { - return external_force_.col(phase); + return this->vector_property(mpm::properties::Vector::ExternalForce, phase); } //! Update internal force (body force / traction force) @@ -155,7 +155,7 @@ class Node : public NodeBase { //! Return internal force at a given node for a given phase //! \param[in] phase Index corresponding to the phase VectorDim internal_force(unsigned phase) const override { - return internal_force_.col(phase); + return this->vector_property(mpm::properties::Vector::InternalForce, phase); } //! Update pressure at the nodes from particle @@ -184,7 +184,7 @@ class Node : public NodeBase { //! Return momentum at a given node for a given phase //! \param[in] phase Index corresponding to the phase VectorDim momentum(unsigned phase) const override { - return momentum_.col(phase); + return this->vector_property(mpm::properties::Vector::Momentum, phase); } //! Compute velocity from the momentum @@ -316,16 +316,10 @@ class Node : public NodeBase { tsl::ordered_map> vector_properties_; - //! External force - Eigen::Matrix external_force_; - //! Internal force - Eigen::Matrix internal_force_; //! Pressure Eigen::Matrix pressure_; //! Displacement Eigen::Matrix contact_displacement_; - //! Momentum - Eigen::Matrix momentum_; //! Velocity constraints std::map velocity_constraints_; //! Rotation matrix for general velocity constraints diff --git a/include/node.tcc b/include/node.tcc index 5abb68a09..49f28e8f4 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -33,6 +33,15 @@ mpm::Node::Node( vector_properties_.emplace( std::make_pair(mpm::properties::Vector::Acceleration, Eigen::Matrix::Zero())); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::Momentum, + Eigen::Matrix::Zero())); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::ExternalForce, + Eigen::Matrix::Zero())); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::InternalForce, + Eigen::Matrix::Zero())); this->initialise(); } @@ -40,17 +49,17 @@ mpm::Node::Node( //! Initialise nodal properties template void mpm::Node::initialise() noexcept { - external_force_.setZero(); - internal_force_.setZero(); pressure_.setZero(); contact_displacement_.setZero(); - momentum_.setZero(); status_ = false; material_ids_.clear(); scalar_properties_.at(mpm::properties::Scalar::Mass).setZero(); scalar_properties_.at(mpm::properties::Scalar::Volume).setZero(); vector_properties_.at(mpm::properties::Vector::Velocity).setZero(); vector_properties_.at(mpm::properties::Vector::Acceleration).setZero(); + vector_properties_.at(mpm::properties::Vector::Momentum).setZero(); + vector_properties_.at(mpm::properties::Vector::ExternalForce).setZero(); + vector_properties_.at(mpm::properties::Vector::InternalForce).setZero(); } //! Initialise shared pointer to nodal properties pool @@ -160,15 +169,8 @@ template void mpm::Node::update_external_force( bool update, unsigned phase, const Eigen::Matrix& force) noexcept { - // Assert - assert(phase < Tnphases); - - // Decide to update or assign - const double factor = (update == true) ? 1. : 0.; - - // Update/assign external force - std::lock_guard guard(node_mutex_); - external_force_.col(phase) = external_force_.col(phase) * factor + force; + this->update_vector_property(mpm::properties::Vector::ExternalForce, update, + phase, force); } //! Update internal force (body force / traction force) @@ -176,15 +178,8 @@ template void mpm::Node::update_internal_force( bool update, unsigned phase, const Eigen::Matrix& force) noexcept { - // Assert - assert(phase < Tnphases); - - // Decide to update or assign - const double factor = (update == true) ? 1. : 0.; - - // Update/assign internal force - std::lock_guard guard(node_mutex_); - internal_force_.col(phase) = internal_force_.col(phase) * factor + force; + this->update_vector_property(mpm::properties::Vector::InternalForce, update, + phase, force); } //! Assign nodal momentum @@ -192,15 +187,8 @@ template void mpm::Node::update_momentum( bool update, unsigned phase, const Eigen::Matrix& momentum) noexcept { - // Assert - assert(phase < Tnphases); - - // Decide to update or assign - const double factor = (update == true) ? 1. : 0.; - - // Update/assign momentum - std::lock_guard guard(node_mutex_); - momentum_.col(phase) = momentum_.col(phase) * factor + momentum; + this->update_vector_property(mpm::properties::Vector::Momentum, update, phase, + momentum); } //! Update pressure at the nodes from particle @@ -235,7 +223,7 @@ void mpm::Node::compute_velocity() { for (unsigned phase = 0; phase < Tnphases; ++phase) { if (this->mass(phase) > tolerance) { vector_properties_.at(mpm::properties::Vector::Velocity).col(phase) = - momentum_.col(phase) / this->mass(phase); + this->momentum(phase) / this->mass(phase); // Check to see if value is below threshold for (unsigned i = 0; i < Tdim; ++i) @@ -268,7 +256,7 @@ bool mpm::Node::compute_acceleration_velocity( if (this->mass(phase) > tolerance) { // acceleration = (unbalaced force / mass) vector_properties_.at(mpm::properties::Vector::Acceleration).col(phase) = - (this->external_force_.col(phase) + this->internal_force_.col(phase)) / + (this->external_force(phase) + this->internal_force(phase)) / this->mass(phase); // Apply friction constraints @@ -304,7 +292,7 @@ bool mpm::Node::compute_acceleration_velocity_cundall( if (this->mass(phase) > tolerance) { // acceleration = (unbalaced force / mass) auto unbalanced_force = - this->external_force_.col(phase) + this->internal_force_.col(phase); + this->external_force(phase) + this->internal_force(phase); vector_properties_.at(mpm::properties::Vector::Acceleration).col(phase) = (unbalanced_force - damping_factor * unbalanced_force.norm() * this->velocity(phase).cwiseSign()) / From 8ed2eb8f4d9273be81ef26674f821252fef2d6ef Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 8 Jul 2020 20:16:43 -0700 Subject: [PATCH 10/55] :wrench: refactor nodal pressure --- include/node.h | 6 +++--- include/node.tcc | 14 +++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/node.h b/include/node.h index d302d3683..4dee319bc 100644 --- a/include/node.h +++ b/include/node.h @@ -172,7 +172,9 @@ class Node : public NodeBase { //! Return pressure at a given node for a given phase //! \param[in] phase Index corresponding to the phase - double pressure(unsigned phase) const override { return pressure_(phase); } + double pressure(unsigned phase) const override { + return this->scalar_property(mpm::properties::Scalar::Pressure, phase); + } //! Update momentum at the nodes //! \param[in] update A boolean to update (true) or assign (false) @@ -316,8 +318,6 @@ class Node : public NodeBase { tsl::ordered_map> vector_properties_; - //! Pressure - Eigen::Matrix pressure_; //! Displacement Eigen::Matrix contact_displacement_; //! Velocity constraints diff --git a/include/node.tcc b/include/node.tcc index 49f28e8f4..f46f812c8 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -25,6 +25,9 @@ mpm::Node::Node( scalar_properties_.emplace( std::make_pair(mpm::properties::Scalar::Volume, Eigen::Matrix::Zero())); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::Pressure, + Eigen::Matrix::Zero())); // Initialize vector properties vector_properties_.emplace( @@ -49,12 +52,16 @@ mpm::Node::Node( //! Initialise nodal properties template void mpm::Node::initialise() noexcept { - pressure_.setZero(); contact_displacement_.setZero(); status_ = false; material_ids_.clear(); + + // Initialise nodal scalar properties scalar_properties_.at(mpm::properties::Scalar::Mass).setZero(); scalar_properties_.at(mpm::properties::Scalar::Volume).setZero(); + scalar_properties_.at(mpm::properties::Scalar::Pressure).setZero(); + + // Initialise nodal vector properties vector_properties_.at(mpm::properties::Vector::Velocity).setZero(); vector_properties_.at(mpm::properties::Vector::Acceleration).setZero(); vector_properties_.at(mpm::properties::Vector::Momentum).setZero(); @@ -202,7 +209,8 @@ void mpm::Node::update_mass_pressure( // Compute pressure from mass*pressure if (this->mass(phase) > tolerance) { std::lock_guard guard(node_mutex_); - pressure_(phase) += mass_pressure / this->mass(phase); + scalar_properties_.at(mpm::properties::Scalar::Pressure)(phase) += + mass_pressure / this->mass(phase); } } @@ -212,7 +220,7 @@ void mpm::Node::assign_pressure(unsigned phase, double pressure) { // Compute pressure from mass*pressure std::lock_guard guard(node_mutex_); - pressure_(phase) = pressure; + scalar_properties_.at(mpm::properties::Scalar::Pressure)(phase) = pressure; } //! Compute velocity from momentum From 02911bf8c85b89fe7ad73985e5919b3bb48b28f3 Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 8 Jul 2020 20:17:07 -0700 Subject: [PATCH 11/55] :wrench: Add scalar and vector properties enum --- include/mpm_properties.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/mpm_properties.h b/include/mpm_properties.h index cff0edced..69e87dac7 100644 --- a/include/mpm_properties.h +++ b/include/mpm_properties.h @@ -4,9 +4,16 @@ namespace mpm { namespace properties { //! Scalar Properties -enum Scalar : unsigned int { Mass }; +enum Scalar : unsigned int { Mass, Volume, MassDensity, Pressure }; //! Vector Properties -enum Vector : unsigned int { Velocity, Displacement, Acceleration }; +enum Vector : unsigned int { + Displacement, + Velocity, + Acceleration, + Momentum, + ExternalForce, + InternalForce +}; } // namespace properties } // namespace mpm From 0feaf6d0e9437306dda82fd9e391c8c695600372 Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 8 Jul 2020 20:33:58 -0700 Subject: [PATCH 12/55] :wrench: aesthetic clean up order of initialise() --- include/node.tcc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/node.tcc b/include/node.tcc index f46f812c8..37c70e490 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -52,9 +52,7 @@ mpm::Node::Node( //! Initialise nodal properties template void mpm::Node::initialise() noexcept { - contact_displacement_.setZero(); status_ = false; - material_ids_.clear(); // Initialise nodal scalar properties scalar_properties_.at(mpm::properties::Scalar::Mass).setZero(); @@ -67,6 +65,10 @@ void mpm::Node::initialise() noexcept { vector_properties_.at(mpm::properties::Vector::Momentum).setZero(); vector_properties_.at(mpm::properties::Vector::ExternalForce).setZero(); vector_properties_.at(mpm::properties::Vector::InternalForce).setZero(); + + // Initialise variables for contact + material_ids_.clear(); + contact_displacement_.setZero(); } //! Initialise shared pointer to nodal properties pool From bcfb8781a4bb2fef6dd5f79c312e83470fa03f23 Mon Sep 17 00:00:00 2001 From: Nanda Date: Thu, 9 Jul 2020 15:54:46 -0700 Subject: [PATCH 13/55] :wrench: fix particle vector_properties_ size order --- include/particles/particle_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index 71f6269fb..54fb2e30a 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -336,7 +336,7 @@ class ParticleBase { //! Scalar properties tsl::ordered_map scalar_properties_; //! Vector properties - tsl::ordered_map> + tsl::ordered_map> vector_properties_; }; // ParticleBase class } // namespace mpm From 99b9eead998f3aaee24a039d02b08551f65ef445 Mon Sep 17 00:00:00 2001 From: Nanda Date: Thu, 9 Jul 2020 15:58:36 -0700 Subject: [PATCH 14/55] :dart: add testings for update and return functions --- tests/particle_test.cc | 124 ++++++++++++++++++++++++++++++++--------- 1 file changed, 98 insertions(+), 26 deletions(-) diff --git a/tests/particle_test.cc b/tests/particle_test.cc index f864a553f..b4e297f09 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -144,24 +144,25 @@ TEST_CASE("Particle is checked for 1D case", "[particle][1D]") { std::shared_ptr> particle = std::make_shared>(id, coords); - // Check scalar properties - SECTION("Check scalar properties") { - // Check mass - REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == - Approx(0.0).epsilon(Tolerance)); - double mass = 100.5; - particle->update_scalar_property(mpm::properties::Scalar::Mass, true, - mass); - REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == - Approx(100.5).epsilon(Tolerance)); - } - // Check mass - REQUIRE(particle->mass() == Approx(100.5).epsilon(Tolerance)); + REQUIRE(particle->mass() == Approx(0.).epsilon(Tolerance)); double mass = 201.0; particle->assign_mass(mass); REQUIRE(particle->mass() == Approx(201.0).epsilon(Tolerance)); + // Check mass using scalar properties + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(201.0).epsilon(Tolerance)); + mass = 100.5; + particle->update_scalar_property(mpm::properties::Scalar::Mass, false, + mass); + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(100.5).epsilon(Tolerance)); + + particle->update_scalar_property(mpm::properties::Scalar::Mass, true, mass); + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(201.).epsilon(Tolerance)); + // Check stress Eigen::Matrix stress; for (unsigned i = 0; i < stress.size(); ++i) stress(i) = 17.51; @@ -181,6 +182,25 @@ TEST_CASE("Particle is checked for 1D case", "[particle][1D]") { for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(17.51).epsilon(Tolerance)); + // Check velocity using vector properties + for (unsigned i = 0; i < velocity.size(); ++i) + REQUIRE(particle->vector_property(mpm::properties::Vector::Velocity)(i) == + Approx(17.51).epsilon(Tolerance)); + + for (unsigned i = 0; i < velocity.size(); ++i) velocity(i) = 13.88; + + particle->update_vector_property(mpm::properties::Vector::Velocity, false, + velocity); + for (unsigned i = 0; i < velocity.size(); ++i) + REQUIRE(particle->vector_property(mpm::properties::Vector::Velocity)(i) == + Approx(13.88).epsilon(Tolerance)); + + particle->update_vector_property(mpm::properties::Vector::Velocity, true, + velocity); + for (unsigned i = 0; i < velocity.size(); ++i) + REQUIRE(particle->vector_property(mpm::properties::Vector::Velocity)(i) == + Approx(27.76).epsilon(Tolerance)); + // Assign volume REQUIRE(particle->assign_volume(0.0) == false); REQUIRE(particle->assign_volume(-5.0) == false); @@ -1236,24 +1256,25 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { std::shared_ptr> particle = std::make_shared>(id, coords); - // Check scalar properties - SECTION("Check scalar properties") { - // Check mass - REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == - Approx(0.0).epsilon(Tolerance)); - double mass = 100.5; - particle->update_scalar_property(mpm::properties::Scalar::Mass, true, - mass); - REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == - Approx(100.5).epsilon(Tolerance)); - } - // Check mass - REQUIRE(particle->mass() == Approx(100.5).epsilon(Tolerance)); + REQUIRE(particle->mass() == Approx(0.).epsilon(Tolerance)); double mass = 201.0; particle->assign_mass(mass); REQUIRE(particle->mass() == Approx(201.0).epsilon(Tolerance)); + // Check mass using scalar properties + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(201.).epsilon(Tolerance)); + mass = 111.11; + particle->update_scalar_property(mpm::properties::Scalar::Mass, false, + mass); + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(111.11).epsilon(Tolerance)); + + particle->update_scalar_property(mpm::properties::Scalar::Mass, true, mass); + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(222.22).epsilon(Tolerance)); + // Check stress Eigen::Matrix stress; for (unsigned i = 0; i < stress.size(); ++i) stress(i) = 17.52; @@ -1273,6 +1294,25 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(19.745).epsilon(Tolerance)); + // Check velocity using vector properties + for (unsigned i = 0; i < velocity.size(); ++i) + REQUIRE(particle->vector_property(mpm::properties::Vector::Velocity)(i) == + Approx(19.745).epsilon(Tolerance)); + + for (unsigned i = 0; i < velocity.size(); ++i) velocity(i) = 11.22; + + particle->update_vector_property(mpm::properties::Vector::Velocity, false, + velocity); + for (unsigned i = 0; i < velocity.size(); ++i) + REQUIRE(particle->vector_property(mpm::properties::Vector::Velocity)(i) == + Approx(11.22).epsilon(Tolerance)); + + particle->update_vector_property(mpm::properties::Vector::Velocity, true, + velocity); + for (unsigned i = 0; i < velocity.size(); ++i) + REQUIRE(particle->vector_property(mpm::properties::Vector::Velocity)(i) == + Approx(22.44).epsilon(Tolerance)); + // Assign volume REQUIRE(particle->assign_volume(0.0) == false); REQUIRE(particle->assign_volume(-5.0) == false); @@ -2544,6 +2584,19 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { particle->assign_mass(mass); REQUIRE(particle->mass() == Approx(100.5).epsilon(Tolerance)); + // Check mass using scalar properties + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(100.5).epsilon(Tolerance)); + mass = 111.11; + particle->update_scalar_property(mpm::properties::Scalar::Mass, false, + mass); + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(111.11).epsilon(Tolerance)); + + particle->update_scalar_property(mpm::properties::Scalar::Mass, true, mass); + REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == + Approx(222.22).epsilon(Tolerance)); + // Check stress Eigen::Matrix stress; for (unsigned i = 0; i < stress.size(); ++i) stress(i) = 1.; @@ -2563,6 +2616,25 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(17.51).epsilon(Tolerance)); + // Check velocity using vector properties + for (unsigned i = 0; i < velocity.size(); ++i) + REQUIRE(particle->vector_property(mpm::properties::Vector::Velocity)(i) == + Approx(17.51).epsilon(Tolerance)); + + for (unsigned i = 0; i < velocity.size(); ++i) velocity(i) = 11.22; + + particle->update_vector_property(mpm::properties::Vector::Velocity, false, + velocity); + for (unsigned i = 0; i < velocity.size(); ++i) + REQUIRE(particle->vector_property(mpm::properties::Vector::Velocity)(i) == + Approx(11.22).epsilon(Tolerance)); + + particle->update_vector_property(mpm::properties::Vector::Velocity, true, + velocity); + for (unsigned i = 0; i < velocity.size(); ++i) + REQUIRE(particle->vector_property(mpm::properties::Vector::Velocity)(i) == + Approx(22.44).epsilon(Tolerance)); + // Assign volume REQUIRE(particle->assign_volume(0.0) == false); REQUIRE(particle->assign_volume(-5.0) == false); From 2f54b90a549494573ae1eb99f6d2b340af5e8623 Mon Sep 17 00:00:00 2001 From: Nanda Date: Thu, 9 Jul 2020 16:14:53 -0700 Subject: [PATCH 15/55] :wrench: add overload functions of mapping --- include/particles/particle_base.h | 17 +++++++++++++++++ include/particles/particle_base.tcc | 23 +++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index 54fb2e30a..da5174bd8 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -148,6 +148,14 @@ class ParticleBase { void map_scalar_property_nodes(mpm::properties::Scalar property, bool update, unsigned phase) noexcept; + //! Map an arbitrary scalar value to nodal scalar property + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Scalar value to be mapped from particle to node + void map_scalar_property_nodes(mpm::properties::Scalar property, bool update, + unsigned phase, double value) noexcept; + //! Return property at a given node for a given phase //! \param[in] phase Index corresponding to the phase double interpolate_scalar_property_nodes(mpm::properties::Scalar property, @@ -173,6 +181,15 @@ class ParticleBase { void map_vector_property_nodes(mpm::properties::Vector property, bool update, unsigned phase) noexcept; + //! Map an arbitrary vector value to nodal vector property + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Vector value to be mapped from particle to node + void map_vector_property_nodes( + mpm::properties::Vector property, bool update, unsigned phase, + const Eigen::Matrix& value) noexcept; + //! Return property at a given node for a given phase //! \param[in] phase Index corresponding to the phase double interpolate_vector_property_nodes(mpm::properties::Vector property, diff --git a/include/particles/particle_base.tcc b/include/particles/particle_base.tcc index 58f52f426..4a389ee6d 100644 --- a/include/particles/particle_base.tcc +++ b/include/particles/particle_base.tcc @@ -46,6 +46,17 @@ void mpm::ParticleBase::map_scalar_property_nodes( property, update, phase, scalar_properties_.at(property) * shapefn_[i]); } +//! Map an arbitrary scalar value to nodal scalar property +template +void mpm::ParticleBase::map_scalar_property_nodes( + mpm::properties::Scalar property, bool update, unsigned phase, + double value) noexcept { + // Map scalar value to nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + nodes_[i]->update_scalar_property(property, update, phase, + value * shapefn_[i]); +} + //! Interpolate scalar property from nodes template double mpm::ParticleBase::interpolate_scalar_property_nodes( @@ -86,6 +97,18 @@ void mpm::ParticleBase::map_vector_property_nodes( property, update, phase, vector_properties_.at(property) * shapefn_[i]); } +//! Map an arbitrary vector value to nodal vector property +template +void mpm::ParticleBase::map_vector_property_nodes( + mpm::properties::Vector property, bool update, unsigned phase, + const Eigen::Matrix& value) noexcept { + // TODO: Check assert needed? + // Map vector property to nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + nodes_[i]->update_vector_property(property, update, phase, + value * shapefn_[i]); +} + //! Interpolate vector property from nodes template double mpm::ParticleBase::interpolate_vector_property_nodes( From 0facda0fc474bd074ad842d0ab99f3a5fc958185 Mon Sep 17 00:00:00 2001 From: Nanda Date: Thu, 9 Jul 2020 16:16:45 -0700 Subject: [PATCH 16/55] :construction: :dart: test nightly benchmarks with refactored particles and nodes --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 57f12f47b..cd3613b44 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -127,7 +127,7 @@ workflows: version: 2 build: jobs: - - gcc + - benchmarks - clang - cppcheck - codecov From 413665457f23634b5c8eb31ed7f1ddcc606b56a8 Mon Sep 17 00:00:00 2001 From: Nanda Date: Thu, 9 Jul 2020 18:30:05 -0700 Subject: [PATCH 17/55] :construction: :dart: refactor map_mass_momentum and tests --- include/particles/particle.h | 3 --- include/particles/particle.tcc | 15 --------------- include/particles/particle_base.h | 3 --- include/particles/particle_functions.tcc | 20 ++++++++++++++++++++ include/solvers/mpm_explicit.tcc | 5 +++-- tests/interface_test.cc | 6 +++--- tests/particle_cell_crossing_test.cc | 12 ++++++------ tests/particle_test.cc | 12 ++++-------- 8 files changed, 36 insertions(+), 40 deletions(-) diff --git a/include/particles/particle.h b/include/particles/particle.h index ad347ba00..0c7c0bfa8 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -135,9 +135,6 @@ class Particle : public ParticleBase { return this->scalar_property(mpm::properties::Scalar::MassDensity); } - //! Map particle mass and momentum to nodes - void map_mass_momentum_to_nodes() noexcept override; - //! Map multimaterial properties to nodes void map_multimaterial_mass_momentum_to_nodes() noexcept override; diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index cead6c73a..c3996f194 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -501,21 +501,6 @@ void mpm::Particle::update_volume() noexcept { (1. + dvolumetric_strain_); } -//! Map particle mass and momentum to nodes -template -void mpm::Particle::map_mass_momentum_to_nodes() noexcept { - // Check if particle mass is set - assert(this->mass() != std::numeric_limits::max()); - - // Map mass and momentum to nodes - for (unsigned i = 0; i < nodes_.size(); ++i) { - nodes_[i]->update_mass(true, mpm::ParticlePhase::Solid, - this->mass() * shapefn_[i]); - nodes_[i]->update_momentum(true, mpm::ParticlePhase::Solid, - this->mass() * shapefn_[i] * this->velocity()); - } -} - //! Map multimaterial properties to nodes template void mpm::Particle::map_multimaterial_mass_momentum_to_nodes() noexcept { diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index da5174bd8..c227e828a 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -198,9 +198,6 @@ class ParticleBase { //! Return mass density virtual double mass_density() const = 0; - //! Map particle mass and momentum to nodes - virtual void map_mass_momentum_to_nodes() noexcept = 0; - //! Map multimaterial properties to nodes virtual void map_multimaterial_mass_momentum_to_nodes() noexcept = 0; diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc index 0cd83e5ee..973bc0f4a 100644 --- a/include/particles/particle_functions.tcc +++ b/include/particles/particle_functions.tcc @@ -1,19 +1,39 @@ // Compute mass of particle namespace mpm { namespace particle { + +// Compute particle mass template void compute_mass(std::shared_ptr> particle) noexcept { // Check if particle volume is set and material ptr is valid assert(particle->volume() != std::numeric_limits::max() && particle->material() != nullptr); + // Mass = volume of particle * mass_density particle->update_scalar_property( mpm::properties::Scalar::MassDensity, false, particle->material()->template property(std::string("density"))); + // Update particle mass particle->update_scalar_property( mpm::properties::Scalar::Mass, false, particle->volume() * particle->mass_density()); } + +//! Map particle mass and momentum to nodes +template +void map_mass_momentum_to_nodes( + std::shared_ptr> particle) noexcept { + // Check if particle mass is set + assert(particle->mass() != std::numeric_limits::max()); + + // Map mass and momentum to nodes + particle->map_scalar_property_nodes(mpm::properties::Scalar::Mass, true, + mpm::ParticlePhase::Solid); + particle->map_vector_property_nodes(mpm::properties::Vector::Momentum, true, + mpm::ParticlePhase::Solid, + particle->mass() * particle->velocity()); +} + } // namespace particle } // namespace mpm diff --git a/include/solvers/mpm_explicit.tcc b/include/solvers/mpm_explicit.tcc index b8bddad5b..1dbdbe60c 100644 --- a/include/solvers/mpm_explicit.tcc +++ b/include/solvers/mpm_explicit.tcc @@ -184,8 +184,9 @@ bool mpm::MPMExplicit::solve() { // Assign mass and momentum to nodes mesh_->iterate_over_particles( - std::bind(&mpm::ParticleBase::map_mass_momentum_to_nodes, - std::placeholders::_1)); + [](std::shared_ptr> ptr) { + return mpm::particle::map_mass_momentum_to_nodes(ptr); + }); #ifdef USE_MPI // Run if there is more than a single MPI task diff --git a/tests/interface_test.cc b/tests/interface_test.cc index cb3b151d7..e9104e799 100644 --- a/tests/interface_test.cc +++ b/tests/interface_test.cc @@ -152,9 +152,9 @@ TEST_CASE("Interface functions are checked", "[interface]") { particle3->map_multimaterial_mass_momentum_to_nodes(); // Map masses and momenta from particles to nodes - particle1->map_mass_momentum_to_nodes(); - particle2->map_mass_momentum_to_nodes(); - particle3->map_mass_momentum_to_nodes(); + mpm::particle::map_mass_momentum_to_nodes(particle1); + mpm::particle::map_mass_momentum_to_nodes(particle2); + mpm::particle::map_mass_momentum_to_nodes(particle3); // Compute velocities at nodes node0->compute_velocity(); diff --git a/tests/particle_cell_crossing_test.cc b/tests/particle_cell_crossing_test.cc index cd084d8f6..ce6acda6e 100644 --- a/tests/particle_cell_crossing_test.cc +++ b/tests/particle_cell_crossing_test.cc @@ -191,9 +191,9 @@ TEST_CASE("Particle cell crossing is checked for 2D case", &mpm::ParticleBase::compute_shapefn, std::placeholders::_1)); // Assign mass and momentum to nodes - mesh->iterate_over_particles( - std::bind(&mpm::ParticleBase::map_mass_momentum_to_nodes, - std::placeholders::_1)); + mesh->iterate_over_particles([](std::shared_ptr> ptr) { + return mpm::particle::map_mass_momentum_to_nodes(ptr); + }); // Iterate over active nodes to compute acceleratation and velocity mesh->iterate_over_nodes_predicate( @@ -452,9 +452,9 @@ TEST_CASE("Particle cell crossing is checked for 3D case", &mpm::ParticleBase::compute_shapefn, std::placeholders::_1)); // Assign mass and momentum to nodes - mesh->iterate_over_particles( - std::bind(&mpm::ParticleBase::map_mass_momentum_to_nodes, - std::placeholders::_1)); + mesh->iterate_over_particles([](std::shared_ptr> ptr) { + return mpm::particle::map_mass_momentum_to_nodes(ptr); + }); // Iterate over active nodes to compute acceleratation and velocity mesh->iterate_over_nodes_predicate( diff --git a/tests/particle_test.cc b/tests/particle_test.cc index b4e297f09..4859c6bd6 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -777,7 +777,6 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // Map particle mass to nodes particle->assign_mass(std::numeric_limits::max()); - // TODO Assert: REQUIRE_NOTHROW(particle->map_mass_momentum_to_nodes()); // Map particle pressure to nodes // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); @@ -795,7 +794,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); - REQUIRE_NOTHROW(particle->map_mass_momentum_to_nodes()); + REQUIRE_NOTHROW(mpm::particle::map_mass_momentum_to_nodes(particle)); // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); REQUIRE(particle->compute_pressure_smoothing() == false); @@ -1096,7 +1095,6 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // Map particle mass to nodes particle->assign_mass(std::numeric_limits::max()); - // TODO Assert: REQUIRE(particle->map_mass_momentum_to_nodes() == false); // Map particle pressure to nodes // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); @@ -1113,7 +1111,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); - REQUIRE_NOTHROW(particle->map_mass_momentum_to_nodes()); + REQUIRE_NOTHROW(mpm::particle::map_mass_momentum_to_nodes(particle)); // Check volumetric strain at centroid volumetric_strain = 0.2; @@ -2064,7 +2062,6 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { // Map particle mass to nodes particle->assign_mass(std::numeric_limits::max()); - // TODO Assert: REQUIRE(particle->map_mass_momentum_to_nodes() == false); // Map particle pressure to nodes // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); @@ -2082,7 +2079,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); - REQUIRE_NOTHROW(particle->map_mass_momentum_to_nodes()); + REQUIRE_NOTHROW(mpm::particle::map_mass_momentum_to_nodes(particle)); // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); REQUIRE(particle->compute_pressure_smoothing() == false); @@ -2377,7 +2374,6 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { // Map particle mass to nodes particle->assign_mass(std::numeric_limits::max()); - // TODO Assert: REQUIRE(particle->map_mass_momentum_to_nodes() == false); // Map particle pressure to nodes // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); @@ -2394,7 +2390,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); - REQUIRE_NOTHROW(particle->map_mass_momentum_to_nodes()); + REQUIRE_NOTHROW(mpm::particle::map_mass_momentum_to_nodes(particle)); // Check volumetric strain at centroid volumetric_strain = 0.5; From cb71c90f3c22f835af6798b225a24a883f345dcf Mon Sep 17 00:00:00 2001 From: Nanda Date: Thu, 9 Jul 2020 22:40:47 -0700 Subject: [PATCH 18/55] :construction: :dart: refactor map_body_force and update_volume --- include/particles/particle.h | 7 ------ include/particles/particle.tcc | 24 --------------------- include/particles/particle_base.h | 6 ------ include/particles/particle_functions.tcc | 27 ++++++++++++++++++++++++ include/solvers/mpm_explicit.tcc | 11 ++++++---- tests/particle_test.cc | 12 ++++------- 6 files changed, 38 insertions(+), 49 deletions(-) diff --git a/include/particles/particle.h b/include/particles/particle.h index 0c7c0bfa8..6cf8e5870 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -127,9 +127,6 @@ class Particle : public ParticleBase { //! Compute volume as cell volume / nparticles void compute_volume() noexcept override; - //! Update volume based on centre volumetric strain rate - void update_volume() noexcept override; - //! \param[in] phase Index corresponding to the phase double mass_density() const override { return this->scalar_property(mpm::properties::Scalar::MassDensity); @@ -199,10 +196,6 @@ class Particle : public ParticleBase { //! Return stress of the particle Eigen::Matrix stress() const override { return stress_; } - //! Map body force - //! \param[in] pgravity Gravity of a particle - void map_body_force(const VectorDim& pgravity) noexcept override; - //! Map internal force inline void map_internal_force() noexcept override; diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index c3996f194..af98eadf7 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -486,21 +486,6 @@ void mpm::Particle::compute_volume() noexcept { this->assign_volume(cell_->volume() / cell_->nparticles()); } -// Update volume based on the central strain rate -template -void mpm::Particle::update_volume() noexcept { - // Check if particle has a valid cell ptr and a valid volume - assert(cell_ != nullptr && - this->volume() != std::numeric_limits::max()); - // Compute at centroid - // Strain rate for reduced integration - scalar_properties_.at(mpm::properties::Scalar::Volume) *= - (1. + dvolumetric_strain_); - scalar_properties_.at(mpm::properties::Scalar::MassDensity) = - scalar_properties_.at(mpm::properties::Scalar::MassDensity) / - (1. + dvolumetric_strain_); -} - //! Map multimaterial properties to nodes template void mpm::Particle::map_multimaterial_mass_momentum_to_nodes() noexcept { @@ -641,15 +626,6 @@ void mpm::Particle::compute_stress() noexcept { material_->compute_stress(stress_, dstrain_, this, &state_variables_); } -//! Map body force -template -void mpm::Particle::map_body_force(const VectorDim& pgravity) noexcept { - // Compute nodal body forces - for (unsigned i = 0; i < nodes_.size(); ++i) - nodes_[i]->update_external_force(true, mpm::ParticlePhase::Solid, - (pgravity * this->mass() * shapefn_(i))); -} - //! Map internal force template <> inline void mpm::Particle<1>::map_internal_force() noexcept { diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index c227e828a..7beaeb6bd 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -127,9 +127,6 @@ class ParticleBase { //! Compute volume of particle virtual void compute_volume() noexcept = 0; - //! Update volume based on centre volumetric strain rate - virtual void update_volume() noexcept = 0; - //! Update scalar property at the particle //! \param[in] property Name of the property to update //! \param[in] update A boolean to update (true) or assign (false) @@ -260,9 +257,6 @@ class ParticleBase { //! Return stress virtual Eigen::Matrix stress() const = 0; - //! Map body force - virtual void map_body_force(const VectorDim& pgravity) noexcept = 0; - //! Map internal force virtual void map_internal_force() noexcept = 0; diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc index 973bc0f4a..39d53689a 100644 --- a/include/particles/particle_functions.tcc +++ b/include/particles/particle_functions.tcc @@ -20,6 +20,23 @@ void compute_mass(std::shared_ptr> particle) noexcept { particle->volume() * particle->mass_density()); } +// Update volume based on the central strain rate +template +void update_volume(std::shared_ptr> particle) noexcept { + // Check if particle has a valid cell ptr and a valid volume + assert(particle->cell_ptr() && + particle->volume() != std::numeric_limits::max()); + + // Compute at centroid + // Strain rate for reduced integration + particle->update_scalar_property( + mpm::properties::Scalar::Volume, false, + (particle->volume() * (1. + particle->dvolumetric_strain()))); + particle->update_scalar_property( + mpm::properties::Scalar::MassDensity, false, + (particle->mass_density() / (1. + particle->dvolumetric_strain()))); +} + //! Map particle mass and momentum to nodes template void map_mass_momentum_to_nodes( @@ -35,5 +52,15 @@ void map_mass_momentum_to_nodes( particle->mass() * particle->velocity()); } +//! Map body force to nodes +template +void map_body_force(std::shared_ptr> particle, + const Eigen::Matrix& pgravity) noexcept { + // Compute nodal body forces + particle->map_vector_property_nodes(mpm::properties::Vector::ExternalForce, + true, mpm::ParticlePhase::Solid, + pgravity * particle->mass()); +} + } // namespace particle } // namespace mpm diff --git a/include/solvers/mpm_explicit.tcc b/include/solvers/mpm_explicit.tcc index 1dbdbe60c..286ab6579 100644 --- a/include/solvers/mpm_explicit.tcc +++ b/include/solvers/mpm_explicit.tcc @@ -43,8 +43,10 @@ void mpm::MPMExplicit::compute_stress_strain(unsigned phase) { &mpm::ParticleBase::compute_strain, std::placeholders::_1, dt_)); // Iterate over each particle to update particle volume - mesh_->iterate_over_particles(std::bind( - &mpm::ParticleBase::update_volume, std::placeholders::_1)); + mesh_->iterate_over_particles( + [](std::shared_ptr> ptr) { + return mpm::particle::update_volume(ptr); + }); // Pressure smoothing if (pressure_smoothing_) this->pressure_smoothing(phase); @@ -255,8 +257,9 @@ bool mpm::MPMExplicit::solve() { { // Iterate over each particle to compute nodal body force mesh_->iterate_over_particles( - std::bind(&mpm::ParticleBase::map_body_force, - std::placeholders::_1, this->gravity_)); + [&value = gravity_](std::shared_ptr> ptr) { + return mpm::particle::map_body_force(ptr, value); + }); // Apply particle traction and map to nodes mesh_->apply_traction_on_particles(this->step_ * this->dt_); diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 4859c6bd6..59afcfe1f 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -711,8 +711,6 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // TODO Assert: REQUIRE_NOTHROW(particle->compute_updated_position(dt, // true)); Compute volume // TODO Assert: REQUIRE(particle->compute_volume() == false); - // Update volume should fail - // TODO Assert: REQUIRE(particle->update_volume() == false); REQUIRE(particle->assign_cell(cell) == true); REQUIRE(cell->status() == true); @@ -887,7 +885,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // Update volume strain rate REQUIRE(particle->volume() == Approx(1.0).epsilon(Tolerance)); particle->compute_strain(dt); - REQUIRE_NOTHROW(particle->update_volume()); + REQUIRE_NOTHROW(mpm::particle::update_volume(particle)); REQUIRE(particle->volume() == Approx(1.2).epsilon(Tolerance)); // Compute stress @@ -910,7 +908,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { Eigen::Matrix gravity; gravity << 0., -9.81; - particle->map_body_force(gravity); + mpm::particle::map_body_force(particle, gravity); // Body force Eigen::Matrix body_force; @@ -2000,8 +1998,6 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { // Compute volume // TODO Assert: REQUIRE(particle->compute_volume() == false); - // Update volume should fail - // TODO Assert: REQUIRE(particle->update_volume() == false); REQUIRE(particle->assign_cell(cell) == true); REQUIRE(cell->status() == true); @@ -2190,7 +2186,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { // Update volume strain rate REQUIRE(particle->volume() == Approx(8.0).epsilon(Tolerance)); particle->compute_strain(dt); - REQUIRE_NOTHROW(particle->update_volume()); + REQUIRE_NOTHROW(mpm::particle::update_volume(particle)); REQUIRE(particle->volume() == Approx(12.0).epsilon(Tolerance)); // Compute stress @@ -2213,7 +2209,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { Eigen::Matrix gravity; gravity << 0., 0., -9.81; - particle->map_body_force(gravity); + mpm::particle::map_body_force(particle, gravity); // Body force Eigen::Matrix body_force; From 52eb2b3522538de680f12957981303ac998b87f4 Mon Sep 17 00:00:00 2001 From: Nanda Date: Fri, 10 Jul 2020 15:05:24 -0700 Subject: [PATCH 19/55] :construction: :dart: change assign_velocity to void and cleanup --- include/node.tcc | 4 ++-- include/particles/particle.h | 4 +++- include/particles/particle.tcc | 9 --------- include/particles/particle_base.h | 2 +- tests/particle_test.cc | 14 +++++++------- 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/include/node.tcc b/include/node.tcc index 37c70e490..d3e2cf755 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -89,7 +89,7 @@ void mpm::Node::update_scalar_property( // Decide to update or assign const double factor = (update == true) ? 1. : 0.; - // Update/assign mass + // Update/assign value std::lock_guard guard(node_mutex_); scalar_properties_.at(property)[phase] = (scalar_properties_.at(property)[phase] * factor) + value; @@ -110,7 +110,7 @@ void mpm::Node::update_vector_property( // Decide to update or assign const double factor = (update == true) ? 1. : 0.; - // Update/assign mass + // Update/assign value std::lock_guard guard(node_mutex_); Eigen::Matrix vecvalue = vector_properties_.at(property).col(phase); diff --git a/include/particles/particle.h b/include/particles/particle.h index 6cf8e5870..dbca8af31 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -202,7 +202,9 @@ class Particle : public ParticleBase { //! Assign velocity to the particle //! \param[in] velocity A vector of particle velocity //! \retval status Assignment status - bool assign_velocity(const VectorDim& velocity) override; + void assign_velocity(const VectorDim& velocity) override { + vector_properties_.at(mpm::properties::Vector::Velocity) = velocity; + }; //! Return velocity of the particle VectorDim velocity() const override { diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index af98eadf7..5d1b57e4b 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -677,15 +677,6 @@ inline void mpm::Particle<3>::map_internal_force() noexcept { } } -// Assign velocity to the particle -template -bool mpm::Particle::assign_velocity( - const Eigen::Matrix& velocity) { - // Assign velocity - vector_properties_.at(mpm::properties::Vector::Velocity) = velocity; - return true; -} - // Assign traction to the particle template bool mpm::Particle::assign_traction(unsigned direction, double traction) { diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index 7beaeb6bd..15f670cc0 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -267,7 +267,7 @@ class ParticleBase { virtual bool compute_pressure_smoothing() noexcept = 0; //! Assign velocity - virtual bool assign_velocity(const VectorDim& velocity) = 0; + virtual void assign_velocity(const VectorDim& velocity) = 0; //! Return velocity virtual VectorDim velocity() const = 0; diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 59afcfe1f..de05b1dc5 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -178,7 +178,7 @@ TEST_CASE("Particle is checked for 1D case", "[particle][1D]") { for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(0.).epsilon(Tolerance)); - REQUIRE(particle->assign_velocity(velocity) == true); + particle->assign_velocity(velocity); for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(17.51).epsilon(Tolerance)); @@ -787,7 +787,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { Eigen::VectorXd velocity; velocity.resize(Dim); for (unsigned i = 0; i < velocity.size(); ++i) velocity(i) = i; - REQUIRE(particle->assign_velocity(velocity) == true); + particle->assign_velocity(velocity); for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); @@ -1104,7 +1104,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // Check velocity velocity.resize(Dim); for (unsigned i = 0; i < velocity.size(); ++i) velocity(i) = i; - REQUIRE(particle->assign_velocity(velocity) == true); + particle->assign_velocity(velocity); for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); @@ -1286,7 +1286,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(0.).epsilon(Tolerance)); - REQUIRE(particle->assign_velocity(velocity) == true); + particle->assign_velocity(velocity); for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(19.745).epsilon(Tolerance)); @@ -2070,7 +2070,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { Eigen::VectorXd velocity; velocity.resize(Dim); for (unsigned i = 0; i < velocity.size(); ++i) velocity(i) = i; - REQUIRE(particle->assign_velocity(velocity) == true); + particle->assign_velocity(velocity); for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); @@ -2381,7 +2381,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { // Check velocity velocity.resize(Dim); for (unsigned i = 0; i < velocity.size(); ++i) velocity(i) = i; - REQUIRE(particle->assign_velocity(velocity) == true); + particle->assign_velocity(velocity); for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(i).epsilon(Tolerance)); @@ -2604,7 +2604,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(0.).epsilon(Tolerance)); - REQUIRE(particle->assign_velocity(velocity) == true); + particle->assign_velocity(velocity); for (unsigned i = 0; i < velocity.size(); ++i) REQUIRE(particle->velocity()(i) == Approx(17.51).epsilon(Tolerance)); From a20a2ab046e6469cc25773f4ddc2dcffd8ece87f Mon Sep 17 00:00:00 2001 From: Nanda Date: Fri, 10 Jul 2020 15:09:47 -0700 Subject: [PATCH 20/55] :dart: revert mass change in particle test --- tests/particle_test.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/particle_test.cc b/tests/particle_test.cc index de05b1dc5..0d5623bc1 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -145,23 +145,23 @@ TEST_CASE("Particle is checked for 1D case", "[particle][1D]") { std::make_shared>(id, coords); // Check mass - REQUIRE(particle->mass() == Approx(0.).epsilon(Tolerance)); - double mass = 201.0; + REQUIRE(particle->mass() == Approx(0.0).epsilon(Tolerance)); + double mass = 100.5; particle->assign_mass(mass); - REQUIRE(particle->mass() == Approx(201.0).epsilon(Tolerance)); + REQUIRE(particle->mass() == Approx(100.5).epsilon(Tolerance)); // Check mass using scalar properties REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == - Approx(201.0).epsilon(Tolerance)); - mass = 100.5; + Approx(100.5).epsilon(Tolerance)); + mass = 200.5; particle->update_scalar_property(mpm::properties::Scalar::Mass, false, mass); REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == - Approx(100.5).epsilon(Tolerance)); + Approx(200.5).epsilon(Tolerance)); particle->update_scalar_property(mpm::properties::Scalar::Mass, true, mass); REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == - Approx(201.).epsilon(Tolerance)); + Approx(401.).epsilon(Tolerance)); // Check stress Eigen::Matrix stress; @@ -1253,14 +1253,14 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { std::make_shared>(id, coords); // Check mass - REQUIRE(particle->mass() == Approx(0.).epsilon(Tolerance)); - double mass = 201.0; + REQUIRE(particle->mass() == Approx(0.0).epsilon(Tolerance)); + double mass = 100.5; particle->assign_mass(mass); - REQUIRE(particle->mass() == Approx(201.0).epsilon(Tolerance)); + REQUIRE(particle->mass() == Approx(100.5).epsilon(Tolerance)); // Check mass using scalar properties REQUIRE(particle->scalar_property(mpm::properties::Scalar::Mass) == - Approx(201.).epsilon(Tolerance)); + Approx(100.5).epsilon(Tolerance)); mass = 111.11; particle->update_scalar_property(mpm::properties::Scalar::Mass, false, mass); From 0a0cf7b4461f35f769c78b4a84087f8c2219fec8 Mon Sep 17 00:00:00 2001 From: Nanda Date: Fri, 10 Jul 2020 15:29:00 -0700 Subject: [PATCH 21/55] :construction: :dart: add return cell pointer and refactor compute_volume --- include/mesh.tcc | 2 +- include/particles/particle.h | 6 +++--- include/particles/particle.tcc | 9 --------- include/particles/particle_base.h | 6 +++--- include/particles/particle_functions.tcc | 12 ++++++++++++ include/solvers/mpm_base.tcc | 6 ++++-- tests/mesh_test_2d.cc | 5 +++-- tests/mesh_test_3d.cc | 5 +++-- tests/particle_cell_crossing_test.cc | 10 ++++++---- tests/particle_test.cc | 18 +++++++----------- 10 files changed, 42 insertions(+), 37 deletions(-) diff --git a/include/mesh.tcc b/include/mesh.tcc index 3f03363c8..68b64879e 100644 --- a/include/mesh.tcc +++ b/include/mesh.tcc @@ -1751,7 +1751,7 @@ void mpm::Mesh::inject_particles(double current_time) { } } for (auto particle : injected_particles) { - particle->compute_volume(); + mpm::particle::compute_volume(particle); mpm::particle::compute_mass(particle); } } diff --git a/include/particles/particle.h b/include/particles/particle.h index dbca8af31..e9fffc0c4 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -103,6 +103,9 @@ class Particle : public ParticleBase { //! Return cell id Index cell_id() const override { return cell_id_; } + //! Return cell ptr + std::shared_ptr> cell() const override { return cell_; } + //! Return cell ptr status bool cell_ptr() const override { return cell_ != nullptr; } @@ -124,9 +127,6 @@ class Particle : public ParticleBase { //! Return size of particle in natural coordinates VectorDim natural_size() const override { return natural_size_; } - //! Compute volume as cell volume / nparticles - void compute_volume() noexcept override; - //! \param[in] phase Index corresponding to the phase double mass_density() const override { return this->scalar_property(mpm::properties::Scalar::MassDensity); diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 5d1b57e4b..4a3deb7d2 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -477,15 +477,6 @@ bool mpm::Particle::assign_volume(double volume) { return status; } -// Compute volume of the particle -template -void mpm::Particle::compute_volume() noexcept { - // Check if particle has a valid cell ptr - assert(cell_ != nullptr); - // Volume of the cell / # of particles - this->assign_volume(cell_->volume() / cell_->nparticles()); -} - //! Map multimaterial properties to nodes template void mpm::Particle::map_multimaterial_mass_momentum_to_nodes() noexcept { diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index 15f670cc0..c84f24436 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -106,6 +106,9 @@ class ParticleBase { //! Return cell id virtual Index cell_id() const = 0; + //! Return cell ptr + virtual std::shared_ptr> cell() const = 0; + //! Return cell ptr status virtual bool cell_ptr() const = 0; @@ -124,9 +127,6 @@ class ParticleBase { //! Return size of particle in natural coordinates virtual VectorDim natural_size() const = 0; - //! Compute volume of particle - virtual void compute_volume() noexcept = 0; - //! Update scalar property at the particle //! \param[in] property Name of the property to update //! \param[in] update A boolean to update (true) or assign (false) diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc index 39d53689a..a8f35bd28 100644 --- a/include/particles/particle_functions.tcc +++ b/include/particles/particle_functions.tcc @@ -20,6 +20,18 @@ void compute_mass(std::shared_ptr> particle) noexcept { particle->volume() * particle->mass_density()); } +// Compute volume of the particle +template +void compute_volume( + std::shared_ptr> particle) noexcept { + // Check if particle has a valid cell ptr + assert(particle->cell_ptr()); + + // Volume of the cell / # of particles + particle->assign_volume(particle->cell()->volume() / + particle->cell()->nparticles()); +} + // Update volume based on the central strain rate template void update_volume(std::shared_ptr> particle) noexcept { diff --git a/include/solvers/mpm_base.tcc b/include/solvers/mpm_base.tcc index 47e5dcd0a..c9393b68f 100644 --- a/include/solvers/mpm_base.tcc +++ b/include/solvers/mpm_base.tcc @@ -300,8 +300,10 @@ bool mpm::MPMBase::initialise_particles() { auto particles_volume_begin = std::chrono::steady_clock::now(); // Compute volume - mesh_->iterate_over_particles(std::bind( - &mpm::ParticleBase::compute_volume, std::placeholders::_1)); + mesh_->iterate_over_particles( + [](std::shared_ptr> ptr) { + return mpm::particle::compute_volume(ptr); + }); // Read and assign particles volumes this->particles_volumes(mesh_props, particle_io); diff --git a/tests/mesh_test_2d.cc b/tests/mesh_test_2d.cc index 7b564bd94..0f44d1d7e 100644 --- a/tests/mesh_test_2d.cc +++ b/tests/mesh_test_2d.cc @@ -966,8 +966,9 @@ TEST_CASE("Mesh is checked for 2D case", "[mesh][2D]") { // Compute volume mesh->iterate_over_particles( - std::bind(&mpm::ParticleBase::compute_volume, - std::placeholders::_1)); + [](std::shared_ptr> ptr) { + return mpm::particle::compute_volume(ptr); + }); mesh->apply_traction_on_particles(10); } diff --git a/tests/mesh_test_3d.cc b/tests/mesh_test_3d.cc index 56d05b503..9805fbea5 100644 --- a/tests/mesh_test_3d.cc +++ b/tests/mesh_test_3d.cc @@ -1061,8 +1061,9 @@ TEST_CASE("Mesh is checked for 3D case", "[mesh][3D]") { // Compute volume mesh->iterate_over_particles( - std::bind(&mpm::ParticleBase::compute_volume, - std::placeholders::_1)); + [](std::shared_ptr> ptr) { + return mpm::particle::compute_volume(ptr); + }); mesh->apply_traction_on_particles(10); } diff --git a/tests/particle_cell_crossing_test.cc b/tests/particle_cell_crossing_test.cc index ce6acda6e..3699ccb31 100644 --- a/tests/particle_cell_crossing_test.cc +++ b/tests/particle_cell_crossing_test.cc @@ -165,8 +165,9 @@ TEST_CASE("Particle cell crossing is checked for 2D case", material)); // Compute volume - mesh->iterate_over_particles(std::bind( - &mpm::ParticleBase::compute_volume, std::placeholders::_1)); + mesh->iterate_over_particles([](std::shared_ptr> ptr) { + return mpm::particle::compute_volume(ptr); + }); // Compute mass mesh->iterate_over_particles([](std::shared_ptr> ptr) { @@ -422,8 +423,9 @@ TEST_CASE("Particle cell crossing is checked for 3D case", material)); // Compute volume - mesh->iterate_over_particles(std::bind( - &mpm::ParticleBase::compute_volume, std::placeholders::_1)); + mesh->iterate_over_particles([](std::shared_ptr> ptr) { + return mpm::particle::compute_volume(ptr); + }); // Compute mass mesh->iterate_over_particles([](std::shared_ptr> ptr) { diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 0d5623bc1..b6d32e942 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -709,8 +709,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // REQUIRE_NOTHROW(particle->compute_updated_position(dt) == false); // Compute updated particle location from nodal velocity should fail // TODO Assert: REQUIRE_NOTHROW(particle->compute_updated_position(dt, - // true)); Compute volume - // TODO Assert: REQUIRE(particle->compute_volume() == false); + // true)); REQUIRE(particle->assign_cell(cell) == true); REQUIRE(cell->status() == true); @@ -729,7 +728,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // Check volume REQUIRE(particle->volume() == Approx(2.0).epsilon(Tolerance)); // Compute volume - REQUIRE_NOTHROW(particle->compute_volume()); + REQUIRE_NOTHROW(mpm::particle::compute_volume(particle)); // Check volume REQUIRE(particle->volume() == Approx(1.0).epsilon(Tolerance)); @@ -762,7 +761,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { REQUIRE(particle->material_id() == 1); // Compute volume - REQUIRE_NOTHROW(particle->compute_volume()); + REQUIRE_NOTHROW(mpm::particle::compute_volume(particle)); // Compute mass REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); @@ -1084,7 +1083,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { REQUIRE(particle->assign_material(material1) == true); // Compute volume - REQUIRE_NOTHROW(particle->compute_volume()); + REQUIRE_NOTHROW(mpm::particle::compute_volume(particle)); // Compute mass REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); @@ -1996,9 +1995,6 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { // TODO Assert: REQUIRE_NOTHROW(particle->compute_updated_position(dt, // true)); - // Compute volume - // TODO Assert: REQUIRE(particle->compute_volume() == false); - REQUIRE(particle->assign_cell(cell) == true); REQUIRE(cell->status() == true); REQUIRE(particle->cell_id() == 10); @@ -2016,7 +2012,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { // Check volume REQUIRE(particle->volume() == Approx(2.0).epsilon(Tolerance)); // Compute volume - REQUIRE_NOTHROW(particle->compute_volume()); + REQUIRE_NOTHROW(mpm::particle::compute_volume(particle)); // Check volume REQUIRE(particle->volume() == Approx(8.0).epsilon(Tolerance)); @@ -2049,7 +2045,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { REQUIRE(particle->material_id() == 0); // Compute volume - REQUIRE_NOTHROW(particle->compute_volume()); + REQUIRE_NOTHROW(mpm::particle::compute_volume(particle)); // Compute mass REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); @@ -2361,7 +2357,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { REQUIRE(particle->assign_material(material1) == true); // Compute volume - REQUIRE_NOTHROW(particle->compute_volume()); + REQUIRE_NOTHROW(mpm::particle::compute_volume(particle)); // Compute mass REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); From fe0f9bfbd4f2de2a3c2c32997576212ffb41d1f4 Mon Sep 17 00:00:00 2001 From: Nanda Date: Fri, 10 Jul 2020 15:32:23 -0700 Subject: [PATCH 22/55] :wrench: add assert in return and update scalar and vector properties --- include/node.tcc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/node.tcc b/include/node.tcc index d3e2cf755..680f9b8e5 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -86,6 +86,9 @@ template void mpm::Node::update_scalar_property( mpm::properties::Scalar property, bool update, unsigned phase, double value) noexcept { + // Assert phase + assert(phase < Tnphases); + // Decide to update or assign const double factor = (update == true) ? 1. : 0.; @@ -99,6 +102,8 @@ void mpm::Node::update_scalar_property( template double mpm::Node::scalar_property( mpm::properties::Scalar property, unsigned phase) const { + // Assert phase + assert(phase < Tnphases); return scalar_properties_.at(property)[phase]; } @@ -107,6 +112,9 @@ template void mpm::Node::update_vector_property( mpm::properties::Vector property, bool update, unsigned phase, const Eigen::Matrix& value) noexcept { + // Assert phase + assert(phase < Tnphases); + // Decide to update or assign const double factor = (update == true) ? 1. : 0.; @@ -121,6 +129,8 @@ void mpm::Node::update_vector_property( template Eigen::Matrix mpm::Node::vector_property( mpm::properties::Vector property, unsigned phase) const { + // Assert phase + assert(phase < Tnphases); return vector_properties_.at(property).col(phase); } From b6937094b3bf733b67ff7c151603801e4a816c37 Mon Sep 17 00:00:00 2001 From: Nanda Date: Fri, 10 Jul 2020 15:44:44 -0700 Subject: [PATCH 23/55] :construction: :dart: return set traction and refactor map traction --- include/mesh.tcc | 6 ++++-- include/particles/particle.h | 4 ++-- include/particles/particle.tcc | 11 ----------- include/particles/particle_base.h | 4 ++-- include/particles/particle_functions.tcc | 12 ++++++++++++ tests/particle_test.cc | 8 ++++---- 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/include/mesh.tcc b/include/mesh.tcc index 68b64879e..be5c037dc 100644 --- a/include/mesh.tcc +++ b/include/mesh.tcc @@ -1202,8 +1202,10 @@ void mpm::Mesh::apply_traction_on_particles(double current_time) { std::placeholders::_1, dir, traction)); } if (!particle_tractions_.empty()) { - this->iterate_over_particles(std::bind( - &mpm::ParticleBase::map_traction_force, std::placeholders::_1)); + this->iterate_over_particles( + [](std::shared_ptr> ptr) { + return mpm::particle::map_traction_force(ptr); + }); } } diff --git a/include/particles/particle.h b/include/particles/particle.h index e9fffc0c4..4bbae8d6d 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -226,8 +226,8 @@ class Particle : public ParticleBase { //! \param[in] phase Index corresponding to the phase VectorDim traction() const override { return traction_; } - //! Map traction force - void map_traction_force() noexcept override; + //! Return set traction bool + bool set_traction() const override { return set_traction_; } //! Compute updated position of the particle //! \param[in] dt Analysis time step diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 4a3deb7d2..859f751d9 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -689,17 +689,6 @@ bool mpm::Particle::assign_traction(unsigned direction, double traction) { return status; } -//! Map traction force -template -void mpm::Particle::map_traction_force() noexcept { - if (this->set_traction_) { - // Map particle traction forces to nodes - for (unsigned i = 0; i < nodes_.size(); ++i) - nodes_[i]->update_external_force(true, mpm::ParticlePhase::Solid, - (shapefn_[i] * traction_)); - } -} - // Compute updated position of the particle template void mpm::Particle::compute_updated_position( diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index c84f24436..9b421202c 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -281,8 +281,8 @@ class ParticleBase { //! Return traction virtual VectorDim traction() const = 0; - //! Map traction force - virtual void map_traction_force() noexcept = 0; + //! Return set traction bool + virtual bool set_traction() const = 0; //! Compute updated position virtual void compute_updated_position( diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc index a8f35bd28..66feba4b9 100644 --- a/include/particles/particle_functions.tcc +++ b/include/particles/particle_functions.tcc @@ -74,5 +74,17 @@ void map_body_force(std::shared_ptr> particle, pgravity * particle->mass()); } +//! Map traction force +template +void map_traction_force( + std::shared_ptr> particle) noexcept { + if (particle->set_traction()) { + // Map particle traction forces to nodes + particle->map_vector_property_nodes(mpm::properties::Vector::ExternalForce, + true, mpm::ParticlePhase::Solid, + particle->traction()); + } +} + } // namespace particle } // namespace mpm diff --git a/tests/particle_test.cc b/tests/particle_test.cc index b6d32e942..46a219242 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -936,7 +936,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // Assign traction to particle particle->assign_traction(direction, mfunction->value(current_time) * traction); - particle->map_traction_force(); + mpm::particle::map_traction_force(particle); // Traction force Eigen::Matrix traction_force; @@ -959,7 +959,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { particle->assign_traction(direction, -traction * mfunction->value(current_time)); // Map traction force - particle->map_traction_force(); + mpm::particle::map_traction_force(particle); // Check nodal external force for (unsigned i = 0; i < traction_force.rows(); ++i) for (unsigned j = 0; j < traction_force.cols(); ++j) @@ -2237,7 +2237,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { particle->assign_traction(direction, mfunction->value(current_time) * traction); // Map traction force - particle->map_traction_force(); + mpm::particle::map_traction_force(particle); // Traction force Eigen::Matrix traction_force; @@ -2264,7 +2264,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { particle->assign_traction(direction, mfunction->value(current_time) * -traction); // Map traction force - particle->map_traction_force(); + mpm::particle::map_traction_force(particle); // Check nodal external force for (unsigned i = 0; i < traction_force.rows(); ++i) for (unsigned j = 0; j < traction_force.cols(); ++j) From 5fc100e83b5b2003df756723d89b6f8d7d7e5f3a Mon Sep 17 00:00:00 2001 From: Nanda Date: Sat, 11 Jul 2020 19:32:26 -0700 Subject: [PATCH 24/55] :construction: :dart: refactor pressure smoothing --- include/mpm_properties.h | 8 +++++++- include/node.h | 3 +++ include/node.tcc | 22 ++++++++++++++++++++++ include/node_base.h | 3 +++ include/particles/particle.h | 3 --- include/particles/particle.tcc | 21 --------------------- include/particles/particle_base.h | 3 --- include/particles/particle_functions.tcc | 16 ++++++++++++++++ include/solvers/mpm_explicit.tcc | 13 ++++++++++--- tests/particle_test.cc | 18 ++---------------- 10 files changed, 63 insertions(+), 47 deletions(-) diff --git a/include/mpm_properties.h b/include/mpm_properties.h index 69e87dac7..4e3568667 100644 --- a/include/mpm_properties.h +++ b/include/mpm_properties.h @@ -4,7 +4,13 @@ namespace mpm { namespace properties { //! Scalar Properties -enum Scalar : unsigned int { Mass, Volume, MassDensity, Pressure }; +enum Scalar : unsigned int { + Mass, + Volume, + MassDensity, + MassPressure, + Pressure +}; //! Vector Properties enum Vector : unsigned int { Displacement, diff --git a/include/node.h b/include/node.h index 4dee319bc..de78f18a7 100644 --- a/include/node.h +++ b/include/node.h @@ -164,6 +164,9 @@ class Node : public NodeBase { void update_mass_pressure(unsigned phase, double mass_pressure) noexcept override; + //! Compute pressure from the mass pressure + virtual void compute_pressure() override; + //! Assign pressure at the nodes from particle //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase diff --git a/include/node.tcc b/include/node.tcc index 680f9b8e5..6bc4ae264 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -25,6 +25,9 @@ mpm::Node::Node( scalar_properties_.emplace( std::make_pair(mpm::properties::Scalar::Volume, Eigen::Matrix::Zero())); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::MassPressure, + Eigen::Matrix::Zero())); scalar_properties_.emplace( std::make_pair(mpm::properties::Scalar::Pressure, Eigen::Matrix::Zero())); @@ -57,6 +60,7 @@ void mpm::Node::initialise() noexcept { // Initialise nodal scalar properties scalar_properties_.at(mpm::properties::Scalar::Mass).setZero(); scalar_properties_.at(mpm::properties::Scalar::Volume).setZero(); + scalar_properties_.at(mpm::properties::Scalar::MassPressure).setZero(); scalar_properties_.at(mpm::properties::Scalar::Pressure).setZero(); // Initialise nodal vector properties @@ -226,6 +230,24 @@ void mpm::Node::update_mass_pressure( } } +//! Compute pressure from mass pressure +//! pressure = mass pressure / mass +template +void mpm::Node::compute_pressure() { + const double tolerance = 1.E-16; + for (unsigned phase = 0; phase < Tnphases; ++phase) { + if (this->mass(phase) > tolerance) { + scalar_properties_.at(mpm::properties::Scalar::Pressure)(phase) = + scalar_properties_.at(mpm::properties::Scalar::MassPressure)(phase) / + this->mass(phase); + + // Check to see if value is below threshold + if (std::abs(this->pressure(phase)) < 1.E-15) + scalar_properties_.at(mpm::properties::Scalar::Pressure)(phase) = 0.; + } + } +} + //! Assign pressure at the nodes from particle template void mpm::Node::assign_pressure(unsigned phase, diff --git a/include/node_base.h b/include/node_base.h index 7e696235d..d85080058 100644 --- a/include/node_base.h +++ b/include/node_base.h @@ -163,6 +163,9 @@ class NodeBase { virtual void update_mass_pressure(unsigned phase, double mass_pressure) noexcept = 0; + //! Compute pressure from the mass pressure + virtual void compute_pressure() = 0; + //! Assign pressure at the nodes from particle //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase diff --git a/include/particles/particle.h b/include/particles/particle.h index 4bbae8d6d..ca84fb21b 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -244,9 +244,6 @@ class Particle : public ParticleBase { : std::numeric_limits::quiet_NaN(); } - //! Map particle pressure to nodes - bool map_pressure_to_nodes() noexcept override; - //! Compute pressure smoothing of the particle based on nodal pressure //! $$\hat{p}_p = \sum_{i = 1}^{n_n} N_i(x_p) p_i$$ bool compute_pressure_smoothing() noexcept override; diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 859f751d9..fc3bd2313 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -727,27 +727,6 @@ void mpm::Particle::compute_updated_position( nodal_velocity * dt; } -//! Map particle pressure to nodes -template -bool mpm::Particle::map_pressure_to_nodes() noexcept { - // Mass is initialized - assert(this->mass() != std::numeric_limits::max()); - - bool status = false; - // Check if particle mass is set and state variable pressure is found - if (this->mass() != std::numeric_limits::max() && - (state_variables_.find("pressure") != state_variables_.end())) { - // Map particle pressure to nodes - for (unsigned i = 0; i < nodes_.size(); ++i) - nodes_[i]->update_mass_pressure( - mpm::ParticlePhase::Solid, - shapefn_[i] * this->mass() * state_variables_["pressure"]); - - status = true; - } - return status; -} - // Compute pressure smoothing of the particle based on nodal pressure template bool mpm::Particle::compute_pressure_smoothing() noexcept { diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index 9b421202c..25101ec42 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -260,9 +260,6 @@ class ParticleBase { //! Map internal force virtual void map_internal_force() noexcept = 0; - //! Map particle pressure to nodes - virtual bool map_pressure_to_nodes() noexcept = 0; - //! Compute pressure smoothing of the particle based on nodal pressure virtual bool compute_pressure_smoothing() noexcept = 0; diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc index 66feba4b9..b5dd74e74 100644 --- a/include/particles/particle_functions.tcc +++ b/include/particles/particle_functions.tcc @@ -64,6 +64,22 @@ void map_mass_momentum_to_nodes( particle->mass() * particle->velocity()); } +//! Map particle pressure to nodes +template +void map_mass_pressure_to_nodes( + std::shared_ptr> particle) noexcept { + // Mass is initialized + assert(particle->mass() != std::numeric_limits::max()); + + // Check if state variable pressure is found + if (particle->pressure() != std::numeric_limits::quiet_NaN()) { + // Map particle pressure to nodes + particle->map_scalar_property_nodes( + mpm::properties::Scalar::MassPressure, true, mpm::ParticlePhase::Solid, + particle->mass() * particle->pressure()); + } +} + //! Map body force to nodes template void map_body_force(std::shared_ptr> particle, diff --git a/include/solvers/mpm_explicit.tcc b/include/solvers/mpm_explicit.tcc index 286ab6579..2fcd60001 100644 --- a/include/solvers/mpm_explicit.tcc +++ b/include/solvers/mpm_explicit.tcc @@ -9,9 +9,16 @@ mpm::MPMExplicit::MPMExplicit(const std::shared_ptr& io) //! MPM Explicit pressure smoothing template void mpm::MPMExplicit::pressure_smoothing(unsigned phase) { - // Assign pressure to nodes - mesh_->iterate_over_particles(std::bind( - &mpm::ParticleBase::map_pressure_to_nodes, std::placeholders::_1)); + // Assign mass pressure to nodes + mesh_->iterate_over_particles( + [](std::shared_ptr> ptr) { + return mpm::particle::map_mass_pressure_to_nodes(ptr); + }); + + // Compute nodal pressure + mesh_->iterate_over_nodes_predicate( + std::bind(&mpm::NodeBase::compute_pressure, std::placeholders::_1), + std::bind(&mpm::NodeBase::status, std::placeholders::_1)); #ifdef USE_MPI int mpi_size = 1; diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 46a219242..6bcc8eb4e 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -775,9 +775,6 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // Map particle mass to nodes particle->assign_mass(std::numeric_limits::max()); - // Map particle pressure to nodes - // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); - // Assign mass to nodes REQUIRE(particle->compute_reference_location() == true); REQUIRE_NOTHROW(particle->compute_shapefn()); @@ -793,7 +790,6 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); REQUIRE_NOTHROW(mpm::particle::map_mass_momentum_to_nodes(particle)); - // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); REQUIRE(particle->compute_pressure_smoothing() == false); // Values of nodal mass @@ -1093,9 +1089,6 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { // Map particle mass to nodes particle->assign_mass(std::numeric_limits::max()); - // Map particle pressure to nodes - // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); - // Assign mass to nodes REQUIRE(particle->compute_reference_location() == true); REQUIRE_NOTHROW(particle->compute_shapefn()); @@ -1122,7 +1115,7 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { particle->pressure() == Approx(-8333333.333333333 * volumetric_strain).epsilon(Tolerance)); - REQUIRE_NOTHROW(particle->map_pressure_to_nodes()); + REQUIRE_NOTHROW(mpm::particle::map_mass_pressure_to_nodes(particle)); REQUIRE(particle->compute_pressure_smoothing() == true); } @@ -2055,9 +2048,6 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { // Map particle mass to nodes particle->assign_mass(std::numeric_limits::max()); - // Map particle pressure to nodes - // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); - // Assign mass to nodes REQUIRE(particle->compute_reference_location() == true); REQUIRE_NOTHROW(particle->compute_shapefn()); @@ -2073,7 +2063,6 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); REQUIRE_NOTHROW(mpm::particle::map_mass_momentum_to_nodes(particle)); - // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); REQUIRE(particle->compute_pressure_smoothing() == false); // Values of nodal mass @@ -2367,9 +2356,6 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { // Map particle mass to nodes particle->assign_mass(std::numeric_limits::max()); - // Map particle pressure to nodes - // TODO Assert: REQUIRE(particle->map_pressure_to_nodes() == false); - // Assign mass to nodes REQUIRE(particle->compute_reference_location() == true); REQUIRE_NOTHROW(particle->compute_shapefn()); @@ -2396,7 +2382,7 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { particle->pressure() == Approx(-8333333.333333333 * volumetric_strain).epsilon(Tolerance)); - REQUIRE_NOTHROW(particle->map_pressure_to_nodes()); + REQUIRE_NOTHROW(mpm::particle::map_mass_pressure_to_nodes(particle)); REQUIRE(particle->compute_pressure_smoothing() == true); } From bfee43aead5248d8e72c6fc14968679106ba1bd5 Mon Sep 17 00:00:00 2001 From: Nanda Date: Sat, 11 Jul 2020 19:44:40 -0700 Subject: [PATCH 25/55] :construction: change assign pressure to update pressure --- include/node.h | 5 +++-- include/node.tcc | 8 ++++---- include/node_base.h | 5 +++-- include/solvers/mpm_explicit.tcc | 4 ++-- tests/node_test.cc | 15 ++++++--------- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/include/node.h b/include/node.h index de78f18a7..7c826037a 100644 --- a/include/node.h +++ b/include/node.h @@ -170,8 +170,9 @@ class Node : public NodeBase { //! Assign pressure at the nodes from particle //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase - //! \param[in] mass_pressure Product of mass x pressure of a particle - void assign_pressure(unsigned phase, double mass_pressure) override; + //! \param[in] pressure Pressure of a particle + virtual void update_pressure(bool update, unsigned phase, + double pressure) override; //! Return pressure at a given node for a given phase //! \param[in] phase Index corresponding to the phase diff --git a/include/node.tcc b/include/node.tcc index 6bc4ae264..0a6cabf11 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -250,11 +250,11 @@ void mpm::Node::compute_pressure() { //! Assign pressure at the nodes from particle template -void mpm::Node::assign_pressure(unsigned phase, +void mpm::Node::update_pressure(bool update, + unsigned phase, double pressure) { - // Compute pressure from mass*pressure - std::lock_guard guard(node_mutex_); - scalar_properties_.at(mpm::properties::Scalar::Pressure)(phase) = pressure; + this->update_scalar_property(mpm::properties::Scalar::Pressure, update, phase, + pressure); } //! Compute velocity from momentum diff --git a/include/node_base.h b/include/node_base.h index d85080058..a117979c7 100644 --- a/include/node_base.h +++ b/include/node_base.h @@ -169,8 +169,9 @@ class NodeBase { //! Assign pressure at the nodes from particle //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase - //! \param[in] mass_pressure Product of mass x pressure of a particle - virtual void assign_pressure(unsigned phase, double mass_pressure) = 0; + //! \param[in] pressure Pressure of a particle + virtual void update_pressure(bool update, unsigned phase, + double pressure) = 0; //! Return pressure at a given node for a given phase //! \param[in] phase Index corresponding to the phase diff --git a/include/solvers/mpm_explicit.tcc b/include/solvers/mpm_explicit.tcc index 2fcd60001..b9b44ca7b 100644 --- a/include/solvers/mpm_explicit.tcc +++ b/include/solvers/mpm_explicit.tcc @@ -31,8 +31,8 @@ void mpm::MPMExplicit::pressure_smoothing(unsigned phase) { // MPI all reduce nodal pressure mesh_->template nodal_halo_exchange( std::bind(&mpm::NodeBase::pressure, std::placeholders::_1, phase), - std::bind(&mpm::NodeBase::assign_pressure, std::placeholders::_1, - phase, std::placeholders::_2)); + std::bind(&mpm::NodeBase::update_pressure, std::placeholders::_1, + false, phase, std::placeholders::_2)); } #endif diff --git a/tests/node_test.cc b/tests/node_test.cc index 3cc92d2a5..6eae5374e 100644 --- a/tests/node_test.cc +++ b/tests/node_test.cc @@ -147,7 +147,7 @@ TEST_CASE("Node is checked for 1D case", "[node][1D]") { REQUIRE(node->pressure(Nphase) == Approx(2001.4).epsilon(Tolerance)); // Assign pressure to 1000 pressure = 1000.; - node->assign_pressure(Nphase, pressure); + REQUIRE_NOTHROW(node->update_pressure(false, Nphase, pressure)); REQUIRE(node->pressure(Nphase) == Approx(1000.0).epsilon(Tolerance)); // Assign mass to 0 mass = 0.; @@ -155,8 +155,7 @@ TEST_CASE("Node is checked for 1D case", "[node][1D]") { REQUIRE(node->mass(Nphase) == Approx(0.0).epsilon(Tolerance)); // Try to update pressure to 2000, should throw and keep to 1000. pressure = 1000.; - const double pmass = 1.5; - node->assign_pressure(Nphase, pressure); + node->update_pressure(false, Nphase, pressure); REQUIRE(node->pressure(Nphase) == Approx(1000.0).epsilon(Tolerance)); } @@ -614,7 +613,7 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { REQUIRE(node->pressure(Nphase) == Approx(2001.4).epsilon(Tolerance)); // Assign pressure to 1000 pressure = 1000.; - node->assign_pressure(Nphase, pressure); + REQUIRE_NOTHROW(node->update_pressure(false, Nphase, pressure)); REQUIRE(node->pressure(Nphase) == Approx(1000.0).epsilon(Tolerance)); // Assign mass to 0 mass = 0.; @@ -622,8 +621,7 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { REQUIRE(node->mass(Nphase) == Approx(0.0).epsilon(Tolerance)); // Try to update pressure to 2000, should throw and keep to 1000. pressure = 1000.; - const double pmass = 1.5; - node->assign_pressure(Nphase, pressure); + node->update_pressure(false, Nphase, pressure); REQUIRE(node->pressure(Nphase) == Approx(1000.0).epsilon(Tolerance)); } @@ -1218,7 +1216,7 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { REQUIRE(node->pressure(Nphase) == Approx(2001.4).epsilon(Tolerance)); // Assign pressure to 1000 pressure = 1000.; - node->assign_pressure(Nphase, pressure); + REQUIRE_NOTHROW(node->update_pressure(false, Nphase, pressure)); REQUIRE(node->pressure(Nphase) == Approx(1000.0).epsilon(Tolerance)); // Assign mass to 0 mass = 0.; @@ -1226,8 +1224,7 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { REQUIRE(node->mass(Nphase) == Approx(0.0).epsilon(Tolerance)); // Try to update pressure to 2000, should throw and keep to 1000. pressure = 1000.; - const double pmass = 1.5; - node->assign_pressure(Nphase, pressure); + node->update_pressure(false, Nphase, pressure); REQUIRE(node->pressure(Nphase) == Approx(1000.0).epsilon(Tolerance)); } From 2727d3442a305aad36faf3b0dffa07e06afad650 Mon Sep 17 00:00:00 2001 From: Nanda Date: Sat, 11 Jul 2020 20:03:36 -0700 Subject: [PATCH 26/55] :construction: :dart: refactor update_mass_pressure --- include/node.h | 3 ++- include/node.tcc | 14 +++---------- include/node_base.h | 3 ++- tests/node_test.cc | 48 +++++++++++++++++++++++++++++++++------------ 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/include/node.h b/include/node.h index 7c826037a..099c2dbae 100644 --- a/include/node.h +++ b/include/node.h @@ -159,9 +159,10 @@ class Node : public NodeBase { } //! Update pressure at the nodes from particle + //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase //! \param[in] mass_pressure Product of mass x pressure of a particle - void update_mass_pressure(unsigned phase, + void update_mass_pressure(bool update, unsigned phase, double mass_pressure) noexcept override; //! Compute pressure from the mass pressure diff --git a/include/node.tcc b/include/node.tcc index 0a6cabf11..54ae7f6d4 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -217,17 +217,9 @@ void mpm::Node::update_momentum( //! Update pressure at the nodes from particle template void mpm::Node::update_mass_pressure( - unsigned phase, double mass_pressure) noexcept { - // Assert - assert(phase < Tnphases); - - const double tolerance = 1.E-16; - // Compute pressure from mass*pressure - if (this->mass(phase) > tolerance) { - std::lock_guard guard(node_mutex_); - scalar_properties_.at(mpm::properties::Scalar::Pressure)(phase) += - mass_pressure / this->mass(phase); - } + bool update, unsigned phase, double mass_pressure) noexcept { + this->update_scalar_property(mpm::properties::Scalar::MassPressure, update, + phase, mass_pressure); } //! Compute pressure from mass pressure diff --git a/include/node_base.h b/include/node_base.h index a117979c7..7ae912b01 100644 --- a/include/node_base.h +++ b/include/node_base.h @@ -158,9 +158,10 @@ class NodeBase { virtual VectorDim internal_force(unsigned phase) const = 0; //! Update pressure at the nodes from particle + //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase //! \param[in] mass_pressure Product of mass x pressure of a particle - virtual void update_mass_pressure(unsigned phase, + virtual void update_mass_pressure(bool update, unsigned phase, double mass_pressure) noexcept = 0; //! Compute pressure from the mass pressure diff --git a/tests/node_test.cc b/tests/node_test.cc index 6eae5374e..db41277af 100644 --- a/tests/node_test.cc +++ b/tests/node_test.cc @@ -140,10 +140,14 @@ TEST_CASE("Node is checked for 1D case", "[node][1D]") { REQUIRE(node->pressure(Nphase) == Approx(0.0).epsilon(Tolerance)); double pressure = 1000.7; // Update pressure to 1000.7 - REQUIRE_NOTHROW(node->update_mass_pressure(Nphase, mass * pressure)); + REQUIRE_NOTHROW( + node->update_mass_pressure(false, Nphase, mass * pressure)); + node->compute_pressure(); REQUIRE(node->pressure(Nphase) == Approx(1000.7).epsilon(Tolerance)); // Update pressure to 2001.4 - REQUIRE_NOTHROW(node->update_mass_pressure(Nphase, mass * pressure)); + REQUIRE_NOTHROW( + node->update_mass_pressure(true, Nphase, mass * pressure)); + node->compute_pressure(); REQUIRE(node->pressure(Nphase) == Approx(2001.4).epsilon(Tolerance)); // Assign pressure to 1000 pressure = 1000.; @@ -153,10 +157,14 @@ TEST_CASE("Node is checked for 1D case", "[node][1D]") { mass = 0.; REQUIRE_NOTHROW(node->update_mass(false, Nphase, mass)); REQUIRE(node->mass(Nphase) == Approx(0.0).epsilon(Tolerance)); - // Try to update pressure to 2000, should throw and keep to 1000. + // Try to update pressure, should throw and keep to 1000. pressure = 1000.; - node->update_pressure(false, Nphase, pressure); + node->update_mass_pressure(false, Nphase, pressure); + node->compute_pressure(); REQUIRE(node->pressure(Nphase) == Approx(1000.0).epsilon(Tolerance)); + // Update pressure to 2000. + REQUIRE_NOTHROW(node->update_pressure(true, Nphase, pressure)); + REQUIRE(node->pressure(Nphase) == Approx(2000.0).epsilon(Tolerance)); } SECTION("Check external force") { @@ -606,10 +614,14 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { REQUIRE(node->pressure(Nphase) == Approx(0.0).epsilon(Tolerance)); double pressure = 1000.7; // Update pressure to 1000.7 - REQUIRE_NOTHROW(node->update_mass_pressure(Nphase, mass * pressure)); + REQUIRE_NOTHROW( + node->update_mass_pressure(false, Nphase, mass * pressure)); + node->compute_pressure(); REQUIRE(node->pressure(Nphase) == Approx(1000.7).epsilon(Tolerance)); // Update pressure to 2001.4 - REQUIRE_NOTHROW(node->update_mass_pressure(Nphase, mass * pressure)); + REQUIRE_NOTHROW( + node->update_mass_pressure(true, Nphase, mass * pressure)); + node->compute_pressure(); REQUIRE(node->pressure(Nphase) == Approx(2001.4).epsilon(Tolerance)); // Assign pressure to 1000 pressure = 1000.; @@ -619,10 +631,14 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { mass = 0.; REQUIRE_NOTHROW(node->update_mass(false, Nphase, mass)); REQUIRE(node->mass(Nphase) == Approx(0.0).epsilon(Tolerance)); - // Try to update pressure to 2000, should throw and keep to 1000. + // Try to update pressure, should throw and keep to 1000. pressure = 1000.; - node->update_pressure(false, Nphase, pressure); + node->update_mass_pressure(false, Nphase, pressure); + node->compute_pressure(); REQUIRE(node->pressure(Nphase) == Approx(1000.0).epsilon(Tolerance)); + // Update pressure to 2000. + REQUIRE_NOTHROW(node->update_pressure(true, Nphase, pressure)); + REQUIRE(node->pressure(Nphase) == Approx(2000.0).epsilon(Tolerance)); } SECTION("Check volume") { @@ -1209,10 +1225,14 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { REQUIRE(node->pressure(Nphase) == Approx(0.0).epsilon(Tolerance)); double pressure = 1000.7; // Update pressure to 1000.7 - REQUIRE_NOTHROW(node->update_mass_pressure(Nphase, mass * pressure)); + REQUIRE_NOTHROW( + node->update_mass_pressure(false, Nphase, mass * pressure)); + node->compute_pressure(); REQUIRE(node->pressure(Nphase) == Approx(1000.7).epsilon(Tolerance)); // Update pressure to 2001.4 - REQUIRE_NOTHROW(node->update_mass_pressure(Nphase, mass * pressure)); + REQUIRE_NOTHROW( + node->update_mass_pressure(true, Nphase, mass * pressure)); + node->compute_pressure(); REQUIRE(node->pressure(Nphase) == Approx(2001.4).epsilon(Tolerance)); // Assign pressure to 1000 pressure = 1000.; @@ -1222,10 +1242,14 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { mass = 0.; REQUIRE_NOTHROW(node->update_mass(false, Nphase, mass)); REQUIRE(node->mass(Nphase) == Approx(0.0).epsilon(Tolerance)); - // Try to update pressure to 2000, should throw and keep to 1000. + // Try to update pressure, should throw and keep to 1000. pressure = 1000.; - node->update_pressure(false, Nphase, pressure); + node->update_mass_pressure(false, Nphase, pressure); + node->compute_pressure(); REQUIRE(node->pressure(Nphase) == Approx(1000.0).epsilon(Tolerance)); + // Update pressure to 2000. + REQUIRE_NOTHROW(node->update_pressure(true, Nphase, pressure)); + REQUIRE(node->pressure(Nphase) == Approx(2000.0).epsilon(Tolerance)); } SECTION("Check external force") { From f9602fd9a953ab2f9409eb59a698a8ee185b120c Mon Sep 17 00:00:00 2001 From: Nanda Date: Sat, 11 Jul 2020 20:39:48 -0700 Subject: [PATCH 27/55] :wrench: fix return value of interpolate vector properties --- include/particles/particle_base.h | 10 ++++++---- include/particles/particle_base.tcc | 7 +++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index 25101ec42..46384b794 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -153,7 +153,8 @@ class ParticleBase { void map_scalar_property_nodes(mpm::properties::Scalar property, bool update, unsigned phase, double value) noexcept; - //! Return property at a given node for a given phase + //! Return an interpolation of scalar property in particle from nodes + //! \param[in] property Name of the property to update //! \param[in] phase Index corresponding to the phase double interpolate_scalar_property_nodes(mpm::properties::Scalar property, unsigned phase) const; @@ -187,10 +188,11 @@ class ParticleBase { mpm::properties::Vector property, bool update, unsigned phase, const Eigen::Matrix& value) noexcept; - //! Return property at a given node for a given phase + //! Return an interpolation of vector property in particle from nodes + //! \param[in] property Name of the property to update //! \param[in] phase Index corresponding to the phase - double interpolate_vector_property_nodes(mpm::properties::Vector property, - unsigned phase) const; + Eigen::Matrix interpolate_vector_property_nodes( + mpm::properties::Vector property, unsigned phase) const; //! Return mass density virtual double mass_density() const = 0; diff --git a/include/particles/particle_base.tcc b/include/particles/particle_base.tcc index 4a389ee6d..f67c45ebf 100644 --- a/include/particles/particle_base.tcc +++ b/include/particles/particle_base.tcc @@ -90,7 +90,6 @@ Eigen::Matrix mpm::ParticleBase::vector_property( template void mpm::ParticleBase::map_vector_property_nodes( mpm::properties::Vector property, bool update, unsigned phase) noexcept { - // TODO: Check assert needed? // Map vector property to nodes for (unsigned i = 0; i < nodes_.size(); ++i) nodes_[i]->update_vector_property( @@ -102,7 +101,6 @@ template void mpm::ParticleBase::map_vector_property_nodes( mpm::properties::Vector property, bool update, unsigned phase, const Eigen::Matrix& value) noexcept { - // TODO: Check assert needed? // Map vector property to nodes for (unsigned i = 0; i < nodes_.size(); ++i) nodes_[i]->update_vector_property(property, update, phase, @@ -111,8 +109,9 @@ void mpm::ParticleBase::map_vector_property_nodes( //! Interpolate vector property from nodes template -double mpm::ParticleBase::interpolate_vector_property_nodes( - mpm::properties::Vector property, unsigned phase) const { +Eigen::Matrix + mpm::ParticleBase::interpolate_vector_property_nodes( + mpm::properties::Vector property, unsigned phase) const { Eigen::Matrix value = Eigen::Matrix::Zero(); // Interpolate vector property from nodes for (unsigned i = 0; i < nodes_.size(); ++i) From a3ee6845fa89c2fcd579e0a164e8d052ea62f1ca Mon Sep 17 00:00:00 2001 From: Nanda Date: Sat, 11 Jul 2020 20:41:20 -0700 Subject: [PATCH 28/55] :construction: :dart: use interpolate functions in particle --- include/particles/particle.tcc | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index fc3bd2313..9499bd73f 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -696,21 +696,14 @@ void mpm::Particle::compute_updated_position( // Check if particle has a valid cell ptr assert(cell_ != nullptr); // Get interpolated nodal velocity - Eigen::Matrix nodal_velocity = - Eigen::Matrix::Zero(); - - for (unsigned i = 0; i < nodes_.size(); ++i) - nodal_velocity += - shapefn_[i] * nodes_[i]->velocity(mpm::ParticlePhase::Solid); + const auto& nodal_velocity = this->interpolate_vector_property_nodes( + mpm::properties::Vector::Velocity, mpm::ParticlePhase::Solid); // Acceleration update if (!velocity_update) { // Get interpolated nodal acceleration - Eigen::Matrix nodal_acceleration = - Eigen::Matrix::Zero(); - for (unsigned i = 0; i < nodes_.size(); ++i) - nodal_acceleration += - shapefn_[i] * nodes_[i]->acceleration(mpm::ParticlePhase::Solid); + const auto& nodal_acceleration = this->interpolate_vector_property_nodes( + mpm::properties::Vector::Acceleration, mpm::ParticlePhase::Solid); // Update particle velocity from interpolated nodal acceleration vector_properties_.at(mpm::properties::Vector::Velocity) += @@ -722,6 +715,7 @@ void mpm::Particle::compute_updated_position( // New position current position + velocity * dt this->coordinates_ += nodal_velocity * dt; + // Update displacement (displacement is initialized from zero) vector_properties_.at(mpm::properties::Vector::Displacement) += nodal_velocity * dt; @@ -737,13 +731,8 @@ bool mpm::Particle::compute_pressure_smoothing() noexcept { // Check if particle has a valid cell ptr if (cell_ != nullptr && (state_variables_.find("pressure") != state_variables_.end())) { - - double pressure = 0.; - // Update particle pressure to interpolated nodal pressure - for (unsigned i = 0; i < this->nodes_.size(); ++i) - pressure += shapefn_[i] * nodes_[i]->pressure(mpm::ParticlePhase::Solid); - - state_variables_["pressure"] = pressure; + state_variables_["pressure"] = this->interpolate_scalar_property_nodes( + mpm::properties::Scalar::Pressure, mpm::ParticlePhase::Solid); status = true; } return status; From 45aa3ba30d382b707a8ec18c28866bd6dbbdcc9f Mon Sep 17 00:00:00 2001 From: Nanda Date: Sun, 12 Jul 2020 20:34:32 -0700 Subject: [PATCH 29/55] :construction: refactor pressure smoothing interpolation --- include/particles/particle.h | 12 ++++++++---- include/particles/particle.tcc | 17 ----------------- include/particles/particle_base.h | 6 +++--- include/particles/particle_functions.tcc | 15 +++++++++++++++ include/solvers/mpm_explicit.tcc | 5 +++-- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/include/particles/particle.h b/include/particles/particle.h index ca84fb21b..e45adc107 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -235,6 +235,14 @@ class Particle : public ParticleBase { void compute_updated_position(double dt, bool velocity_update = false) noexcept override; + //! Assign a state variable + //! \param[in] var State variable + //! \param[in] value State variable to be assigned + void assign_state_variable(const std::string& var, double value) override { + if (state_variables_.find(var) != state_variables_.end()) + state_variables_.at(var) = value; + } + //! Return a state variable //! \param[in] var State variable //! \retval Quantity of the state history variable @@ -244,10 +252,6 @@ class Particle : public ParticleBase { : std::numeric_limits::quiet_NaN(); } - //! Compute pressure smoothing of the particle based on nodal pressure - //! $$\hat{p}_p = \sum_{i = 1}^{n_n} N_i(x_p) p_i$$ - bool compute_pressure_smoothing() noexcept override; - //! Return pressure of the particles double pressure() const override { return (state_variables_.find("pressure") != state_variables_.end()) diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 9499bd73f..98ca378f9 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -721,23 +721,6 @@ void mpm::Particle::compute_updated_position( nodal_velocity * dt; } -// Compute pressure smoothing of the particle based on nodal pressure -template -bool mpm::Particle::compute_pressure_smoothing() noexcept { - // Assert - assert(cell_ != nullptr); - - bool status = false; - // Check if particle has a valid cell ptr - if (cell_ != nullptr && - (state_variables_.find("pressure") != state_variables_.end())) { - state_variables_["pressure"] = this->interpolate_scalar_property_nodes( - mpm::properties::Scalar::Pressure, mpm::ParticlePhase::Solid); - status = true; - } - return status; -} - //! Apply particle velocity constraints template void mpm::Particle::apply_particle_velocity_constraints(unsigned dir, diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index 46384b794..231dbcb40 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -262,9 +262,6 @@ class ParticleBase { //! Map internal force virtual void map_internal_force() noexcept = 0; - //! Compute pressure smoothing of the particle based on nodal pressure - virtual bool compute_pressure_smoothing() noexcept = 0; - //! Assign velocity virtual void assign_velocity(const VectorDim& velocity) = 0; @@ -287,6 +284,9 @@ class ParticleBase { virtual void compute_updated_position( double dt, bool velocity_update = false) noexcept = 0; + //! Return a state variable + virtual void assign_state_variable(const std::string& var, double value) = 0; + //! Return a state variable virtual double state_variable(const std::string& var) const = 0; diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc index b5dd74e74..7134f0c89 100644 --- a/include/particles/particle_functions.tcc +++ b/include/particles/particle_functions.tcc @@ -102,5 +102,20 @@ void map_traction_force( } } +// Compute pressure smoothing of the particle based on nodal pressure +template +void compute_pressure_smoothing( + std::shared_ptr> particle) noexcept { + // Assert + assert(particle->cell_ptr()); + + // Check if particle has pressure + if (particle->pressure() != std::numeric_limits::quiet_NaN()) { + double pressure = particle->interpolate_scalar_property_nodes( + mpm::properties::Scalar::Pressure, mpm::ParticlePhase::Solid); + particle->assign_state_variable("pressure", pressure); + } +} + } // namespace particle } // namespace mpm diff --git a/include/solvers/mpm_explicit.tcc b/include/solvers/mpm_explicit.tcc index b9b44ca7b..0b097bb32 100644 --- a/include/solvers/mpm_explicit.tcc +++ b/include/solvers/mpm_explicit.tcc @@ -38,8 +38,9 @@ void mpm::MPMExplicit::pressure_smoothing(unsigned phase) { // Smooth pressure over particles mesh_->iterate_over_particles( - std::bind(&mpm::ParticleBase::compute_pressure_smoothing, - std::placeholders::_1)); + [](std::shared_ptr> ptr) { + return mpm::particle::compute_pressure_smoothing(ptr); + }); } //! MPM Explicit compute stress strain From 212505e6af1f698a93c26867a930259cde745e55 Mon Sep 17 00:00:00 2001 From: Nanda Date: Sun, 12 Jul 2020 20:34:47 -0700 Subject: [PATCH 30/55] :dart: fix and add pressure mapping test --- tests/particle_test.cc | 66 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 6bcc8eb4e..21e5ad332 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -790,8 +790,6 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); REQUIRE_NOTHROW(mpm::particle::map_mass_momentum_to_nodes(particle)); - REQUIRE(particle->compute_pressure_smoothing() == false); - // Values of nodal mass std::array nodal_mass{562.5, 187.5, 62.5, 187.5}; // Check nodal mass @@ -1075,6 +1073,9 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { Factory, unsigned, const Json&>::instance() ->create("Newtonian2D", std::move(mid1), jmaterial1); + // Reset nodal properties + for (const auto& node : nodes) node->initialise(); + // Assign material properties REQUIRE(particle->assign_material(material1) == true); @@ -1115,8 +1116,38 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { particle->pressure() == Approx(-8333333.333333333 * volumetric_strain).epsilon(Tolerance)); + // Check return and assign state variable + REQUIRE( + particle->state_variable("pressure") == + Approx(-8333333.333333333 * volumetric_strain).epsilon(Tolerance)); + + REQUIRE_NOTHROW(particle->assign_state_variable( + "pressure", -8333333.333333333 * volumetric_strain)); + + // Check pressure smoothing REQUIRE_NOTHROW(mpm::particle::map_mass_pressure_to_nodes(particle)); - REQUIRE(particle->compute_pressure_smoothing() == true); + for (const auto& node : nodes) node->compute_pressure(); + REQUIRE_NOTHROW(mpm::particle::compute_pressure_smoothing(particle)); + REQUIRE( + particle->pressure() == + Approx(-8333333.333333333 * volumetric_strain).epsilon(Tolerance)); + + // Assign pressure equal to 1.0, we expect its smoothed value is also 1.0 + REQUIRE_NOTHROW(particle->assign_state_variable("pressure", 1.0)); + REQUIRE(particle->pressure() == Approx(1.0).epsilon(Tolerance)); + + for (const auto& node : nodes) + node->update_scalar_property(mpm::properties::Scalar::MassPressure, + false, 0, 0.0); + REQUIRE_NOTHROW(mpm::particle::map_mass_pressure_to_nodes(particle)); + for (const auto& node : nodes) { + REQUIRE( + node->scalar_property(mpm::properties::Scalar::MassPressure, 0) == + node->scalar_property(mpm::properties::Scalar::Mass, 0)); + node->compute_pressure(); + } + REQUIRE_NOTHROW(mpm::particle::compute_pressure_smoothing(particle)); + REQUIRE(particle->pressure() == Approx(1.0).epsilon(Tolerance)); } SECTION("Particle assign state variables") { @@ -2063,8 +2094,6 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { REQUIRE_NOTHROW(mpm::particle::compute_mass(particle)); REQUIRE_NOTHROW(mpm::particle::map_mass_momentum_to_nodes(particle)); - REQUIRE(particle->compute_pressure_smoothing() == false); - // Values of nodal mass std::array nodal_mass{125., 375., 1125., 375., 375., 1125., 3375., 1125.}; @@ -2382,8 +2411,33 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { particle->pressure() == Approx(-8333333.333333333 * volumetric_strain).epsilon(Tolerance)); + // Check return and assign state variable + REQUIRE( + particle->state_variable("pressure") == + Approx(-8333333.333333333 * volumetric_strain).epsilon(Tolerance)); + + REQUIRE_NOTHROW(particle->assign_state_variable( + "pressure", -8333333.333333333 * volumetric_strain)); + + // Check pressure smoothing + REQUIRE_NOTHROW(mpm::particle::map_mass_pressure_to_nodes(particle)); + for (const auto& node : nodes) node->compute_pressure(); + REQUIRE_NOTHROW(mpm::particle::compute_pressure_smoothing(particle)); + REQUIRE(particle->pressure() == + Approx(-2083333.3333333333).epsilon(Tolerance)); + + // Assign pressure equal to 1.0, we expect its smoothed value is 0.5 as + // the nodal mass is mapped twice in this case + REQUIRE_NOTHROW(particle->assign_state_variable("pressure", 1.0)); + REQUIRE(particle->pressure() == Approx(1.0).epsilon(Tolerance)); + + for (const auto& node : nodes) + node->update_scalar_property(mpm::properties::Scalar::MassPressure, + false, 0, 0.0); REQUIRE_NOTHROW(mpm::particle::map_mass_pressure_to_nodes(particle)); - REQUIRE(particle->compute_pressure_smoothing() == true); + for (const auto& node : nodes) node->compute_pressure(); + REQUIRE_NOTHROW(mpm::particle::compute_pressure_smoothing(particle)); + REQUIRE(particle->pressure() == Approx(0.5).epsilon(Tolerance)); } SECTION("Particle assign state variables") { From 1251bd7f326e652318ff22590cf187ad19dc286e Mon Sep 17 00:00:00 2001 From: Nanda Date: Sun, 12 Jul 2020 20:39:05 -0700 Subject: [PATCH 31/55] :construction: add TODO notes in particle.tcc --- include/particles/particle.tcc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 98ca378f9..d7e740876 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -478,6 +478,7 @@ bool mpm::Particle::assign_volume(double volume) { } //! Map multimaterial properties to nodes +// TODO: Contact function to be refactored template void mpm::Particle::map_multimaterial_mass_momentum_to_nodes() noexcept { // Check if particle mass is set @@ -496,6 +497,7 @@ void mpm::Particle::map_multimaterial_mass_momentum_to_nodes() noexcept { } //! Map multimaterial displacements to nodes +// TODO: Contact function to be refactored template void mpm::Particle::map_multimaterial_displacements_to_nodes() noexcept { // Check if particle mass is set @@ -512,6 +514,7 @@ void mpm::Particle::map_multimaterial_displacements_to_nodes() noexcept { } //! Map multimaterial domain gradients to nodes +// TODO: Contact function to be refactored template void mpm::Particle< Tdim>::map_multimaterial_domain_gradients_to_nodes() noexcept { @@ -722,6 +725,8 @@ void mpm::Particle::compute_updated_position( } //! Apply particle velocity constraints +// TODO: revisit constraint application in vector properties, should assign +// direction template void mpm::Particle::apply_particle_velocity_constraints(unsigned dir, double velocity) { @@ -736,6 +741,7 @@ Eigen::VectorXd mpm::Particle::tensor_data(const std::string& property) { } //! Assign material id of this particle to nodes +// TODO: Contact function to be refactored template void mpm::Particle::append_material_id_to_nodes() const { for (unsigned i = 0; i < nodes_.size(); ++i) From 27ea5d6f14588c9986c9bd027cbfa385ee3f4bae Mon Sep 17 00:00:00 2001 From: Nanda Date: Sun, 12 Jul 2020 20:52:06 -0700 Subject: [PATCH 32/55] :dart: add test for assign and return scalar and vector property --- tests/node_test.cc | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/node_test.cc b/tests/node_test.cc index db41277af..9bd3584c7 100644 --- a/tests/node_test.cc +++ b/tests/node_test.cc @@ -134,6 +134,16 @@ TEST_CASE("Node is checked for 1D case", "[node][1D]") { mass = 100.; REQUIRE_NOTHROW(node->update_mass(false, Nphase, mass)); REQUIRE(node->mass(Nphase) == Approx(100.0).epsilon(Tolerance)); + // Assign mass to 200 using scalar property update true + REQUIRE_NOTHROW(node->update_scalar_property(mpm::properties::Scalar::Mass, + true, Nphase, mass)); + REQUIRE(node->scalar_property(mpm::properties::Scalar::Mass, Nphase) == + Approx(200.0).epsilon(Tolerance)); + // Assign mass to 100 using scalar property update false + REQUIRE_NOTHROW(node->update_scalar_property(mpm::properties::Scalar::Mass, + false, Nphase, mass)); + REQUIRE(node->scalar_property(mpm::properties::Scalar::Mass, Nphase) == + Approx(100.0).epsilon(Tolerance)); SECTION("Check nodal pressure") { // Check pressure @@ -268,6 +278,22 @@ TEST_CASE("Node is checked for 1D case", "[node][1D]") { for (unsigned i = 0; i < force.size(); ++i) REQUIRE(node->internal_force(Nphase)(i) == Approx(10.).epsilon(Tolerance)); + + // Assign force to 20 using vector property update true + REQUIRE_NOTHROW(node->update_vector_property( + mpm::properties::Vector::InternalForce, true, Nphase, force)); + for (unsigned i = 0; i < force.size(); ++i) + REQUIRE(node->vector_property(mpm::properties::Vector::InternalForce, + Nphase)(i) == + Approx(20.).epsilon(Tolerance)); + + // Assign force to 10 using vector property update false + REQUIRE_NOTHROW(node->update_vector_property( + mpm::properties::Vector::InternalForce, false, Nphase, force)); + for (unsigned i = 0; i < force.size(); ++i) + REQUIRE(node->vector_property(mpm::properties::Vector::InternalForce, + Nphase)(i) == + Approx(10.).epsilon(Tolerance)); } SECTION("Check compute acceleration and velocity") { From ae339f1a80790e21997fd3ea8171515c0e950a85 Mon Sep 17 00:00:00 2001 From: Nanda Date: Mon, 13 Jul 2020 09:56:18 -0700 Subject: [PATCH 33/55] :wrench: clang format --- include/solvers/mpm_base.tcc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/solvers/mpm_base.tcc b/include/solvers/mpm_base.tcc index faabd0398..15ec41886 100644 --- a/include/solvers/mpm_base.tcc +++ b/include/solvers/mpm_base.tcc @@ -813,8 +813,9 @@ void mpm::MPMBase::nodal_velocity_constraints( // Add velocity constraint to mesh auto velocity_constraint = std::make_shared(nset_id, dir, velocity); - bool velocity_constraints = constraints_->assign_nodal_velocity_constraint( - nset_id, velocity_constraint); + bool velocity_constraints = + constraints_->assign_nodal_velocity_constraint( + nset_id, velocity_constraint); if (!velocity_constraints) throw std::runtime_error( "Nodal velocity constraint is not properly assigned"); From f34b64c2aa25cde88ca3ce8170c8bfbe403cbb5a Mon Sep 17 00:00:00 2001 From: Nanda Date: Mon, 13 Jul 2020 18:20:20 -0700 Subject: [PATCH 34/55] :wrench: remove virtual in initialise particle --- include/particles/particle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/particles/particle.h b/include/particles/particle.h index e45adc107..06ece14dc 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -55,7 +55,7 @@ class Particle : public ParticleBase { //! \param[in] particle HDF5 data of particle //! \param[in] material Material associated with the particle //! \retval status Status of reading HDF5 particle - virtual bool initialise_particle( + bool initialise_particle( const HDF5Particle& particle, const std::shared_ptr>& material) override; From 164491fd7f5c7ef09536405aeb2ae0d5fd8947ea Mon Sep 17 00:00:00 2001 From: Nanda Date: Mon, 13 Jul 2020 20:44:16 -0700 Subject: [PATCH 35/55] :dart: :construction_worker: revert benchmarks to gcc --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cd3613b44..57f12f47b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -127,7 +127,7 @@ workflows: version: 2 build: jobs: - - benchmarks + - gcc - clang - cppcheck - codecov From 3d35f450f71d6d69b979379795462aecf5e96cf7 Mon Sep 17 00:00:00 2001 From: Nanda Date: Tue, 14 Jul 2020 10:35:26 -0700 Subject: [PATCH 36/55] :wrench: modify explanation as suggested --- include/node.h | 2 ++ include/node_base.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/node.h b/include/node.h index 099c2dbae..79dfe271f 100644 --- a/include/node.h +++ b/include/node.h @@ -74,6 +74,7 @@ class Node : public NodeBase { unsigned phase, double value) noexcept override; //! Return property at a given node for a given phase + //! \param[in] property Name of the property to return //! \param[in] phase Index corresponding to the phase double scalar_property(mpm::properties::Scalar property, unsigned phase) const override; @@ -88,6 +89,7 @@ class Node : public NodeBase { const Eigen::Matrix& value) noexcept override; //! Return property at a given node for a given phase + //! \param[in] property Name of the property to return //! \param[in] phase Index corresponding to the phase virtual Eigen::Matrix vector_property( mpm::properties::Vector property, unsigned phase) const override; diff --git a/include/node_base.h b/include/node_base.h index 7ae912b01..018dc31fc 100644 --- a/include/node_base.h +++ b/include/node_base.h @@ -82,6 +82,7 @@ class NodeBase { double value) noexcept = 0; //! Return property at a given node for a given phase + //! \param[in] property Name of the property to return //! \param[in] phase Index corresponding to the phase virtual double scalar_property(mpm::properties::Scalar property, unsigned phase) const = 0; @@ -96,6 +97,7 @@ class NodeBase { const Eigen::Matrix& value) noexcept = 0; //! Return property at a given node for a given phase + //! \param[in] property Name of the property to return //! \param[in] phase Index corresponding to the phase virtual Eigen::Matrix vector_property( mpm::properties::Vector property, unsigned phase) const = 0; From deb54a7f29411cd2b060c95885ce101b636d7879 Mon Sep 17 00:00:00 2001 From: Nanda Date: Tue, 14 Jul 2020 17:25:44 -0700 Subject: [PATCH 37/55] :construction: :dart: some minor corrections and test addition --- include/particles/particle.h | 10 +++++++--- include/particles/particle_base.h | 4 +++- tests/mpm_explicit_usf_test.cc | 18 ++++++++++++++++++ tests/particle_test.cc | 8 ++++---- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/include/particles/particle.h b/include/particles/particle.h index 06ece14dc..b0b20b255 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -127,7 +127,7 @@ class Particle : public ParticleBase { //! Return size of particle in natural coordinates VectorDim natural_size() const override { return natural_size_; } - //! \param[in] phase Index corresponding to the phase + //! Return mass density double mass_density() const override { return this->scalar_property(mpm::properties::Scalar::MassDensity); } @@ -143,7 +143,6 @@ class Particle : public ParticleBase { //! Assign nodal mass to particles //! \param[in] mass Mass from the particles in a cell - //! \retval status Assignment status void assign_mass(double mass) override { scalar_properties_.at(mpm::properties::Scalar::Mass) = mass; } @@ -201,7 +200,6 @@ class Particle : public ParticleBase { //! Assign velocity to the particle //! \param[in] velocity A vector of particle velocity - //! \retval status Assignment status void assign_velocity(const VectorDim& velocity) override { vector_properties_.at(mpm::properties::Vector::Velocity) = velocity; }; @@ -252,6 +250,12 @@ class Particle : public ParticleBase { : std::numeric_limits::quiet_NaN(); } + //! Assign a state variable + //! \param[in] value Particle pressure to be assigned + void assign_pressure(double pressure) override { + this->assign_state_variable("pressure", pressure); + } + //! Return pressure of the particles double pressure() const override { return (state_variables_.find("pressure") != state_variables_.end()) diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index 231dbcb40..fb119bc2a 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -211,7 +211,6 @@ class ParticleBase { const std::shared_ptr>& material) = 0; //! Return material - //! \retval material Pointer to a material virtual std::shared_ptr> material() const = 0; //! Return material id @@ -232,6 +231,9 @@ class ParticleBase { //! Return mass virtual double mass() const = 0; + //! Assign pressure + virtual void assign_pressure(double pressure) = 0; + //! Return pressure virtual double pressure() const = 0; diff --git a/tests/mpm_explicit_usf_test.cc b/tests/mpm_explicit_usf_test.cc index 89062396f..3d7b6de49 100644 --- a/tests/mpm_explicit_usf_test.cc +++ b/tests/mpm_explicit_usf_test.cc @@ -85,6 +85,15 @@ TEST_CASE("MPM 2D Explicit implementation is checked", // Solve REQUIRE(mpm->solve() == true); } + + SECTION("Check pressure smoothing") { + // Create an IO object + auto io = std::make_unique(argc, argv); + // Run explicit MPM + auto mpm = std::make_unique>(std::move(io)); + // Pressure smoothing + REQUIRE_NOTHROW(mpm->pressure_smoothing(0)); + } } // Check MPM Explicit @@ -162,4 +171,13 @@ TEST_CASE("MPM 3D Explicit implementation is checked", // Solve REQUIRE(mpm->solve() == true); } + + SECTION("Check pressure smoothing") { + // Create an IO object + auto io = std::make_unique(argc, argv); + // Run explicit MPM + auto mpm = std::make_unique>(std::move(io)); + // Pressure smoothing + REQUIRE_NOTHROW(mpm->pressure_smoothing(0)); + } } diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 21e5ad332..3663d2889 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -1121,8 +1121,8 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { particle->state_variable("pressure") == Approx(-8333333.333333333 * volumetric_strain).epsilon(Tolerance)); - REQUIRE_NOTHROW(particle->assign_state_variable( - "pressure", -8333333.333333333 * volumetric_strain)); + REQUIRE_NOTHROW( + particle->assign_pressure(-8333333.333333333 * volumetric_strain)); // Check pressure smoothing REQUIRE_NOTHROW(mpm::particle::map_mass_pressure_to_nodes(particle)); @@ -2416,8 +2416,8 @@ TEST_CASE("Particle is checked for 3D case", "[particle][3D]") { particle->state_variable("pressure") == Approx(-8333333.333333333 * volumetric_strain).epsilon(Tolerance)); - REQUIRE_NOTHROW(particle->assign_state_variable( - "pressure", -8333333.333333333 * volumetric_strain)); + REQUIRE_NOTHROW( + particle->assign_pressure(-8333333.333333333 * volumetric_strain)); // Check pressure smoothing REQUIRE_NOTHROW(mpm::particle::map_mass_pressure_to_nodes(particle)); From fcfcf871ab47404f23978f5bc2c54b587ee098af Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Fri, 17 Jul 2020 14:45:24 -0500 Subject: [PATCH 38/55] :hammer: Improve performance with vector --- include/mpm_properties.h | 22 ++++---- include/node.h | 7 +-- include/node.tcc | 90 ++++++++++++++++++------------- include/particles/particle.tcc | 21 ++++---- include/particles/particle_base.h | 5 +- 5 files changed, 80 insertions(+), 65 deletions(-) diff --git a/include/mpm_properties.h b/include/mpm_properties.h index 4e3568667..f3aea193e 100644 --- a/include/mpm_properties.h +++ b/include/mpm_properties.h @@ -5,20 +5,20 @@ namespace mpm { namespace properties { //! Scalar Properties enum Scalar : unsigned int { - Mass, - Volume, - MassDensity, - MassPressure, - Pressure + Mass = 0, + Volume = 1, + MassDensity = 2, + MassPressure = 3, + Pressure = 4 }; //! Vector Properties enum Vector : unsigned int { - Displacement, - Velocity, - Acceleration, - Momentum, - ExternalForce, - InternalForce + Displacement = 0, + Velocity = 1, + Acceleration = 2, + Momentum = 3, + ExternalForce = 4, + InternalForce = 5 }; } // namespace properties } // namespace mpm diff --git a/include/node.h b/include/node.h index 79dfe271f..a49d36741 100644 --- a/include/node.h +++ b/include/node.h @@ -319,12 +319,9 @@ class Node : public NodeBase { //! Status bool status_{false}; //! Scalar properties - tsl::ordered_map> - scalar_properties_; + std::vector> scalar_properties_; //! Vector properties - tsl::ordered_map> - vector_properties_; + std::vector> vector_properties_; //! Displacement Eigen::Matrix contact_displacement_; //! Velocity constraints diff --git a/include/node.tcc b/include/node.tcc index 54ae7f6d4..7d3051e47 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -19,35 +19,38 @@ mpm::Node::Node( concentrated_force_.setZero(); // Initialize scalar properties - scalar_properties_.emplace( - std::make_pair(mpm::properties::Scalar::Mass, - Eigen::Matrix::Zero())); - scalar_properties_.emplace( - std::make_pair(mpm::properties::Scalar::Volume, - Eigen::Matrix::Zero())); - scalar_properties_.emplace( - std::make_pair(mpm::properties::Scalar::MassPressure, - Eigen::Matrix::Zero())); - scalar_properties_.emplace( - std::make_pair(mpm::properties::Scalar::Pressure, - Eigen::Matrix::Zero())); + scalar_properties_.reserve(5); + // Mass + scalar_properties_.emplace_back(Eigen::Matrix::Zero()); + // Volume + scalar_properties_.emplace_back(Eigen::Matrix::Zero()); + // MassDensity + scalar_properties_.emplace_back(Eigen::Matrix::Zero()); + // MassPressure + scalar_properties_.emplace_back(Eigen::Matrix::Zero()); + // Pressure + scalar_properties_.emplace_back(Eigen::Matrix::Zero()); // Initialize vector properties - vector_properties_.emplace( - std::make_pair(mpm::properties::Vector::Velocity, - Eigen::Matrix::Zero())); - vector_properties_.emplace( - std::make_pair(mpm::properties::Vector::Acceleration, - Eigen::Matrix::Zero())); - vector_properties_.emplace( - std::make_pair(mpm::properties::Vector::Momentum, - Eigen::Matrix::Zero())); - vector_properties_.emplace( - std::make_pair(mpm::properties::Vector::ExternalForce, - Eigen::Matrix::Zero())); - vector_properties_.emplace( - std::make_pair(mpm::properties::Vector::InternalForce, - Eigen::Matrix::Zero())); + vector_properties_.resize(6); + // Displacement + vector_properties_.emplace_back( + Eigen::Matrix::Zero()); + // Velocity + vector_properties_.emplace_back( + Eigen::Matrix::Zero()); + // Acceleration + vector_properties_.emplace_back( + Eigen::Matrix::Zero()); + // Momentum + vector_properties_.emplace_back( + Eigen::Matrix::Zero()); + // ExternalForce + vector_properties_.emplace_back( + Eigen::Matrix::Zero()); + // InternalForce + vector_properties_.emplace_back( + Eigen::Matrix::Zero()); this->initialise(); } @@ -58,17 +61,32 @@ void mpm::Node::initialise() noexcept { status_ = false; // Initialise nodal scalar properties - scalar_properties_.at(mpm::properties::Scalar::Mass).setZero(); - scalar_properties_.at(mpm::properties::Scalar::Volume).setZero(); - scalar_properties_.at(mpm::properties::Scalar::MassPressure).setZero(); - scalar_properties_.at(mpm::properties::Scalar::Pressure).setZero(); + scalar_properties_.resize(5); + scalar_properties_.at(mpm::properties::Scalar::Mass) = + Eigen::Matrix::Zero(); + scalar_properties_.at(mpm::properties::Scalar::Volume) = + Eigen::Matrix::Zero(); + scalar_properties_.at(mpm::properties::Scalar::MassDensity) = + Eigen::Matrix::Zero(); + scalar_properties_.at(mpm::properties::Scalar::MassPressure) = + Eigen::Matrix::Zero(); + scalar_properties_.at(mpm::properties::Scalar::Pressure) = + Eigen::Matrix::Zero(); // Initialise nodal vector properties - vector_properties_.at(mpm::properties::Vector::Velocity).setZero(); - vector_properties_.at(mpm::properties::Vector::Acceleration).setZero(); - vector_properties_.at(mpm::properties::Vector::Momentum).setZero(); - vector_properties_.at(mpm::properties::Vector::ExternalForce).setZero(); - vector_properties_.at(mpm::properties::Vector::InternalForce).setZero(); + vector_properties_.resize(6); + vector_properties_.at(mpm::properties::Vector::Displacement) = + Eigen::Matrix::Zero(); + vector_properties_.at(mpm::properties::Vector::Velocity) = + Eigen::Matrix::Zero(); + vector_properties_.at(mpm::properties::Vector::Acceleration) = + Eigen::Matrix::Zero(); + vector_properties_.at(mpm::properties::Vector::Momentum) = + Eigen::Matrix::Zero(); + vector_properties_.at(mpm::properties::Vector::ExternalForce) = + Eigen::Matrix::Zero(); + vector_properties_.at(mpm::properties::Vector::InternalForce) = + Eigen::Matrix::Zero(); // Initialise variables for contact material_ids_.clear(); diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 0d5d3c680..2ce5bc060 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -9,6 +9,8 @@ mpm::Particle::Particle(Index id, const VectorDim& coord) nodes_.clear(); // Set material pointer to null material_ = nullptr; + scalar_properties_.resize(3); + vector_properties_.resize(3); // Logger std::string logger = "particle" + std::to_string(Tdim) + "d::" + std::to_string(id); @@ -239,18 +241,17 @@ void mpm::Particle::initialise() { volumetric_strain_centroid_ = 0.; // Initialize scalar properties - scalar_properties_.emplace( - std::make_pair(mpm::properties::Scalar::Mass, double(0.))); - scalar_properties_.emplace( - std::make_pair(mpm::properties::Scalar::MassDensity, double(0.))); - scalar_properties_.emplace(std::make_pair( - mpm::properties::Scalar::Volume, std::numeric_limits::max())); + scalar_properties_.resize(3); + scalar_properties_.at(mpm::properties::Scalar::Mass) = 0.; + scalar_properties_.at(mpm::properties::Scalar::MassDensity) = 0.; + scalar_properties_.at(mpm::properties::Scalar::Volume) = + std::numeric_limits::max(); // Initialize vector properties - vector_properties_.emplace( - std::make_pair(mpm::properties::Vector::Displacement, VectorDim::Zero())); - vector_properties_.emplace( - std::make_pair(mpm::properties::Vector::Velocity, VectorDim::Zero())); + vector_properties_.resize(2); + vector_properties_.at(mpm::properties::Vector::Displacement) = + VectorDim::Zero(); + vector_properties_.at(mpm::properties::Vector::Velocity) = VectorDim::Zero(); // Initialize vector data properties this->properties_["stresses"] = [&]() { return stress(); }; diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index fb119bc2a..3982ea5ee 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -343,10 +343,9 @@ class ParticleBase { //! Shape functions Eigen::VectorXd shapefn_; //! Scalar properties - tsl::ordered_map scalar_properties_; + std::vector scalar_properties_; //! Vector properties - tsl::ordered_map> - vector_properties_; + std::vector> vector_properties_; }; // ParticleBase class } // namespace mpm From 385fccafa0b6eae7b393d3bf1d46b845fea57ff7 Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Sat, 18 Jul 2020 06:46:40 -0500 Subject: [PATCH 39/55] :pencil: AssocVector --- CMakeLists.txt | 4 +- external/assoc_vector.h | 4955 +++++++++++++++++++++++++++++ external/flat | 1 + include/node.h | 5 +- include/node_base.h | 6 + include/particles/particle_base.h | 4 +- 6 files changed, 4968 insertions(+), 7 deletions(-) create mode 100644 external/assoc_vector.h create mode 160000 external/flat diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fc7cfa1d..fc8e2accb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,9 +45,9 @@ option(HALO_EXCHANGE "Enable halo exchange" OFF) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) # Boost Archive -find_package(Boost REQUIRED COMPONENTS filesystem system) +find_package(Boost REQUIRED COMPONENTS container filesystem system) include_directories(${BOOST_INCLUDE_DIRS}) -link_libraries(${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}) +link_libraries(${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CONTAINER_LIBRARY}) # Eigen find_package(Eigen3 REQUIRED) diff --git a/external/assoc_vector.h b/external/assoc_vector.h new file mode 100644 index 000000000..7a3ae766d --- /dev/null +++ b/external/assoc_vector.h @@ -0,0 +1,4955 @@ +/* + * Copyright (C) 2012 £ukasz Czerwiñski + * + * GitHub: https://github.com/wo3kie/AssocVector + * Website: http://www.lukaszczerwinski.pl/assoc_vector.en.html + * + * Distributed under the BSD Software License (see file license) + */ + +#ifndef ASSOC_VECTOR_HPP +#define ASSOC_VECTOR_HPP + +#ifndef __GXX_EXPERIMENTAL_CXX0X__ + #error C++11 is required to run this code, please use AssocVector 1.0.x instead. +#endif + +// includes.begin + +#include +#include +#include +#include + +#include +#include + +// includes.end + +// configuration.begin + +#if ( __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 ) + #define AV_HAS_TRIVIAL_DESTRUCTOR( type ) std::is_trivially_destructible< type >::value + #define AV_MOVE_IF_NOEXCEPT std::move_if_noexcept +#else + #define AV_HAS_TRIVIAL_DESTRUCTOR( type ) __has_trivial_destructor( type ) + #define AV_MOVE_IF_NOEXCEPT std::move +#endif + +// configuration.end + +#ifndef NDEBUG + #define AV_DEBUG +#endif + +#ifdef AV_DEBUG + #define AV_PRECONDITION( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} + #define AV_CHECK( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} + #define AV_POSTCONDITION( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} + #define AV_ERROR() if( (true) ){int*i=0;*i=0;} +#else + #define AV_PRECONDITION( condition ) (void)( 0 ); + #define AV_CHECK( condition ) (void)( 0 ); + #define AV_POSTCONDITION( condition ) (void)( 0 ); + #define AV_ERROR() (void)( 0 ); +#endif + +namespace util +{ + // + // CmpByFirst + // + template< typename _Pair, typename _Cmp > + struct CmpByFirst + { + CmpByFirst( _Cmp const & cmp = _Cmp() ) + : _cmp( cmp ) + { + } + + bool operator()( _Pair const & lhs, _Pair const & rhs )const + { + return _cmp( lhs.first, rhs.first ); + } + + bool operator()( _Pair const & pair, typename _Pair::first_type const & value )const + { + return _cmp( pair.first, value ); + } + + bool operator()( typename _Pair::first_type const & value, _Pair const & pair )const + { + return _cmp( value, pair.first ); + } + + private: + _Cmp _cmp; + }; +} + +namespace util +{ + // + // equal + // + template< + typename _T1 + , typename _T2 + > + inline bool equal( + _T1 const & first + , _T2 const & second + ) + { + if( first < second ){ + return false; + } + + if( second < first ){ + return false; + } + + return true; + } + + // + // less_equal + // + template< + typename _T1 + , typename _T2 + > + inline bool less_equal( + _T1 const & first + , _T2 const & second + ) + { + return ( second < first ) == false; + } +} + +namespace util +{ + // + // is_between + // + template< + typename _T1 + , typename _T2 + > + inline bool is_between( + _T1 const & first + , _T2 const & value + , _T1 const & last + ) + { + AV_PRECONDITION( less_equal( first, last ) ); + + return ( value < first ) == false && ( last < value ) == false; + } +} + +namespace util +{ + // + // destroy_range + // + namespace detail + { + template< bool _HasTrivialDestructor > + struct DestroyRangeImpl + { + }; + + template<> + struct DestroyRangeImpl< true > + { + template< typename _Ptr > + static + void destroy( _Ptr, _Ptr ) + { + } + }; + + template<> + struct DestroyRangeImpl< false > + { + template< typename _Ptr > + static + void destroy( _Ptr first, _Ptr const last ) + { + AV_PRECONDITION( less_equal( first, last ) ); + + typedef typename std::iterator_traits< _Ptr >::value_type T; + + for( /*empty*/ ; first != last ; ++ first ){ + first -> T::~T(); + } + } + }; + } + + template< typename _Ptr > + inline void destroy_range( + _Ptr first + , _Ptr const last + ) + { + typedef typename std::iterator_traits< _Ptr >::value_type T; + + detail::DestroyRangeImpl< AV_HAS_TRIVIAL_DESTRUCTOR( T ) >::destroy( first, last ); + } +} + +namespace util +{ + // + // move + // + template< + typename _InputPtr + , typename _OutputPtr + > + inline void move( + _InputPtr first + , _InputPtr last + , _OutputPtr first2 + ) + { + if( first < first2 ){ + std::move_backward( first, last, first2 + ( last - first ) ); + } + else if( first > first2 ){ + std::move( first, last, first2 ); + } + else{ + // first == first2 -> do nothing + } + } + + template< + typename _InputPtr + , typename _OutputPtr + > + inline void copy( + _InputPtr first + , _InputPtr last + , _OutputPtr first2 + ) + { + if( first < first2 ){ + std::copy_backward( first, last, first2 + ( last - first ) ); + } + else if( first > first2 ){ + std::copy( first, last, first2 ); + } + else{ + // first == first2 -> do nothing + } + } + + + + namespace detail + { + + template< bool _MoveDoesNotThrow > + struct MoveIfNoExcept + { + }; + + template<> + struct MoveIfNoExcept< true > + { + template< + typename _InputPtr + , typename _OutputPtr + > + static + void move( _InputPtr first, _InputPtr const last, _OutputPtr first2 ) + { + move( first, last, first2 ); + } + }; + + template<> + struct MoveIfNoExcept< false > + { + template< + typename _InputPtr + , typename _OutputPtr + > + static + void move( _InputPtr first, _InputPtr const last, _OutputPtr first2 ) + { + copy( first, last, first2 ); + } + }; + } + + template< + typename _InputPtr + , typename _OutputPtr + > + inline void move_if_noexcept( + _InputPtr first + , _InputPtr const last + , _OutputPtr first2 + ) + { + typedef typename std::iterator_traits< _InputPtr >::value_type T; + + detail::MoveIfNoExcept< true >::move( first, last, first2 ); + } + +} + +namespace util +{ + +template< + typename _Iterator + , typename _T + , typename _Cmp +> +_Iterator +last_less_equal( + _Iterator first + , _Iterator last + , _T const & t + , _Cmp cmp +) +{ + AV_PRECONDITION( less_equal( first, last ) ); + + if( first == last ){ + return last; + } + + _Iterator greaterEqual = std::lower_bound( first, last, t, cmp ); + + if( greaterEqual != last ) + { + // lower_bound returns first greater_than/equal_to but we need last less_than + + bool const isEqual = cmp( t, * greaterEqual ) == false; + + if( isEqual ) + { + // that is OK, request item was found + return greaterEqual; + } + } + + if( greaterEqual == first ) + { + // requested item does not exist in container + + // 6 8 10 13 17 19 20 21 22 24 end + // ^ lower_bound( 1 ): + // requested: ^ + + return last; + } + else + { + // we need to go one item backward + + // 6 8 10 13 17 19 20 21 22 24 + // lower_bound( 23 ): ^ + // requested: ^ + + return -- greaterEqual; + } +} + +} + +namespace util +{ + +template< + typename _InputIterator1 + , typename _InputIterator2 + , typename _Cmp +> +bool has_intersection( + _InputIterator1 first1 + , _InputIterator1 const last1 + , _InputIterator2 first2 + , _InputIterator2 const last2 + , _Cmp const & cmp +) +{ + while( first1 != last1 && first2 != last2 ) + { + if( cmp( * first1, * first2 ) ){ + ++first1; + } + else if( cmp( * first2, * first1 ) ){ + ++first2; + } + else{ + return true; + } + } + + return false; +} + +} + +namespace array +{ + // + // I need much more control over Array inside AssocVector than std::vector offers + // + + // C' style array with some useful methods and functions + + // + // Array + // + template< + typename _T + , typename _Alloc = std::allocator< _T > + > + struct ArrayBase + { + struct ArrayImpl + : public _Alloc + { + ArrayImpl( _Alloc const & alloc = _Alloc() ) + : _Alloc( alloc ) + , _data( nullptr ) + , _capacity( 0 ) + , _size( 0 ) + { + } + + ArrayImpl( std::size_t capacity, _Alloc const & alloc = _Alloc() ) + : _Alloc( alloc ) + , _data( capacity ? this->allocate( capacity ) : nullptr ) + , _capacity( capacity ) + , _size( 0 ) + { + AV_PRECONDITION( _capacity < this->max_size() ); + } + + ~ArrayImpl() + { + util::destroy_range( _data, _data + _size ); + + this->deallocate( _data, _capacity ); + } + + void swap( ArrayImpl & other )noexcept + { + std::swap( _data, other._data ); + std::swap( _capacity, other._capacity ); + std::swap( _size, other._size ); + } + + public: + _T * _data; + + std::size_t _capacity; + std::size_t _size; + }; + + typedef _Alloc allocator_type; + + allocator_type + get_allocator() const { + return * static_cast< allocator_type const * >( & this->_impl ); + } + + public: + ArrayBase( _Alloc const & alloc = _Alloc() ) + : _impl( alloc ) + { + } + + ArrayBase( std::size_t capacity, _Alloc const & alloc = _Alloc() ) + : _impl( capacity, alloc ) + { + } + + ~ArrayBase() = default; + + ArrayBase( ArrayBase const & other ) = delete; + ArrayBase( ArrayBase && other ) = delete; + + ArrayBase & operator=( ArrayBase const & other ) = delete; + ArrayBase & operator=( ArrayBase && other ) = delete; + + protected: + ArrayImpl _impl; + }; + + template< + typename _T + , typename _Alloc = std::allocator< _T > + > + struct Array + : protected ArrayBase< _T, _Alloc > + { + private: + typedef ArrayBase< _T, _Alloc > Base; + + public: + using Base::get_allocator; + + public: + typedef _T value_type; + + typedef _T * iterator; + typedef _T const * const_iterator; + + public: + Array( _Alloc const & alloc = _Alloc() ) + : Base( alloc ) + { + } + + Array( std::size_t capacity, _Alloc const & alloc = _Alloc() ) + : Base( capacity, alloc ) + { + } + + Array( Array const & other ) + // In std::vector new vector's capacity is equal to old vector's size. + // Array's capacity is equal to old array's capacity to ensure invariant that: + // sqrt( storage.capacity ) == buffer.capacity + // sqrt( storage.capacity ) == erased.capacity + : Base( other.capacity(), other.get_allocator() ) + { + for( /*empty*/ ; this->_impl._size < other.size() ; ++ this->_impl._size ){ + get_allocator().construct( + this->_impl._data + this->_impl._size + , * ( other._impl._data + this->_impl._size ) + ); + } + } + + Array( Array && other ) + : Base( 0, other.get_allocator() ) + { + this->_impl.swap( other._impl ); + } + + ~Array() = default; + + Array & operator=( Array const & other ) + { + Array temp( other ); + + swap( temp ); + + return * this; + } + + Array & operator=( Array && other ) + { + this->_impl.swap( other._impl ); + + return * this; + } + + void swap( Array & other )noexcept + { + this->_impl.swap( other._impl ); + } + + void reserve( std::size_t capacity ) + { + if( get_allocator().max_size() < capacity ){ + throw std::length_error( "Array::reserve" ); + } + + if( capacity <= getCapacity() ){ + return; + } + + Array< _T, _Alloc > temp( capacity, get_allocator() ); + + std::uninitialized_copy( + std::make_move_iterator( begin() ) + , std::make_move_iterator( end() ) + , temp.begin() + ); + + swap( temp ); + } + + iterator begin()noexcept + { + return getData(); + } + + const_iterator begin()const noexcept + { + return getData(); + } + + const_iterator cbegin()const noexcept + { + return getData(); + } + + iterator end()noexcept + { + return getData() + getSize(); + } + + const_iterator end()const noexcept + { + return getData() + getSize(); + } + + const_iterator cend()const noexcept + { + return getData() + getSize(); + } + + bool empty()const noexcept + { + return getSize() == 0; + } + + bool full()const noexcept + { + return size() == capacity(); + } + + std::size_t size()const noexcept + { + return this->_impl._size; + } + + std::size_t getSize()const noexcept + { + return size(); + } + + void setSize( std::size_t newSize ) noexcept + { + AV_PRECONDITION( getData() != 0 || newSize == 0 ); + + this->_impl._size = newSize; + } + + std::size_t capacity()const noexcept + { + return this->_impl._capacity; + } + + std::size_t getCapacity()const noexcept + { + return capacity(); + } + + value_type & front() noexcept + { + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( empty() == false ); + + return getData()[ 0 ]; + } + + value_type const & front()const noexcept + { + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( empty() == false ); + + return getData()[ 0 ]; + } + + value_type & back() noexcept + { + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( empty() == false ); + + return getData()[ getSize() - 1 ]; + } + + value_type const & back()const noexcept + { + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( empty() == false ); + + return getData()[ getSize() - 1 ]; + } + + _T const * data()const noexcept + { + return this->_impl._data; + } + + _T const * getData()const noexcept + { + return data(); + } + + _T * data() noexcept + { + return this->_impl._data; + } + + _T * getData() noexcept + { + return data(); + } + + value_type & operator[]( std::size_t index ) noexcept + { + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( index < size() ); + + return getData()[ index ]; + } + + value_type const & operator[]( std::size_t index )const noexcept + { + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( index < size() ); + + return getData()[ index ]; + } + + template< typename _T2 > + void + insert( + typename Array< _T >::iterator const pos + , _T2 && t + ) + { + AV_PRECONDITION( util::less_equal( size() + 1, capacity() ) ); + AV_PRECONDITION( util::is_between( begin(), pos, end() ) ); + + iterator const oldEnd = end(); + + get_allocator().construct( end() ); + setSize( getSize() + 1 ); + + if( pos != oldEnd ) + { + util::move( pos, oldEnd, pos + 1 ); + } + + * pos = AV_MOVE_IF_NOEXCEPT( t ); + } + + // Array::push_back is not implemented to ensure invariant that: + // sqrt( storage.capacity ) == buffer.capacity + // sqrt( storage.capacity ) == erased.capacity + template< typename __T2 > + void place_back( __T2 && value ) + { + AV_CHECK( getData() ); + AV_CHECK( capacity() > 0 ); + AV_CHECK( getSize() < capacity() ); + + get_allocator().construct( end(), std::forward< __T2 >( value ) ); + setSize( getSize() + 1 ); + } + + void + erase( typename Array< _T >::iterator pos ) + { + AV_PRECONDITION( empty() == false ); + AV_PRECONDITION( util::less_equal( begin(), pos ) ); + AV_PRECONDITION( pos < end() ); + + util::move( pos + 1, end(), pos ); + get_allocator().destroy( end() - 1 ); + setSize( getSize() - 1 ); + } + + private: + void setCapacity( std::size_t newCapacity ) noexcept + { + AV_PRECONDITION( getData() != 0 || newCapacity == 0 ); + + this->_impl._capacity = newCapacity; + } + + void setData( _T * t ) noexcept + { + this->_impl._data = t; + } + }; +} + +namespace array +{ + template< + typename _Iterator + , typename _T + , typename _Cmp + > + _Iterator + binary_search( + _Iterator first + , _Iterator last + , _T const & t + , _Cmp cmp + ) + { + AV_PRECONDITION( util::less_equal( first, last ) ); + + _Iterator const greaterEqual = std::lower_bound( first, last, t, cmp ); + + if( greaterEqual == last ){ + return last; + } + + bool const isEqual = cmp( t, * greaterEqual ) == false; + + if( isEqual ){ + return greaterEqual; + } + + return last; + } + + template< + typename _T + , typename _T2 + , typename _Cmp + > + std::pair< typename Array< _T >::iterator, bool > + insert_in_sorted( + Array< _T > & array + , _T2 && t + , _Cmp cmp + ) + { + AV_PRECONDITION( util::less_equal( array.size() + 1, array.capacity() ) ); + + typename Array< _T >::iterator const greaterEqual + = std::lower_bound( array.begin(), array.end(), t, cmp ); + + if( greaterEqual != array.end() ) + { + bool const isEqual = cmp( t, * greaterEqual ) == false; + + if( isEqual ){ + return std::make_pair( greaterEqual, false ); + } + } + + array.insert( greaterEqual, std::forward< _T2 >( t ) ); + + return std::make_pair( greaterEqual, true ); + } + + template< typename _T > + void + erase_removed( + array::Array< _T > & storage + , array::Array< typename array::Array< _T >::const_iterator > const & erased + ) + { + AV_PRECONDITION( util::less_equal( erased.size(), storage.size() ) ); + + if( erased.empty() ){ + return; + } + + typedef typename array::Array< _T >::const_iterator StorageConstIterator; + typedef typename array::Array< _T >::iterator StorageIterator; + typedef typename array::Array< StorageConstIterator >::const_iterator ErasedConstIterator; + + StorageIterator currentInStorage = const_cast< StorageIterator >( erased.front() ); + AV_CHECK( util::is_between( storage.begin(), currentInStorage, storage.end() ) ); + + StorageIterator const endInStorage = storage.end(); + + StorageIterator whereInsertInStorage = const_cast< StorageIterator >( erased.front() ); + AV_CHECK( util::is_between( storage.begin(), whereInsertInStorage, storage.end() ) ); + + ErasedConstIterator currentInErased = erased.begin(); + ErasedConstIterator const endInErased = erased.end(); + + while( currentInStorage != endInStorage ) + { + AV_CHECK( util::is_between( storage.begin(), whereInsertInStorage, storage.end() ) ); + + if( + currentInErased != endInErased + && currentInStorage == ( * currentInErased ) + ) + { + ++ currentInStorage; + ++ currentInErased; + } + else + { + ( * whereInsertInStorage ) = AV_MOVE_IF_NOEXCEPT( * currentInStorage ); + + ++ whereInsertInStorage; + ++ currentInStorage; + } + } + + AV_POSTCONDITION( currentInErased == endInErased ); + + storage.setSize( storage.size() - erased.size() ); + } + + template< + typename _T + , typename _Cmp + > + void + move_merge( + array::Array< _T > & storage + , array::Array< _T > & buffer + , _Cmp const & cmp = _Cmp() + ) + { + AV_PRECONDITION( util::less_equal( storage.size() + buffer.size(), storage.capacity() ) ); + + typedef typename array::Array< _T >::iterator Iterator; + + Iterator rWhereInsertInStorage = storage.begin() + storage.size() + buffer.size() - 1; + + Iterator rCurrentInStorage = storage.begin() + storage.size() - 1; + Iterator const rEndInStorage = storage.begin() - 1; + + Iterator rCurrentInBuffer = buffer.begin() + buffer.size() - 1; + Iterator const rEndInBuffer = buffer.begin() - 1; + + std::size_t numberOfItemsToCreateByPlacementNew = buffer.size(); + + while( + rCurrentInBuffer != rEndInBuffer + && numberOfItemsToCreateByPlacementNew != 0 + ) + { + AV_CHECK( rWhereInsertInStorage != 0 ); + AV_CHECK( rCurrentInStorage != 0 ); + AV_CHECK( rCurrentInBuffer != 0 ); + + if( + rCurrentInStorage == rEndInStorage + || cmp( * rCurrentInStorage, * rCurrentInBuffer ) + ) + { + new ( static_cast< void * >( rWhereInsertInStorage ) ) + _T( AV_MOVE_IF_NOEXCEPT( * rCurrentInBuffer ) ); + + -- rCurrentInBuffer; + } + else + { + new ( static_cast< void * >( rWhereInsertInStorage ) ) + _T( AV_MOVE_IF_NOEXCEPT( * rCurrentInStorage ) ); + + -- rCurrentInStorage; + } + + -- numberOfItemsToCreateByPlacementNew; + -- rWhereInsertInStorage; + } + + AV_CHECK( numberOfItemsToCreateByPlacementNew == 0 ); + + while( rCurrentInBuffer != rEndInBuffer ) + { + AV_CHECK( rWhereInsertInStorage != 0 ); + AV_CHECK( rCurrentInStorage != 0 ); + AV_CHECK( rCurrentInBuffer != 0 ); + + if( + rCurrentInStorage == rEndInStorage + || cmp( * rCurrentInStorage, * rCurrentInBuffer ) + ) + { + * rWhereInsertInStorage = AV_MOVE_IF_NOEXCEPT( * rCurrentInBuffer ); + + -- rCurrentInBuffer; + } + else + { + * rWhereInsertInStorage = AV_MOVE_IF_NOEXCEPT( * rCurrentInStorage ); + + -- rCurrentInStorage; + } + + -- rWhereInsertInStorage; + } + + storage.setSize( storage.size() + buffer.size() ); + } +} + +namespace util +{ + +template< + typename _InputPtr1 + , typename _InputPtr2 + , typename _OutputPtr + , typename _Cmp +> +_OutputPtr +move_merge_into_uninitialized( + _InputPtr1 first1 + , _InputPtr1 last1 + , _InputPtr2 first2 + , _InputPtr2 last2 + , _OutputPtr output + , _Cmp cmp = _Cmp() +) +{ + AV_PRECONDITION( util::less_equal( first1, last1 ) ); + AV_PRECONDITION( util::less_equal( first2, last2 ) ); + + while( first1 != last1 && first2 != last2 ) + { + AV_CHECK( first1 != 0 ); + AV_CHECK( first2 != 0 ); + AV_CHECK( output != 0 ); + + if( cmp( * first1, * first2 ) ) + { + new ( static_cast< void * >( output ) ) + typename std::iterator_traits< _OutputPtr >::value_type( std::move( * first1 ) ); + + ++ output; + ++ first1; + } + else + { + new ( static_cast< void * >( output ) ) + typename std::iterator_traits< _OutputPtr >::value_type( std::move( * first2 ) ); + + ++ output; + ++ first2; + } + } + + if( first1 == last1 ){ + return std::uninitialized_copy( + std::make_move_iterator( first2 ) + , std::make_move_iterator( last2 ) + , output + ); + } + + if( first2 == last2 ){ + return std::uninitialized_copy( + std::make_move_iterator( first1 ) + , std::make_move_iterator( last1 ) + , output + ); + } + + return output; +} + +} + +namespace detail +{ + template< typename _Iterator > + bool equal( _Iterator const & lhs, _Iterator const & rhs ) + { + if( lhs.getContainer() != rhs.getContainer() ){ + return false; + } + + // for empty container returns that begin == end + // despite on fact they are not + if( lhs.getContainer()->empty() ){ + return true; + } + + return lhs.getCurrent() == rhs.getCurrent(); + } + + // + // AssocVectorLazyIterator + // + + template< + typename _Iterator + , typename _Container + > + struct AssocVectorLazyIterator + { + public: + typedef typename std::iterator_traits< _Iterator >::pointer pointer_mutable; + + public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef typename std::iterator_traits< _Iterator >::value_type value_type; + typedef typename std::iterator_traits< _Iterator >::difference_type difference_type; + + // make key const + typedef std::pair< + typename value_type::first_type const + , typename value_type::second_type + > & reference; + + // make key const + typedef std::pair< + typename value_type::first_type const + , typename value_type::second_type + > * pointer; + + private: + struct _CurrentInErased + { + _CurrentInErased( typename _Container::_Erased::const_iterator current ) + : _current( current ) + { + } + + _CurrentInErased( _CurrentInErased const & other ) + : _current( other._current ) + { + } + + _CurrentInErased & operator=( _CurrentInErased const & other ) + { + _current = other._current; + + return * this; + } + + _CurrentInErased & operator=( typename _Container::_Erased::const_iterator current ) + { + _current = current; + + return * this; + } + + bool is_end( _Container const * container )const + { + return _current == container->erased().end(); + } + + bool is_not_end( _Container const * container )const + { + return ! is_end( container ); + } + + bool is_begin( _Container const * container )const + { + return _current == container->erased().begin(); + } + + bool is_not_begin( _Container const * container )const + { + return ! is_begin( container ); + } + + void increment( _Container const * container ) + { + AV_PRECONDITION( is_not_end( container ) ); + + ++ _current; + } + + void try_increment( _Container const * container ) + { + if( is_end( container ) ){ + return; + } + + increment( container ); + } + + void decrement( _Container const * container ) + { + AV_PRECONDITION( is_not_begin( container ) ); + + -- _current; + } + + void try_decrement( _Container const * container ) + { + if( is_begin( container ) ){ + return; + } + + decrement( container ); + } + + bool validate( _Container const * container )const + { + bool const result + = util::is_between( + container->erased().begin() + , _current + , container->erased().end() + ); + + if( result ){ + return true; + } + + AV_ERROR(); + + return false; + } + + typename _Container::_Erased::value_type const & + get( _Container const * container )const noexcept + { + AV_PRECONDITION( _current ); + AV_PRECONDITION( is_not_end( container ) ); + AV_PRECONDITION( validate( container ) ); + + return * _current; + } + + typename _Container::_Erased::const_iterator data()const noexcept + { + return _current; + } + + operator bool()const noexcept + { + return _current != 0; + } + + private: + typename _Container::_Erased::const_iterator _current; + }; + + struct _CurrentInBuffer + { + _CurrentInBuffer( pointer_mutable current ) + : _current( current ) + { + } + + _CurrentInBuffer( _CurrentInBuffer const & other ) + : _current( other._current ) + { + } + + _CurrentInBuffer & operator=( _CurrentInBuffer const & other ) + { + _current = other._current; + + return * this; + } + + _CurrentInBuffer & operator=( pointer_mutable current ) + { + _current = current; + + return * this; + } + + bool is_begin( _Container const * container )const + { + return _current == container->buffer().begin(); + } + + bool is_not_begin( _Container const * container )const + { + return ! is_begin( container ); + } + + bool is_end( _Container const * container )const + { + return _current == container->buffer().end(); + } + + bool is_not_end( _Container const * container )const + { + return ! is_end( container ); + } + + void increment( _Container const * container ) + { + AV_PRECONDITION( is_not_end( container ) ); + + ++ _current; + } + + void try_increment( _Container const * container ) + { + if( is_end( container ) ){ + return; + } + + increment( container ); + } + + void decrement( _Container const * container ) + { + AV_PRECONDITION( is_not_begin( container ) ); + + -- _current; + } + + void try_decrement( _Container const * container ) + { + if( is_begin( container ) ){ + return; + } + + decrement( container ); + } + + bool validate( _Container const * container )const + { + bool const result + = util::is_between( + container->buffer().begin() + , _current + , container->buffer().end() + ); + + if( result ){ + return true; + } + + AV_ERROR(); + + return false; + } + + typename _Container::_Storage::value_type const & + get( _Container const * container )const noexcept + { + AV_PRECONDITION( _current ); + AV_PRECONDITION( is_not_end( container ) ); + AV_PRECONDITION( validate( container ) ); + + return * _current; + } + + operator bool()const noexcept + { + return _current != 0; + } + + pointer_mutable data()const noexcept + { + return _current; + } + private: + pointer_mutable _current; + }; + + struct _CurrentInStorage + { + // begin is always set at first not erased item, marked with ^ + // end is always set at the end of container, marked with $ + + // Case 1: no item erased + // storage: ^1 3 5 7$ + // erased : + + // Case 2: item erased from front + // storage: 1 ^3 5 7$ + // erased : 1 + + // Case 3: item erased from back + // storage: ^1 3 5 7$ + // erased : 7 + + // Case 4: item erased from front and back + // storage: 1 ^3 5 7$ + // erased : 1 7 + + _CurrentInStorage( pointer_mutable current ) + : _dir( 1 ) + , _current( current ) + { + } + + _CurrentInStorage( _CurrentInStorage const & other ) + : _dir( other._dir ) + , _current( other._current ) + { + } + + _CurrentInStorage & operator=( _CurrentInStorage const & other ) + { + _dir = other._dir; + _current = other._current; + + return * this; + } + + bool operator==( _CurrentInStorage const & other )const + { + return _current == other._current; + } + + bool operator!=( _CurrentInStorage const & other )const + { + return _current != other._current; + } + + bool operator==( typename _Container::_Erased::value_type inErased )const + { + return _current == inErased; + } + + bool operator!=( typename _Container::_Erased::value_type inErased )const + { + return _current != inErased; + } + + bool is_begin( _Container const * container )const + { + _CurrentInStorage currentInStorage = const_cast< pointer_mutable >( container->storage().begin() ); + _CurrentInErased currentInErased = container->erased().begin(); + + currentInStorage.setOnNotErased( currentInErased, container ); + + return data() == currentInStorage.data(); + } + + bool is_not_begin( _Container const * container )const + { + return ! is_begin( container ); + } + + bool _is_begin( _Container const * container )const + { + return _current == container->storage().begin(); + } + + bool _is_not_begin( _Container const * container )const + { + return ! _is_begin( container ); + } + + bool is_end( _Container const * container )const + { + return _current == container->storage().end(); + } + + bool is_not_end( _Container const * container )const + { + return ! is_end( container ); + } + + void + increment( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + AV_PRECONDITION( is_not_end( container ) ); + + increment( container ); + + if( _dir == -1 ) + { + _dir = 1; + currentInErased.try_increment( container ); + } + + setOnNotErased( currentInErased, container ); + } + + void + try_increment( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + if( is_end( container ) ){ + return; + } + + increment( currentInErased, container ); + } + + void + decrement( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + AV_PRECONDITION( is_not_begin( container ) ); + + decrement( container ); + + if( _dir == 1 ) + { + _dir = -1; + currentInErased.try_decrement( container ); + } + + setOnNotErasedBackward( currentInErased, container ); + } + + void + try_decrement( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + if( _is_begin( container ) ){ + return; + } + + decrement( currentInErased, container ); + } + + void + setOnNotErased( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + if( is_end( container ) ) + { + if( !currentInErased ) + { + currentInErased = container->erased().end(); + } + + return; + } + + if( !currentInErased ) + { + currentInErased = std::lower_bound( + container->erased().begin() + , container->erased().end() + , data() + , std::less< typename _Container::_Storage::const_iterator >() + ); + } + + if( _dir == -1 ) + { + _dir = 1; + currentInErased.try_increment( container ); + } + + while( + is_not_end( container ) + && currentInErased.is_not_end( container ) + && data() == currentInErased.get( container ) + ) + { + increment( container ); + currentInErased.increment( container ); + } + + AV_POSTCONDITION( currentInErased ); + + AV_POSTCONDITION( + ( + is_end( container ) + && currentInErased.is_end( container ) + ) + || currentInErased.is_end( container ) + || *this != currentInErased.get( container ) + ); + } + + void + setOnNotErasedBackward( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + AV_CHECK( is_not_end( container ) ); + + if( _dir == 1 ) + { + _dir = -1; + currentInErased.try_decrement( container ); + } + + while( + is_not_begin( container ) + && currentInErased.is_not_end( container ) + && data() == currentInErased.get( container ) + ) + { + decrement( container ); + currentInErased.try_decrement( container ); + } + + AV_POSTCONDITION( validate( container ) ); + + AV_POSTCONDITION( + currentInErased.is_end( container ) + || *this != currentInErased.get( container ) + ); + } + + operator bool()const noexcept + { + return _current != 0; + } + + bool validate( _Container const * container )const + { + bool const result = util::is_between( + container->storage().begin() + , _current + , container->storage().end() + ); + + if( result ){ + return true; + } + + AV_ERROR(); + + return false; + } + + typename _Container::_Storage::value_type const & + get( _Container const * container )const noexcept + { + AV_PRECONDITION( _current ); + AV_PRECONDITION( is_not_end( container ) ); + + return * _current; + } + + pointer_mutable data()const noexcept + { + return _current; + } + + private: + void increment( _Container const * container ) + { + AV_PRECONDITION( is_not_end( container ) ); + + ++ _current; + } + + void decrement( _Container const * container ) + { + AV_PRECONDITION( is_not_begin( container ) ); + + -- _current; + } + + private: + int _dir; + + pointer_mutable _current; + }; + + struct _Current + { + _Current( pointer_mutable current ) + : _current( current ) + { + } + + _Current( _CurrentInStorage const & inStorage ) + : _current( inStorage.data() ) + { + } + + _Current( _CurrentInBuffer const & inBuffer ) + : _current( inBuffer.data() ) + { + } + + _Current & operator=( _Current const & other ) + { + _current = other._current; + + return * this; + } + + _Current & operator=( _CurrentInStorage const & inStorage ) + { + _current = inStorage.data(); + + return * this; + } + + _Current & operator=( _CurrentInBuffer const & inBuffer ) + { + _current = inBuffer.data(); + + return * this; + } + + bool operator==( _Current const & other )const + { + return _current == other._current; + } + + bool operator==( _CurrentInStorage const & inStorage )const + { + return _current == inStorage.data(); + } + + bool operator==( _CurrentInBuffer const & inBuffer )const + { + return _current == inBuffer.data(); + } + + bool validate( + _CurrentInStorage currentInStorage + , _CurrentInBuffer currentInBuffer + , _Container const * container + )const + { + AV_PRECONDITION( currentInStorage || currentInBuffer ); + AV_PRECONDITION( container != 0 ); + + if( !currentInStorage ) + { + if( _current == currentInBuffer.data() ){ + return true; + } + + AV_ERROR(); + + return false; + } + + if( !currentInBuffer ) + { + if( _current == currentInStorage.data() ){ + return true; + } + + AV_ERROR(); + + return false; + } + + // if 'setLower' does not work 'validateCurrent' does not work as well :O( + bool const result + = _current == getLower( currentInStorage, currentInBuffer, container ).data(); + + if( result ){ + return true; + } + + AV_ERROR(); + + return false; + } + + void setLower( + _CurrentInStorage currentInStorage + , _CurrentInBuffer currentInBuffer + , _Container const * container + ) + { + _current = getLower( currentInStorage, currentInBuffer, container ).data(); + } + + _Current getLower( + _CurrentInStorage currentInStorage + , _CurrentInBuffer currentInBuffer + , _Container const * container + )const + { + AV_CHECK( currentInStorage ); + AV_CHECK( currentInBuffer ); + + if( currentInStorage.is_end( container ) ) + { + if( currentInBuffer.is_end( container ) ){ + return _Current( 0 ); + } + else{ + return _Current( currentInBuffer ); + } + } + else + { + if( currentInBuffer.is_end( container ) ){ + return _Current( currentInStorage ); + } + else + { + if( container->value_comp()( + currentInStorage.get( container ) + , currentInBuffer.get( container ) + ) + ){ + return _Current( currentInStorage ); + } + else{ + return _Current( currentInBuffer ); + } + } + } + } + + operator bool()const noexcept + { + return _current != 0; + } + + typename _Container::_Storage::value_type const & + get( _Container const * container )const noexcept + { + return * _current; + } + + pointer_mutable data()const noexcept + { + return _current; + } + + private: + pointer_mutable _current; + }; + + public: + AssocVectorLazyIterator( + typename _Container::value_compare const & cmp = typename _Container::value_compare() + ) + : _container( 0 ) + + , _currentInStorage( 0 ) + , _currentInBuffer( 0 ) + , _currentInErased( 0 ) + + , _current( 0 ) + { + } + + template< typename _Iter > + AssocVectorLazyIterator( AssocVectorLazyIterator< _Iter, _Container > const & other ) + : _container( other.getContainer() ) + + , _currentInStorage( other.getCurrentInStorage() ) + , _currentInBuffer( other.getCurrentInBuffer() ) + , _currentInErased( other.getCurrentInErased() ) + + , _current( other.getCurrent() ) + { + } + + AssocVectorLazyIterator( + _Container const * container + , pointer_mutable currentInStorage + , pointer_mutable currentInBuffer + , typename _Container::_Erased::const_iterator currentInErased + , pointer_mutable current + ) + : _container( container ) + + , _currentInStorage( currentInStorage ) + , _currentInBuffer( currentInBuffer ) + , _currentInErased( currentInErased ) + + , _current( current ) + { + AV_PRECONDITION( container != 0 ); + AV_PRECONDITION( validate() ); + + if( _currentInStorage && _currentInBuffer && !_currentInErased && !_current ) + { + // not found in storage, insert to buffer + // erase from buffer + find in storage + // lower_bound + // upper_bound + + // _currentInStorage <- fix against '!_currentInErased' + _currentInStorage.setOnNotErased( _currentInErased, _container ); + + // _current <- get it right now + _current.setLower( _currentInStorage, _currentInBuffer, _container ); + } + else + if( _currentInStorage && _currentInBuffer && !_currentInErased && _current ) + { + // not found in storage, found in buffer + // not found in storage, inserted to buffer + // erased from storage's back + + // _currentInStorage <- fix against '!_currentInErased' + _currentInStorage.setOnNotErased( _currentInErased, _container ); + } + else + if( _currentInStorage && _currentInBuffer && _currentInErased && !_current ) + { + // begin iterator + // end iterator + // erase from storage, not merged + find in buffer + // erase from storage, merged + find in buffer + find in storage + + // _currentInStorage <- check against _currentInErased + _currentInStorage.setOnNotErased( _currentInErased, _container ); + + // _current <- get it right now + _current.setLower( _currentInStorage, _currentInBuffer, _container ); + } + else + if( ! _currentInStorage && ! _currentInBuffer && ! _currentInErased && !_current ) + { + // begin iterator on empty AssocVector + // end iterator on empty AssocVector + + // return, do not make validation + return; + } + + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + AV_POSTCONDITION( _currentInStorage || _currentInBuffer ); + AV_POSTCONDITION( _currentInErased ); + AV_POSTCONDITION( _container != 0 ); + } + + AssocVectorLazyIterator & + operator=( AssocVectorLazyIterator const & other ) + { + _container = other._container; + + _currentInStorage = other._currentInStorage; + _currentInBuffer = other._currentInBuffer; + _currentInErased = other._currentInErased; + + _current = other._current; + + return * this; + } + + bool operator==( AssocVectorLazyIterator const & other )const + { + this->resolveLazyValues(); + other.resolveLazyValues(); + + if( isEmpty() && other.isEmpty() ){ + return getContainer() == other.getContainer(); + } + + AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return equal( *this, other ); + } + + bool operator!=( AssocVectorLazyIterator const & other )const + { + this->resolveLazyValues(); + other.resolveLazyValues(); + + return ! ( ( * this ) == other ); + } + + AssocVectorLazyIterator & operator++() + { + AV_PRECONDITION( isEmpty() == false ); + + resolveLazyValues(); + + AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + if( _current == _Current( 0 ) ){ + return * this; + } + else if( _current == _currentInStorage ){ + _currentInStorage.try_increment( _currentInErased, _container ); + } + else if( _current == _currentInBuffer ){ + _currentInBuffer.try_increment( _container ); + } + else{ + AV_ERROR(); + } + + _current.setLower( _currentInStorage, _currentInBuffer, _container ); + + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return * this; + } + + AssocVectorLazyIterator operator++( int ) + { + AssocVectorLazyIterator result( * this ); + + ( * this ).operator++(); + + return result; + } + + AssocVectorLazyIterator & operator--() + { + AV_PRECONDITION( isEmpty() == false ); + + resolveLazyValues(); + + AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + if( + _currentInStorage.is_begin( _container ) + && _currentInBuffer.is_begin( _container ) + ) + { + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return * this; + } + + if( _currentInStorage.is_begin( _container ) ) + { + _currentInBuffer.decrement( _container ); + + _current = _currentInBuffer; + + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return * this; + } + + if( _currentInBuffer.is_begin( _container ) ) + { + _currentInStorage.decrement( _currentInErased, _container ); + + _current = _currentInStorage; + + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return * this; + } + + _CurrentInStorage currentInStorage = _currentInStorage; + _CurrentInBuffer currentInBuffer = _currentInBuffer; + + _CurrentInErased currentInErased = _currentInErased; + + currentInStorage.decrement( currentInErased, _container ); + currentInBuffer.decrement( _container ); + + if( + _container->value_comp()( + currentInStorage.get( _container ) + , currentInBuffer.get( _container ) + ) + ) + { + _currentInBuffer = currentInBuffer; + + _current = _currentInBuffer; + } + else + { + _currentInStorage = currentInStorage; + _currentInErased = currentInErased; + + _current = _currentInStorage; + } + + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return * this; + } + + AssocVectorLazyIterator operator--( int ) + { + AssocVectorLazyIterator result( * this ); + + ( * this ).operator--(); + + return result; + } + + reference operator*()const + { + return * get(); + } + + pointer operator->()const + { + return get(); + } + + pointer get()const + { + AV_PRECONDITION( isEmpty() == false ); + AV_PRECONDITION( _current ); + AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + // make key const + // pair< T1, T2 > * -> pair< T1 const, T2 > * + //return reinterpret_cast< pointer >( _current ); + + return + reinterpret_cast< pointer >( + const_cast< void * >( + reinterpret_cast< void const * >( _current.data() ) + ) + ); + } + + // public for copy constructor only : Iterator -> ConstIterator + _Container const * getContainer()const noexcept + { + return _container; + } + + pointer_mutable getCurrentInStorage()const noexcept + { + return _currentInStorage.data(); + } + + pointer_mutable getCurrentInBuffer()const noexcept + { + return _currentInBuffer.data(); + } + + typename _Container::_Erased::const_iterator getCurrentInErased()const noexcept + { + return _currentInErased.data(); + } + + pointer_mutable getCurrent()const noexcept + { + return _current.data(); + } + + private: + bool isEmpty()const + { + if( _currentInStorage ){ + return false; + } + if( _currentInBuffer ){ + return false; + } + if( _currentInErased ){ + return false; + } + if( _current ){ + return false; + } + + return true; + } + + /*const function*/ + static + void + resolveLazyCurrentInBuffer( + _CurrentInBuffer & currentInBuffer + , _Current const current + , _Container const * container + ) + { + if( currentInBuffer ){ + return; + } + + currentInBuffer = const_cast< pointer_mutable >( + std::lower_bound( + container->buffer().begin() + , container->buffer().end() + , current.get( container ) + , container->value_comp() + ) + ); + } + + static + void + resolveLazyCurrentInStorage( + _CurrentInStorage & currentInStorage + , _CurrentInErased & currentInErased + , _Current const current + , _Container const * container + ) + { + if( currentInStorage ){ + return; + } + + currentInStorage = const_cast< pointer_mutable >( + std::lower_bound( + container->storage().begin() + , container->storage().end() + , current.get( container ) + , container->value_comp() + ) + ); + + currentInStorage.setOnNotErased( currentInErased, container ); + } + + void + resolveLazyValues()const + { + resolveLazyCurrentInBuffer( _currentInBuffer, _current, _container ); + resolveLazyCurrentInStorage( _currentInStorage, _currentInErased, _current, _container ); + } + + /*pure function*/ + bool + validate()const + { + if( !_currentInStorage && _currentInBuffer && _currentInErased && _current ) + { + // not found in storage, inserted to buffer, buffer merged to storage + + AV_CHECK( _currentInBuffer.validate( _container ) ); + AV_CHECK( _currentInErased.is_end( _container ) ); + AV_CHECK( _current == _currentInBuffer ); + + // _currentInStorage <- lazy in operator++/operator--/operator==/operator!= + } + else + if( _currentInStorage && !_currentInBuffer && _currentInErased && !_current ) + { + AV_ERROR(); + + return false; + } + else + if( _currentInStorage && !_currentInBuffer && _currentInErased && _current ) + { + // found in storage, not found in erased + + AV_CHECK( _currentInStorage.validate( _container ) ); + AV_CHECK( + std::binary_search( + _container->erased().begin() + , _container->erased().end() + , _currentInStorage.data() + ) == false + ); + AV_CHECK( _currentInErased.validate( _container ) ); + AV_CHECK( _current == _currentInStorage ); + + // _currentInBuffer <- lazy in operator++/operator--/operator==/operator!= + } + else + if( _currentInStorage && _currentInBuffer && !_currentInErased && !_current ) + { + // not found in storage, insert to buffer + // erase from buffer + find in storage + // lower_bound + // upper_bound + + AV_CHECK( _currentInStorage.validate( _container ) ); + AV_CHECK( _currentInBuffer.validate( _container ) ); + + // _currentInStorage <- fix against '!_currentInErased' + // _current <- get it right now + } + else + if( _currentInStorage && _currentInBuffer && !_currentInErased && _current ) + { + // not found in storage, found in buffer + // not found in storage, inserted to buffer + // erased from storage's back + + AV_CHECK( _currentInStorage.validate( _container ) ); + AV_CHECK( _currentInBuffer.validate( _container ) ); + AV_CHECK( _current == _currentInBuffer ); + + // _currentInStorage <- fix against '!_currentInErased' + // _currentInErased <- get it right now + } + else + if( _currentInStorage && _currentInBuffer && _currentInErased && !_current ) + { + // begin iterator + // end iterator + // erase from storage, not merged + find in buffer + // erase from storage, merged + find in buffer + find in storage + + AV_CHECK( _currentInStorage.validate( _container ) ); + AV_CHECK( _currentInBuffer.validate( _container ) ); + AV_CHECK( _currentInErased.validate( _container ) ); + + // _currentInStorage <- check against _currentInErased + // _current <- get it right now + } + else + if( ! _currentInStorage && ! _currentInBuffer && ! _currentInErased && !_current ) + { + // begin iterator on empty AssocVector + // end iterator on empty AssocVector + } + else + { + AV_ERROR(); + + return false; + } + + return true; + } + + private: + _Container const * _container; + + // mutable since lazy values in operator==()const / operator!=()const + mutable _CurrentInStorage _currentInStorage; + mutable _CurrentInBuffer _currentInBuffer; + mutable _CurrentInErased _currentInErased; + + _Current _current; + }; + + template< + typename _Iterator + , typename _Container + > + std::ostream & + operator<<( + std::ostream & out + , AssocVectorLazyIterator< _Iterator, _Container > const & iter + ) + { + out << "S: " << iter.getCurrentInStorage(); + + if( iter.getCurrentInStorage() == 0 ){ + out << " (null)"; + } + else if( iter.getContainer()->storage().end() == iter.getCurrentInStorage() ){ + out << " (end)"; + } + else{ + out << " " << * iter.getCurrentInStorage(); + } + + out << "\nB: " << iter.getCurrentInBuffer(); + + if( iter.getCurrentInBuffer() == 0 ){ + out << " (null)"; + } + else if( iter.getContainer()->buffer().end() == iter.getCurrentInBuffer() ){ + out << " (end)"; + } + else{ + out << " " << * iter.getCurrentInBuffer(); + } + + out << "\nE: " << iter.getCurrentInErased(); + + if( iter.getCurrentInErased() == 0 ){ + out << " (null)"; + } + else if( iter.getContainer()->erased().end() == iter.getCurrentInErased() ){ + out << " (end)"; + } + else{ + AV_CHECK( * iter.getCurrentInErased() ); + + out << " " << * * iter.getCurrentInErased(); + } + + out << "\nC: " << iter.getCurrent(); + + if( iter.getCurrent() == 0 ){ + out << " (null)"; + } + else{ + out << " " << * iter.getCurrent(); + } + + std::flush( out ); + + return out; + } + + // + // _AssocVectorIterator, simplified version of AssocVectorLazyIterator, works with _find and _end + // + template< + typename _Iterator + , typename _Container + > + struct _AssocVectorIterator + { + private: + typedef typename std::iterator_traits< _Iterator >::pointer pointer_mutable; + + public: + typedef typename std::iterator_traits< _Iterator >::value_type value_type; + + // make key const + typedef std::pair< + typename value_type::first_type const + , typename value_type::second_type + > & reference; + + // make key const + typedef std::pair< + typename value_type::first_type const + , typename value_type::second_type + > * pointer; + + public: + _AssocVectorIterator( + typename _Container::value_compare const & cmp = typename _Container::value_compare() + ) + : _current( 0 ) + { + } + + template< typename _Iter > + _AssocVectorIterator( _AssocVectorIterator< _Iter, _Container > const & other ) + : _current( other.getCurrent() ) + { + } + + _AssocVectorIterator( pointer_mutable current ) + : _current( current ) + { + } + + _AssocVectorIterator & + operator=( _AssocVectorIterator const & other ) + { + _current = other._current; + + return * this; + } + + bool operator==( _AssocVectorIterator const & other )const + { + return _current == other.getCurrent(); + } + + bool operator!=( _AssocVectorIterator const & other )const + { + return ! ( ( * this ) == other ); + } + + reference operator*()const + { + AV_PRECONDITION( _current != 0 ); + + return * get(); + } + + pointer operator->()const + { + AV_PRECONDITION( _current != 0 ); + + return get(); + } + + pointer get()const + { + AV_PRECONDITION( _current != 0 ); + + // make key const + // pair< T1, T2 > * -> pair< T1 const, T2 > * + //return reinterpret_cast< pointer >( _current ); + + return + reinterpret_cast< pointer >( + const_cast< void * >( + reinterpret_cast< void const * >( _current ) + ) + ); + } + + operator bool()const noexcept + { + return _current != 0; + } + + // public for copy constructor only : Iterator -> ConstIterator + pointer_mutable getCurrent()const noexcept + { + return _current; + } + + private: + pointer_mutable _current; + }; + + template< + typename _Iterator + , typename _Container + > + std::ostream & operator<<( + std::ostream & out + , _AssocVectorIterator< _Iterator, _Container > const & iter + ) + { + out << "S: " << iter.getCurrent(); + + if( iter.getCurrent() == 0 ){ + out << " (null)(end)"; + } + else{ + out << " " << * iter.get(); + } + + return out; + } + +} // namespace detail + +template< + typename _Key + , typename _Mapped + , typename _Cmp = std::less< _Key > + , typename _Allocator = std::allocator< std::pair< _Key, _Mapped > > +> +struct AssocVector +{ +private: + typedef std::pair< _Key, _Mapped > value_type_mutable; + typedef std::pair< _Key const, _Mapped > value_type_key_const; + +public: + typedef _Key key_type; + typedef _Mapped mapped_type; + + typedef value_type_key_const value_type; + + typedef typename _Allocator::size_type size_type; + typedef typename _Allocator::difference_type difference_type; + + typedef typename _Allocator::pointer pointer; + typedef typename _Allocator::const_pointer const_pointer; + + typedef _Cmp key_compare; + typedef util::CmpByFirst< value_type_mutable, _Cmp > value_compare; + + typedef _Allocator allocator_type; + + typedef mapped_type & reference; + typedef mapped_type const & const_reference; + + typedef detail::AssocVectorLazyIterator< value_type_mutable *, AssocVector > iterator; + typedef detail::AssocVectorLazyIterator< value_type_mutable const *, AssocVector > const_iterator; + + typedef std::reverse_iterator< iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + + typedef array::Array< value_type_mutable > _Storage; + typedef array::Array< typename _Storage::const_iterator > _Erased; + +#ifdef AV_ENABLE_EXTENSIONS + public: +#else + private: +#endif + + // + // extension, faster, non STL compatible version of iterator, working with _find end _end + // + typedef detail::_AssocVectorIterator< value_type_mutable *, AssocVector > _iterator; + typedef detail::_AssocVectorIterator< value_type_mutable const *, AssocVector > _const_iterator; + +private: + struct _FindOrInsertToBufferResult + { + typename _Storage::iterator _inBuffer; + bool _isInserted; + bool _isReallocated; + }; + + struct _TryRemoveBackResult + { + bool _anyItemRemoved; + bool _erasedItemRemoved; + }; + + struct _FindImplResult + { + typename _Storage::iterator _inStorage; + typename _Storage::iterator _inBuffer; + typename _Erased::iterator _inErased; + typename _Storage::iterator _current; + + bool validate()const + { + return + ( _current == 0 && _inStorage == 0 && _inBuffer == 0 && _inErased == 0 ) + || _inStorage != 0; + } + }; + + struct _InsertImplResult + { + bool _isInserted; + + typename _Storage::iterator _inStorage; + typename _Storage::iterator _inBuffer; + typename _Erased::iterator _inErased; + typename _Storage::iterator _current; + + bool validate()const + { + if( _current == 0 ) + { + AV_ERROR(); + + return false; + } + + if( _inStorage == 0 && ( _inBuffer == 0 || _inErased == 0 ) ) + { + AV_ERROR(); + + return false; + } + + return true; + } + }; + + struct _TryEraseFromStorageResult + { + typename _Erased::iterator _inErased; + bool _isErased; + bool _isMerged; + }; + +public: + // + // constructor + // + explicit + AssocVector( + _Cmp const & cmp = _Cmp() + , _Allocator const & allocator = _Allocator() + ); + + explicit + AssocVector( _Allocator const & allocator ); + + template< typename __InputIterator > + AssocVector( + __InputIterator first + , __InputIterator last + , _Cmp const & cmp = _Cmp() + , _Allocator const & allocator = _Allocator() + ); + + AssocVector( AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other ); + AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other + , _Allocator const & allocator + ); + + AssocVector( AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other ); + AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other + , _Allocator const & allocator + ); + + AssocVector( + std::initializer_list< value_type > list + , _Cmp const & cmp = _Cmp() + , _Allocator const & allocator = _Allocator() + ); + + // + // destructor + // + inline ~AssocVector(); + + // + // clear + // + inline void clear() noexcept; + + // + // operator= + // + AssocVector & operator=( AssocVector const & other ); + AssocVector & operator=( AssocVector && other ); + + // + // methods + // + void reserve( std::size_t newCapacity ); + void swap( AssocVector & other ) noexcept; + + // + // iterators + // + inline iterator begin(); + inline const_iterator begin()const; + inline const_iterator cbegin()const; + + inline reverse_iterator rbegin(); + inline const_reverse_iterator rbegin()const; + inline const_reverse_iterator crbegin()const; + + inline iterator end(); + inline const_iterator end()const; + inline const_iterator cend()const; + + inline reverse_iterator rend(); + inline const_reverse_iterator rend()const; + inline const_reverse_iterator crend()const; + + // + // size + // + inline bool empty()const noexcept; + inline std::size_t size()const noexcept; + inline std::size_t capacity()const noexcept; + inline std::size_t max_size()const noexcept; + + // + // insert + // + std::pair< iterator, bool > insert( value_type const & value ); + + template< typename __ValueType > + std::pair< iterator, bool > insert( __ValueType && value ); + + iterator insert( const_iterator hint, value_type const & value ); + + template< typename __ValueType > + iterator insert( const_iterator hint, __ValueType && value ); + + template< typename _Iterator > + inline void insert( _Iterator first, _Iterator last ); + + inline void insert( std::initializer_list< value_type > list ); + + // + // emplace + // + template< class... __Args > + std::pair< iterator, bool > emplace( __Args... args ); + + template< class... __Args > + std::pair< iterator, bool > emplace_hint( const_iterator hint, __Args... args ); + + // + // find + // + iterator find( key_type const & k ); + const_iterator find( key_type const & k )const; + + iterator lower_bound( key_type const & k ); + const_iterator lower_bound( key_type const & k )const; + + iterator upper_bound( key_type const & k ); + const_iterator upper_bound( key_type const & k )const; + + std::pair< iterator, iterator > equal_range( key_type const & k ); + std::pair< const_iterator, const_iterator > equal_range( key_type const & k )const; + + // + // count + // + inline std::size_t count( key_type const & k )const; + + // + // operator[] + // + reference operator[]( key_type const & k ); + reference operator[]( key_type && k ); + + // + // at + // + reference at( key_type const & k ); + const_reference at( key_type const & k )const; + + // + // erase + // + std::size_t erase( key_type const & k ); + iterator erase( iterator pos ); + + // + // observers + // + key_compare key_comp()const + { + return _cmp; + } + + value_compare value_comp()const + { + return value_compare( _cmp ); + } + + //allocator_type get_allocator()const + //{ + // return _allocator; + //} + +#ifdef AV_ENABLE_EXTENSIONS + public: +#else + private: +#endif + + // + // extension, flatenize container, enforce merge of _storage with _erased and with _buffer + // + void _merge(); + + // + // extension, faster, non STL compatible version of insert + // + bool _insert( value_type const & value ); + + template< typename __ValueType > + bool _insert( __ValueType && value ); + + // + // extension, faster, non STL compatible version of end, works with _find + // + inline _iterator _end(); + inline _const_iterator _end()const; + + // + // extension, faster, non STL compatible version of find, works with _end + // + _iterator _find( key_type const & k ); + _const_iterator _find( key_type const & k )const; + + // + // extension, faster, non STL compatible version of erase + // + bool _erase( iterator pos ); + +private: + bool validateStorage()const; + bool validateBuffer()const; + bool validateErased()const; + bool validate()const; + + // + // merge + // + void mergeStorageWithBuffer(); + void mergeStorageWithErased(); + + // + // insert + // + template< typename __ValueType > + void pushBack( __ValueType && value ); + + template< typename __ValueType > + bool shouldBePushBack( __ValueType && value )const; + + template< typename __ValueType > + _FindOrInsertToBufferResult + findOrInsertToBuffer( __ValueType && value ); + + // + // insertImpl, function does as little as needed but returns as much data as possible + // + template< typename __ValueType > + _InsertImplResult + insertImpl( __ValueType && value ); + + // + // emplace_impl + // + template< class __Head, class... __Tail > + std::pair< iterator, bool > emplaceImpl( __Head && head, __Tail... tail ); + + // + // erase + // + _TryRemoveBackResult + tryRemoveStorageBack( typename _Storage::iterator pos ); + + // + // tryEraseFromStorage + // + _TryEraseFromStorageResult + tryEraseFromStorage( typename _Storage::iterator pos ); + + // + // isErased + // + bool isErased( typename _Storage::const_iterator iterator )const; + + // + // findImpl, function does as little as needed but returns as much data as possible + // + _FindImplResult + findImpl( key_type const & key ); + + // + // getAllocator (method specialization) + // + //_Allocator getAllocator( _Storage const & ){ return _allocator; } + // + //typename _Allocator::template rebind< typename _Storage::const_iterator >::other + //getAllocator( _Erased const & ) + //{return typename _Allocator::template rebind< typename _Storage::const_iterator >::other( _allocator );} + +public: // public for unit tests only + void dump( int width = -1 )const; + + std::size_t bufferSize()const{ return _buffer.size(); } + std::size_t bufferCapacity()const{ return _buffer.capacity(); } + _Storage const & storage()const{ return _storage; } + + std::size_t storageSize()const{ return _storage.size(); } + std::size_t storageCapacity()const{ return _storage.capacity(); } + _Storage const & buffer()const{ return _buffer; } + + std::size_t erasedSize()const{ return _erased.size(); } + std::size_t erasedCapacity()const{ return _erased.capacity(); } + _Erased const & erased()const{ return _erased; } + + static std::size_t calculateNewBufferCapacity( std::size_t storageSize ); + static std::size_t calculateNewErasedCapacity( std::size_t storageSize ); + static std::size_t calculateNewStorageCapacity( std::size_t storageSize ); + +private: + _Storage _storage; + _Storage _buffer; + _Erased _erased; + + _Cmp _cmp; +}; + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool operator==( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & lhs + , AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & rhs +) +{ + if( lhs.size() != rhs.size() ){ + return false; + } + + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator begin = lhs.begin(); + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator const end = lhs.end(); + + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator begin2 = rhs.begin(); + + for( /*empty*/ ; begin != end ; ++ begin, ++ begin2 ) + { + if( begin->first != begin2->first ){ + return false; + } + + if( begin->second != begin2->second ){ + return false; + } + } + + return true; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool operator!=( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & lhs + , AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & rhs +) +{ + return ! ( lhs == rhs ); +} + +// +// Method Definitions +// + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + _Cmp const & cmp + , _Allocator const & allocator +) + : _cmp( cmp ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( _Allocator const & allocator ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __InputIterator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + __InputIterator first + , __InputIterator last + , _Cmp const & cmp + , _Allocator const & allocator +) + : _cmp( cmp ) +{ + AV_PRECONDITION( std::distance( first, last ) >= 0 ); + + std::size_t const size = std::distance( first, last ); + + if( size > 0 ) + { + reserve( size ); + + for( /*empty*/ ; first != last ; ++ first ){ + insert( * first ); + } + } + + AV_POSTCONDITION( validate() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other +) + : _storage( other._storage ) + , _buffer( other._buffer ) + , _erased( other._erased ) + , _cmp( other._cmp ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other + , _Allocator const & allocator +) + : _storage( other._storage, allocator ) + , _buffer( other._buffer, allocator ) + , _erased( other._erased, allocator ) + , _cmp( other._cmp, allocator ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other +) + : _storage( std::move( other._storage ) ) + , _buffer( std::move( other._buffer ) ) + , _erased( std::move( other._erased ) ) + , _cmp( other._cmp ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other + , _Allocator const & allocator +) + : _storage( std::move( other._storage ) ) + , _buffer( std::move( other._buffer ) ) + , _erased( std::move( other._erased ) ) + , _cmp( other._cmp ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + std::initializer_list< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::value_type > list + , _Cmp const & cmp + , _Allocator const & allocator +) + : _cmp( cmp ) +{ + reserve( list.size() ); + + insert( list ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::~AssocVector() +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::clear() noexcept +{ + util::destroy_range( _storage.begin(), _storage.end() ); + util::destroy_range( _buffer.begin(), _buffer.end() ); + + _storage.setSize( 0 ); + _buffer.setSize( 0 ); + _erased.setSize( 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator > & +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator=( AssocVector const & other ) +{ + AssocVector temp( other ); + temp.swap( * this ); + + return * this; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator > & +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator=( AssocVector && other ) +{ + AssocVector temp( std::move( other ) ); + + temp.swap( * this ); + + return * this; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reserve( std::size_t newStorageCapacity ) +{ + if( _storage.get_allocator().max_size() < newStorageCapacity ){ + throw std::length_error( "AssocVector< _K, _M, _C, _A >::reserve" ); + } + + if( util::less_equal( newStorageCapacity, _storage.capacity() ) ){ + return; + } + + {// _erased + if( _erased.empty() == false ){ + mergeStorageWithErased(); + } + + _erased.reserve( calculateNewErasedCapacity( newStorageCapacity ) ); + + AV_CHECK( _erased.empty() ); + } + + std::size_t const newBufferCapacity + = calculateNewBufferCapacity( newStorageCapacity ); + + std::size_t const newStorageSize = _storage.size() + _buffer.size(); + + { + _Storage newStorage( newStorageCapacity, _storage.get_allocator() ); + _Storage newBuffer( newBufferCapacity, _buffer.get_allocator() ); + + util::move_merge_into_uninitialized( + _storage.begin() + , _storage.end() + , _buffer.begin() + , _buffer.end() + , newStorage.begin() + , value_comp() + ); + + newStorage.swap( _storage ); + newBuffer.swap( _buffer ); + }// call newStorage newBuffer destructors + + _storage.setSize( newStorageSize ); + + AV_POSTCONDITION( _buffer.empty() ); + AV_POSTCONDITION( validate() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::begin() +{ + return iterator( this, _storage.begin(), _buffer.begin(), _erased.begin(), 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rbegin() +{ + return reverse_iterator( end() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::begin()const +{ + return const_iterator( this, _storage.begin(), _buffer.begin(), _erased.begin(), 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::cbegin()const +{ + return begin(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rbegin()const +{ + return const_reverse_iterator( end() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::crbegin()const +{ + return rbegin(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::end() +{ + return iterator( this, _storage.end(), _buffer.end(), _erased.end(), 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rend() +{ + return reverse_iterator( begin() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::end()const +{ + return const_iterator( this, _storage.end(), _buffer.end(), _erased.end(), 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::cend()const +{ + return end(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rend()const +{ + return const_reverse_iterator( begin() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::crend()const +{ + return rend(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_end() +{ + return _iterator( 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_end()const +{ + return _const_iterator( 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::empty()const noexcept +{ + return size() == 0; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::size()const noexcept +{ + return _storage.size() + _buffer.size() - _erased.size(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::capacity()const noexcept +{ + return _storage.capacity() + _buffer.capacity(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::max_size()const noexcept +{ + return _storage.get_allocator().max_size(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( value_type const & value ) +{ + _InsertImplResult const result = insertImpl( value ); + + return std::make_pair( + iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ) + , result._isInserted + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( __ValueType && value ) +{ + _InsertImplResult const result = insertImpl( std::forward< __ValueType >( value ) ); + + return std::make_pair( + iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ) + , result._isInserted + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator hint + , value_type const & value +) +{ + ( void )( hint ); + + _InsertImplResult const result = insertImpl( value ); + + return iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( + const_iterator hint + , __ValueType && value +) +{ + ( void )( hint ); + + _InsertImplResult const result = insertImpl( std::forward< __ValueType >( value ) ); + + return iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( + std::initializer_list< value_type > list +) +{ + insert( list.begin(), list.end() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_insert( value_type const & value ) +{ + return insertImpl( value )._isInserted; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_insert( __ValueType && value ) +{ + return insertImpl( std::forward< __ValueType >( value ) )._isInserted; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_InsertImplResult +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insertImpl( __ValueType && value ) +{ + _Key const & k = value.first; + _Mapped const & m = value.second; + + {//push back to storage + if( shouldBePushBack( value ) ) + { + pushBack( std::forward< __ValueType >( value ) ); + + _InsertImplResult result; + + { + AV_CHECK( _storage.empty() == false ); + + result._inStorage = ( _storage.end() - 1 ); + result._current = ( _storage.end() - 1 ); + } + + result._isInserted = true; + result._inBuffer = 0; + result._inErased = _erased.end(); + + AV_POSTCONDITION( result.validate() ); + AV_POSTCONDITION( validate() ); + + return result; + } + } + + typename _Storage::iterator const greaterEqualInStorage + = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); + + bool const notPresentInStorage + = greaterEqualInStorage == _storage.end() + || key_comp()( k, greaterEqualInStorage->first ); + + {//find or insert to buffer + if( notPresentInStorage ) + { + _FindOrInsertToBufferResult const findOrInsertToBufferResult + = findOrInsertToBuffer( std::forward< __ValueType >( value ) ); + + _InsertImplResult result; + result._isInserted = findOrInsertToBufferResult._isInserted; + + if( findOrInsertToBufferResult._isReallocated ) + { + result._inStorage = 0; + result._inErased = _erased.end(); + } + else + { + result._inStorage = greaterEqualInStorage; + result._inErased = 0; + } + + result._inBuffer = findOrInsertToBufferResult._inBuffer; + result._current = findOrInsertToBufferResult._inBuffer; + + AV_POSTCONDITION( result.validate() ); + AV_POSTCONDITION( validate() ); + + return result; + } + } + + {// check if not erased + typename _Erased::iterator const greaterEqualInErased = std::lower_bound( + _erased.begin() + , _erased.end() + , greaterEqualInStorage + , std::less< typename _Storage::const_iterator >() + ); + + bool const itemNotMarkedAsErased + = greaterEqualInErased == _erased.end() + || std::less< typename _Storage::const_iterator >() + ( greaterEqualInStorage, * greaterEqualInErased ); + + if( itemNotMarkedAsErased ) + {// item is in storage and is not marked as erased + _InsertImplResult result; + result._isInserted = false; + result._inStorage = greaterEqualInStorage; + result._inBuffer = 0; + result._inErased = greaterEqualInErased; + result._current = greaterEqualInStorage; + + AV_POSTCONDITION( result.validate() ); + AV_POSTCONDITION( validate() ); + + return result; + } + else + {// item is in storage but is marked as erased + _erased.erase( greaterEqualInErased ); + + greaterEqualInStorage->second = m; + + _InsertImplResult result; + result._isInserted = true; + result._inStorage = greaterEqualInStorage; + result._inBuffer = 0; + + // greaterEqualInErased is after 'Array::erase' but still valid + result._inErased = greaterEqualInErased; + + result._current = greaterEqualInStorage; + + AV_POSTCONDITION( validate() ); + AV_POSTCONDITION( result.validate() ); + + return result; + } + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename _Iterator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( _Iterator const begin, _Iterator const end ) +{ + for( _Iterator current = begin ; current != end ; ++ current ){ + insert( * current ); + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + class... __Args +> +std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplace( __Args... args ) +{ + return emplaceImpl( args... ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + class... __Args +> +std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplace_hint( const_iterator hint, __Args... args ) +{ + ( void )( hint ); + + return emplaceImpl( args... ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + class __Head + , class... __Tail +> +std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplaceImpl( __Head && head, __Tail... tail ) +{ + _InsertImplResult const result + = insertImpl( value_type_mutable( key_type( head ), mapped_type( tail... ) ) ); + + return std::make_pair( + iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ) + , result._isInserted + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::shouldBePushBack( __ValueType && value )const +{ + bool pushBackToStorage = false; + + {// should be pushed back + if( _storage.empty() ) + { + if( _buffer.empty() ){ + pushBackToStorage = true; + } + else + { + if( _cmp( _buffer.back().first, value.first ) ){ + pushBackToStorage = true; + } + } + } + else + { + if( _buffer.empty() ) + { + if( _cmp( _storage.back().first, value.first ) ){ + pushBackToStorage = true; + } + } + else + { + if( _cmp( _storage.back().first, value.first ) + && _cmp( _buffer.back().first, value.first ) + ){ + pushBackToStorage = true; + } + } + } + } + + return pushBackToStorage; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::pushBack( __ValueType && value ) +{ + if( _storage.size() != _storage.capacity() ){ + _storage.place_back( std::forward< __ValueType >( value ) ); + + AV_POSTCONDITION( validate() ); + + return; + } + + std::size_t const newStorageCapacity = calculateNewStorageCapacity( _storage.capacity() ); + std::size_t const newBufferCapacity = calculateNewBufferCapacity( newStorageCapacity ); + std::size_t const newErasedCapacity = calculateNewErasedCapacity( newStorageCapacity ); + + if( util::less_equal( newStorageCapacity, _storage.capacity() ) ){ + return; + } + + if( _storage.get_allocator().max_size() < newStorageCapacity ){ + throw std::length_error( "AssocVector::reserve" ); + } + + _Storage newStorage( newStorageCapacity, _storage.get_allocator() ); + _Storage newBuffer( newBufferCapacity, _buffer.get_allocator() ); + _Erased newErased( newErasedCapacity, _erased.get_allocator() ); + + {// may throw + iterator current = begin(); + iterator const end = this->end(); + + while( current != end ) + { + // for '++ current' object has still exist and can not be moved before + typename iterator::pointer_mutable const current_raw_ptr = current.getCurrent(); + + ++ current; + + newStorage.place_back( AV_MOVE_IF_NOEXCEPT( * current_raw_ptr ) ); + } + } + + // may throw an exception in __ValueType copy constructor, not exception safe + newStorage.place_back( std::forward< __ValueType >( value ) ); + + newStorage.swap( _storage ); + newBuffer.swap( _buffer ); + newErased.swap( _erased ); + + AV_POSTCONDITION( _buffer.empty() ); + AV_POSTCONDITION( _erased.empty() ); + + AV_POSTCONDITION( validate() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_TryRemoveBackResult +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::tryRemoveStorageBack( + typename _Storage::iterator pos +) +{ + if( pos + 1 != _storage.end() ) + { + _TryRemoveBackResult result; + result._anyItemRemoved = false; + result._erasedItemRemoved = false; + + return result; + } + + _storage.get_allocator().destroy( pos ); + + _storage.setSize( _storage.size() - 1 ); + + if( + _erased.empty() == false + && _erased.back() == pos + ) + { + _erased.setSize( _erased.size() - 1 ); + + _TryRemoveBackResult result; + result._anyItemRemoved = true; + result._erasedItemRemoved = true; + + AV_POSTCONDITION( validate() ); + + return result; + } + + _TryRemoveBackResult result; + result._anyItemRemoved = true; + result._erasedItemRemoved = false; + + AV_POSTCONDITION( validate() ); + + return result; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_TryEraseFromStorageResult +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::tryEraseFromStorage( + typename _Storage::iterator pos +) +{ + std::pair< typename _Erased::iterator, bool > const insertInSortedResult + = array::insert_in_sorted( + _erased + , typename _Storage::const_iterator( pos ) + , std::less< typename _Storage::const_iterator >() + ); + + if( _erased.full() ) + { + mergeStorageWithErased(); + + _TryEraseFromStorageResult result; + result._inErased = _erased.end(); + result._isErased = true; + result._isMerged = true; + + AV_POSTCONDITION( validate() ); + + return result; + } + else + { + _TryEraseFromStorageResult result; + result._inErased = insertInSortedResult.first; + result._isErased = insertInSortedResult.second; + result._isMerged = false; + + AV_POSTCONDITION( validate() ); + + return result; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::isErased( + typename AssocVector::_Storage::const_iterator iterator +)const +{ + typename _Erased::const_iterator const foundInErased = array::binary_search( + _erased.begin() + , _erased.end() + , iterator + , std::less< typename _Storage::const_iterator >() + ); + + return foundInErased != _erased.end(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_FindImplResult +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::findImpl( _Key const & k ) +{ + typename _Storage::iterator const greaterEqualInStorage + = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); + + bool const presentInStorage + = greaterEqualInStorage != _storage.end() + && key_comp()( k, greaterEqualInStorage->first ) == false; + + {// item is in storage, check in erased + if( presentInStorage ) + { + typename _Erased::iterator greaterEqualInErased = std::lower_bound( + _erased.begin() + , _erased.end() + , greaterEqualInStorage + , std::less< typename _Storage::const_iterator >() + ); + + bool const itemNotMarkedAsErased + = greaterEqualInErased == _erased.end() + || std::less< typename _Storage::const_iterator >()( + greaterEqualInStorage + , * greaterEqualInErased + ); + + if( itemNotMarkedAsErased ) + { + _FindImplResult result; + result._inStorage = greaterEqualInStorage; + result._inBuffer = 0; + result._inErased = greaterEqualInErased; + result._current = greaterEqualInStorage; + + AV_POSTCONDITION( result.validate() ); + + return result; + } + else + { + _FindImplResult result; + result._inStorage = 0; + result._inBuffer = 0; + result._inErased = 0; + result._current = 0; + + AV_POSTCONDITION( result.validate() ); + + return result; + } + } + } + + {// check in buffer + typename _Storage::iterator const greaterEqualInBuffer + = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); + + bool const presentInBuffer + = greaterEqualInBuffer != _buffer.end() + && key_comp()( k, greaterEqualInBuffer->first ) == false; + + if( presentInBuffer ) + { + _FindImplResult result; + result._inStorage = greaterEqualInStorage; + result._inBuffer = greaterEqualInBuffer; + result._inErased = 0; + result._current = greaterEqualInBuffer; + + AV_POSTCONDITION( result.validate() ); + + return result; + } + else + { + _FindImplResult result; + result._inStorage = 0; + result._inBuffer = 0; + result._inErased = 0; + result._current = 0; + + AV_POSTCONDITION( result.validate() ); + + return result; + } + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::find( _Key const & k ) +{ + _FindImplResult const result = findImpl( k ); + + if( result._current == 0 ){ + return end(); + } + else + { + return iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ); + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::at( _Key const & k ) +{ + _FindImplResult const result = findImpl( k ); + + if( result._current == 0 ){ + throw std::out_of_range( "AssocVector::at" ); + } + else + { + return result._current->second; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reference +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::at( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->at( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::find( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->find( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::lower_bound( _Key const & k ) +{ + typename _Storage::iterator const greaterEqualInStorage + = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); + + typename _Storage::iterator const greaterEqualInBuffer + = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); + + return iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::lower_bound( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->lower_bound( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::upper_bound( _Key const & k ) +{ + typename _Storage::iterator const greaterInStorage + = std::upper_bound( _storage.begin(), _storage.end(), k, value_comp() ); + + typename _Storage::iterator const greaterInBuffer + = std::upper_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); + + return iterator( this, greaterInStorage, greaterInBuffer, 0, 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::upper_bound( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->upper_bound( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::pair< + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator + , typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::equal_range( _Key const & k ) +{ + typename _Storage::iterator const greaterEqualInStorage + = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); + + bool const notPresentInStorage + = greaterEqualInStorage == _storage.end() + || key_comp()( k, greaterEqualInStorage->first ); + + typename _Storage::iterator const greaterEqualInBuffer + = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); + + bool const notPresentInBuffer + = greaterEqualInBuffer == _buffer.end() + || key_comp()( k, greaterEqualInBuffer->first ); + + if( notPresentInStorage == false ) + { + return std::make_pair( + iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) + , iterator( this, greaterEqualInStorage + 1, greaterEqualInBuffer, 0, 0 ) + ); + } + + if( notPresentInBuffer == false ) + { + return std::make_pair( + iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) + , iterator( this, greaterEqualInStorage, greaterEqualInBuffer + 1, 0, 0 ) + ); + } + + return std::make_pair( + iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) + , iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::pair< + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator + , typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::equal_range( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->equal_range( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_find( _Key const & k ) +{ + return _iterator( findImpl( k )._current ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_find( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->_find( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateStorage()const +{ + if( _storage.size() > _storage.capacity() ) + { + AV_ERROR(); + + return false; + } + + if( std::is_sorted( _storage.begin(), _storage.end(), value_comp() ) == false ) + { + AV_ERROR(); + + return false; + } + + return true; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateBuffer()const +{ + if( _buffer.size() > _buffer.capacity() ) + { + AV_ERROR(); + + return false; + } + + if( _buffer.empty() ){ + return true; + } + + if( std::is_sorted( _buffer.begin(), _buffer.end(), value_comp() ) == false ) + { + AV_ERROR(); + + return false; + } + + if( + util::has_intersection( + _buffer.begin() + , _buffer.end() + , _storage.begin() + , _storage.end() + , value_comp() + ) + ) + { + AV_ERROR(); + + return false; + } + + return true; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateErased()const +{ + if( _erased.size() > _erased.capacity() ) + { + AV_ERROR(); + + return false; + } + + if( _erased.empty() ){ + return true; + } + + if( + std::is_sorted( + _erased.begin() + , _erased.end() + , std::less< typename _Storage::const_iterator >() + ) == false + ) + { + AV_ERROR(); + + return false; + } + + AV_CHECK( _erased.front() >= _storage.begin() ); + AV_CHECK( _erased.back() < _storage.end() ); + + return true; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validate()const +{ + if( calculateNewBufferCapacity( _storage.capacity() ) != _buffer.capacity() ){ + return false; + } + + if( calculateNewErasedCapacity( _storage.capacity() ) != _erased.capacity() ){ + return false; + } + + return validateStorage() && validateBuffer() && validateErased(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_merge() +{ + if( size() > _storage.capacity() ) + { + reserve( calculateNewStorageCapacity( _storage.capacity() ) ); + + return; + } + + if( _erased.empty() == false ){ + mergeStorageWithErased(); + } + + mergeStorageWithBuffer(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator[]( key_type const & k ) +{ + return insert( value_type( k, mapped_type() ) ).first->second; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator[]( key_type && k ) +{ + return insert( value_type( std::move( k ), mapped_type() ) ).first->second; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::count( key_type const & k )const +{ + return _find( k ) ? 1 : 0; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::erase( key_type const & k ) +{ + typename _Storage::iterator const foundInStorage + = array::binary_search( _storage.begin(), _storage.end(), k, value_comp() ); + + {//erase from _buffer + if( foundInStorage == _storage.end() ) + { + typename _Storage::iterator const foundInBuffer + = array::binary_search( _buffer.begin(), _buffer.end(), k, value_comp() ); + + if( foundInBuffer == _buffer.end() ) + { + AV_POSTCONDITION( validate() ); + + return 0; + } + else + { + _buffer.erase( foundInBuffer ); + + AV_POSTCONDITION( validate() ); + + return 1; + } + } + } + + {//erase from back + _TryRemoveBackResult const result = tryRemoveStorageBack( foundInStorage ); + + if( result._anyItemRemoved ) + { + if( result._erasedItemRemoved ) + { + AV_POSTCONDITION( validate() ); + + return 0; + } + else + { + AV_POSTCONDITION( validate() ); + + return 1; + } + } + } + + {//erase from _storage + bool const result = tryEraseFromStorage( foundInStorage )._isErased ? 1 : 0; + + AV_POSTCONDITION( validate() ); + + return result; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::erase( iterator pos ) +{ + if( pos == end() ){ + return end(); + } + + // iterator::get converts : pair< T1, T2 > * -> pair< T1 const, T2 > * + // revert real iterator type: pair< T1 const, T2 > * -> pair< T1, T2 > * + value_type_mutable * const posBase = reinterpret_cast< value_type_mutable * >( pos.get() ); + + _Key const key = pos->first; + + {//erase from _buffer + if( util::is_between( _buffer.begin(), posBase, _buffer.end() ) ) + { + _buffer.erase( posBase ); + + typename _Storage::iterator const greaterEqualInStorage + = pos.getCurrentInStorage() + ? pos.getCurrentInStorage() + : std::lower_bound( _storage.begin(), _storage.end(), key, value_comp() ); + + AV_POSTCONDITION( validate() ); + + return iterator( this, greaterEqualInStorage, posBase, 0, 0 ); + } + } + + {//item not present in container + if( util::is_between( _storage.begin(), posBase, _storage.end() ) == false ){ + return end(); + } + } + + {//erase from back + _TryRemoveBackResult const result = tryRemoveStorageBack( posBase ); + + if( result._anyItemRemoved ) + { + typename _Storage::iterator const greaterEqualInBuffer + = pos.getCurrentInBuffer() + ? pos.getCurrentInBuffer() + : std::lower_bound( _buffer.begin(), _buffer.end(), key, value_comp() ); + + if( greaterEqualInBuffer == _buffer.end() ) + { + AV_POSTCONDITION( validate() ); + + return end(); + } + else + { + AV_POSTCONDITION( validate() ); + + return iterator( this, _storage.end(), greaterEqualInBuffer, 0, 0 ); + } + } + } + + {//erase from _storage + _TryEraseFromStorageResult const result = tryEraseFromStorage( posBase ); + + if( result._isErased == false ){ + return end(); + } + + typename _Storage::iterator const greaterEqualInBuffer + = pos.getCurrentInBuffer() + ? pos.getCurrentInBuffer() + : std::lower_bound( _buffer.begin(), _buffer.end(), key, value_comp() ); + + if( result._isMerged == false ) + { + AV_POSTCONDITION( validate() ); + + return iterator( this, posBase + 1, greaterEqualInBuffer, result._inErased + 1, 0 ); + } + + typename _Storage::iterator const greaterEqualInStorage + = std::lower_bound( _storage.begin(), _storage.end(), key, value_comp() ); + + AV_POSTCONDITION( validate() ); + + return iterator( this, greaterEqualInStorage, greaterEqualInBuffer, _erased.end(), 0 ); + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_erase( iterator pos ) +{ + // iterator::get converts : pair< T1, T2 > * -> pair< T1 const, T2 > * + // revert real iterator type: pair< T1 const, T2 > * -> pair< T1, T2 > * + value_type_mutable * const posBase = reinterpret_cast< value_type_mutable * >( pos.get() ); + + {//erase from _buffer + if( util::is_between( _buffer.begin(), posBase, _buffer.end() ) ) + { + _buffer.erase( posBase ); + + AV_POSTCONDITION( validate() ); + + return true; + } + } + + {//item not present in container + if( util::is_between( _storage.begin(), posBase, _storage.end() ) == false ){ + return false; + } + } + + {//erase from back + _TryRemoveBackResult const result = tryRemoveStorageBack( posBase ); + + if( result._anyItemRemoved ) + { + AV_POSTCONDITION( validate() ); + + return true; + } + } + + {//erase from _storage + bool const result = tryEraseFromStorage( posBase )._isErased; + + AV_POSTCONDITION( validate() ); + + return result; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::swap( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > & other +) noexcept +{ + std::swap( _storage, other._storage ); + std::swap( _buffer, other._buffer ); + std::swap( _erased, other._erased ); + + std::swap( _cmp, other._cmp ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::mergeStorageWithBuffer() +{ + AV_PRECONDITION( _erased.empty() ); + + array::move_merge( _storage, _buffer, value_comp() ); + + util::destroy_range( _buffer.begin(), _buffer.end() ); + + _buffer.setSize( 0 ); + + AV_POSTCONDITION( _buffer.empty() ); + AV_POSTCONDITION( validateStorage() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::mergeStorageWithErased() +{ + typename _Storage::iterator const end = _storage.end(); + + array::erase_removed( _storage, _erased ); + + util::destroy_range( _storage.end(), end ); + + _erased.setSize( 0 ); + + AV_POSTCONDITION( _erased.empty() ); + AV_POSTCONDITION( validateStorage() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_FindOrInsertToBufferResult +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::findOrInsertToBuffer( __ValueType && value ) +{ + typename _Storage::iterator const greaterEqualInBuffer + = std::lower_bound( _buffer.begin(), _buffer.end(), value.first, value_comp() ); + + if( greaterEqualInBuffer != _buffer.end() ) + { + bool const isEqual = _cmp( value.first, greaterEqualInBuffer->first ) == false; + + if( isEqual ) + { + _FindOrInsertToBufferResult result; + result._inBuffer = greaterEqualInBuffer; + result._isInserted = false; + result._isReallocated = false; + + return result; + } + } + + if( _buffer.full() ) + { + _merge(); + + AV_CHECK( _buffer.empty() ); + + _buffer.insert( + _buffer.begin() + , value_type_mutable( std::forward< __ValueType >( value ) ) + ); + + _FindOrInsertToBufferResult result; + result._inBuffer = _buffer.begin(); + result._isInserted = true; + result._isReallocated = true; + + AV_POSTCONDITION( validate() ); + + return result; + } + else + { + _buffer.insert( + greaterEqualInBuffer + , value_type_mutable( std::forward< __ValueType >( value ) ) + ); + + _FindOrInsertToBufferResult result; + result._inBuffer = greaterEqualInBuffer; + result._isInserted = true; + result._isReallocated = false; + + AV_POSTCONDITION( validate() ); + + return result; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewBufferCapacity( + std::size_t storageSize +) +{ + return static_cast< std::size_t >( 1.0 * sqrt( storageSize )); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewErasedCapacity( + std::size_t storageSize +) +{ + return calculateNewBufferCapacity( storageSize ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewStorageCapacity( + std::size_t storageSize +) +{ + if( storageSize == 0 ){ + return 1; + } + else if( storageSize == 1 ){ + // size=1 size=2 + // capacity=1 capacity=2 + // S:[a] S:[ab] + // B:[b] -> reserve -> B:[ ] + // E:[ ] E:[ ] + + return 4; + } + else{ + return 2 * storageSize; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void AssocVector< _Key, _Mapped, _Cmp, _Allocator >::dump( int width )const +{ + std::cout << "storage: "; + for( unsigned i = 0 ; i < _storage.size() ; ++ i ) + { + if( i > 0 && width > 0 && ( i % width == 0 ) ){ + std::cout << "\n "; + } + + std::cout << " (" << _storage[i].first << "," << _storage[i].second << ")"; + } + + std::cout << std::endl << "buffer: "; + for( unsigned i = 0 ; i < _buffer.size() ; ++ i ) + { + if( i > 0 && width > 0 && ( i % width == 0 ) ){ + std::cout << "\n "; + } + + std::cout << " (" << _buffer[i].first << "," << _buffer[i].second << ")"; + } + + std::cout << std::endl << "erased: "; + for( unsigned i = 0 ; i < _erased.size() ; ++ i ) + { + if( i > 0 && width > 0 && ( i % width == 0 ) ){ + std::cout << "\n "; + } + + std::cout << " (" << (*_erased[i]).first << "," << (*_erased[i]).second << ")"; + } + + std::cout << "." << std::endl; +} + +#endif diff --git a/external/flat b/external/flat new file mode 160000 index 000000000..2e8d469f7 --- /dev/null +++ b/external/flat @@ -0,0 +1 @@ +Subproject commit 2e8d469f7933cd640c64c6d8e8ba98f6ed9f2efc diff --git a/include/node.h b/include/node.h index 79dfe271f..dba34e5cf 100644 --- a/include/node.h +++ b/include/node.h @@ -319,11 +319,10 @@ class Node : public NodeBase { //! Status bool status_{false}; //! Scalar properties - tsl::ordered_map> + fc::vector_map> scalar_properties_; //! Vector properties - tsl::ordered_map> + fc::vector_map> vector_properties_; //! Displacement Eigen::Matrix contact_displacement_; diff --git a/include/node_base.h b/include/node_base.h index 018dc31fc..8cacffd2e 100644 --- a/include/node_base.h +++ b/include/node_base.h @@ -1,15 +1,21 @@ #ifndef MPM_NODE_BASE_H_ #define MPM_NODE_BASE_H_ +#include + #include +#include #include #include #include #include +#include +#include #include #include #include +#include #include #include "data_types.h" diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index fb119bc2a..9eb0dc6e2 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -343,9 +343,9 @@ class ParticleBase { //! Shape functions Eigen::VectorXd shapefn_; //! Scalar properties - tsl::ordered_map scalar_properties_; + fc::vector_map scalar_properties_; //! Vector properties - tsl::ordered_map> + fc::vector_map> vector_properties_; }; // ParticleBase class } // namespace mpm From 86341a6a612843ec242b1a9dc0b708d0e7c401c8 Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Mon, 20 Jul 2020 16:53:38 -0500 Subject: [PATCH 40/55] :fire: Remove unnecessary files --- CMakeLists.txt | 4 +- external/assoc_vector.h | 4955 --------------------------------------- 2 files changed, 2 insertions(+), 4957 deletions(-) delete mode 100644 external/assoc_vector.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fc8e2accb..5fc7cfa1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,9 +45,9 @@ option(HALO_EXCHANGE "Enable halo exchange" OFF) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) # Boost Archive -find_package(Boost REQUIRED COMPONENTS container filesystem system) +find_package(Boost REQUIRED COMPONENTS filesystem system) include_directories(${BOOST_INCLUDE_DIRS}) -link_libraries(${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_CONTAINER_LIBRARY}) +link_libraries(${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}) # Eigen find_package(Eigen3 REQUIRED) diff --git a/external/assoc_vector.h b/external/assoc_vector.h deleted file mode 100644 index 7a3ae766d..000000000 --- a/external/assoc_vector.h +++ /dev/null @@ -1,4955 +0,0 @@ -/* - * Copyright (C) 2012 £ukasz Czerwiñski - * - * GitHub: https://github.com/wo3kie/AssocVector - * Website: http://www.lukaszczerwinski.pl/assoc_vector.en.html - * - * Distributed under the BSD Software License (see file license) - */ - -#ifndef ASSOC_VECTOR_HPP -#define ASSOC_VECTOR_HPP - -#ifndef __GXX_EXPERIMENTAL_CXX0X__ - #error C++11 is required to run this code, please use AssocVector 1.0.x instead. -#endif - -// includes.begin - -#include -#include -#include -#include - -#include -#include - -// includes.end - -// configuration.begin - -#if ( __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 ) - #define AV_HAS_TRIVIAL_DESTRUCTOR( type ) std::is_trivially_destructible< type >::value - #define AV_MOVE_IF_NOEXCEPT std::move_if_noexcept -#else - #define AV_HAS_TRIVIAL_DESTRUCTOR( type ) __has_trivial_destructor( type ) - #define AV_MOVE_IF_NOEXCEPT std::move -#endif - -// configuration.end - -#ifndef NDEBUG - #define AV_DEBUG -#endif - -#ifdef AV_DEBUG - #define AV_PRECONDITION( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} - #define AV_CHECK( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} - #define AV_POSTCONDITION( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} - #define AV_ERROR() if( (true) ){int*i=0;*i=0;} -#else - #define AV_PRECONDITION( condition ) (void)( 0 ); - #define AV_CHECK( condition ) (void)( 0 ); - #define AV_POSTCONDITION( condition ) (void)( 0 ); - #define AV_ERROR() (void)( 0 ); -#endif - -namespace util -{ - // - // CmpByFirst - // - template< typename _Pair, typename _Cmp > - struct CmpByFirst - { - CmpByFirst( _Cmp const & cmp = _Cmp() ) - : _cmp( cmp ) - { - } - - bool operator()( _Pair const & lhs, _Pair const & rhs )const - { - return _cmp( lhs.first, rhs.first ); - } - - bool operator()( _Pair const & pair, typename _Pair::first_type const & value )const - { - return _cmp( pair.first, value ); - } - - bool operator()( typename _Pair::first_type const & value, _Pair const & pair )const - { - return _cmp( value, pair.first ); - } - - private: - _Cmp _cmp; - }; -} - -namespace util -{ - // - // equal - // - template< - typename _T1 - , typename _T2 - > - inline bool equal( - _T1 const & first - , _T2 const & second - ) - { - if( first < second ){ - return false; - } - - if( second < first ){ - return false; - } - - return true; - } - - // - // less_equal - // - template< - typename _T1 - , typename _T2 - > - inline bool less_equal( - _T1 const & first - , _T2 const & second - ) - { - return ( second < first ) == false; - } -} - -namespace util -{ - // - // is_between - // - template< - typename _T1 - , typename _T2 - > - inline bool is_between( - _T1 const & first - , _T2 const & value - , _T1 const & last - ) - { - AV_PRECONDITION( less_equal( first, last ) ); - - return ( value < first ) == false && ( last < value ) == false; - } -} - -namespace util -{ - // - // destroy_range - // - namespace detail - { - template< bool _HasTrivialDestructor > - struct DestroyRangeImpl - { - }; - - template<> - struct DestroyRangeImpl< true > - { - template< typename _Ptr > - static - void destroy( _Ptr, _Ptr ) - { - } - }; - - template<> - struct DestroyRangeImpl< false > - { - template< typename _Ptr > - static - void destroy( _Ptr first, _Ptr const last ) - { - AV_PRECONDITION( less_equal( first, last ) ); - - typedef typename std::iterator_traits< _Ptr >::value_type T; - - for( /*empty*/ ; first != last ; ++ first ){ - first -> T::~T(); - } - } - }; - } - - template< typename _Ptr > - inline void destroy_range( - _Ptr first - , _Ptr const last - ) - { - typedef typename std::iterator_traits< _Ptr >::value_type T; - - detail::DestroyRangeImpl< AV_HAS_TRIVIAL_DESTRUCTOR( T ) >::destroy( first, last ); - } -} - -namespace util -{ - // - // move - // - template< - typename _InputPtr - , typename _OutputPtr - > - inline void move( - _InputPtr first - , _InputPtr last - , _OutputPtr first2 - ) - { - if( first < first2 ){ - std::move_backward( first, last, first2 + ( last - first ) ); - } - else if( first > first2 ){ - std::move( first, last, first2 ); - } - else{ - // first == first2 -> do nothing - } - } - - template< - typename _InputPtr - , typename _OutputPtr - > - inline void copy( - _InputPtr first - , _InputPtr last - , _OutputPtr first2 - ) - { - if( first < first2 ){ - std::copy_backward( first, last, first2 + ( last - first ) ); - } - else if( first > first2 ){ - std::copy( first, last, first2 ); - } - else{ - // first == first2 -> do nothing - } - } - - - - namespace detail - { - - template< bool _MoveDoesNotThrow > - struct MoveIfNoExcept - { - }; - - template<> - struct MoveIfNoExcept< true > - { - template< - typename _InputPtr - , typename _OutputPtr - > - static - void move( _InputPtr first, _InputPtr const last, _OutputPtr first2 ) - { - move( first, last, first2 ); - } - }; - - template<> - struct MoveIfNoExcept< false > - { - template< - typename _InputPtr - , typename _OutputPtr - > - static - void move( _InputPtr first, _InputPtr const last, _OutputPtr first2 ) - { - copy( first, last, first2 ); - } - }; - } - - template< - typename _InputPtr - , typename _OutputPtr - > - inline void move_if_noexcept( - _InputPtr first - , _InputPtr const last - , _OutputPtr first2 - ) - { - typedef typename std::iterator_traits< _InputPtr >::value_type T; - - detail::MoveIfNoExcept< true >::move( first, last, first2 ); - } - -} - -namespace util -{ - -template< - typename _Iterator - , typename _T - , typename _Cmp -> -_Iterator -last_less_equal( - _Iterator first - , _Iterator last - , _T const & t - , _Cmp cmp -) -{ - AV_PRECONDITION( less_equal( first, last ) ); - - if( first == last ){ - return last; - } - - _Iterator greaterEqual = std::lower_bound( first, last, t, cmp ); - - if( greaterEqual != last ) - { - // lower_bound returns first greater_than/equal_to but we need last less_than - - bool const isEqual = cmp( t, * greaterEqual ) == false; - - if( isEqual ) - { - // that is OK, request item was found - return greaterEqual; - } - } - - if( greaterEqual == first ) - { - // requested item does not exist in container - - // 6 8 10 13 17 19 20 21 22 24 end - // ^ lower_bound( 1 ): - // requested: ^ - - return last; - } - else - { - // we need to go one item backward - - // 6 8 10 13 17 19 20 21 22 24 - // lower_bound( 23 ): ^ - // requested: ^ - - return -- greaterEqual; - } -} - -} - -namespace util -{ - -template< - typename _InputIterator1 - , typename _InputIterator2 - , typename _Cmp -> -bool has_intersection( - _InputIterator1 first1 - , _InputIterator1 const last1 - , _InputIterator2 first2 - , _InputIterator2 const last2 - , _Cmp const & cmp -) -{ - while( first1 != last1 && first2 != last2 ) - { - if( cmp( * first1, * first2 ) ){ - ++first1; - } - else if( cmp( * first2, * first1 ) ){ - ++first2; - } - else{ - return true; - } - } - - return false; -} - -} - -namespace array -{ - // - // I need much more control over Array inside AssocVector than std::vector offers - // - - // C' style array with some useful methods and functions - - // - // Array - // - template< - typename _T - , typename _Alloc = std::allocator< _T > - > - struct ArrayBase - { - struct ArrayImpl - : public _Alloc - { - ArrayImpl( _Alloc const & alloc = _Alloc() ) - : _Alloc( alloc ) - , _data( nullptr ) - , _capacity( 0 ) - , _size( 0 ) - { - } - - ArrayImpl( std::size_t capacity, _Alloc const & alloc = _Alloc() ) - : _Alloc( alloc ) - , _data( capacity ? this->allocate( capacity ) : nullptr ) - , _capacity( capacity ) - , _size( 0 ) - { - AV_PRECONDITION( _capacity < this->max_size() ); - } - - ~ArrayImpl() - { - util::destroy_range( _data, _data + _size ); - - this->deallocate( _data, _capacity ); - } - - void swap( ArrayImpl & other )noexcept - { - std::swap( _data, other._data ); - std::swap( _capacity, other._capacity ); - std::swap( _size, other._size ); - } - - public: - _T * _data; - - std::size_t _capacity; - std::size_t _size; - }; - - typedef _Alloc allocator_type; - - allocator_type - get_allocator() const { - return * static_cast< allocator_type const * >( & this->_impl ); - } - - public: - ArrayBase( _Alloc const & alloc = _Alloc() ) - : _impl( alloc ) - { - } - - ArrayBase( std::size_t capacity, _Alloc const & alloc = _Alloc() ) - : _impl( capacity, alloc ) - { - } - - ~ArrayBase() = default; - - ArrayBase( ArrayBase const & other ) = delete; - ArrayBase( ArrayBase && other ) = delete; - - ArrayBase & operator=( ArrayBase const & other ) = delete; - ArrayBase & operator=( ArrayBase && other ) = delete; - - protected: - ArrayImpl _impl; - }; - - template< - typename _T - , typename _Alloc = std::allocator< _T > - > - struct Array - : protected ArrayBase< _T, _Alloc > - { - private: - typedef ArrayBase< _T, _Alloc > Base; - - public: - using Base::get_allocator; - - public: - typedef _T value_type; - - typedef _T * iterator; - typedef _T const * const_iterator; - - public: - Array( _Alloc const & alloc = _Alloc() ) - : Base( alloc ) - { - } - - Array( std::size_t capacity, _Alloc const & alloc = _Alloc() ) - : Base( capacity, alloc ) - { - } - - Array( Array const & other ) - // In std::vector new vector's capacity is equal to old vector's size. - // Array's capacity is equal to old array's capacity to ensure invariant that: - // sqrt( storage.capacity ) == buffer.capacity - // sqrt( storage.capacity ) == erased.capacity - : Base( other.capacity(), other.get_allocator() ) - { - for( /*empty*/ ; this->_impl._size < other.size() ; ++ this->_impl._size ){ - get_allocator().construct( - this->_impl._data + this->_impl._size - , * ( other._impl._data + this->_impl._size ) - ); - } - } - - Array( Array && other ) - : Base( 0, other.get_allocator() ) - { - this->_impl.swap( other._impl ); - } - - ~Array() = default; - - Array & operator=( Array const & other ) - { - Array temp( other ); - - swap( temp ); - - return * this; - } - - Array & operator=( Array && other ) - { - this->_impl.swap( other._impl ); - - return * this; - } - - void swap( Array & other )noexcept - { - this->_impl.swap( other._impl ); - } - - void reserve( std::size_t capacity ) - { - if( get_allocator().max_size() < capacity ){ - throw std::length_error( "Array::reserve" ); - } - - if( capacity <= getCapacity() ){ - return; - } - - Array< _T, _Alloc > temp( capacity, get_allocator() ); - - std::uninitialized_copy( - std::make_move_iterator( begin() ) - , std::make_move_iterator( end() ) - , temp.begin() - ); - - swap( temp ); - } - - iterator begin()noexcept - { - return getData(); - } - - const_iterator begin()const noexcept - { - return getData(); - } - - const_iterator cbegin()const noexcept - { - return getData(); - } - - iterator end()noexcept - { - return getData() + getSize(); - } - - const_iterator end()const noexcept - { - return getData() + getSize(); - } - - const_iterator cend()const noexcept - { - return getData() + getSize(); - } - - bool empty()const noexcept - { - return getSize() == 0; - } - - bool full()const noexcept - { - return size() == capacity(); - } - - std::size_t size()const noexcept - { - return this->_impl._size; - } - - std::size_t getSize()const noexcept - { - return size(); - } - - void setSize( std::size_t newSize ) noexcept - { - AV_PRECONDITION( getData() != 0 || newSize == 0 ); - - this->_impl._size = newSize; - } - - std::size_t capacity()const noexcept - { - return this->_impl._capacity; - } - - std::size_t getCapacity()const noexcept - { - return capacity(); - } - - value_type & front() noexcept - { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( empty() == false ); - - return getData()[ 0 ]; - } - - value_type const & front()const noexcept - { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( empty() == false ); - - return getData()[ 0 ]; - } - - value_type & back() noexcept - { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( empty() == false ); - - return getData()[ getSize() - 1 ]; - } - - value_type const & back()const noexcept - { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( empty() == false ); - - return getData()[ getSize() - 1 ]; - } - - _T const * data()const noexcept - { - return this->_impl._data; - } - - _T const * getData()const noexcept - { - return data(); - } - - _T * data() noexcept - { - return this->_impl._data; - } - - _T * getData() noexcept - { - return data(); - } - - value_type & operator[]( std::size_t index ) noexcept - { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( index < size() ); - - return getData()[ index ]; - } - - value_type const & operator[]( std::size_t index )const noexcept - { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( index < size() ); - - return getData()[ index ]; - } - - template< typename _T2 > - void - insert( - typename Array< _T >::iterator const pos - , _T2 && t - ) - { - AV_PRECONDITION( util::less_equal( size() + 1, capacity() ) ); - AV_PRECONDITION( util::is_between( begin(), pos, end() ) ); - - iterator const oldEnd = end(); - - get_allocator().construct( end() ); - setSize( getSize() + 1 ); - - if( pos != oldEnd ) - { - util::move( pos, oldEnd, pos + 1 ); - } - - * pos = AV_MOVE_IF_NOEXCEPT( t ); - } - - // Array::push_back is not implemented to ensure invariant that: - // sqrt( storage.capacity ) == buffer.capacity - // sqrt( storage.capacity ) == erased.capacity - template< typename __T2 > - void place_back( __T2 && value ) - { - AV_CHECK( getData() ); - AV_CHECK( capacity() > 0 ); - AV_CHECK( getSize() < capacity() ); - - get_allocator().construct( end(), std::forward< __T2 >( value ) ); - setSize( getSize() + 1 ); - } - - void - erase( typename Array< _T >::iterator pos ) - { - AV_PRECONDITION( empty() == false ); - AV_PRECONDITION( util::less_equal( begin(), pos ) ); - AV_PRECONDITION( pos < end() ); - - util::move( pos + 1, end(), pos ); - get_allocator().destroy( end() - 1 ); - setSize( getSize() - 1 ); - } - - private: - void setCapacity( std::size_t newCapacity ) noexcept - { - AV_PRECONDITION( getData() != 0 || newCapacity == 0 ); - - this->_impl._capacity = newCapacity; - } - - void setData( _T * t ) noexcept - { - this->_impl._data = t; - } - }; -} - -namespace array -{ - template< - typename _Iterator - , typename _T - , typename _Cmp - > - _Iterator - binary_search( - _Iterator first - , _Iterator last - , _T const & t - , _Cmp cmp - ) - { - AV_PRECONDITION( util::less_equal( first, last ) ); - - _Iterator const greaterEqual = std::lower_bound( first, last, t, cmp ); - - if( greaterEqual == last ){ - return last; - } - - bool const isEqual = cmp( t, * greaterEqual ) == false; - - if( isEqual ){ - return greaterEqual; - } - - return last; - } - - template< - typename _T - , typename _T2 - , typename _Cmp - > - std::pair< typename Array< _T >::iterator, bool > - insert_in_sorted( - Array< _T > & array - , _T2 && t - , _Cmp cmp - ) - { - AV_PRECONDITION( util::less_equal( array.size() + 1, array.capacity() ) ); - - typename Array< _T >::iterator const greaterEqual - = std::lower_bound( array.begin(), array.end(), t, cmp ); - - if( greaterEqual != array.end() ) - { - bool const isEqual = cmp( t, * greaterEqual ) == false; - - if( isEqual ){ - return std::make_pair( greaterEqual, false ); - } - } - - array.insert( greaterEqual, std::forward< _T2 >( t ) ); - - return std::make_pair( greaterEqual, true ); - } - - template< typename _T > - void - erase_removed( - array::Array< _T > & storage - , array::Array< typename array::Array< _T >::const_iterator > const & erased - ) - { - AV_PRECONDITION( util::less_equal( erased.size(), storage.size() ) ); - - if( erased.empty() ){ - return; - } - - typedef typename array::Array< _T >::const_iterator StorageConstIterator; - typedef typename array::Array< _T >::iterator StorageIterator; - typedef typename array::Array< StorageConstIterator >::const_iterator ErasedConstIterator; - - StorageIterator currentInStorage = const_cast< StorageIterator >( erased.front() ); - AV_CHECK( util::is_between( storage.begin(), currentInStorage, storage.end() ) ); - - StorageIterator const endInStorage = storage.end(); - - StorageIterator whereInsertInStorage = const_cast< StorageIterator >( erased.front() ); - AV_CHECK( util::is_between( storage.begin(), whereInsertInStorage, storage.end() ) ); - - ErasedConstIterator currentInErased = erased.begin(); - ErasedConstIterator const endInErased = erased.end(); - - while( currentInStorage != endInStorage ) - { - AV_CHECK( util::is_between( storage.begin(), whereInsertInStorage, storage.end() ) ); - - if( - currentInErased != endInErased - && currentInStorage == ( * currentInErased ) - ) - { - ++ currentInStorage; - ++ currentInErased; - } - else - { - ( * whereInsertInStorage ) = AV_MOVE_IF_NOEXCEPT( * currentInStorage ); - - ++ whereInsertInStorage; - ++ currentInStorage; - } - } - - AV_POSTCONDITION( currentInErased == endInErased ); - - storage.setSize( storage.size() - erased.size() ); - } - - template< - typename _T - , typename _Cmp - > - void - move_merge( - array::Array< _T > & storage - , array::Array< _T > & buffer - , _Cmp const & cmp = _Cmp() - ) - { - AV_PRECONDITION( util::less_equal( storage.size() + buffer.size(), storage.capacity() ) ); - - typedef typename array::Array< _T >::iterator Iterator; - - Iterator rWhereInsertInStorage = storage.begin() + storage.size() + buffer.size() - 1; - - Iterator rCurrentInStorage = storage.begin() + storage.size() - 1; - Iterator const rEndInStorage = storage.begin() - 1; - - Iterator rCurrentInBuffer = buffer.begin() + buffer.size() - 1; - Iterator const rEndInBuffer = buffer.begin() - 1; - - std::size_t numberOfItemsToCreateByPlacementNew = buffer.size(); - - while( - rCurrentInBuffer != rEndInBuffer - && numberOfItemsToCreateByPlacementNew != 0 - ) - { - AV_CHECK( rWhereInsertInStorage != 0 ); - AV_CHECK( rCurrentInStorage != 0 ); - AV_CHECK( rCurrentInBuffer != 0 ); - - if( - rCurrentInStorage == rEndInStorage - || cmp( * rCurrentInStorage, * rCurrentInBuffer ) - ) - { - new ( static_cast< void * >( rWhereInsertInStorage ) ) - _T( AV_MOVE_IF_NOEXCEPT( * rCurrentInBuffer ) ); - - -- rCurrentInBuffer; - } - else - { - new ( static_cast< void * >( rWhereInsertInStorage ) ) - _T( AV_MOVE_IF_NOEXCEPT( * rCurrentInStorage ) ); - - -- rCurrentInStorage; - } - - -- numberOfItemsToCreateByPlacementNew; - -- rWhereInsertInStorage; - } - - AV_CHECK( numberOfItemsToCreateByPlacementNew == 0 ); - - while( rCurrentInBuffer != rEndInBuffer ) - { - AV_CHECK( rWhereInsertInStorage != 0 ); - AV_CHECK( rCurrentInStorage != 0 ); - AV_CHECK( rCurrentInBuffer != 0 ); - - if( - rCurrentInStorage == rEndInStorage - || cmp( * rCurrentInStorage, * rCurrentInBuffer ) - ) - { - * rWhereInsertInStorage = AV_MOVE_IF_NOEXCEPT( * rCurrentInBuffer ); - - -- rCurrentInBuffer; - } - else - { - * rWhereInsertInStorage = AV_MOVE_IF_NOEXCEPT( * rCurrentInStorage ); - - -- rCurrentInStorage; - } - - -- rWhereInsertInStorage; - } - - storage.setSize( storage.size() + buffer.size() ); - } -} - -namespace util -{ - -template< - typename _InputPtr1 - , typename _InputPtr2 - , typename _OutputPtr - , typename _Cmp -> -_OutputPtr -move_merge_into_uninitialized( - _InputPtr1 first1 - , _InputPtr1 last1 - , _InputPtr2 first2 - , _InputPtr2 last2 - , _OutputPtr output - , _Cmp cmp = _Cmp() -) -{ - AV_PRECONDITION( util::less_equal( first1, last1 ) ); - AV_PRECONDITION( util::less_equal( first2, last2 ) ); - - while( first1 != last1 && first2 != last2 ) - { - AV_CHECK( first1 != 0 ); - AV_CHECK( first2 != 0 ); - AV_CHECK( output != 0 ); - - if( cmp( * first1, * first2 ) ) - { - new ( static_cast< void * >( output ) ) - typename std::iterator_traits< _OutputPtr >::value_type( std::move( * first1 ) ); - - ++ output; - ++ first1; - } - else - { - new ( static_cast< void * >( output ) ) - typename std::iterator_traits< _OutputPtr >::value_type( std::move( * first2 ) ); - - ++ output; - ++ first2; - } - } - - if( first1 == last1 ){ - return std::uninitialized_copy( - std::make_move_iterator( first2 ) - , std::make_move_iterator( last2 ) - , output - ); - } - - if( first2 == last2 ){ - return std::uninitialized_copy( - std::make_move_iterator( first1 ) - , std::make_move_iterator( last1 ) - , output - ); - } - - return output; -} - -} - -namespace detail -{ - template< typename _Iterator > - bool equal( _Iterator const & lhs, _Iterator const & rhs ) - { - if( lhs.getContainer() != rhs.getContainer() ){ - return false; - } - - // for empty container returns that begin == end - // despite on fact they are not - if( lhs.getContainer()->empty() ){ - return true; - } - - return lhs.getCurrent() == rhs.getCurrent(); - } - - // - // AssocVectorLazyIterator - // - - template< - typename _Iterator - , typename _Container - > - struct AssocVectorLazyIterator - { - public: - typedef typename std::iterator_traits< _Iterator >::pointer pointer_mutable; - - public: - typedef std::bidirectional_iterator_tag iterator_category; - typedef typename std::iterator_traits< _Iterator >::value_type value_type; - typedef typename std::iterator_traits< _Iterator >::difference_type difference_type; - - // make key const - typedef std::pair< - typename value_type::first_type const - , typename value_type::second_type - > & reference; - - // make key const - typedef std::pair< - typename value_type::first_type const - , typename value_type::second_type - > * pointer; - - private: - struct _CurrentInErased - { - _CurrentInErased( typename _Container::_Erased::const_iterator current ) - : _current( current ) - { - } - - _CurrentInErased( _CurrentInErased const & other ) - : _current( other._current ) - { - } - - _CurrentInErased & operator=( _CurrentInErased const & other ) - { - _current = other._current; - - return * this; - } - - _CurrentInErased & operator=( typename _Container::_Erased::const_iterator current ) - { - _current = current; - - return * this; - } - - bool is_end( _Container const * container )const - { - return _current == container->erased().end(); - } - - bool is_not_end( _Container const * container )const - { - return ! is_end( container ); - } - - bool is_begin( _Container const * container )const - { - return _current == container->erased().begin(); - } - - bool is_not_begin( _Container const * container )const - { - return ! is_begin( container ); - } - - void increment( _Container const * container ) - { - AV_PRECONDITION( is_not_end( container ) ); - - ++ _current; - } - - void try_increment( _Container const * container ) - { - if( is_end( container ) ){ - return; - } - - increment( container ); - } - - void decrement( _Container const * container ) - { - AV_PRECONDITION( is_not_begin( container ) ); - - -- _current; - } - - void try_decrement( _Container const * container ) - { - if( is_begin( container ) ){ - return; - } - - decrement( container ); - } - - bool validate( _Container const * container )const - { - bool const result - = util::is_between( - container->erased().begin() - , _current - , container->erased().end() - ); - - if( result ){ - return true; - } - - AV_ERROR(); - - return false; - } - - typename _Container::_Erased::value_type const & - get( _Container const * container )const noexcept - { - AV_PRECONDITION( _current ); - AV_PRECONDITION( is_not_end( container ) ); - AV_PRECONDITION( validate( container ) ); - - return * _current; - } - - typename _Container::_Erased::const_iterator data()const noexcept - { - return _current; - } - - operator bool()const noexcept - { - return _current != 0; - } - - private: - typename _Container::_Erased::const_iterator _current; - }; - - struct _CurrentInBuffer - { - _CurrentInBuffer( pointer_mutable current ) - : _current( current ) - { - } - - _CurrentInBuffer( _CurrentInBuffer const & other ) - : _current( other._current ) - { - } - - _CurrentInBuffer & operator=( _CurrentInBuffer const & other ) - { - _current = other._current; - - return * this; - } - - _CurrentInBuffer & operator=( pointer_mutable current ) - { - _current = current; - - return * this; - } - - bool is_begin( _Container const * container )const - { - return _current == container->buffer().begin(); - } - - bool is_not_begin( _Container const * container )const - { - return ! is_begin( container ); - } - - bool is_end( _Container const * container )const - { - return _current == container->buffer().end(); - } - - bool is_not_end( _Container const * container )const - { - return ! is_end( container ); - } - - void increment( _Container const * container ) - { - AV_PRECONDITION( is_not_end( container ) ); - - ++ _current; - } - - void try_increment( _Container const * container ) - { - if( is_end( container ) ){ - return; - } - - increment( container ); - } - - void decrement( _Container const * container ) - { - AV_PRECONDITION( is_not_begin( container ) ); - - -- _current; - } - - void try_decrement( _Container const * container ) - { - if( is_begin( container ) ){ - return; - } - - decrement( container ); - } - - bool validate( _Container const * container )const - { - bool const result - = util::is_between( - container->buffer().begin() - , _current - , container->buffer().end() - ); - - if( result ){ - return true; - } - - AV_ERROR(); - - return false; - } - - typename _Container::_Storage::value_type const & - get( _Container const * container )const noexcept - { - AV_PRECONDITION( _current ); - AV_PRECONDITION( is_not_end( container ) ); - AV_PRECONDITION( validate( container ) ); - - return * _current; - } - - operator bool()const noexcept - { - return _current != 0; - } - - pointer_mutable data()const noexcept - { - return _current; - } - private: - pointer_mutable _current; - }; - - struct _CurrentInStorage - { - // begin is always set at first not erased item, marked with ^ - // end is always set at the end of container, marked with $ - - // Case 1: no item erased - // storage: ^1 3 5 7$ - // erased : - - // Case 2: item erased from front - // storage: 1 ^3 5 7$ - // erased : 1 - - // Case 3: item erased from back - // storage: ^1 3 5 7$ - // erased : 7 - - // Case 4: item erased from front and back - // storage: 1 ^3 5 7$ - // erased : 1 7 - - _CurrentInStorage( pointer_mutable current ) - : _dir( 1 ) - , _current( current ) - { - } - - _CurrentInStorage( _CurrentInStorage const & other ) - : _dir( other._dir ) - , _current( other._current ) - { - } - - _CurrentInStorage & operator=( _CurrentInStorage const & other ) - { - _dir = other._dir; - _current = other._current; - - return * this; - } - - bool operator==( _CurrentInStorage const & other )const - { - return _current == other._current; - } - - bool operator!=( _CurrentInStorage const & other )const - { - return _current != other._current; - } - - bool operator==( typename _Container::_Erased::value_type inErased )const - { - return _current == inErased; - } - - bool operator!=( typename _Container::_Erased::value_type inErased )const - { - return _current != inErased; - } - - bool is_begin( _Container const * container )const - { - _CurrentInStorage currentInStorage = const_cast< pointer_mutable >( container->storage().begin() ); - _CurrentInErased currentInErased = container->erased().begin(); - - currentInStorage.setOnNotErased( currentInErased, container ); - - return data() == currentInStorage.data(); - } - - bool is_not_begin( _Container const * container )const - { - return ! is_begin( container ); - } - - bool _is_begin( _Container const * container )const - { - return _current == container->storage().begin(); - } - - bool _is_not_begin( _Container const * container )const - { - return ! _is_begin( container ); - } - - bool is_end( _Container const * container )const - { - return _current == container->storage().end(); - } - - bool is_not_end( _Container const * container )const - { - return ! is_end( container ); - } - - void - increment( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - AV_PRECONDITION( is_not_end( container ) ); - - increment( container ); - - if( _dir == -1 ) - { - _dir = 1; - currentInErased.try_increment( container ); - } - - setOnNotErased( currentInErased, container ); - } - - void - try_increment( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - if( is_end( container ) ){ - return; - } - - increment( currentInErased, container ); - } - - void - decrement( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - AV_PRECONDITION( is_not_begin( container ) ); - - decrement( container ); - - if( _dir == 1 ) - { - _dir = -1; - currentInErased.try_decrement( container ); - } - - setOnNotErasedBackward( currentInErased, container ); - } - - void - try_decrement( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - if( _is_begin( container ) ){ - return; - } - - decrement( currentInErased, container ); - } - - void - setOnNotErased( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - if( is_end( container ) ) - { - if( !currentInErased ) - { - currentInErased = container->erased().end(); - } - - return; - } - - if( !currentInErased ) - { - currentInErased = std::lower_bound( - container->erased().begin() - , container->erased().end() - , data() - , std::less< typename _Container::_Storage::const_iterator >() - ); - } - - if( _dir == -1 ) - { - _dir = 1; - currentInErased.try_increment( container ); - } - - while( - is_not_end( container ) - && currentInErased.is_not_end( container ) - && data() == currentInErased.get( container ) - ) - { - increment( container ); - currentInErased.increment( container ); - } - - AV_POSTCONDITION( currentInErased ); - - AV_POSTCONDITION( - ( - is_end( container ) - && currentInErased.is_end( container ) - ) - || currentInErased.is_end( container ) - || *this != currentInErased.get( container ) - ); - } - - void - setOnNotErasedBackward( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - AV_CHECK( is_not_end( container ) ); - - if( _dir == 1 ) - { - _dir = -1; - currentInErased.try_decrement( container ); - } - - while( - is_not_begin( container ) - && currentInErased.is_not_end( container ) - && data() == currentInErased.get( container ) - ) - { - decrement( container ); - currentInErased.try_decrement( container ); - } - - AV_POSTCONDITION( validate( container ) ); - - AV_POSTCONDITION( - currentInErased.is_end( container ) - || *this != currentInErased.get( container ) - ); - } - - operator bool()const noexcept - { - return _current != 0; - } - - bool validate( _Container const * container )const - { - bool const result = util::is_between( - container->storage().begin() - , _current - , container->storage().end() - ); - - if( result ){ - return true; - } - - AV_ERROR(); - - return false; - } - - typename _Container::_Storage::value_type const & - get( _Container const * container )const noexcept - { - AV_PRECONDITION( _current ); - AV_PRECONDITION( is_not_end( container ) ); - - return * _current; - } - - pointer_mutable data()const noexcept - { - return _current; - } - - private: - void increment( _Container const * container ) - { - AV_PRECONDITION( is_not_end( container ) ); - - ++ _current; - } - - void decrement( _Container const * container ) - { - AV_PRECONDITION( is_not_begin( container ) ); - - -- _current; - } - - private: - int _dir; - - pointer_mutable _current; - }; - - struct _Current - { - _Current( pointer_mutable current ) - : _current( current ) - { - } - - _Current( _CurrentInStorage const & inStorage ) - : _current( inStorage.data() ) - { - } - - _Current( _CurrentInBuffer const & inBuffer ) - : _current( inBuffer.data() ) - { - } - - _Current & operator=( _Current const & other ) - { - _current = other._current; - - return * this; - } - - _Current & operator=( _CurrentInStorage const & inStorage ) - { - _current = inStorage.data(); - - return * this; - } - - _Current & operator=( _CurrentInBuffer const & inBuffer ) - { - _current = inBuffer.data(); - - return * this; - } - - bool operator==( _Current const & other )const - { - return _current == other._current; - } - - bool operator==( _CurrentInStorage const & inStorage )const - { - return _current == inStorage.data(); - } - - bool operator==( _CurrentInBuffer const & inBuffer )const - { - return _current == inBuffer.data(); - } - - bool validate( - _CurrentInStorage currentInStorage - , _CurrentInBuffer currentInBuffer - , _Container const * container - )const - { - AV_PRECONDITION( currentInStorage || currentInBuffer ); - AV_PRECONDITION( container != 0 ); - - if( !currentInStorage ) - { - if( _current == currentInBuffer.data() ){ - return true; - } - - AV_ERROR(); - - return false; - } - - if( !currentInBuffer ) - { - if( _current == currentInStorage.data() ){ - return true; - } - - AV_ERROR(); - - return false; - } - - // if 'setLower' does not work 'validateCurrent' does not work as well :O( - bool const result - = _current == getLower( currentInStorage, currentInBuffer, container ).data(); - - if( result ){ - return true; - } - - AV_ERROR(); - - return false; - } - - void setLower( - _CurrentInStorage currentInStorage - , _CurrentInBuffer currentInBuffer - , _Container const * container - ) - { - _current = getLower( currentInStorage, currentInBuffer, container ).data(); - } - - _Current getLower( - _CurrentInStorage currentInStorage - , _CurrentInBuffer currentInBuffer - , _Container const * container - )const - { - AV_CHECK( currentInStorage ); - AV_CHECK( currentInBuffer ); - - if( currentInStorage.is_end( container ) ) - { - if( currentInBuffer.is_end( container ) ){ - return _Current( 0 ); - } - else{ - return _Current( currentInBuffer ); - } - } - else - { - if( currentInBuffer.is_end( container ) ){ - return _Current( currentInStorage ); - } - else - { - if( container->value_comp()( - currentInStorage.get( container ) - , currentInBuffer.get( container ) - ) - ){ - return _Current( currentInStorage ); - } - else{ - return _Current( currentInBuffer ); - } - } - } - } - - operator bool()const noexcept - { - return _current != 0; - } - - typename _Container::_Storage::value_type const & - get( _Container const * container )const noexcept - { - return * _current; - } - - pointer_mutable data()const noexcept - { - return _current; - } - - private: - pointer_mutable _current; - }; - - public: - AssocVectorLazyIterator( - typename _Container::value_compare const & cmp = typename _Container::value_compare() - ) - : _container( 0 ) - - , _currentInStorage( 0 ) - , _currentInBuffer( 0 ) - , _currentInErased( 0 ) - - , _current( 0 ) - { - } - - template< typename _Iter > - AssocVectorLazyIterator( AssocVectorLazyIterator< _Iter, _Container > const & other ) - : _container( other.getContainer() ) - - , _currentInStorage( other.getCurrentInStorage() ) - , _currentInBuffer( other.getCurrentInBuffer() ) - , _currentInErased( other.getCurrentInErased() ) - - , _current( other.getCurrent() ) - { - } - - AssocVectorLazyIterator( - _Container const * container - , pointer_mutable currentInStorage - , pointer_mutable currentInBuffer - , typename _Container::_Erased::const_iterator currentInErased - , pointer_mutable current - ) - : _container( container ) - - , _currentInStorage( currentInStorage ) - , _currentInBuffer( currentInBuffer ) - , _currentInErased( currentInErased ) - - , _current( current ) - { - AV_PRECONDITION( container != 0 ); - AV_PRECONDITION( validate() ); - - if( _currentInStorage && _currentInBuffer && !_currentInErased && !_current ) - { - // not found in storage, insert to buffer - // erase from buffer + find in storage - // lower_bound - // upper_bound - - // _currentInStorage <- fix against '!_currentInErased' - _currentInStorage.setOnNotErased( _currentInErased, _container ); - - // _current <- get it right now - _current.setLower( _currentInStorage, _currentInBuffer, _container ); - } - else - if( _currentInStorage && _currentInBuffer && !_currentInErased && _current ) - { - // not found in storage, found in buffer - // not found in storage, inserted to buffer - // erased from storage's back - - // _currentInStorage <- fix against '!_currentInErased' - _currentInStorage.setOnNotErased( _currentInErased, _container ); - } - else - if( _currentInStorage && _currentInBuffer && _currentInErased && !_current ) - { - // begin iterator - // end iterator - // erase from storage, not merged + find in buffer - // erase from storage, merged + find in buffer + find in storage - - // _currentInStorage <- check against _currentInErased - _currentInStorage.setOnNotErased( _currentInErased, _container ); - - // _current <- get it right now - _current.setLower( _currentInStorage, _currentInBuffer, _container ); - } - else - if( ! _currentInStorage && ! _currentInBuffer && ! _currentInErased && !_current ) - { - // begin iterator on empty AssocVector - // end iterator on empty AssocVector - - // return, do not make validation - return; - } - - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - AV_POSTCONDITION( _currentInStorage || _currentInBuffer ); - AV_POSTCONDITION( _currentInErased ); - AV_POSTCONDITION( _container != 0 ); - } - - AssocVectorLazyIterator & - operator=( AssocVectorLazyIterator const & other ) - { - _container = other._container; - - _currentInStorage = other._currentInStorage; - _currentInBuffer = other._currentInBuffer; - _currentInErased = other._currentInErased; - - _current = other._current; - - return * this; - } - - bool operator==( AssocVectorLazyIterator const & other )const - { - this->resolveLazyValues(); - other.resolveLazyValues(); - - if( isEmpty() && other.isEmpty() ){ - return getContainer() == other.getContainer(); - } - - AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return equal( *this, other ); - } - - bool operator!=( AssocVectorLazyIterator const & other )const - { - this->resolveLazyValues(); - other.resolveLazyValues(); - - return ! ( ( * this ) == other ); - } - - AssocVectorLazyIterator & operator++() - { - AV_PRECONDITION( isEmpty() == false ); - - resolveLazyValues(); - - AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - if( _current == _Current( 0 ) ){ - return * this; - } - else if( _current == _currentInStorage ){ - _currentInStorage.try_increment( _currentInErased, _container ); - } - else if( _current == _currentInBuffer ){ - _currentInBuffer.try_increment( _container ); - } - else{ - AV_ERROR(); - } - - _current.setLower( _currentInStorage, _currentInBuffer, _container ); - - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return * this; - } - - AssocVectorLazyIterator operator++( int ) - { - AssocVectorLazyIterator result( * this ); - - ( * this ).operator++(); - - return result; - } - - AssocVectorLazyIterator & operator--() - { - AV_PRECONDITION( isEmpty() == false ); - - resolveLazyValues(); - - AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - if( - _currentInStorage.is_begin( _container ) - && _currentInBuffer.is_begin( _container ) - ) - { - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return * this; - } - - if( _currentInStorage.is_begin( _container ) ) - { - _currentInBuffer.decrement( _container ); - - _current = _currentInBuffer; - - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return * this; - } - - if( _currentInBuffer.is_begin( _container ) ) - { - _currentInStorage.decrement( _currentInErased, _container ); - - _current = _currentInStorage; - - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return * this; - } - - _CurrentInStorage currentInStorage = _currentInStorage; - _CurrentInBuffer currentInBuffer = _currentInBuffer; - - _CurrentInErased currentInErased = _currentInErased; - - currentInStorage.decrement( currentInErased, _container ); - currentInBuffer.decrement( _container ); - - if( - _container->value_comp()( - currentInStorage.get( _container ) - , currentInBuffer.get( _container ) - ) - ) - { - _currentInBuffer = currentInBuffer; - - _current = _currentInBuffer; - } - else - { - _currentInStorage = currentInStorage; - _currentInErased = currentInErased; - - _current = _currentInStorage; - } - - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return * this; - } - - AssocVectorLazyIterator operator--( int ) - { - AssocVectorLazyIterator result( * this ); - - ( * this ).operator--(); - - return result; - } - - reference operator*()const - { - return * get(); - } - - pointer operator->()const - { - return get(); - } - - pointer get()const - { - AV_PRECONDITION( isEmpty() == false ); - AV_PRECONDITION( _current ); - AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - // make key const - // pair< T1, T2 > * -> pair< T1 const, T2 > * - //return reinterpret_cast< pointer >( _current ); - - return - reinterpret_cast< pointer >( - const_cast< void * >( - reinterpret_cast< void const * >( _current.data() ) - ) - ); - } - - // public for copy constructor only : Iterator -> ConstIterator - _Container const * getContainer()const noexcept - { - return _container; - } - - pointer_mutable getCurrentInStorage()const noexcept - { - return _currentInStorage.data(); - } - - pointer_mutable getCurrentInBuffer()const noexcept - { - return _currentInBuffer.data(); - } - - typename _Container::_Erased::const_iterator getCurrentInErased()const noexcept - { - return _currentInErased.data(); - } - - pointer_mutable getCurrent()const noexcept - { - return _current.data(); - } - - private: - bool isEmpty()const - { - if( _currentInStorage ){ - return false; - } - if( _currentInBuffer ){ - return false; - } - if( _currentInErased ){ - return false; - } - if( _current ){ - return false; - } - - return true; - } - - /*const function*/ - static - void - resolveLazyCurrentInBuffer( - _CurrentInBuffer & currentInBuffer - , _Current const current - , _Container const * container - ) - { - if( currentInBuffer ){ - return; - } - - currentInBuffer = const_cast< pointer_mutable >( - std::lower_bound( - container->buffer().begin() - , container->buffer().end() - , current.get( container ) - , container->value_comp() - ) - ); - } - - static - void - resolveLazyCurrentInStorage( - _CurrentInStorage & currentInStorage - , _CurrentInErased & currentInErased - , _Current const current - , _Container const * container - ) - { - if( currentInStorage ){ - return; - } - - currentInStorage = const_cast< pointer_mutable >( - std::lower_bound( - container->storage().begin() - , container->storage().end() - , current.get( container ) - , container->value_comp() - ) - ); - - currentInStorage.setOnNotErased( currentInErased, container ); - } - - void - resolveLazyValues()const - { - resolveLazyCurrentInBuffer( _currentInBuffer, _current, _container ); - resolveLazyCurrentInStorage( _currentInStorage, _currentInErased, _current, _container ); - } - - /*pure function*/ - bool - validate()const - { - if( !_currentInStorage && _currentInBuffer && _currentInErased && _current ) - { - // not found in storage, inserted to buffer, buffer merged to storage - - AV_CHECK( _currentInBuffer.validate( _container ) ); - AV_CHECK( _currentInErased.is_end( _container ) ); - AV_CHECK( _current == _currentInBuffer ); - - // _currentInStorage <- lazy in operator++/operator--/operator==/operator!= - } - else - if( _currentInStorage && !_currentInBuffer && _currentInErased && !_current ) - { - AV_ERROR(); - - return false; - } - else - if( _currentInStorage && !_currentInBuffer && _currentInErased && _current ) - { - // found in storage, not found in erased - - AV_CHECK( _currentInStorage.validate( _container ) ); - AV_CHECK( - std::binary_search( - _container->erased().begin() - , _container->erased().end() - , _currentInStorage.data() - ) == false - ); - AV_CHECK( _currentInErased.validate( _container ) ); - AV_CHECK( _current == _currentInStorage ); - - // _currentInBuffer <- lazy in operator++/operator--/operator==/operator!= - } - else - if( _currentInStorage && _currentInBuffer && !_currentInErased && !_current ) - { - // not found in storage, insert to buffer - // erase from buffer + find in storage - // lower_bound - // upper_bound - - AV_CHECK( _currentInStorage.validate( _container ) ); - AV_CHECK( _currentInBuffer.validate( _container ) ); - - // _currentInStorage <- fix against '!_currentInErased' - // _current <- get it right now - } - else - if( _currentInStorage && _currentInBuffer && !_currentInErased && _current ) - { - // not found in storage, found in buffer - // not found in storage, inserted to buffer - // erased from storage's back - - AV_CHECK( _currentInStorage.validate( _container ) ); - AV_CHECK( _currentInBuffer.validate( _container ) ); - AV_CHECK( _current == _currentInBuffer ); - - // _currentInStorage <- fix against '!_currentInErased' - // _currentInErased <- get it right now - } - else - if( _currentInStorage && _currentInBuffer && _currentInErased && !_current ) - { - // begin iterator - // end iterator - // erase from storage, not merged + find in buffer - // erase from storage, merged + find in buffer + find in storage - - AV_CHECK( _currentInStorage.validate( _container ) ); - AV_CHECK( _currentInBuffer.validate( _container ) ); - AV_CHECK( _currentInErased.validate( _container ) ); - - // _currentInStorage <- check against _currentInErased - // _current <- get it right now - } - else - if( ! _currentInStorage && ! _currentInBuffer && ! _currentInErased && !_current ) - { - // begin iterator on empty AssocVector - // end iterator on empty AssocVector - } - else - { - AV_ERROR(); - - return false; - } - - return true; - } - - private: - _Container const * _container; - - // mutable since lazy values in operator==()const / operator!=()const - mutable _CurrentInStorage _currentInStorage; - mutable _CurrentInBuffer _currentInBuffer; - mutable _CurrentInErased _currentInErased; - - _Current _current; - }; - - template< - typename _Iterator - , typename _Container - > - std::ostream & - operator<<( - std::ostream & out - , AssocVectorLazyIterator< _Iterator, _Container > const & iter - ) - { - out << "S: " << iter.getCurrentInStorage(); - - if( iter.getCurrentInStorage() == 0 ){ - out << " (null)"; - } - else if( iter.getContainer()->storage().end() == iter.getCurrentInStorage() ){ - out << " (end)"; - } - else{ - out << " " << * iter.getCurrentInStorage(); - } - - out << "\nB: " << iter.getCurrentInBuffer(); - - if( iter.getCurrentInBuffer() == 0 ){ - out << " (null)"; - } - else if( iter.getContainer()->buffer().end() == iter.getCurrentInBuffer() ){ - out << " (end)"; - } - else{ - out << " " << * iter.getCurrentInBuffer(); - } - - out << "\nE: " << iter.getCurrentInErased(); - - if( iter.getCurrentInErased() == 0 ){ - out << " (null)"; - } - else if( iter.getContainer()->erased().end() == iter.getCurrentInErased() ){ - out << " (end)"; - } - else{ - AV_CHECK( * iter.getCurrentInErased() ); - - out << " " << * * iter.getCurrentInErased(); - } - - out << "\nC: " << iter.getCurrent(); - - if( iter.getCurrent() == 0 ){ - out << " (null)"; - } - else{ - out << " " << * iter.getCurrent(); - } - - std::flush( out ); - - return out; - } - - // - // _AssocVectorIterator, simplified version of AssocVectorLazyIterator, works with _find and _end - // - template< - typename _Iterator - , typename _Container - > - struct _AssocVectorIterator - { - private: - typedef typename std::iterator_traits< _Iterator >::pointer pointer_mutable; - - public: - typedef typename std::iterator_traits< _Iterator >::value_type value_type; - - // make key const - typedef std::pair< - typename value_type::first_type const - , typename value_type::second_type - > & reference; - - // make key const - typedef std::pair< - typename value_type::first_type const - , typename value_type::second_type - > * pointer; - - public: - _AssocVectorIterator( - typename _Container::value_compare const & cmp = typename _Container::value_compare() - ) - : _current( 0 ) - { - } - - template< typename _Iter > - _AssocVectorIterator( _AssocVectorIterator< _Iter, _Container > const & other ) - : _current( other.getCurrent() ) - { - } - - _AssocVectorIterator( pointer_mutable current ) - : _current( current ) - { - } - - _AssocVectorIterator & - operator=( _AssocVectorIterator const & other ) - { - _current = other._current; - - return * this; - } - - bool operator==( _AssocVectorIterator const & other )const - { - return _current == other.getCurrent(); - } - - bool operator!=( _AssocVectorIterator const & other )const - { - return ! ( ( * this ) == other ); - } - - reference operator*()const - { - AV_PRECONDITION( _current != 0 ); - - return * get(); - } - - pointer operator->()const - { - AV_PRECONDITION( _current != 0 ); - - return get(); - } - - pointer get()const - { - AV_PRECONDITION( _current != 0 ); - - // make key const - // pair< T1, T2 > * -> pair< T1 const, T2 > * - //return reinterpret_cast< pointer >( _current ); - - return - reinterpret_cast< pointer >( - const_cast< void * >( - reinterpret_cast< void const * >( _current ) - ) - ); - } - - operator bool()const noexcept - { - return _current != 0; - } - - // public for copy constructor only : Iterator -> ConstIterator - pointer_mutable getCurrent()const noexcept - { - return _current; - } - - private: - pointer_mutable _current; - }; - - template< - typename _Iterator - , typename _Container - > - std::ostream & operator<<( - std::ostream & out - , _AssocVectorIterator< _Iterator, _Container > const & iter - ) - { - out << "S: " << iter.getCurrent(); - - if( iter.getCurrent() == 0 ){ - out << " (null)(end)"; - } - else{ - out << " " << * iter.get(); - } - - return out; - } - -} // namespace detail - -template< - typename _Key - , typename _Mapped - , typename _Cmp = std::less< _Key > - , typename _Allocator = std::allocator< std::pair< _Key, _Mapped > > -> -struct AssocVector -{ -private: - typedef std::pair< _Key, _Mapped > value_type_mutable; - typedef std::pair< _Key const, _Mapped > value_type_key_const; - -public: - typedef _Key key_type; - typedef _Mapped mapped_type; - - typedef value_type_key_const value_type; - - typedef typename _Allocator::size_type size_type; - typedef typename _Allocator::difference_type difference_type; - - typedef typename _Allocator::pointer pointer; - typedef typename _Allocator::const_pointer const_pointer; - - typedef _Cmp key_compare; - typedef util::CmpByFirst< value_type_mutable, _Cmp > value_compare; - - typedef _Allocator allocator_type; - - typedef mapped_type & reference; - typedef mapped_type const & const_reference; - - typedef detail::AssocVectorLazyIterator< value_type_mutable *, AssocVector > iterator; - typedef detail::AssocVectorLazyIterator< value_type_mutable const *, AssocVector > const_iterator; - - typedef std::reverse_iterator< iterator > reverse_iterator; - typedef std::reverse_iterator< const_iterator > const_reverse_iterator; - - typedef array::Array< value_type_mutable > _Storage; - typedef array::Array< typename _Storage::const_iterator > _Erased; - -#ifdef AV_ENABLE_EXTENSIONS - public: -#else - private: -#endif - - // - // extension, faster, non STL compatible version of iterator, working with _find end _end - // - typedef detail::_AssocVectorIterator< value_type_mutable *, AssocVector > _iterator; - typedef detail::_AssocVectorIterator< value_type_mutable const *, AssocVector > _const_iterator; - -private: - struct _FindOrInsertToBufferResult - { - typename _Storage::iterator _inBuffer; - bool _isInserted; - bool _isReallocated; - }; - - struct _TryRemoveBackResult - { - bool _anyItemRemoved; - bool _erasedItemRemoved; - }; - - struct _FindImplResult - { - typename _Storage::iterator _inStorage; - typename _Storage::iterator _inBuffer; - typename _Erased::iterator _inErased; - typename _Storage::iterator _current; - - bool validate()const - { - return - ( _current == 0 && _inStorage == 0 && _inBuffer == 0 && _inErased == 0 ) - || _inStorage != 0; - } - }; - - struct _InsertImplResult - { - bool _isInserted; - - typename _Storage::iterator _inStorage; - typename _Storage::iterator _inBuffer; - typename _Erased::iterator _inErased; - typename _Storage::iterator _current; - - bool validate()const - { - if( _current == 0 ) - { - AV_ERROR(); - - return false; - } - - if( _inStorage == 0 && ( _inBuffer == 0 || _inErased == 0 ) ) - { - AV_ERROR(); - - return false; - } - - return true; - } - }; - - struct _TryEraseFromStorageResult - { - typename _Erased::iterator _inErased; - bool _isErased; - bool _isMerged; - }; - -public: - // - // constructor - // - explicit - AssocVector( - _Cmp const & cmp = _Cmp() - , _Allocator const & allocator = _Allocator() - ); - - explicit - AssocVector( _Allocator const & allocator ); - - template< typename __InputIterator > - AssocVector( - __InputIterator first - , __InputIterator last - , _Cmp const & cmp = _Cmp() - , _Allocator const & allocator = _Allocator() - ); - - AssocVector( AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other ); - AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other - , _Allocator const & allocator - ); - - AssocVector( AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other ); - AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other - , _Allocator const & allocator - ); - - AssocVector( - std::initializer_list< value_type > list - , _Cmp const & cmp = _Cmp() - , _Allocator const & allocator = _Allocator() - ); - - // - // destructor - // - inline ~AssocVector(); - - // - // clear - // - inline void clear() noexcept; - - // - // operator= - // - AssocVector & operator=( AssocVector const & other ); - AssocVector & operator=( AssocVector && other ); - - // - // methods - // - void reserve( std::size_t newCapacity ); - void swap( AssocVector & other ) noexcept; - - // - // iterators - // - inline iterator begin(); - inline const_iterator begin()const; - inline const_iterator cbegin()const; - - inline reverse_iterator rbegin(); - inline const_reverse_iterator rbegin()const; - inline const_reverse_iterator crbegin()const; - - inline iterator end(); - inline const_iterator end()const; - inline const_iterator cend()const; - - inline reverse_iterator rend(); - inline const_reverse_iterator rend()const; - inline const_reverse_iterator crend()const; - - // - // size - // - inline bool empty()const noexcept; - inline std::size_t size()const noexcept; - inline std::size_t capacity()const noexcept; - inline std::size_t max_size()const noexcept; - - // - // insert - // - std::pair< iterator, bool > insert( value_type const & value ); - - template< typename __ValueType > - std::pair< iterator, bool > insert( __ValueType && value ); - - iterator insert( const_iterator hint, value_type const & value ); - - template< typename __ValueType > - iterator insert( const_iterator hint, __ValueType && value ); - - template< typename _Iterator > - inline void insert( _Iterator first, _Iterator last ); - - inline void insert( std::initializer_list< value_type > list ); - - // - // emplace - // - template< class... __Args > - std::pair< iterator, bool > emplace( __Args... args ); - - template< class... __Args > - std::pair< iterator, bool > emplace_hint( const_iterator hint, __Args... args ); - - // - // find - // - iterator find( key_type const & k ); - const_iterator find( key_type const & k )const; - - iterator lower_bound( key_type const & k ); - const_iterator lower_bound( key_type const & k )const; - - iterator upper_bound( key_type const & k ); - const_iterator upper_bound( key_type const & k )const; - - std::pair< iterator, iterator > equal_range( key_type const & k ); - std::pair< const_iterator, const_iterator > equal_range( key_type const & k )const; - - // - // count - // - inline std::size_t count( key_type const & k )const; - - // - // operator[] - // - reference operator[]( key_type const & k ); - reference operator[]( key_type && k ); - - // - // at - // - reference at( key_type const & k ); - const_reference at( key_type const & k )const; - - // - // erase - // - std::size_t erase( key_type const & k ); - iterator erase( iterator pos ); - - // - // observers - // - key_compare key_comp()const - { - return _cmp; - } - - value_compare value_comp()const - { - return value_compare( _cmp ); - } - - //allocator_type get_allocator()const - //{ - // return _allocator; - //} - -#ifdef AV_ENABLE_EXTENSIONS - public: -#else - private: -#endif - - // - // extension, flatenize container, enforce merge of _storage with _erased and with _buffer - // - void _merge(); - - // - // extension, faster, non STL compatible version of insert - // - bool _insert( value_type const & value ); - - template< typename __ValueType > - bool _insert( __ValueType && value ); - - // - // extension, faster, non STL compatible version of end, works with _find - // - inline _iterator _end(); - inline _const_iterator _end()const; - - // - // extension, faster, non STL compatible version of find, works with _end - // - _iterator _find( key_type const & k ); - _const_iterator _find( key_type const & k )const; - - // - // extension, faster, non STL compatible version of erase - // - bool _erase( iterator pos ); - -private: - bool validateStorage()const; - bool validateBuffer()const; - bool validateErased()const; - bool validate()const; - - // - // merge - // - void mergeStorageWithBuffer(); - void mergeStorageWithErased(); - - // - // insert - // - template< typename __ValueType > - void pushBack( __ValueType && value ); - - template< typename __ValueType > - bool shouldBePushBack( __ValueType && value )const; - - template< typename __ValueType > - _FindOrInsertToBufferResult - findOrInsertToBuffer( __ValueType && value ); - - // - // insertImpl, function does as little as needed but returns as much data as possible - // - template< typename __ValueType > - _InsertImplResult - insertImpl( __ValueType && value ); - - // - // emplace_impl - // - template< class __Head, class... __Tail > - std::pair< iterator, bool > emplaceImpl( __Head && head, __Tail... tail ); - - // - // erase - // - _TryRemoveBackResult - tryRemoveStorageBack( typename _Storage::iterator pos ); - - // - // tryEraseFromStorage - // - _TryEraseFromStorageResult - tryEraseFromStorage( typename _Storage::iterator pos ); - - // - // isErased - // - bool isErased( typename _Storage::const_iterator iterator )const; - - // - // findImpl, function does as little as needed but returns as much data as possible - // - _FindImplResult - findImpl( key_type const & key ); - - // - // getAllocator (method specialization) - // - //_Allocator getAllocator( _Storage const & ){ return _allocator; } - // - //typename _Allocator::template rebind< typename _Storage::const_iterator >::other - //getAllocator( _Erased const & ) - //{return typename _Allocator::template rebind< typename _Storage::const_iterator >::other( _allocator );} - -public: // public for unit tests only - void dump( int width = -1 )const; - - std::size_t bufferSize()const{ return _buffer.size(); } - std::size_t bufferCapacity()const{ return _buffer.capacity(); } - _Storage const & storage()const{ return _storage; } - - std::size_t storageSize()const{ return _storage.size(); } - std::size_t storageCapacity()const{ return _storage.capacity(); } - _Storage const & buffer()const{ return _buffer; } - - std::size_t erasedSize()const{ return _erased.size(); } - std::size_t erasedCapacity()const{ return _erased.capacity(); } - _Erased const & erased()const{ return _erased; } - - static std::size_t calculateNewBufferCapacity( std::size_t storageSize ); - static std::size_t calculateNewErasedCapacity( std::size_t storageSize ); - static std::size_t calculateNewStorageCapacity( std::size_t storageSize ); - -private: - _Storage _storage; - _Storage _buffer; - _Erased _erased; - - _Cmp _cmp; -}; - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool operator==( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & lhs - , AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & rhs -) -{ - if( lhs.size() != rhs.size() ){ - return false; - } - - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator begin = lhs.begin(); - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator const end = lhs.end(); - - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator begin2 = rhs.begin(); - - for( /*empty*/ ; begin != end ; ++ begin, ++ begin2 ) - { - if( begin->first != begin2->first ){ - return false; - } - - if( begin->second != begin2->second ){ - return false; - } - } - - return true; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool operator!=( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & lhs - , AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & rhs -) -{ - return ! ( lhs == rhs ); -} - -// -// Method Definitions -// - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - _Cmp const & cmp - , _Allocator const & allocator -) - : _cmp( cmp ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( _Allocator const & allocator ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __InputIterator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - __InputIterator first - , __InputIterator last - , _Cmp const & cmp - , _Allocator const & allocator -) - : _cmp( cmp ) -{ - AV_PRECONDITION( std::distance( first, last ) >= 0 ); - - std::size_t const size = std::distance( first, last ); - - if( size > 0 ) - { - reserve( size ); - - for( /*empty*/ ; first != last ; ++ first ){ - insert( * first ); - } - } - - AV_POSTCONDITION( validate() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other -) - : _storage( other._storage ) - , _buffer( other._buffer ) - , _erased( other._erased ) - , _cmp( other._cmp ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other - , _Allocator const & allocator -) - : _storage( other._storage, allocator ) - , _buffer( other._buffer, allocator ) - , _erased( other._erased, allocator ) - , _cmp( other._cmp, allocator ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other -) - : _storage( std::move( other._storage ) ) - , _buffer( std::move( other._buffer ) ) - , _erased( std::move( other._erased ) ) - , _cmp( other._cmp ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other - , _Allocator const & allocator -) - : _storage( std::move( other._storage ) ) - , _buffer( std::move( other._buffer ) ) - , _erased( std::move( other._erased ) ) - , _cmp( other._cmp ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - std::initializer_list< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::value_type > list - , _Cmp const & cmp - , _Allocator const & allocator -) - : _cmp( cmp ) -{ - reserve( list.size() ); - - insert( list ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::~AssocVector() -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::clear() noexcept -{ - util::destroy_range( _storage.begin(), _storage.end() ); - util::destroy_range( _buffer.begin(), _buffer.end() ); - - _storage.setSize( 0 ); - _buffer.setSize( 0 ); - _erased.setSize( 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator > & -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator=( AssocVector const & other ) -{ - AssocVector temp( other ); - temp.swap( * this ); - - return * this; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator > & -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator=( AssocVector && other ) -{ - AssocVector temp( std::move( other ) ); - - temp.swap( * this ); - - return * this; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reserve( std::size_t newStorageCapacity ) -{ - if( _storage.get_allocator().max_size() < newStorageCapacity ){ - throw std::length_error( "AssocVector< _K, _M, _C, _A >::reserve" ); - } - - if( util::less_equal( newStorageCapacity, _storage.capacity() ) ){ - return; - } - - {// _erased - if( _erased.empty() == false ){ - mergeStorageWithErased(); - } - - _erased.reserve( calculateNewErasedCapacity( newStorageCapacity ) ); - - AV_CHECK( _erased.empty() ); - } - - std::size_t const newBufferCapacity - = calculateNewBufferCapacity( newStorageCapacity ); - - std::size_t const newStorageSize = _storage.size() + _buffer.size(); - - { - _Storage newStorage( newStorageCapacity, _storage.get_allocator() ); - _Storage newBuffer( newBufferCapacity, _buffer.get_allocator() ); - - util::move_merge_into_uninitialized( - _storage.begin() - , _storage.end() - , _buffer.begin() - , _buffer.end() - , newStorage.begin() - , value_comp() - ); - - newStorage.swap( _storage ); - newBuffer.swap( _buffer ); - }// call newStorage newBuffer destructors - - _storage.setSize( newStorageSize ); - - AV_POSTCONDITION( _buffer.empty() ); - AV_POSTCONDITION( validate() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::begin() -{ - return iterator( this, _storage.begin(), _buffer.begin(), _erased.begin(), 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rbegin() -{ - return reverse_iterator( end() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::begin()const -{ - return const_iterator( this, _storage.begin(), _buffer.begin(), _erased.begin(), 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::cbegin()const -{ - return begin(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rbegin()const -{ - return const_reverse_iterator( end() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::crbegin()const -{ - return rbegin(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::end() -{ - return iterator( this, _storage.end(), _buffer.end(), _erased.end(), 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rend() -{ - return reverse_iterator( begin() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::end()const -{ - return const_iterator( this, _storage.end(), _buffer.end(), _erased.end(), 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::cend()const -{ - return end(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rend()const -{ - return const_reverse_iterator( begin() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::crend()const -{ - return rend(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_end() -{ - return _iterator( 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_end()const -{ - return _const_iterator( 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::empty()const noexcept -{ - return size() == 0; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::size()const noexcept -{ - return _storage.size() + _buffer.size() - _erased.size(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::capacity()const noexcept -{ - return _storage.capacity() + _buffer.capacity(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::max_size()const noexcept -{ - return _storage.get_allocator().max_size(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( value_type const & value ) -{ - _InsertImplResult const result = insertImpl( value ); - - return std::make_pair( - iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ) - , result._isInserted - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( __ValueType && value ) -{ - _InsertImplResult const result = insertImpl( std::forward< __ValueType >( value ) ); - - return std::make_pair( - iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ) - , result._isInserted - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator hint - , value_type const & value -) -{ - ( void )( hint ); - - _InsertImplResult const result = insertImpl( value ); - - return iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( - const_iterator hint - , __ValueType && value -) -{ - ( void )( hint ); - - _InsertImplResult const result = insertImpl( std::forward< __ValueType >( value ) ); - - return iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( - std::initializer_list< value_type > list -) -{ - insert( list.begin(), list.end() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_insert( value_type const & value ) -{ - return insertImpl( value )._isInserted; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_insert( __ValueType && value ) -{ - return insertImpl( std::forward< __ValueType >( value ) )._isInserted; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_InsertImplResult -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insertImpl( __ValueType && value ) -{ - _Key const & k = value.first; - _Mapped const & m = value.second; - - {//push back to storage - if( shouldBePushBack( value ) ) - { - pushBack( std::forward< __ValueType >( value ) ); - - _InsertImplResult result; - - { - AV_CHECK( _storage.empty() == false ); - - result._inStorage = ( _storage.end() - 1 ); - result._current = ( _storage.end() - 1 ); - } - - result._isInserted = true; - result._inBuffer = 0; - result._inErased = _erased.end(); - - AV_POSTCONDITION( result.validate() ); - AV_POSTCONDITION( validate() ); - - return result; - } - } - - typename _Storage::iterator const greaterEqualInStorage - = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); - - bool const notPresentInStorage - = greaterEqualInStorage == _storage.end() - || key_comp()( k, greaterEqualInStorage->first ); - - {//find or insert to buffer - if( notPresentInStorage ) - { - _FindOrInsertToBufferResult const findOrInsertToBufferResult - = findOrInsertToBuffer( std::forward< __ValueType >( value ) ); - - _InsertImplResult result; - result._isInserted = findOrInsertToBufferResult._isInserted; - - if( findOrInsertToBufferResult._isReallocated ) - { - result._inStorage = 0; - result._inErased = _erased.end(); - } - else - { - result._inStorage = greaterEqualInStorage; - result._inErased = 0; - } - - result._inBuffer = findOrInsertToBufferResult._inBuffer; - result._current = findOrInsertToBufferResult._inBuffer; - - AV_POSTCONDITION( result.validate() ); - AV_POSTCONDITION( validate() ); - - return result; - } - } - - {// check if not erased - typename _Erased::iterator const greaterEqualInErased = std::lower_bound( - _erased.begin() - , _erased.end() - , greaterEqualInStorage - , std::less< typename _Storage::const_iterator >() - ); - - bool const itemNotMarkedAsErased - = greaterEqualInErased == _erased.end() - || std::less< typename _Storage::const_iterator >() - ( greaterEqualInStorage, * greaterEqualInErased ); - - if( itemNotMarkedAsErased ) - {// item is in storage and is not marked as erased - _InsertImplResult result; - result._isInserted = false; - result._inStorage = greaterEqualInStorage; - result._inBuffer = 0; - result._inErased = greaterEqualInErased; - result._current = greaterEqualInStorage; - - AV_POSTCONDITION( result.validate() ); - AV_POSTCONDITION( validate() ); - - return result; - } - else - {// item is in storage but is marked as erased - _erased.erase( greaterEqualInErased ); - - greaterEqualInStorage->second = m; - - _InsertImplResult result; - result._isInserted = true; - result._inStorage = greaterEqualInStorage; - result._inBuffer = 0; - - // greaterEqualInErased is after 'Array::erase' but still valid - result._inErased = greaterEqualInErased; - - result._current = greaterEqualInStorage; - - AV_POSTCONDITION( validate() ); - AV_POSTCONDITION( result.validate() ); - - return result; - } - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename _Iterator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( _Iterator const begin, _Iterator const end ) -{ - for( _Iterator current = begin ; current != end ; ++ current ){ - insert( * current ); - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - class... __Args -> -std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplace( __Args... args ) -{ - return emplaceImpl( args... ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - class... __Args -> -std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplace_hint( const_iterator hint, __Args... args ) -{ - ( void )( hint ); - - return emplaceImpl( args... ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - class __Head - , class... __Tail -> -std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplaceImpl( __Head && head, __Tail... tail ) -{ - _InsertImplResult const result - = insertImpl( value_type_mutable( key_type( head ), mapped_type( tail... ) ) ); - - return std::make_pair( - iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ) - , result._isInserted - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::shouldBePushBack( __ValueType && value )const -{ - bool pushBackToStorage = false; - - {// should be pushed back - if( _storage.empty() ) - { - if( _buffer.empty() ){ - pushBackToStorage = true; - } - else - { - if( _cmp( _buffer.back().first, value.first ) ){ - pushBackToStorage = true; - } - } - } - else - { - if( _buffer.empty() ) - { - if( _cmp( _storage.back().first, value.first ) ){ - pushBackToStorage = true; - } - } - else - { - if( _cmp( _storage.back().first, value.first ) - && _cmp( _buffer.back().first, value.first ) - ){ - pushBackToStorage = true; - } - } - } - } - - return pushBackToStorage; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::pushBack( __ValueType && value ) -{ - if( _storage.size() != _storage.capacity() ){ - _storage.place_back( std::forward< __ValueType >( value ) ); - - AV_POSTCONDITION( validate() ); - - return; - } - - std::size_t const newStorageCapacity = calculateNewStorageCapacity( _storage.capacity() ); - std::size_t const newBufferCapacity = calculateNewBufferCapacity( newStorageCapacity ); - std::size_t const newErasedCapacity = calculateNewErasedCapacity( newStorageCapacity ); - - if( util::less_equal( newStorageCapacity, _storage.capacity() ) ){ - return; - } - - if( _storage.get_allocator().max_size() < newStorageCapacity ){ - throw std::length_error( "AssocVector::reserve" ); - } - - _Storage newStorage( newStorageCapacity, _storage.get_allocator() ); - _Storage newBuffer( newBufferCapacity, _buffer.get_allocator() ); - _Erased newErased( newErasedCapacity, _erased.get_allocator() ); - - {// may throw - iterator current = begin(); - iterator const end = this->end(); - - while( current != end ) - { - // for '++ current' object has still exist and can not be moved before - typename iterator::pointer_mutable const current_raw_ptr = current.getCurrent(); - - ++ current; - - newStorage.place_back( AV_MOVE_IF_NOEXCEPT( * current_raw_ptr ) ); - } - } - - // may throw an exception in __ValueType copy constructor, not exception safe - newStorage.place_back( std::forward< __ValueType >( value ) ); - - newStorage.swap( _storage ); - newBuffer.swap( _buffer ); - newErased.swap( _erased ); - - AV_POSTCONDITION( _buffer.empty() ); - AV_POSTCONDITION( _erased.empty() ); - - AV_POSTCONDITION( validate() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_TryRemoveBackResult -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::tryRemoveStorageBack( - typename _Storage::iterator pos -) -{ - if( pos + 1 != _storage.end() ) - { - _TryRemoveBackResult result; - result._anyItemRemoved = false; - result._erasedItemRemoved = false; - - return result; - } - - _storage.get_allocator().destroy( pos ); - - _storage.setSize( _storage.size() - 1 ); - - if( - _erased.empty() == false - && _erased.back() == pos - ) - { - _erased.setSize( _erased.size() - 1 ); - - _TryRemoveBackResult result; - result._anyItemRemoved = true; - result._erasedItemRemoved = true; - - AV_POSTCONDITION( validate() ); - - return result; - } - - _TryRemoveBackResult result; - result._anyItemRemoved = true; - result._erasedItemRemoved = false; - - AV_POSTCONDITION( validate() ); - - return result; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_TryEraseFromStorageResult -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::tryEraseFromStorage( - typename _Storage::iterator pos -) -{ - std::pair< typename _Erased::iterator, bool > const insertInSortedResult - = array::insert_in_sorted( - _erased - , typename _Storage::const_iterator( pos ) - , std::less< typename _Storage::const_iterator >() - ); - - if( _erased.full() ) - { - mergeStorageWithErased(); - - _TryEraseFromStorageResult result; - result._inErased = _erased.end(); - result._isErased = true; - result._isMerged = true; - - AV_POSTCONDITION( validate() ); - - return result; - } - else - { - _TryEraseFromStorageResult result; - result._inErased = insertInSortedResult.first; - result._isErased = insertInSortedResult.second; - result._isMerged = false; - - AV_POSTCONDITION( validate() ); - - return result; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::isErased( - typename AssocVector::_Storage::const_iterator iterator -)const -{ - typename _Erased::const_iterator const foundInErased = array::binary_search( - _erased.begin() - , _erased.end() - , iterator - , std::less< typename _Storage::const_iterator >() - ); - - return foundInErased != _erased.end(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_FindImplResult -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::findImpl( _Key const & k ) -{ - typename _Storage::iterator const greaterEqualInStorage - = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); - - bool const presentInStorage - = greaterEqualInStorage != _storage.end() - && key_comp()( k, greaterEqualInStorage->first ) == false; - - {// item is in storage, check in erased - if( presentInStorage ) - { - typename _Erased::iterator greaterEqualInErased = std::lower_bound( - _erased.begin() - , _erased.end() - , greaterEqualInStorage - , std::less< typename _Storage::const_iterator >() - ); - - bool const itemNotMarkedAsErased - = greaterEqualInErased == _erased.end() - || std::less< typename _Storage::const_iterator >()( - greaterEqualInStorage - , * greaterEqualInErased - ); - - if( itemNotMarkedAsErased ) - { - _FindImplResult result; - result._inStorage = greaterEqualInStorage; - result._inBuffer = 0; - result._inErased = greaterEqualInErased; - result._current = greaterEqualInStorage; - - AV_POSTCONDITION( result.validate() ); - - return result; - } - else - { - _FindImplResult result; - result._inStorage = 0; - result._inBuffer = 0; - result._inErased = 0; - result._current = 0; - - AV_POSTCONDITION( result.validate() ); - - return result; - } - } - } - - {// check in buffer - typename _Storage::iterator const greaterEqualInBuffer - = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); - - bool const presentInBuffer - = greaterEqualInBuffer != _buffer.end() - && key_comp()( k, greaterEqualInBuffer->first ) == false; - - if( presentInBuffer ) - { - _FindImplResult result; - result._inStorage = greaterEqualInStorage; - result._inBuffer = greaterEqualInBuffer; - result._inErased = 0; - result._current = greaterEqualInBuffer; - - AV_POSTCONDITION( result.validate() ); - - return result; - } - else - { - _FindImplResult result; - result._inStorage = 0; - result._inBuffer = 0; - result._inErased = 0; - result._current = 0; - - AV_POSTCONDITION( result.validate() ); - - return result; - } - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::find( _Key const & k ) -{ - _FindImplResult const result = findImpl( k ); - - if( result._current == 0 ){ - return end(); - } - else - { - return iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ); - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::at( _Key const & k ) -{ - _FindImplResult const result = findImpl( k ); - - if( result._current == 0 ){ - throw std::out_of_range( "AssocVector::at" ); - } - else - { - return result._current->second; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reference -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::at( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->at( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::find( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->find( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::lower_bound( _Key const & k ) -{ - typename _Storage::iterator const greaterEqualInStorage - = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); - - typename _Storage::iterator const greaterEqualInBuffer - = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); - - return iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::lower_bound( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->lower_bound( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::upper_bound( _Key const & k ) -{ - typename _Storage::iterator const greaterInStorage - = std::upper_bound( _storage.begin(), _storage.end(), k, value_comp() ); - - typename _Storage::iterator const greaterInBuffer - = std::upper_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); - - return iterator( this, greaterInStorage, greaterInBuffer, 0, 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::upper_bound( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->upper_bound( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::pair< - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator - , typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::equal_range( _Key const & k ) -{ - typename _Storage::iterator const greaterEqualInStorage - = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); - - bool const notPresentInStorage - = greaterEqualInStorage == _storage.end() - || key_comp()( k, greaterEqualInStorage->first ); - - typename _Storage::iterator const greaterEqualInBuffer - = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); - - bool const notPresentInBuffer - = greaterEqualInBuffer == _buffer.end() - || key_comp()( k, greaterEqualInBuffer->first ); - - if( notPresentInStorage == false ) - { - return std::make_pair( - iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) - , iterator( this, greaterEqualInStorage + 1, greaterEqualInBuffer, 0, 0 ) - ); - } - - if( notPresentInBuffer == false ) - { - return std::make_pair( - iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) - , iterator( this, greaterEqualInStorage, greaterEqualInBuffer + 1, 0, 0 ) - ); - } - - return std::make_pair( - iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) - , iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::pair< - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator - , typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::equal_range( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->equal_range( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_find( _Key const & k ) -{ - return _iterator( findImpl( k )._current ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_find( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->_find( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateStorage()const -{ - if( _storage.size() > _storage.capacity() ) - { - AV_ERROR(); - - return false; - } - - if( std::is_sorted( _storage.begin(), _storage.end(), value_comp() ) == false ) - { - AV_ERROR(); - - return false; - } - - return true; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateBuffer()const -{ - if( _buffer.size() > _buffer.capacity() ) - { - AV_ERROR(); - - return false; - } - - if( _buffer.empty() ){ - return true; - } - - if( std::is_sorted( _buffer.begin(), _buffer.end(), value_comp() ) == false ) - { - AV_ERROR(); - - return false; - } - - if( - util::has_intersection( - _buffer.begin() - , _buffer.end() - , _storage.begin() - , _storage.end() - , value_comp() - ) - ) - { - AV_ERROR(); - - return false; - } - - return true; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateErased()const -{ - if( _erased.size() > _erased.capacity() ) - { - AV_ERROR(); - - return false; - } - - if( _erased.empty() ){ - return true; - } - - if( - std::is_sorted( - _erased.begin() - , _erased.end() - , std::less< typename _Storage::const_iterator >() - ) == false - ) - { - AV_ERROR(); - - return false; - } - - AV_CHECK( _erased.front() >= _storage.begin() ); - AV_CHECK( _erased.back() < _storage.end() ); - - return true; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validate()const -{ - if( calculateNewBufferCapacity( _storage.capacity() ) != _buffer.capacity() ){ - return false; - } - - if( calculateNewErasedCapacity( _storage.capacity() ) != _erased.capacity() ){ - return false; - } - - return validateStorage() && validateBuffer() && validateErased(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_merge() -{ - if( size() > _storage.capacity() ) - { - reserve( calculateNewStorageCapacity( _storage.capacity() ) ); - - return; - } - - if( _erased.empty() == false ){ - mergeStorageWithErased(); - } - - mergeStorageWithBuffer(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator[]( key_type const & k ) -{ - return insert( value_type( k, mapped_type() ) ).first->second; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator[]( key_type && k ) -{ - return insert( value_type( std::move( k ), mapped_type() ) ).first->second; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::count( key_type const & k )const -{ - return _find( k ) ? 1 : 0; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::erase( key_type const & k ) -{ - typename _Storage::iterator const foundInStorage - = array::binary_search( _storage.begin(), _storage.end(), k, value_comp() ); - - {//erase from _buffer - if( foundInStorage == _storage.end() ) - { - typename _Storage::iterator const foundInBuffer - = array::binary_search( _buffer.begin(), _buffer.end(), k, value_comp() ); - - if( foundInBuffer == _buffer.end() ) - { - AV_POSTCONDITION( validate() ); - - return 0; - } - else - { - _buffer.erase( foundInBuffer ); - - AV_POSTCONDITION( validate() ); - - return 1; - } - } - } - - {//erase from back - _TryRemoveBackResult const result = tryRemoveStorageBack( foundInStorage ); - - if( result._anyItemRemoved ) - { - if( result._erasedItemRemoved ) - { - AV_POSTCONDITION( validate() ); - - return 0; - } - else - { - AV_POSTCONDITION( validate() ); - - return 1; - } - } - } - - {//erase from _storage - bool const result = tryEraseFromStorage( foundInStorage )._isErased ? 1 : 0; - - AV_POSTCONDITION( validate() ); - - return result; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::erase( iterator pos ) -{ - if( pos == end() ){ - return end(); - } - - // iterator::get converts : pair< T1, T2 > * -> pair< T1 const, T2 > * - // revert real iterator type: pair< T1 const, T2 > * -> pair< T1, T2 > * - value_type_mutable * const posBase = reinterpret_cast< value_type_mutable * >( pos.get() ); - - _Key const key = pos->first; - - {//erase from _buffer - if( util::is_between( _buffer.begin(), posBase, _buffer.end() ) ) - { - _buffer.erase( posBase ); - - typename _Storage::iterator const greaterEqualInStorage - = pos.getCurrentInStorage() - ? pos.getCurrentInStorage() - : std::lower_bound( _storage.begin(), _storage.end(), key, value_comp() ); - - AV_POSTCONDITION( validate() ); - - return iterator( this, greaterEqualInStorage, posBase, 0, 0 ); - } - } - - {//item not present in container - if( util::is_between( _storage.begin(), posBase, _storage.end() ) == false ){ - return end(); - } - } - - {//erase from back - _TryRemoveBackResult const result = tryRemoveStorageBack( posBase ); - - if( result._anyItemRemoved ) - { - typename _Storage::iterator const greaterEqualInBuffer - = pos.getCurrentInBuffer() - ? pos.getCurrentInBuffer() - : std::lower_bound( _buffer.begin(), _buffer.end(), key, value_comp() ); - - if( greaterEqualInBuffer == _buffer.end() ) - { - AV_POSTCONDITION( validate() ); - - return end(); - } - else - { - AV_POSTCONDITION( validate() ); - - return iterator( this, _storage.end(), greaterEqualInBuffer, 0, 0 ); - } - } - } - - {//erase from _storage - _TryEraseFromStorageResult const result = tryEraseFromStorage( posBase ); - - if( result._isErased == false ){ - return end(); - } - - typename _Storage::iterator const greaterEqualInBuffer - = pos.getCurrentInBuffer() - ? pos.getCurrentInBuffer() - : std::lower_bound( _buffer.begin(), _buffer.end(), key, value_comp() ); - - if( result._isMerged == false ) - { - AV_POSTCONDITION( validate() ); - - return iterator( this, posBase + 1, greaterEqualInBuffer, result._inErased + 1, 0 ); - } - - typename _Storage::iterator const greaterEqualInStorage - = std::lower_bound( _storage.begin(), _storage.end(), key, value_comp() ); - - AV_POSTCONDITION( validate() ); - - return iterator( this, greaterEqualInStorage, greaterEqualInBuffer, _erased.end(), 0 ); - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_erase( iterator pos ) -{ - // iterator::get converts : pair< T1, T2 > * -> pair< T1 const, T2 > * - // revert real iterator type: pair< T1 const, T2 > * -> pair< T1, T2 > * - value_type_mutable * const posBase = reinterpret_cast< value_type_mutable * >( pos.get() ); - - {//erase from _buffer - if( util::is_between( _buffer.begin(), posBase, _buffer.end() ) ) - { - _buffer.erase( posBase ); - - AV_POSTCONDITION( validate() ); - - return true; - } - } - - {//item not present in container - if( util::is_between( _storage.begin(), posBase, _storage.end() ) == false ){ - return false; - } - } - - {//erase from back - _TryRemoveBackResult const result = tryRemoveStorageBack( posBase ); - - if( result._anyItemRemoved ) - { - AV_POSTCONDITION( validate() ); - - return true; - } - } - - {//erase from _storage - bool const result = tryEraseFromStorage( posBase )._isErased; - - AV_POSTCONDITION( validate() ); - - return result; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::swap( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > & other -) noexcept -{ - std::swap( _storage, other._storage ); - std::swap( _buffer, other._buffer ); - std::swap( _erased, other._erased ); - - std::swap( _cmp, other._cmp ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::mergeStorageWithBuffer() -{ - AV_PRECONDITION( _erased.empty() ); - - array::move_merge( _storage, _buffer, value_comp() ); - - util::destroy_range( _buffer.begin(), _buffer.end() ); - - _buffer.setSize( 0 ); - - AV_POSTCONDITION( _buffer.empty() ); - AV_POSTCONDITION( validateStorage() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::mergeStorageWithErased() -{ - typename _Storage::iterator const end = _storage.end(); - - array::erase_removed( _storage, _erased ); - - util::destroy_range( _storage.end(), end ); - - _erased.setSize( 0 ); - - AV_POSTCONDITION( _erased.empty() ); - AV_POSTCONDITION( validateStorage() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_FindOrInsertToBufferResult -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::findOrInsertToBuffer( __ValueType && value ) -{ - typename _Storage::iterator const greaterEqualInBuffer - = std::lower_bound( _buffer.begin(), _buffer.end(), value.first, value_comp() ); - - if( greaterEqualInBuffer != _buffer.end() ) - { - bool const isEqual = _cmp( value.first, greaterEqualInBuffer->first ) == false; - - if( isEqual ) - { - _FindOrInsertToBufferResult result; - result._inBuffer = greaterEqualInBuffer; - result._isInserted = false; - result._isReallocated = false; - - return result; - } - } - - if( _buffer.full() ) - { - _merge(); - - AV_CHECK( _buffer.empty() ); - - _buffer.insert( - _buffer.begin() - , value_type_mutable( std::forward< __ValueType >( value ) ) - ); - - _FindOrInsertToBufferResult result; - result._inBuffer = _buffer.begin(); - result._isInserted = true; - result._isReallocated = true; - - AV_POSTCONDITION( validate() ); - - return result; - } - else - { - _buffer.insert( - greaterEqualInBuffer - , value_type_mutable( std::forward< __ValueType >( value ) ) - ); - - _FindOrInsertToBufferResult result; - result._inBuffer = greaterEqualInBuffer; - result._isInserted = true; - result._isReallocated = false; - - AV_POSTCONDITION( validate() ); - - return result; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewBufferCapacity( - std::size_t storageSize -) -{ - return static_cast< std::size_t >( 1.0 * sqrt( storageSize )); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewErasedCapacity( - std::size_t storageSize -) -{ - return calculateNewBufferCapacity( storageSize ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewStorageCapacity( - std::size_t storageSize -) -{ - if( storageSize == 0 ){ - return 1; - } - else if( storageSize == 1 ){ - // size=1 size=2 - // capacity=1 capacity=2 - // S:[a] S:[ab] - // B:[b] -> reserve -> B:[ ] - // E:[ ] E:[ ] - - return 4; - } - else{ - return 2 * storageSize; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void AssocVector< _Key, _Mapped, _Cmp, _Allocator >::dump( int width )const -{ - std::cout << "storage: "; - for( unsigned i = 0 ; i < _storage.size() ; ++ i ) - { - if( i > 0 && width > 0 && ( i % width == 0 ) ){ - std::cout << "\n "; - } - - std::cout << " (" << _storage[i].first << "," << _storage[i].second << ")"; - } - - std::cout << std::endl << "buffer: "; - for( unsigned i = 0 ; i < _buffer.size() ; ++ i ) - { - if( i > 0 && width > 0 && ( i % width == 0 ) ){ - std::cout << "\n "; - } - - std::cout << " (" << _buffer[i].first << "," << _buffer[i].second << ")"; - } - - std::cout << std::endl << "erased: "; - for( unsigned i = 0 ; i < _erased.size() ; ++ i ) - { - if( i > 0 && width > 0 && ( i % width == 0 ) ){ - std::cout << "\n "; - } - - std::cout << " (" << (*_erased[i]).first << "," << (*_erased[i]).second << ")"; - } - - std::cout << "." << std::endl; -} - -#endif From c8b65cd007524d9c73fc409192513c56f8d387ea Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Mon, 20 Jul 2020 16:56:01 -0500 Subject: [PATCH 41/55] :fire: Delete flat_map --- external/flat | 1 - 1 file changed, 1 deletion(-) delete mode 160000 external/flat diff --git a/external/flat b/external/flat deleted file mode 160000 index 2e8d469f7..000000000 --- a/external/flat +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2e8d469f7933cd640c64c6d8e8ba98f6ed9f2efc From 542198482d6388351ca21564d881eaa89c21d41f Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Mon, 20 Jul 2020 16:56:58 -0500 Subject: [PATCH 42/55] :pencil: External flat_map --- external/flat/LICENSE_1_0.txt | 23 ++ external/flat/README.md | 65 ++++ external/flat/flat_map.hpp | 251 +++++++++++++ external/flat/flat_multimap.hpp | 133 +++++++ external/flat/flat_multiset.hpp | 117 ++++++ external/flat/flat_set.hpp | 129 +++++++ external/flat/impl/class_def.hpp | 60 +++ external/flat/impl/container_traits.hpp | 25 ++ external/flat/impl/flat_impl.hpp | 470 ++++++++++++++++++++++++ 9 files changed, 1273 insertions(+) create mode 100644 external/flat/LICENSE_1_0.txt create mode 100644 external/flat/README.md create mode 100644 external/flat/flat_map.hpp create mode 100644 external/flat/flat_multimap.hpp create mode 100644 external/flat/flat_multiset.hpp create mode 100644 external/flat/flat_set.hpp create mode 100644 external/flat/impl/class_def.hpp create mode 100644 external/flat/impl/container_traits.hpp create mode 100644 external/flat/impl/flat_impl.hpp diff --git a/external/flat/LICENSE_1_0.txt b/external/flat/LICENSE_1_0.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/external/flat/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/external/flat/README.md b/external/flat/README.md new file mode 100644 index 000000000..e3b89eab0 --- /dev/null +++ b/external/flat/README.md @@ -0,0 +1,65 @@ +# C++ Flat Containers Library + +Fast+efficient associative containers using sorted arrays, with an interface based on standard containers. + +This was part of a C++ standard proposal and I recommend that you read it for more details: http://pubby.github.io/proposal.html + +#### Container Adaptors +- `fc::flat_map` +- `fc::flat_multimap` +- `fc::flat_set` +- `fc::flat_multiset` + +#### Class Aliases +- `fc::vector_map` +- `fc::vector_multimap` +- `fc::vector_set` +- `fc::vector_multiset` + +#### New Member Functions +- `has` + - `map.has(key)` returns a pointer to key's mapped value if it exists, otherwise returns null. +- `insert` (delayed sort) + - `map.insert(first, last, fc::delay_sort)` performs insertion with a delayed sort optimization. +- Constructors (delayed sort overload) + - Constructs flat container using a delayed sort optimization. +- Constructors (container construct overload) + - Constructs flat container by forwarding arguments to the underlying container member. + +#### What's an adaptor? + +Container adaptors allow you to use any vector-like sequence container for the underlying storage. +`std::vector` is the natural choice, but classes like `boost::small_vector` and `boost::static_vector` are useful too. Because `std::vector` is the most common in usage, aliases are provided for convenience. + +For basic use, just use the `std::vector` aliases. + +#### The public Members: `container` and `underlying` + +The public member `container` allows access to the underlying storage container. Note that it is the user's responsibility to keep the container in a sorted, unique state. + +Likewise, the public member of iterators: `underlying`, allows access to the underlying storage container's iterators. + +*Example: Delayed sort optimization using `.container`* + + std::flat_multiset set; + while(cpu_temperature() < 100) + set.container.push_back(cpu_temperature()); + std::sort(set.begin(), set.end()); + +#### Const Iteration by Default + +For safety reasons, flat container iterators are const by default. To bypass this safety and get non-const iterators, one can either iterate `.container` or take the `.underlying` of the iterator. + +*Example: Modify values in a way that preserves sortedness* + + for(auto& v : set.container) + v *= 2; + +*Example: Same thing but with `.underlying`* + + for(auto it = v.begin(); it != v.end(); ++it) + (*v.underlying) *= 2; + +#### Helper Types + +The directory `include_extra` contains convenience typedefs for use with Boost.Container. diff --git a/external/flat/flat_map.hpp b/external/flat/flat_map.hpp new file mode 100644 index 000000000..7e6ce507c --- /dev/null +++ b/external/flat/flat_map.hpp @@ -0,0 +1,251 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#ifndef LIB_FLAT_FLAT_MAP_HPP +#define LIB_FLAT_FLAT_MAP_HPP + +#include "impl/flat_impl.hpp" + +namespace fc { +namespace impl { + +template +class flat_map_base +: public flat_container_base +{ +#include "impl/container_traits.hpp" + using mapped_type = typename value_type::second_type; + using B = flat_container_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using value_compare = first_compare; + value_compare value_comp() const { return value_compare(B::key_comp()); } + + using B::insert; + using B::erase; + + // Element access + + mapped_type const* has(key_type const& key) const + { + const_iterator it = self()->find(key); + return it == self()->end() ? nullptr : &it.underlying->second; + } + + mapped_type* has(key_type const& key) + { + iterator it = self()->find(key); + return it == self()->end() ? nullptr : &it.underlying->second; + } + + mapped_type const& at(key_type const& key) const + { + if(mapped_type const* ptr = has(key)) + return *ptr; + throw std::out_of_range("flat_map::at"); + } + + mapped_type& at(key_type const& key) + { + if(mapped_type* ptr = has(key)) + return *ptr; + throw std::out_of_range("flat_map::at"); + } + + mapped_type& operator[](key_type const& key) + { return self()->try_emplace(key).first.underlying->second; } + + mapped_type& operator[](key_type&& key) + { + return self()->try_emplace(std::move(key)).first.underlying->second; + } + + // Modifiers + + std::pair insert(value_type const& value) + { return insert_(value); } + + std::pair insert(value_type&& value) + { return insert_(std::move(value)); } + + template + void insert(InputIt first, InputIt last, delay_sort_t) + { + this->ds_insert_(first, last); + auto it = std::unique( + self()->container.begin(), self()->container.end(), + impl::eq_comp{value_comp()}); + self()->container.erase(it, self()->container.end()); + } + + template + std::pair insert_or_assign(key_type const& key, M&& obj) + { return insert_or_assign_(key, std::forward(obj)); } + + template + std::pair insert_or_assign(key_type&& key, M&& obj) + { return insert_or_assign_(std::move(key), std::forward(obj)); } + + template + std::pair insert_or_assign(const_iterator hint, + key_type const& key, M&& obj) + { return insert_or_assign(key, std::forward(obj)); } + + template + std::pair insert_or_assign(const_iterator hint, + key_type&& key, M&& obj) + { return insert_or_assign(std::move(key), std::forward(obj)); } + + template + std::pair try_emplace(key_type const& key, Args&&... args) + { return try_emplace_(key, std::forward(args)...); } + + template + std::pair try_emplace(key_type&& key, Args&&... args) + { return try_emplace_(std::move(key), std::forward(args)...); } + + template + iterator try_emplace(const_iterator hint, + key_type const& key, Args&&... args) + { return try_emplace_(key, std::forward(args)...).first; } + + template + iterator try_emplace(const_iterator hint, key_type&& key, Args&&... args) + { + return try_emplace_(std::move(key), + std::forward(args)...).first; + } + + size_type erase(key_type const& key) + { + const_iterator it = self()->find(key); + if(it == self()->end()) + return 0; + self()->container.erase(it.underlying); + return 1; + } + + // Lookup + + size_type count(key_type const& key) const + { + return self()->find(key) != self()->end(); + } + +private: + template + std::pair insert_(V&& value) + { + iterator it = self()->lower_bound(value.first); + if(it == self()->end() || self()->value_comp()(value, *it)) + { + it = self()->container.insert(it.underlying, + std::forward(value)); + return std::make_pair(it, true); + } + return std::make_pair(it, false); + } + + template + std::pair insert_or_assign_(K&& key, M&& obj) + { + iterator it = self()->lower_bound(key); + if(it == self()->end() || self()->key_comp()(key, it->first)) + { + it = self()->container.insert(it.underlying, + value_type(std::forward(key), std::forward(obj))); + return std::make_pair(it, true); + } + it.underlying->second = std::forward(obj); + return std::make_pair(it, false); + } + + template + std::pair try_emplace_(K&& key, Args&&... args) + { + iterator it = self()->lower_bound(key); + if(it == self()->end() || self()->key_comp()(key, it->first)) + { + it = self()->container.emplace(it.underlying, + value_type(std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(args)...))); + return std::make_pair(it, true); + } + return std::make_pair(it, false); + } +}; + +template +class flat_map_base> +: public flat_map_base +{ +#include "impl/container_traits.hpp" + using mapped_type = typename value_type::second_type; + using B = flat_map_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + + using B::insert; + using B::count; + + // Modifiers + + template + std::pair insert(P&& value) + { + iterator it = self()->lower_bound(value.first); + if(it == self()->end() || self()->value_comp()(value, *it)) + { + it = self()->container.insert( + it.underlying, std::forward

(value)); + return std::make_pair(it, true); + } + return std::make_pair(it, false); + } + + // Lookup + + template + size_type count(K const& key) const + { + return self()->find(key) != self()->end(); + } +}; + +} // namespace impl + +template> +class flat_map +: public impl::flat_map_base, + typename Container::value_type::first_type, Container, Compare> +{ +#define FLATNAME flat_map +#define FLATKEY typename Container::value_type::first_type +#include "impl/class_def.hpp" +#undef FLATNAME +#undef FLATKEY +}; + +template> +using vector_map = flat_map>, Compare>; + +template +inline bool operator==(const flat_map& lhs, const flat_map& rhs) +{ + return lhs.container == rhs.container; +} +template +inline bool operator!=(const flat_map& lhs, const flat_map& rhs) +{ + return lhs.container != rhs.container; +} + +} // namespace fc + +#endif diff --git a/external/flat/flat_multimap.hpp b/external/flat/flat_multimap.hpp new file mode 100644 index 000000000..169be155e --- /dev/null +++ b/external/flat/flat_multimap.hpp @@ -0,0 +1,133 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#ifndef LIB_FLAT_FLAT_MULTIMAP_HPP +#define LIB_FLAT_FLAT_MULTIMAP_HPP + +#include "impl/flat_impl.hpp" + +namespace fc { +namespace impl { + +template +class flat_multimap_base +: public flat_container_base +{ +#include "impl/container_traits.hpp" + using mapped_type = typename value_type::second_type; + using B = flat_container_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using value_compare = first_compare; + value_compare value_comp() const { return value_compare(B::key_comp()); } + + using B::insert; + using B::erase; + + // Modifiers + + iterator insert(value_type const& value) + { + iterator it = self()->upper_bound(value.first); + return self()->container.insert(it.underlying, value); + } + + iterator insert(value_type&& value) + { + iterator it = self()->upper_bound(value.first); + return self()->container.insert(it.underlying, std::move(value)); + } + + template + void insert(InputIt first, InputIt last, delay_sort_t) + { this->ds_insert_(first, last); } + + size_type erase(key_type const& key) + { + auto it_pair = self()->equal_range(key); + std::size_t ret = std::distance(it_pair.first, it_pair.second); + self()->container.erase(it_pair.first.underlying, + it_pair.second.underlying); + return ret; + } + + // Lookup + + size_type count(key_type const& key) const + { + auto it_pair = self()->equal_range(key); + return std::distance(it_pair.first, it_pair.second); + } + +}; + +template +class flat_multimap_base> +: public flat_multimap_base +{ +#include "impl/container_traits.hpp" + using mapped_type = typename value_type::second_type; + using B = flat_multimap_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + + using B::insert; + using B::count; + + // Modifiers + + template + iterator insert(P&& value) + { + iterator it = self()->upper_bound(value.first); + return self()->container.insert(it.underlying, + std::forward

(value)); + } + + // Lookup + + template + size_type count(K const& key) const + { + auto it_pair = self()->equal_range(key); + return std::distance(it_pair.first, it_pair.second); + } +}; + +} // namespace impl + +template> +class flat_multimap +: public impl::flat_multimap_base, + typename Container::value_type::first_type, Container, Compare> +{ +#define FLATNAME flat_multimap +#define FLATKEY typename Container::value_type::first_type +#include "impl/class_def.hpp" +#undef FLATNAME +#undef FLATKEY +}; + +template> +using vector_multimap + = flat_multimap>, Compare>; + +template +inline bool operator==(const flat_multimap& lhs, const flat_multimap& rhs) +{ + return lhs.container == rhs.container; +} +template +inline bool operator!=(const flat_multimap& lhs, const flat_multimap& rhs) +{ + return lhs.container != rhs.container; +} + +} // namespace fc + +#endif diff --git a/external/flat/flat_multiset.hpp b/external/flat/flat_multiset.hpp new file mode 100644 index 000000000..beca937ff --- /dev/null +++ b/external/flat/flat_multiset.hpp @@ -0,0 +1,117 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#ifndef LIB_FLAT_FLAT_MULTISET_HPP +#define LIB_FLAT_FLAT_MULTISET_HPP + +#include "impl/flat_impl.hpp" + +namespace fc { +namespace impl { + +template +class flat_multiset_base +: public flat_container_base +{ +#include "impl/container_traits.hpp" + using B = flat_container_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using value_compare = Compare; + value_compare value_comp() const { return value_compare(B::key_comp()); } + + using B::insert; + using B::erase; + + // Modifiers + + iterator insert(value_type const& value) + { + iterator it = self()->upper_bound(value); + return self()->container.insert(it.underlying, value); + } + + iterator insert(value_type&& value) + { + iterator it = self()->upper_bound(value); + return self()->container.insert(it.underlying, std::move(value)); + } + + template + void insert(InputIt first, InputIt last, delay_sort_t) + { this->ds_insert_(first, last); } + + size_type erase(key_type const& key) + { + auto it_pair = self()->equal_range(key); + std::size_t ret = std::distance(it_pair.first, it_pair.second); + self()->container.erase(it_pair.first.underlying, + it_pair.second.underlying); + return ret; + } + + // Lookup + + size_type count(key_type const& key) const + { + auto it_pair = self()->equal_range(key); + return std::distance(it_pair.first, it_pair.second); + } +}; + +template +class flat_multiset_base> +: public flat_multiset_base +{ +#include "impl/container_traits.hpp" + using B = flat_multiset_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using B::count; + + // Lookup + + template + size_type count(K const& key) const + { + auto it_pair = self()->equal_range(key); + return std::distance(it_pair.first, it_pair.second); + } +}; + +} // namespace impl + +template> +class flat_multiset +: public impl::flat_multiset_base, + typename Container::value_type, Container, Compare> +{ +#define FLATNAME flat_multiset +#define FLATKEY typename Container::value_type +#include "impl/class_def.hpp" +#undef FLATNAME +#undef FLATKEY +}; + +template> +using vector_multiset = flat_multiset, Compare>; + +template +inline bool operator==(const flat_multiset& lhs, const flat_multiset& rhs) +{ + return lhs.container == rhs.container; +} +template +inline bool operator!=(const flat_multiset& lhs, const flat_multiset& rhs) +{ + return lhs.container != rhs.container; +} + +} // namespace fc + +#endif diff --git a/external/flat/flat_set.hpp b/external/flat/flat_set.hpp new file mode 100644 index 000000000..7f95049cb --- /dev/null +++ b/external/flat/flat_set.hpp @@ -0,0 +1,129 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#ifndef LIB_FLAT_FLAT_SET_HPP +#define LIB_FLAT_FLAT_SET_HPP + +#include "impl/flat_impl.hpp" + +namespace fc { +namespace impl { + +template +class flat_set_base +: public flat_container_base +{ +#include "impl/container_traits.hpp" + using B = flat_container_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using value_compare = Compare; + value_compare value_comp() const { return value_compare(B::key_comp()); } + + using B::insert; + using B::erase; + + // Modifiers + + std::pair insert(value_type const& value) + { return insert_(value); } + + std::pair insert(value_type&& value) + { return insert_(std::move(value)); } + + template + void insert(InputIt first, InputIt last, delay_sort_t) + { + this->ds_insert_(first, last); + auto it = std::unique( + self()->container.begin(), self()->container.end(), + impl::eq_comp{value_comp()}); + self()->container.erase(it, self()->container.end()); + } + + size_type erase(key_type const& key) + { + const_iterator it = self()->find(key); + if(it == self()->end()) + return 0; + self()->container.erase(it.underlying); + return 1; + } + + // Lookup + + size_type count(key_type const& key) const + { + return self()->find(key) != self()->end(); + } + +private: + template + std::pair insert_(V&& value) + { + iterator it = self()->lower_bound(value); + if(it == self()->end() || self()->value_comp()(value, *it)) + { + it = self()->container.insert(it.underlying, + std::forward(value)); + return std::make_pair(it, true); + } + return std::make_pair(it, false); + } +}; + +template +class flat_set_base> +: public flat_set_base +{ +#include "impl/container_traits.hpp" + using B = flat_set_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using B::count; + + // Lookup + + template + size_type count(K const& key) const + { + return self()->find(key) != self()->end(); + } +}; + +} // namespace impl + +template> +class flat_set +: public impl::flat_set_base, + typename Container::value_type, Container, Compare> +{ +#define FLATNAME flat_set +#define FLATKEY typename Container::value_type +#include "impl/class_def.hpp" +#undef FLATNAME +#undef FLATKEY +}; + +template> +using vector_set = flat_set, Compare>; + +template +inline bool operator==(const flat_set& lhs, const flat_set& rhs) +{ + return lhs.container == rhs.container; +} +template +inline bool operator!=(const flat_set& lhs, const flat_set& rhs) +{ + return lhs.container != rhs.container; +} + +} // namespace fc + +#endif diff --git a/external/flat/impl/class_def.hpp b/external/flat/impl/class_def.hpp new file mode 100644 index 000000000..d29e24b2f --- /dev/null +++ b/external/flat/impl/class_def.hpp @@ -0,0 +1,60 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +private: + using D = FLATNAME; + using Key = FLATKEY; +public: +#include "container_traits.hpp" + + FLATNAME() = default; + explicit FLATNAME(Compare const& comp) : comp(comp), container() {} + + template + FLATNAME(InputIt first, InputIt last) + : FLATNAME() { this->insert(first, last); } + + template + FLATNAME(InputIt first, InputIt last, Compare const& comp) + : FLATNAME(comp) { this->insert(first, last); } + + template + FLATNAME(InputIt first, InputIt last, delay_sort_t d) + : FLATNAME() { this->insert(first, last, d); } + + template + FLATNAME(InputIt first, InputIt last, Compare const& comp, delay_sort_t d) + : FLATNAME(comp) { this->insert(first, last, d); } + + FLATNAME(FLATNAME const&) = default; + FLATNAME(FLATNAME&&) = default; + + FLATNAME(std::initializer_list ilist) + : FLATNAME() { this->insert(ilist); } + + FLATNAME(std::initializer_list ilist, delay_sort_t d) + : FLATNAME() { this->insert(ilist, d); } + + FLATNAME(std::initializer_list ilist, + Compare const& comp, delay_sort_t d) + : FLATNAME(comp) { this->insert(ilist, d); } + + template + explicit FLATNAME(container_construct_t, Args&&... args) + : container(std::forward(args)...), comp() {} + + template + FLATNAME(Compare const& comp, container_construct_t, Args&&... args) + : container(std::forward(args)...), comp(comp) {} + + FLATNAME& operator=(FLATNAME const&) = default; + FLATNAME& operator=(FLATNAME&&) = default; + FLATNAME& operator=(std::initializer_list ilist) + { this->clear(); this->insert(ilist); return *this; } + + Container container; + Compare comp; + +#undef FLATNAME +#undef FLATKEY diff --git a/external/flat/impl/container_traits.hpp b/external/flat/impl/container_traits.hpp new file mode 100644 index 000000000..31e16423d --- /dev/null +++ b/external/flat/impl/container_traits.hpp @@ -0,0 +1,25 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +using container_type = Container; +using key_type = Key; +using size_type = typename container_type::size_type; +using difference_type = typename container_type::difference_type; +using value_type = typename container_type::value_type; +using iterator + = impl::flat_iterator< + typename container_type::iterator, + impl::dummy_iterator>; +using const_iterator + = impl::flat_iterator< + typename container_type::const_iterator, + iterator>; +using reverse_iterator + = impl::flat_iterator< + typename container_type::reverse_iterator, + impl::dummy_iterator>; +using const_reverse_iterator + = impl::flat_iterator< + typename container_type::const_reverse_iterator, + reverse_iterator>; diff --git a/external/flat/impl/flat_impl.hpp b/external/flat/impl/flat_impl.hpp new file mode 100644 index 000000000..2d639620e --- /dev/null +++ b/external/flat/impl/flat_impl.hpp @@ -0,0 +1,470 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#ifndef LIB_FLAT_FLAT_IMPL_HPP +#define LIB_FLAT_FLAT_IMPL_HPP + +#include +#include +#include +#include +#include + +namespace fc { + +struct delay_sort_t {}; +constexpr delay_sort_t delay_sort = {}; + +struct container_construct_t {}; +constexpr container_construct_t container_construct = {}; + +namespace impl { + +template +using transparent_key_t = std::reference_wrapper; + +template +struct eq_comp +{ + template + bool operator()(A const& lhs, B const& rhs) + { return !comp(lhs, rhs) && !comp(rhs, lhs); } + Comp comp; +}; + + +template +struct dummy_iterator +{ + dummy_iterator() = delete; + dummy_iterator(dummy_iterator const&) = delete; + It underlying; +}; + +template::iterator_category> +class flat_iterator; + +template +class flat_iterator +{ + using traits = std::iterator_traits; +public: + using difference_type = typename traits::difference_type; + using value_type = typename traits::value_type const; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::random_access_iterator_tag; + + flat_iterator() = default; + flat_iterator(flat_iterator const&) = default; + flat_iterator(flat_iterator&&) = default; + flat_iterator(Convert const& c) : underlying(c.underlying) {} + flat_iterator(Convert&& c) : underlying(std::move(c.underlying)) {} + flat_iterator(It const& underlying) : underlying(underlying) {} + flat_iterator(It&& underlying) : underlying(std::move(underlying)) {} + + flat_iterator& operator=(flat_iterator const& u) = default; + flat_iterator& operator=(flat_iterator&& u) = default; + flat_iterator& operator=(It const& u) + { this->underlying = u; return *this; } + flat_iterator& operator=(It&& u) + { this->underlying = std::move(u); return *this; } + + reference operator*() const { return *underlying; } + pointer operator->() const { return std::addressof(*underlying); } + + flat_iterator& operator++() { ++this->underlying; return *this; } + flat_iterator operator++(int) + { flat_iterator it = *this; ++this->underlying; return it; } + + flat_iterator& operator--() { --this->underlying; return *this; } + flat_iterator operator--(int) + { flat_iterator it = *this; --this->underlying; return it; } + + flat_iterator& operator+=(difference_type d) + { this->underlying += d; return *this; } + flat_iterator& operator-=(difference_type d) + { this->underlying -= d; return *this; } + + flat_iterator operator+(difference_type d) const + { return this->underlying + d; } + flat_iterator operator-(difference_type d) const + { return this->underlying - d; } + + difference_type operator-(flat_iterator const& o) const + { return this->underlying - o.underlying; } + + reference operator[](difference_type d) const { return *(*this + d); } + + auto operator==(flat_iterator const& o) const + { + using namespace std::rel_ops; + return this->underlying == o.underlying; + } + auto operator!=(flat_iterator const& o) const + { + using namespace std::rel_ops; + return this->underlying != o.underlying; + } + auto operator<(flat_iterator const& o) const + { return this->underlying < o.underlying; } + auto operator<=(flat_iterator const& o) const + { return this->underlying <= o.underlying; } + auto operator>(flat_iterator const& o) const + { return this->underlying > o.underlying; } + auto operator>=(flat_iterator const& o) const + { return this->underlying >= o.underlying; } + + It underlying; +}; + +template, typename TransparentKey = void> +struct first_compare +{ + first_compare(const Compare& comp) : + compare(comp) { + } + + bool operator()(Pair const& lhs, Pair const& rhs) const + { + return compare.get()(lhs.first, rhs.first); + } + + bool operator()(typename Pair::first_type const& lhs, Pair const& rhs) const + { + return compare.get()(lhs, rhs.first); + } + + bool operator()(Pair const& lhs, typename Pair::first_type const& rhs) const + { + return compare.get()(lhs.first, rhs); + } + + template + bool operator()(transparent_key_t const& lhs, Pair const& rhs) const + { + return compare.get()(lhs.get(), rhs.first); + } + + template + bool operator()(transparent_key_t const& lhs, typename Pair::first_type const& rhs) const + { + return compare.get()(lhs.get(), rhs); + } + + template + bool operator()(Pair const& lhs, transparent_key_t const& rhs) const + { + return compare.get()(lhs.first, rhs.get()); + } + + template + bool operator()(typename Pair::first_type const& lhs, transparent_key_t const& rhs) const + { + return compare.get()(lhs, rhs.get()); + } + + std::reference_wrapper compare; +}; + +template +class flat_container_base +{ +#include "container_traits.hpp" + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using key_compare = Compare; + const key_compare &key_comp() const { return self()->comp; } + + // Iterators + + const_iterator cbegin() const + noexcept(noexcept(std::declval().container.cbegin())) + { return self()->container.cbegin(); } + const_iterator begin() const + noexcept(noexcept(std::declval().container.begin())) + { return self()->container.begin(); } + iterator begin() + noexcept(noexcept(std::declval().container.begin())) + { return self()->container.begin(); } + + const_iterator cend() const + noexcept(noexcept(std::declval().container.cend())) + { return self()->container.cend(); } + const_iterator end() const + noexcept(noexcept(std::declval().container.end())) + { return self()->container.end(); } + iterator end() + noexcept(noexcept(std::declval().container.end())) + { return self()->container.end(); } + + const_reverse_iterator crbegin() const + noexcept(noexcept(std::declval().container.crbegin())) + { return self()->container.crbegin(); } + const_reverse_iterator rbegin() const + noexcept(noexcept(std::declval().container.rbegin())) + { return self()->container.rbegin(); } + reverse_iterator rbegin() + noexcept(noexcept(std::declval().container.rbegin())) + { return self()->container.rbegin(); } + + const_reverse_iterator crend() const + noexcept(noexcept(std::declval().container.crend())) + { return self()->container.crend(); } + const_reverse_iterator rend() const + noexcept(noexcept(std::declval().container.rend())) + { return self()->container.rend(); } + reverse_iterator rend() + noexcept(noexcept(std::declval().container.rend())) + { return self()->container.rend(); } + + // Capacity + + bool empty() const + noexcept(noexcept(std::declval().container.empty())) + { return self()->container.empty(); } + + size_type size() const + noexcept(noexcept(std::declval().container.size())) + { return self()->container.size(); } + + // Modifiers + + iterator insert(const_iterator hint, value_type const& value) + { return self()->insert(value).first; } + + iterator insert(const_iterator hint, value_type&& value) + { return self()->insert(std::move(value)).first; } + + template + void insert(InputIt first, InputIt last) + { + for(InputIt it = first; it != last; ++it) + self()->insert(*it); + } + + void insert(std::initializer_list ilist) + { self()->insert(ilist.begin(), ilist.end()); } + + void insert(std::initializer_list ilist, delay_sort_t d) + { self()->insert(ilist.begin(), ilist.end(), d); } + + template + auto emplace(Args&&... args) + { return self()->insert(value_type(std::forward(args)...)); } + + template + auto emplace_hint(const_iterator hint, Args&&... args) + { return self()->insert(value_type(std::forward(args)...)); } + + iterator erase(const_iterator pos) + noexcept(noexcept(std::declval().container.erase(pos.underlying))) + { return self()->container.erase(pos.underlying); } + + iterator erase(const_iterator first, const_iterator last) + noexcept(noexcept(std::declval().container.erase(first.underlying, + last.underlying))) + { return self()->container.erase(first.underlying, last.underlying); } + + void clear() + noexcept(noexcept(std::declval().container.clear())) + { self()->container.clear(); } + + void swap(D& other) + noexcept(D::has_noexcept_swap()) + { + using std::swap; + swap(self()->container, other.container); + } + + // Lookup + + const_iterator find(key_type const& key) const + { + const_iterator it = self()->lower_bound(key); + if(it == self()->end() || self()->value_comp()(key, *it)) + return self()->end(); + return it; + } + + iterator find(key_type const& key) + { + iterator it = self()->lower_bound(key); + if(it == self()->end() || self()->value_comp()(key, *it)) + return self()->end(); + return it; + } + + const_iterator lower_bound(key_type const& key) const + { + return std::lower_bound( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + + iterator lower_bound(key_type const& key) + { + return std::lower_bound( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + + const_iterator upper_bound(key_type const& key) const + { + return std::upper_bound( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + + iterator upper_bound(key_type const& key) + { + return std::upper_bound( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + + std::pair + equal_range(key_type const& key) const + { + return std::equal_range( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + + std::pair equal_range(key_type const& key) + { + return std::equal_range( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + +private: + static constexpr bool has_noexcept_swap() + { + using std::swap; + return noexcept(swap(*static_cast(nullptr), + *static_cast(nullptr))); + } + +protected: + template + void ds_insert_(InputIt first, InputIt last) + { + size_type const i = self()->size(); + for(InputIt it = first; it != last; ++it) + self()->container.push_back(*it); + std::sort( + self()->container.begin()+i, + self()->container.end(), + self()->value_comp()); + std::inplace_merge( + self()->container.begin(), + self()->container.begin()+i, + self()->container.end()); + // Note: Not calling unique here. Do it in the caller. + } +}; + +template +class flat_container_base> +: public flat_container_base +{ +#include "container_traits.hpp" + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } + using B = flat_container_base; +public: + + using B::insert; + using B::find; + using B::lower_bound; + using B::upper_bound; + using B::equal_range; + + // Modifiers + + template + iterator insert(const_iterator hint, P&& value) + { return insert(std::forward

(value)).first; } + + // Lookup + + template + const_iterator find(K const& key) const + { + const_iterator it = self()->lower_bound(key); + if (it == self()->end() || self()->value_comp()(std::ref(key), *it)) + return self()->end(); + return it; + } + + template + iterator find(K const& key) + { + iterator it = self()->lower_bound(key); + if (it == self()->end() || self()->value_comp()(std::ref(key), *it)) + return self()->end(); + return it; + } + + template + const_iterator lower_bound(K const& key) const + { + return std::lower_bound( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } + + template + iterator lower_bound(K const& key) + { + return std::lower_bound( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } + + template + const_iterator upper_bound(K const& key) const + { + return std::upper_bound( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } + + template + iterator upper_bound(K const& key) + { + return std::upper_bound( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } + + template + std::pair + equal_range(K const& key) const + { + return std::equal_range( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } + + template + std::pair equal_range(K const& key) + { + return std::equal_range( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } +}; + +} // namespace fc +} // namespace impl + +#endif From 23be1008d6a8747f5078cb9f5748683d02a535bb Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Mon, 20 Jul 2020 17:01:25 -0500 Subject: [PATCH 43/55] :fire: Remove unused includes --- include/node_base.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/node_base.h b/include/node_base.h index 8cacffd2e..b5fe54744 100644 --- a/include/node_base.h +++ b/include/node_base.h @@ -1,22 +1,16 @@ #ifndef MPM_NODE_BASE_H_ #define MPM_NODE_BASE_H_ -#include - #include -#include #include #include #include #include -#include -#include #include #include #include #include -#include #include "data_types.h" #include "function_base.h" From 222ca55f527e6d16e4d7e98b80ceb3ae4787d295 Mon Sep 17 00:00:00 2001 From: Nanda Date: Tue, 21 Jul 2020 10:05:32 -0700 Subject: [PATCH 44/55] :wrench: change flat map initialisation --- include/mpm_properties.h | 22 ++++----- include/node.tcc | 81 +++++++++++++++------------------- include/particles/particle.tcc | 21 +++++---- 3 files changed, 57 insertions(+), 67 deletions(-) diff --git a/include/mpm_properties.h b/include/mpm_properties.h index f3aea193e..4e3568667 100644 --- a/include/mpm_properties.h +++ b/include/mpm_properties.h @@ -5,20 +5,20 @@ namespace mpm { namespace properties { //! Scalar Properties enum Scalar : unsigned int { - Mass = 0, - Volume = 1, - MassDensity = 2, - MassPressure = 3, - Pressure = 4 + Mass, + Volume, + MassDensity, + MassPressure, + Pressure }; //! Vector Properties enum Vector : unsigned int { - Displacement = 0, - Velocity = 1, - Acceleration = 2, - Momentum = 3, - ExternalForce = 4, - InternalForce = 5 + Displacement, + Velocity, + Acceleration, + Momentum, + ExternalForce, + InternalForce }; } // namespace properties } // namespace mpm diff --git a/include/node.tcc b/include/node.tcc index 7d3051e47..996f259fc 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -19,38 +19,44 @@ mpm::Node::Node( concentrated_force_.setZero(); // Initialize scalar properties - scalar_properties_.reserve(5); // Mass - scalar_properties_.emplace_back(Eigen::Matrix::Zero()); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::Mass, + Eigen::Matrix::Zero())); // Volume - scalar_properties_.emplace_back(Eigen::Matrix::Zero()); - // MassDensity - scalar_properties_.emplace_back(Eigen::Matrix::Zero()); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::Volume, + Eigen::Matrix::Zero())); // MassPressure - scalar_properties_.emplace_back(Eigen::Matrix::Zero()); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::MassPressure, + Eigen::Matrix::Zero())); // Pressure - scalar_properties_.emplace_back(Eigen::Matrix::Zero()); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::Pressure, + Eigen::Matrix::Zero())); // Initialize vector properties - vector_properties_.resize(6); - // Displacement - vector_properties_.emplace_back( - Eigen::Matrix::Zero()); // Velocity - vector_properties_.emplace_back( - Eigen::Matrix::Zero()); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::Velocity, + Eigen::Matrix::Zero())); // Acceleration - vector_properties_.emplace_back( - Eigen::Matrix::Zero()); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::Acceleration, + Eigen::Matrix::Zero())); // Momentum - vector_properties_.emplace_back( - Eigen::Matrix::Zero()); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::Momentum, + Eigen::Matrix::Zero())); // ExternalForce - vector_properties_.emplace_back( - Eigen::Matrix::Zero()); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::ExternalForce, + Eigen::Matrix::Zero())); // InternalForce - vector_properties_.emplace_back( - Eigen::Matrix::Zero()); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::InternalForce, + Eigen::Matrix::Zero())); this->initialise(); } @@ -61,32 +67,17 @@ void mpm::Node::initialise() noexcept { status_ = false; // Initialise nodal scalar properties - scalar_properties_.resize(5); - scalar_properties_.at(mpm::properties::Scalar::Mass) = - Eigen::Matrix::Zero(); - scalar_properties_.at(mpm::properties::Scalar::Volume) = - Eigen::Matrix::Zero(); - scalar_properties_.at(mpm::properties::Scalar::MassDensity) = - Eigen::Matrix::Zero(); - scalar_properties_.at(mpm::properties::Scalar::MassPressure) = - Eigen::Matrix::Zero(); - scalar_properties_.at(mpm::properties::Scalar::Pressure) = - Eigen::Matrix::Zero(); + scalar_properties_.at(mpm::properties::Scalar::Mass).setZero(); + scalar_properties_.at(mpm::properties::Scalar::Volume).setZero(); + scalar_properties_.at(mpm::properties::Scalar::MassPressure).setZero(); + scalar_properties_.at(mpm::properties::Scalar::Pressure).setZero(); // Initialise nodal vector properties - vector_properties_.resize(6); - vector_properties_.at(mpm::properties::Vector::Displacement) = - Eigen::Matrix::Zero(); - vector_properties_.at(mpm::properties::Vector::Velocity) = - Eigen::Matrix::Zero(); - vector_properties_.at(mpm::properties::Vector::Acceleration) = - Eigen::Matrix::Zero(); - vector_properties_.at(mpm::properties::Vector::Momentum) = - Eigen::Matrix::Zero(); - vector_properties_.at(mpm::properties::Vector::ExternalForce) = - Eigen::Matrix::Zero(); - vector_properties_.at(mpm::properties::Vector::InternalForce) = - Eigen::Matrix::Zero(); + vector_properties_.at(mpm::properties::Vector::Velocity).setZero(); + vector_properties_.at(mpm::properties::Vector::Acceleration).setZero(); + vector_properties_.at(mpm::properties::Vector::Momentum).setZero(); + vector_properties_.at(mpm::properties::Vector::ExternalForce).setZero(); + vector_properties_.at(mpm::properties::Vector::InternalForce).setZero(); // Initialise variables for contact material_ids_.clear(); diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 2ce5bc060..0d5d3c680 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -9,8 +9,6 @@ mpm::Particle::Particle(Index id, const VectorDim& coord) nodes_.clear(); // Set material pointer to null material_ = nullptr; - scalar_properties_.resize(3); - vector_properties_.resize(3); // Logger std::string logger = "particle" + std::to_string(Tdim) + "d::" + std::to_string(id); @@ -241,17 +239,18 @@ void mpm::Particle::initialise() { volumetric_strain_centroid_ = 0.; // Initialize scalar properties - scalar_properties_.resize(3); - scalar_properties_.at(mpm::properties::Scalar::Mass) = 0.; - scalar_properties_.at(mpm::properties::Scalar::MassDensity) = 0.; - scalar_properties_.at(mpm::properties::Scalar::Volume) = - std::numeric_limits::max(); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::Mass, double(0.))); + scalar_properties_.emplace( + std::make_pair(mpm::properties::Scalar::MassDensity, double(0.))); + scalar_properties_.emplace(std::make_pair( + mpm::properties::Scalar::Volume, std::numeric_limits::max())); // Initialize vector properties - vector_properties_.resize(2); - vector_properties_.at(mpm::properties::Vector::Displacement) = - VectorDim::Zero(); - vector_properties_.at(mpm::properties::Vector::Velocity) = VectorDim::Zero(); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::Displacement, VectorDim::Zero())); + vector_properties_.emplace( + std::make_pair(mpm::properties::Vector::Velocity, VectorDim::Zero())); // Initialize vector data properties this->properties_["stresses"] = [&]() { return stress(); }; From da461c310ec3521837dbae3441a08676b73bd693 Mon Sep 17 00:00:00 2001 From: Krishna Kumar Date: Mon, 20 Jul 2020 16:56:58 -0500 Subject: [PATCH 45/55] :pencil: External flat_map --- external/flat/LICENSE_1_0.txt | 23 ++ external/flat/README.md | 65 ++++ external/flat/flat_map.hpp | 251 +++++++++++++ external/flat/flat_multimap.hpp | 133 +++++++ external/flat/flat_multiset.hpp | 117 ++++++ external/flat/flat_set.hpp | 129 +++++++ external/flat/impl/class_def.hpp | 60 +++ external/flat/impl/container_traits.hpp | 25 ++ external/flat/impl/flat_impl.hpp | 470 ++++++++++++++++++++++++ 9 files changed, 1273 insertions(+) create mode 100644 external/flat/LICENSE_1_0.txt create mode 100644 external/flat/README.md create mode 100644 external/flat/flat_map.hpp create mode 100644 external/flat/flat_multimap.hpp create mode 100644 external/flat/flat_multiset.hpp create mode 100644 external/flat/flat_set.hpp create mode 100644 external/flat/impl/class_def.hpp create mode 100644 external/flat/impl/container_traits.hpp create mode 100644 external/flat/impl/flat_impl.hpp diff --git a/external/flat/LICENSE_1_0.txt b/external/flat/LICENSE_1_0.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/external/flat/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/external/flat/README.md b/external/flat/README.md new file mode 100644 index 000000000..e3b89eab0 --- /dev/null +++ b/external/flat/README.md @@ -0,0 +1,65 @@ +# C++ Flat Containers Library + +Fast+efficient associative containers using sorted arrays, with an interface based on standard containers. + +This was part of a C++ standard proposal and I recommend that you read it for more details: http://pubby.github.io/proposal.html + +#### Container Adaptors +- `fc::flat_map` +- `fc::flat_multimap` +- `fc::flat_set` +- `fc::flat_multiset` + +#### Class Aliases +- `fc::vector_map` +- `fc::vector_multimap` +- `fc::vector_set` +- `fc::vector_multiset` + +#### New Member Functions +- `has` + - `map.has(key)` returns a pointer to key's mapped value if it exists, otherwise returns null. +- `insert` (delayed sort) + - `map.insert(first, last, fc::delay_sort)` performs insertion with a delayed sort optimization. +- Constructors (delayed sort overload) + - Constructs flat container using a delayed sort optimization. +- Constructors (container construct overload) + - Constructs flat container by forwarding arguments to the underlying container member. + +#### What's an adaptor? + +Container adaptors allow you to use any vector-like sequence container for the underlying storage. +`std::vector` is the natural choice, but classes like `boost::small_vector` and `boost::static_vector` are useful too. Because `std::vector` is the most common in usage, aliases are provided for convenience. + +For basic use, just use the `std::vector` aliases. + +#### The public Members: `container` and `underlying` + +The public member `container` allows access to the underlying storage container. Note that it is the user's responsibility to keep the container in a sorted, unique state. + +Likewise, the public member of iterators: `underlying`, allows access to the underlying storage container's iterators. + +*Example: Delayed sort optimization using `.container`* + + std::flat_multiset set; + while(cpu_temperature() < 100) + set.container.push_back(cpu_temperature()); + std::sort(set.begin(), set.end()); + +#### Const Iteration by Default + +For safety reasons, flat container iterators are const by default. To bypass this safety and get non-const iterators, one can either iterate `.container` or take the `.underlying` of the iterator. + +*Example: Modify values in a way that preserves sortedness* + + for(auto& v : set.container) + v *= 2; + +*Example: Same thing but with `.underlying`* + + for(auto it = v.begin(); it != v.end(); ++it) + (*v.underlying) *= 2; + +#### Helper Types + +The directory `include_extra` contains convenience typedefs for use with Boost.Container. diff --git a/external/flat/flat_map.hpp b/external/flat/flat_map.hpp new file mode 100644 index 000000000..7e6ce507c --- /dev/null +++ b/external/flat/flat_map.hpp @@ -0,0 +1,251 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#ifndef LIB_FLAT_FLAT_MAP_HPP +#define LIB_FLAT_FLAT_MAP_HPP + +#include "impl/flat_impl.hpp" + +namespace fc { +namespace impl { + +template +class flat_map_base +: public flat_container_base +{ +#include "impl/container_traits.hpp" + using mapped_type = typename value_type::second_type; + using B = flat_container_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using value_compare = first_compare; + value_compare value_comp() const { return value_compare(B::key_comp()); } + + using B::insert; + using B::erase; + + // Element access + + mapped_type const* has(key_type const& key) const + { + const_iterator it = self()->find(key); + return it == self()->end() ? nullptr : &it.underlying->second; + } + + mapped_type* has(key_type const& key) + { + iterator it = self()->find(key); + return it == self()->end() ? nullptr : &it.underlying->second; + } + + mapped_type const& at(key_type const& key) const + { + if(mapped_type const* ptr = has(key)) + return *ptr; + throw std::out_of_range("flat_map::at"); + } + + mapped_type& at(key_type const& key) + { + if(mapped_type* ptr = has(key)) + return *ptr; + throw std::out_of_range("flat_map::at"); + } + + mapped_type& operator[](key_type const& key) + { return self()->try_emplace(key).first.underlying->second; } + + mapped_type& operator[](key_type&& key) + { + return self()->try_emplace(std::move(key)).first.underlying->second; + } + + // Modifiers + + std::pair insert(value_type const& value) + { return insert_(value); } + + std::pair insert(value_type&& value) + { return insert_(std::move(value)); } + + template + void insert(InputIt first, InputIt last, delay_sort_t) + { + this->ds_insert_(first, last); + auto it = std::unique( + self()->container.begin(), self()->container.end(), + impl::eq_comp{value_comp()}); + self()->container.erase(it, self()->container.end()); + } + + template + std::pair insert_or_assign(key_type const& key, M&& obj) + { return insert_or_assign_(key, std::forward(obj)); } + + template + std::pair insert_or_assign(key_type&& key, M&& obj) + { return insert_or_assign_(std::move(key), std::forward(obj)); } + + template + std::pair insert_or_assign(const_iterator hint, + key_type const& key, M&& obj) + { return insert_or_assign(key, std::forward(obj)); } + + template + std::pair insert_or_assign(const_iterator hint, + key_type&& key, M&& obj) + { return insert_or_assign(std::move(key), std::forward(obj)); } + + template + std::pair try_emplace(key_type const& key, Args&&... args) + { return try_emplace_(key, std::forward(args)...); } + + template + std::pair try_emplace(key_type&& key, Args&&... args) + { return try_emplace_(std::move(key), std::forward(args)...); } + + template + iterator try_emplace(const_iterator hint, + key_type const& key, Args&&... args) + { return try_emplace_(key, std::forward(args)...).first; } + + template + iterator try_emplace(const_iterator hint, key_type&& key, Args&&... args) + { + return try_emplace_(std::move(key), + std::forward(args)...).first; + } + + size_type erase(key_type const& key) + { + const_iterator it = self()->find(key); + if(it == self()->end()) + return 0; + self()->container.erase(it.underlying); + return 1; + } + + // Lookup + + size_type count(key_type const& key) const + { + return self()->find(key) != self()->end(); + } + +private: + template + std::pair insert_(V&& value) + { + iterator it = self()->lower_bound(value.first); + if(it == self()->end() || self()->value_comp()(value, *it)) + { + it = self()->container.insert(it.underlying, + std::forward(value)); + return std::make_pair(it, true); + } + return std::make_pair(it, false); + } + + template + std::pair insert_or_assign_(K&& key, M&& obj) + { + iterator it = self()->lower_bound(key); + if(it == self()->end() || self()->key_comp()(key, it->first)) + { + it = self()->container.insert(it.underlying, + value_type(std::forward(key), std::forward(obj))); + return std::make_pair(it, true); + } + it.underlying->second = std::forward(obj); + return std::make_pair(it, false); + } + + template + std::pair try_emplace_(K&& key, Args&&... args) + { + iterator it = self()->lower_bound(key); + if(it == self()->end() || self()->key_comp()(key, it->first)) + { + it = self()->container.emplace(it.underlying, + value_type(std::piecewise_construct, + std::forward_as_tuple(std::forward(key)), + std::forward_as_tuple(std::forward(args)...))); + return std::make_pair(it, true); + } + return std::make_pair(it, false); + } +}; + +template +class flat_map_base> +: public flat_map_base +{ +#include "impl/container_traits.hpp" + using mapped_type = typename value_type::second_type; + using B = flat_map_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + + using B::insert; + using B::count; + + // Modifiers + + template + std::pair insert(P&& value) + { + iterator it = self()->lower_bound(value.first); + if(it == self()->end() || self()->value_comp()(value, *it)) + { + it = self()->container.insert( + it.underlying, std::forward

(value)); + return std::make_pair(it, true); + } + return std::make_pair(it, false); + } + + // Lookup + + template + size_type count(K const& key) const + { + return self()->find(key) != self()->end(); + } +}; + +} // namespace impl + +template> +class flat_map +: public impl::flat_map_base, + typename Container::value_type::first_type, Container, Compare> +{ +#define FLATNAME flat_map +#define FLATKEY typename Container::value_type::first_type +#include "impl/class_def.hpp" +#undef FLATNAME +#undef FLATKEY +}; + +template> +using vector_map = flat_map>, Compare>; + +template +inline bool operator==(const flat_map& lhs, const flat_map& rhs) +{ + return lhs.container == rhs.container; +} +template +inline bool operator!=(const flat_map& lhs, const flat_map& rhs) +{ + return lhs.container != rhs.container; +} + +} // namespace fc + +#endif diff --git a/external/flat/flat_multimap.hpp b/external/flat/flat_multimap.hpp new file mode 100644 index 000000000..169be155e --- /dev/null +++ b/external/flat/flat_multimap.hpp @@ -0,0 +1,133 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#ifndef LIB_FLAT_FLAT_MULTIMAP_HPP +#define LIB_FLAT_FLAT_MULTIMAP_HPP + +#include "impl/flat_impl.hpp" + +namespace fc { +namespace impl { + +template +class flat_multimap_base +: public flat_container_base +{ +#include "impl/container_traits.hpp" + using mapped_type = typename value_type::second_type; + using B = flat_container_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using value_compare = first_compare; + value_compare value_comp() const { return value_compare(B::key_comp()); } + + using B::insert; + using B::erase; + + // Modifiers + + iterator insert(value_type const& value) + { + iterator it = self()->upper_bound(value.first); + return self()->container.insert(it.underlying, value); + } + + iterator insert(value_type&& value) + { + iterator it = self()->upper_bound(value.first); + return self()->container.insert(it.underlying, std::move(value)); + } + + template + void insert(InputIt first, InputIt last, delay_sort_t) + { this->ds_insert_(first, last); } + + size_type erase(key_type const& key) + { + auto it_pair = self()->equal_range(key); + std::size_t ret = std::distance(it_pair.first, it_pair.second); + self()->container.erase(it_pair.first.underlying, + it_pair.second.underlying); + return ret; + } + + // Lookup + + size_type count(key_type const& key) const + { + auto it_pair = self()->equal_range(key); + return std::distance(it_pair.first, it_pair.second); + } + +}; + +template +class flat_multimap_base> +: public flat_multimap_base +{ +#include "impl/container_traits.hpp" + using mapped_type = typename value_type::second_type; + using B = flat_multimap_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + + using B::insert; + using B::count; + + // Modifiers + + template + iterator insert(P&& value) + { + iterator it = self()->upper_bound(value.first); + return self()->container.insert(it.underlying, + std::forward

(value)); + } + + // Lookup + + template + size_type count(K const& key) const + { + auto it_pair = self()->equal_range(key); + return std::distance(it_pair.first, it_pair.second); + } +}; + +} // namespace impl + +template> +class flat_multimap +: public impl::flat_multimap_base, + typename Container::value_type::first_type, Container, Compare> +{ +#define FLATNAME flat_multimap +#define FLATKEY typename Container::value_type::first_type +#include "impl/class_def.hpp" +#undef FLATNAME +#undef FLATKEY +}; + +template> +using vector_multimap + = flat_multimap>, Compare>; + +template +inline bool operator==(const flat_multimap& lhs, const flat_multimap& rhs) +{ + return lhs.container == rhs.container; +} +template +inline bool operator!=(const flat_multimap& lhs, const flat_multimap& rhs) +{ + return lhs.container != rhs.container; +} + +} // namespace fc + +#endif diff --git a/external/flat/flat_multiset.hpp b/external/flat/flat_multiset.hpp new file mode 100644 index 000000000..beca937ff --- /dev/null +++ b/external/flat/flat_multiset.hpp @@ -0,0 +1,117 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#ifndef LIB_FLAT_FLAT_MULTISET_HPP +#define LIB_FLAT_FLAT_MULTISET_HPP + +#include "impl/flat_impl.hpp" + +namespace fc { +namespace impl { + +template +class flat_multiset_base +: public flat_container_base +{ +#include "impl/container_traits.hpp" + using B = flat_container_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using value_compare = Compare; + value_compare value_comp() const { return value_compare(B::key_comp()); } + + using B::insert; + using B::erase; + + // Modifiers + + iterator insert(value_type const& value) + { + iterator it = self()->upper_bound(value); + return self()->container.insert(it.underlying, value); + } + + iterator insert(value_type&& value) + { + iterator it = self()->upper_bound(value); + return self()->container.insert(it.underlying, std::move(value)); + } + + template + void insert(InputIt first, InputIt last, delay_sort_t) + { this->ds_insert_(first, last); } + + size_type erase(key_type const& key) + { + auto it_pair = self()->equal_range(key); + std::size_t ret = std::distance(it_pair.first, it_pair.second); + self()->container.erase(it_pair.first.underlying, + it_pair.second.underlying); + return ret; + } + + // Lookup + + size_type count(key_type const& key) const + { + auto it_pair = self()->equal_range(key); + return std::distance(it_pair.first, it_pair.second); + } +}; + +template +class flat_multiset_base> +: public flat_multiset_base +{ +#include "impl/container_traits.hpp" + using B = flat_multiset_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using B::count; + + // Lookup + + template + size_type count(K const& key) const + { + auto it_pair = self()->equal_range(key); + return std::distance(it_pair.first, it_pair.second); + } +}; + +} // namespace impl + +template> +class flat_multiset +: public impl::flat_multiset_base, + typename Container::value_type, Container, Compare> +{ +#define FLATNAME flat_multiset +#define FLATKEY typename Container::value_type +#include "impl/class_def.hpp" +#undef FLATNAME +#undef FLATKEY +}; + +template> +using vector_multiset = flat_multiset, Compare>; + +template +inline bool operator==(const flat_multiset& lhs, const flat_multiset& rhs) +{ + return lhs.container == rhs.container; +} +template +inline bool operator!=(const flat_multiset& lhs, const flat_multiset& rhs) +{ + return lhs.container != rhs.container; +} + +} // namespace fc + +#endif diff --git a/external/flat/flat_set.hpp b/external/flat/flat_set.hpp new file mode 100644 index 000000000..7f95049cb --- /dev/null +++ b/external/flat/flat_set.hpp @@ -0,0 +1,129 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#ifndef LIB_FLAT_FLAT_SET_HPP +#define LIB_FLAT_FLAT_SET_HPP + +#include "impl/flat_impl.hpp" + +namespace fc { +namespace impl { + +template +class flat_set_base +: public flat_container_base +{ +#include "impl/container_traits.hpp" + using B = flat_container_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using value_compare = Compare; + value_compare value_comp() const { return value_compare(B::key_comp()); } + + using B::insert; + using B::erase; + + // Modifiers + + std::pair insert(value_type const& value) + { return insert_(value); } + + std::pair insert(value_type&& value) + { return insert_(std::move(value)); } + + template + void insert(InputIt first, InputIt last, delay_sort_t) + { + this->ds_insert_(first, last); + auto it = std::unique( + self()->container.begin(), self()->container.end(), + impl::eq_comp{value_comp()}); + self()->container.erase(it, self()->container.end()); + } + + size_type erase(key_type const& key) + { + const_iterator it = self()->find(key); + if(it == self()->end()) + return 0; + self()->container.erase(it.underlying); + return 1; + } + + // Lookup + + size_type count(key_type const& key) const + { + return self()->find(key) != self()->end(); + } + +private: + template + std::pair insert_(V&& value) + { + iterator it = self()->lower_bound(value); + if(it == self()->end() || self()->value_comp()(value, *it)) + { + it = self()->container.insert(it.underlying, + std::forward(value)); + return std::make_pair(it, true); + } + return std::make_pair(it, false); + } +}; + +template +class flat_set_base> +: public flat_set_base +{ +#include "impl/container_traits.hpp" + using B = flat_set_base; + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using B::count; + + // Lookup + + template + size_type count(K const& key) const + { + return self()->find(key) != self()->end(); + } +}; + +} // namespace impl + +template> +class flat_set +: public impl::flat_set_base, + typename Container::value_type, Container, Compare> +{ +#define FLATNAME flat_set +#define FLATKEY typename Container::value_type +#include "impl/class_def.hpp" +#undef FLATNAME +#undef FLATKEY +}; + +template> +using vector_set = flat_set, Compare>; + +template +inline bool operator==(const flat_set& lhs, const flat_set& rhs) +{ + return lhs.container == rhs.container; +} +template +inline bool operator!=(const flat_set& lhs, const flat_set& rhs) +{ + return lhs.container != rhs.container; +} + +} // namespace fc + +#endif diff --git a/external/flat/impl/class_def.hpp b/external/flat/impl/class_def.hpp new file mode 100644 index 000000000..d29e24b2f --- /dev/null +++ b/external/flat/impl/class_def.hpp @@ -0,0 +1,60 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +private: + using D = FLATNAME; + using Key = FLATKEY; +public: +#include "container_traits.hpp" + + FLATNAME() = default; + explicit FLATNAME(Compare const& comp) : comp(comp), container() {} + + template + FLATNAME(InputIt first, InputIt last) + : FLATNAME() { this->insert(first, last); } + + template + FLATNAME(InputIt first, InputIt last, Compare const& comp) + : FLATNAME(comp) { this->insert(first, last); } + + template + FLATNAME(InputIt first, InputIt last, delay_sort_t d) + : FLATNAME() { this->insert(first, last, d); } + + template + FLATNAME(InputIt first, InputIt last, Compare const& comp, delay_sort_t d) + : FLATNAME(comp) { this->insert(first, last, d); } + + FLATNAME(FLATNAME const&) = default; + FLATNAME(FLATNAME&&) = default; + + FLATNAME(std::initializer_list ilist) + : FLATNAME() { this->insert(ilist); } + + FLATNAME(std::initializer_list ilist, delay_sort_t d) + : FLATNAME() { this->insert(ilist, d); } + + FLATNAME(std::initializer_list ilist, + Compare const& comp, delay_sort_t d) + : FLATNAME(comp) { this->insert(ilist, d); } + + template + explicit FLATNAME(container_construct_t, Args&&... args) + : container(std::forward(args)...), comp() {} + + template + FLATNAME(Compare const& comp, container_construct_t, Args&&... args) + : container(std::forward(args)...), comp(comp) {} + + FLATNAME& operator=(FLATNAME const&) = default; + FLATNAME& operator=(FLATNAME&&) = default; + FLATNAME& operator=(std::initializer_list ilist) + { this->clear(); this->insert(ilist); return *this; } + + Container container; + Compare comp; + +#undef FLATNAME +#undef FLATKEY diff --git a/external/flat/impl/container_traits.hpp b/external/flat/impl/container_traits.hpp new file mode 100644 index 000000000..31e16423d --- /dev/null +++ b/external/flat/impl/container_traits.hpp @@ -0,0 +1,25 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +using container_type = Container; +using key_type = Key; +using size_type = typename container_type::size_type; +using difference_type = typename container_type::difference_type; +using value_type = typename container_type::value_type; +using iterator + = impl::flat_iterator< + typename container_type::iterator, + impl::dummy_iterator>; +using const_iterator + = impl::flat_iterator< + typename container_type::const_iterator, + iterator>; +using reverse_iterator + = impl::flat_iterator< + typename container_type::reverse_iterator, + impl::dummy_iterator>; +using const_reverse_iterator + = impl::flat_iterator< + typename container_type::const_reverse_iterator, + reverse_iterator>; diff --git a/external/flat/impl/flat_impl.hpp b/external/flat/impl/flat_impl.hpp new file mode 100644 index 000000000..2d639620e --- /dev/null +++ b/external/flat/impl/flat_impl.hpp @@ -0,0 +1,470 @@ +// Copyright Pubby 2016 +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#ifndef LIB_FLAT_FLAT_IMPL_HPP +#define LIB_FLAT_FLAT_IMPL_HPP + +#include +#include +#include +#include +#include + +namespace fc { + +struct delay_sort_t {}; +constexpr delay_sort_t delay_sort = {}; + +struct container_construct_t {}; +constexpr container_construct_t container_construct = {}; + +namespace impl { + +template +using transparent_key_t = std::reference_wrapper; + +template +struct eq_comp +{ + template + bool operator()(A const& lhs, B const& rhs) + { return !comp(lhs, rhs) && !comp(rhs, lhs); } + Comp comp; +}; + + +template +struct dummy_iterator +{ + dummy_iterator() = delete; + dummy_iterator(dummy_iterator const&) = delete; + It underlying; +}; + +template::iterator_category> +class flat_iterator; + +template +class flat_iterator +{ + using traits = std::iterator_traits; +public: + using difference_type = typename traits::difference_type; + using value_type = typename traits::value_type const; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::random_access_iterator_tag; + + flat_iterator() = default; + flat_iterator(flat_iterator const&) = default; + flat_iterator(flat_iterator&&) = default; + flat_iterator(Convert const& c) : underlying(c.underlying) {} + flat_iterator(Convert&& c) : underlying(std::move(c.underlying)) {} + flat_iterator(It const& underlying) : underlying(underlying) {} + flat_iterator(It&& underlying) : underlying(std::move(underlying)) {} + + flat_iterator& operator=(flat_iterator const& u) = default; + flat_iterator& operator=(flat_iterator&& u) = default; + flat_iterator& operator=(It const& u) + { this->underlying = u; return *this; } + flat_iterator& operator=(It&& u) + { this->underlying = std::move(u); return *this; } + + reference operator*() const { return *underlying; } + pointer operator->() const { return std::addressof(*underlying); } + + flat_iterator& operator++() { ++this->underlying; return *this; } + flat_iterator operator++(int) + { flat_iterator it = *this; ++this->underlying; return it; } + + flat_iterator& operator--() { --this->underlying; return *this; } + flat_iterator operator--(int) + { flat_iterator it = *this; --this->underlying; return it; } + + flat_iterator& operator+=(difference_type d) + { this->underlying += d; return *this; } + flat_iterator& operator-=(difference_type d) + { this->underlying -= d; return *this; } + + flat_iterator operator+(difference_type d) const + { return this->underlying + d; } + flat_iterator operator-(difference_type d) const + { return this->underlying - d; } + + difference_type operator-(flat_iterator const& o) const + { return this->underlying - o.underlying; } + + reference operator[](difference_type d) const { return *(*this + d); } + + auto operator==(flat_iterator const& o) const + { + using namespace std::rel_ops; + return this->underlying == o.underlying; + } + auto operator!=(flat_iterator const& o) const + { + using namespace std::rel_ops; + return this->underlying != o.underlying; + } + auto operator<(flat_iterator const& o) const + { return this->underlying < o.underlying; } + auto operator<=(flat_iterator const& o) const + { return this->underlying <= o.underlying; } + auto operator>(flat_iterator const& o) const + { return this->underlying > o.underlying; } + auto operator>=(flat_iterator const& o) const + { return this->underlying >= o.underlying; } + + It underlying; +}; + +template, typename TransparentKey = void> +struct first_compare +{ + first_compare(const Compare& comp) : + compare(comp) { + } + + bool operator()(Pair const& lhs, Pair const& rhs) const + { + return compare.get()(lhs.first, rhs.first); + } + + bool operator()(typename Pair::first_type const& lhs, Pair const& rhs) const + { + return compare.get()(lhs, rhs.first); + } + + bool operator()(Pair const& lhs, typename Pair::first_type const& rhs) const + { + return compare.get()(lhs.first, rhs); + } + + template + bool operator()(transparent_key_t const& lhs, Pair const& rhs) const + { + return compare.get()(lhs.get(), rhs.first); + } + + template + bool operator()(transparent_key_t const& lhs, typename Pair::first_type const& rhs) const + { + return compare.get()(lhs.get(), rhs); + } + + template + bool operator()(Pair const& lhs, transparent_key_t const& rhs) const + { + return compare.get()(lhs.first, rhs.get()); + } + + template + bool operator()(typename Pair::first_type const& lhs, transparent_key_t const& rhs) const + { + return compare.get()(lhs, rhs.get()); + } + + std::reference_wrapper compare; +}; + +template +class flat_container_base +{ +#include "container_traits.hpp" + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } +public: + using key_compare = Compare; + const key_compare &key_comp() const { return self()->comp; } + + // Iterators + + const_iterator cbegin() const + noexcept(noexcept(std::declval().container.cbegin())) + { return self()->container.cbegin(); } + const_iterator begin() const + noexcept(noexcept(std::declval().container.begin())) + { return self()->container.begin(); } + iterator begin() + noexcept(noexcept(std::declval().container.begin())) + { return self()->container.begin(); } + + const_iterator cend() const + noexcept(noexcept(std::declval().container.cend())) + { return self()->container.cend(); } + const_iterator end() const + noexcept(noexcept(std::declval().container.end())) + { return self()->container.end(); } + iterator end() + noexcept(noexcept(std::declval().container.end())) + { return self()->container.end(); } + + const_reverse_iterator crbegin() const + noexcept(noexcept(std::declval().container.crbegin())) + { return self()->container.crbegin(); } + const_reverse_iterator rbegin() const + noexcept(noexcept(std::declval().container.rbegin())) + { return self()->container.rbegin(); } + reverse_iterator rbegin() + noexcept(noexcept(std::declval().container.rbegin())) + { return self()->container.rbegin(); } + + const_reverse_iterator crend() const + noexcept(noexcept(std::declval().container.crend())) + { return self()->container.crend(); } + const_reverse_iterator rend() const + noexcept(noexcept(std::declval().container.rend())) + { return self()->container.rend(); } + reverse_iterator rend() + noexcept(noexcept(std::declval().container.rend())) + { return self()->container.rend(); } + + // Capacity + + bool empty() const + noexcept(noexcept(std::declval().container.empty())) + { return self()->container.empty(); } + + size_type size() const + noexcept(noexcept(std::declval().container.size())) + { return self()->container.size(); } + + // Modifiers + + iterator insert(const_iterator hint, value_type const& value) + { return self()->insert(value).first; } + + iterator insert(const_iterator hint, value_type&& value) + { return self()->insert(std::move(value)).first; } + + template + void insert(InputIt first, InputIt last) + { + for(InputIt it = first; it != last; ++it) + self()->insert(*it); + } + + void insert(std::initializer_list ilist) + { self()->insert(ilist.begin(), ilist.end()); } + + void insert(std::initializer_list ilist, delay_sort_t d) + { self()->insert(ilist.begin(), ilist.end(), d); } + + template + auto emplace(Args&&... args) + { return self()->insert(value_type(std::forward(args)...)); } + + template + auto emplace_hint(const_iterator hint, Args&&... args) + { return self()->insert(value_type(std::forward(args)...)); } + + iterator erase(const_iterator pos) + noexcept(noexcept(std::declval().container.erase(pos.underlying))) + { return self()->container.erase(pos.underlying); } + + iterator erase(const_iterator first, const_iterator last) + noexcept(noexcept(std::declval().container.erase(first.underlying, + last.underlying))) + { return self()->container.erase(first.underlying, last.underlying); } + + void clear() + noexcept(noexcept(std::declval().container.clear())) + { self()->container.clear(); } + + void swap(D& other) + noexcept(D::has_noexcept_swap()) + { + using std::swap; + swap(self()->container, other.container); + } + + // Lookup + + const_iterator find(key_type const& key) const + { + const_iterator it = self()->lower_bound(key); + if(it == self()->end() || self()->value_comp()(key, *it)) + return self()->end(); + return it; + } + + iterator find(key_type const& key) + { + iterator it = self()->lower_bound(key); + if(it == self()->end() || self()->value_comp()(key, *it)) + return self()->end(); + return it; + } + + const_iterator lower_bound(key_type const& key) const + { + return std::lower_bound( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + + iterator lower_bound(key_type const& key) + { + return std::lower_bound( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + + const_iterator upper_bound(key_type const& key) const + { + return std::upper_bound( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + + iterator upper_bound(key_type const& key) + { + return std::upper_bound( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + + std::pair + equal_range(key_type const& key) const + { + return std::equal_range( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + + std::pair equal_range(key_type const& key) + { + return std::equal_range( + self()->begin(), self()->end(), + key, self()->value_comp()); + } + +private: + static constexpr bool has_noexcept_swap() + { + using std::swap; + return noexcept(swap(*static_cast(nullptr), + *static_cast(nullptr))); + } + +protected: + template + void ds_insert_(InputIt first, InputIt last) + { + size_type const i = self()->size(); + for(InputIt it = first; it != last; ++it) + self()->container.push_back(*it); + std::sort( + self()->container.begin()+i, + self()->container.end(), + self()->value_comp()); + std::inplace_merge( + self()->container.begin(), + self()->container.begin()+i, + self()->container.end()); + // Note: Not calling unique here. Do it in the caller. + } +}; + +template +class flat_container_base> +: public flat_container_base +{ +#include "container_traits.hpp" + D const* self() const { return static_cast(this); } + D* self() { return static_cast(this); } + using B = flat_container_base; +public: + + using B::insert; + using B::find; + using B::lower_bound; + using B::upper_bound; + using B::equal_range; + + // Modifiers + + template + iterator insert(const_iterator hint, P&& value) + { return insert(std::forward

(value)).first; } + + // Lookup + + template + const_iterator find(K const& key) const + { + const_iterator it = self()->lower_bound(key); + if (it == self()->end() || self()->value_comp()(std::ref(key), *it)) + return self()->end(); + return it; + } + + template + iterator find(K const& key) + { + iterator it = self()->lower_bound(key); + if (it == self()->end() || self()->value_comp()(std::ref(key), *it)) + return self()->end(); + return it; + } + + template + const_iterator lower_bound(K const& key) const + { + return std::lower_bound( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } + + template + iterator lower_bound(K const& key) + { + return std::lower_bound( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } + + template + const_iterator upper_bound(K const& key) const + { + return std::upper_bound( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } + + template + iterator upper_bound(K const& key) + { + return std::upper_bound( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } + + template + std::pair + equal_range(K const& key) const + { + return std::equal_range( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } + + template + std::pair equal_range(K const& key) + { + return std::equal_range( + self()->begin(), self()->end(), + std::ref(key), self()->value_comp()); + } +}; + +} // namespace fc +} // namespace impl + +#endif From 532aca85f5d3c078ee7554efd3fd6f14a64f74c5 Mon Sep 17 00:00:00 2001 From: Nanda Date: Tue, 21 Jul 2020 10:19:33 -0700 Subject: [PATCH 46/55] :wrench: add mpm_properties --- include/mpm_properties.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 include/mpm_properties.h diff --git a/include/mpm_properties.h b/include/mpm_properties.h new file mode 100644 index 000000000..c582c8ff4 --- /dev/null +++ b/include/mpm_properties.h @@ -0,0 +1,26 @@ +#ifndef MPM_PROPERTIES_H_ +#define MPM_PROPERTIES_H_ + +namespace mpm { +namespace properties { +//! Scalar Properties +enum Scalar : unsigned int { + Mass, + Volume, + MassDensity, + MassPressure, + Pressure +}; +//! Vector Properties +enum Vector : unsigned int { + Displacement, + Velocity, + Acceleration, + Momentum, + ExternalForce, + InternalForce +}; +} // namespace properties +} // namespace mpm + +#endif // MPM_PROPERTIES_H_ \ No newline at end of file From b017a9948968a1965988ce6c5fe03fee1a84fc46 Mon Sep 17 00:00:00 2001 From: Nanda Date: Tue, 21 Jul 2020 10:36:17 -0700 Subject: [PATCH 47/55] :wrench: clang-format --- include/solvers/mpm_base.tcc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/solvers/mpm_base.tcc b/include/solvers/mpm_base.tcc index b7870da8f..d3c91d936 100644 --- a/include/solvers/mpm_base.tcc +++ b/include/solvers/mpm_base.tcc @@ -811,8 +811,9 @@ void mpm::MPMBase::nodal_velocity_constraints( // Add velocity constraint to mesh auto velocity_constraint = std::make_shared(nset_id, dir, velocity); - bool velocity_constraints = constraints_->assign_nodal_velocity_constraint( - nset_id, velocity_constraint); + bool velocity_constraints = + constraints_->assign_nodal_velocity_constraint( + nset_id, velocity_constraint); if (!velocity_constraints) throw std::runtime_error( "Nodal velocity constraint is not properly assigned"); From 6de587f0326aa964ecf09d90c5a35c7d899a25e6 Mon Sep 17 00:00:00 2001 From: Nanda Date: Tue, 21 Jul 2020 10:36:42 -0700 Subject: [PATCH 48/55] :wrench: add functions and container in node --- include/node.h | 35 ++++++++++++++++++++++++++++++ include/node.tcc | 53 +++++++++++++++++++++++++++++++++++++++++++++ include/node_base.h | 32 +++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) diff --git a/include/node.h b/include/node.h index c815a8f78..f7c8e8b0e 100644 --- a/include/node.h +++ b/include/node.h @@ -65,6 +65,35 @@ class Node : public NodeBase { //! Return status bool status() const override { return status_; } + //! Update scalar property at the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Property value from the particles in a cell + void update_scalar_property(mpm::properties::Scalar property, bool update, + unsigned phase, double value) noexcept override; + + //! Return property at a given node for a given phase + //! \param[in] property Name of the property to return + //! \param[in] phase Index corresponding to the phase + double scalar_property(mpm::properties::Scalar property, + unsigned phase) const override; + + //! Update vector property at the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Property value from the particles in a cell + virtual void update_vector_property( + mpm::properties::Vector property, bool update, unsigned phase, + const Eigen::Matrix& value) noexcept override; + + //! Return property at a given node for a given phase + //! \param[in] property Name of the property to return + //! \param[in] phase Index corresponding to the phase + virtual Eigen::Matrix vector_property( + mpm::properties::Vector property, unsigned phase) const override; + //! Update mass at the nodes from particle //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase @@ -278,6 +307,12 @@ class Node : public NodeBase { unsigned dof_{std::numeric_limits::max()}; //! Status bool status_{false}; + //! Scalar properties + fc::vector_map> + scalar_properties_; + //! Vector properties + fc::vector_map> + vector_properties_; //! Mass Eigen::Matrix mass_; //! Volume diff --git a/include/node.tcc b/include/node.tcc index b120493f1..2b3f2bdc5 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -46,6 +46,59 @@ void mpm::Node::initialise_property_handle( this->prop_id_ = prop_id; } +//! Update scalar property at the nodes from particle +template +void mpm::Node::update_scalar_property( + mpm::properties::Scalar property, bool update, unsigned phase, + double value) noexcept { + // Assert phase + assert(phase < Tnphases); + + // Decide to update or assign + const double factor = (update == true) ? 1. : 0.; + + // Update/assign value + std::lock_guard guard(node_mutex_); + scalar_properties_.at(property)[phase] = + (scalar_properties_.at(property)[phase] * factor) + value; +} + +//! Update scalar property at the nodes from particle +template +double mpm::Node::scalar_property( + mpm::properties::Scalar property, unsigned phase) const { + // Assert phase + assert(phase < Tnphases); + return scalar_properties_.at(property)[phase]; +} + +//! Update vector property at the nodes from particle +template +void mpm::Node::update_vector_property( + mpm::properties::Vector property, bool update, unsigned phase, + const Eigen::Matrix& value) noexcept { + // Assert phase + assert(phase < Tnphases); + + // Decide to update or assign + const double factor = (update == true) ? 1. : 0.; + + // Update/assign value + std::lock_guard guard(node_mutex_); + Eigen::Matrix vecvalue = + vector_properties_.at(property).col(phase); + vector_properties_.at(property).col(phase) = (vecvalue * factor) + value; +} + +//! Update vector property at the nodes from particle +template +Eigen::Matrix mpm::Node::vector_property( + mpm::properties::Vector property, unsigned phase) const { + // Assert phase + assert(phase < Tnphases); + return vector_properties_.at(property).col(phase); +} + //! Update mass at the nodes from particle template void mpm::Node::update_mass(bool update, unsigned phase, diff --git a/include/node_base.h b/include/node_base.h index 17eddfdca..d751d7688 100644 --- a/include/node_base.h +++ b/include/node_base.h @@ -10,9 +10,11 @@ #include #include +#include #include "data_types.h" #include "function_base.h" +#include "mpm_properties.h" #include "nodal_properties.h" namespace mpm { @@ -70,6 +72,36 @@ class NodeBase { //! Return status virtual bool status() const = 0; + //! Update scalar property at the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Property value from the particles in a cell + virtual void update_scalar_property(mpm::properties::Scalar property, + bool update, unsigned phase, + double value) noexcept = 0; + + //! Return property at a given node for a given phase + //! \param[in] property Name of the property to return + //! \param[in] phase Index corresponding to the phase + virtual double scalar_property(mpm::properties::Scalar property, + unsigned phase) const = 0; + + //! Update vector property at the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Property value from the particles in a cell + virtual void update_vector_property( + mpm::properties::Vector property, bool update, unsigned phase, + const Eigen::Matrix& value) noexcept = 0; + + //! Return property at a given node for a given phase + //! \param[in] property Name of the property to return + //! \param[in] phase Index corresponding to the phase + virtual Eigen::Matrix vector_property( + mpm::properties::Vector property, unsigned phase) const = 0; + //! Update mass at the nodes from particle //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase From 9f42d94c06d01eb394694fd3153b562f25b459a3 Mon Sep 17 00:00:00 2001 From: Nanda Date: Tue, 21 Jul 2020 11:05:04 -0700 Subject: [PATCH 49/55] :wrench: add functions and container in particle --- include/particles/particle.h | 4 +- include/particles/particle_base.h | 74 ++++++++++++++++++++ include/particles/particle_base.tcc | 103 ++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+), 2 deletions(-) diff --git a/include/particles/particle.h b/include/particles/particle.h index f5f02925f..2b20e65fb 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -307,6 +307,8 @@ class Particle : public ParticleBase { using ParticleBase::state_variables_; //! Neighbour particles using ParticleBase::neighbours_; + //! Shape functions + using ParticleBase::shapefn_; //! Volumetric mass density (mass / volume) double mass_density_{0.}; //! Mass @@ -339,8 +341,6 @@ class Particle : public ParticleBase { bool set_traction_{false}; //! Surface Traction (given as a stress; force/area) Eigen::Matrix traction_; - //! Shape functions - Eigen::VectorXd shapefn_; //! dN/dX Eigen::MatrixXd dn_dx_; //! dN/dX at cell centroid diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index d8e006fb3..ced7b4f1f 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -124,6 +124,73 @@ class ParticleBase { //! Return size of particle in natural coordinates virtual VectorDim natural_size() const = 0; + //! Update scalar property at the particle + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] value Property value from the particles in a cell + void update_scalar_property(mpm::properties::Scalar property, bool update, + double value) noexcept; + + //! Return property + //! \param[in] phase Index corresponding to the phase + double scalar_property(mpm::properties::Scalar property) const; + + //! Map scalar property to the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + void map_scalar_property_nodes(mpm::properties::Scalar property, bool update, + unsigned phase) noexcept; + + //! Map an arbitrary scalar value to nodal scalar property + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Scalar value to be mapped from particle to node + void map_scalar_property_nodes(mpm::properties::Scalar property, bool update, + unsigned phase, double value) noexcept; + + //! Return an interpolation of scalar property in particle from nodes + //! \param[in] property Name of the property to update + //! \param[in] phase Index corresponding to the phase + double interpolate_scalar_property_nodes(mpm::properties::Scalar property, + unsigned phase) const; + + //! Update vector property at the particle + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] value Property value from the particles in a cell + void update_vector_property( + mpm::properties::Vector property, bool update, + const Eigen::Matrix& value) noexcept; + + //! Return property + //! \param[in] phase Index corresponding to the phase + Eigen::Matrix vector_property( + mpm::properties::Vector property) const; + + //! Map vector property to the nodes + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + void map_vector_property_nodes(mpm::properties::Vector property, bool update, + unsigned phase) noexcept; + + //! Map an arbitrary vector value to nodal vector property + //! \param[in] property Name of the property to update + //! \param[in] update A boolean to update (true) or assign (false) + //! \param[in] phase Index corresponding to the phase + //! \param[in] value Vector value to be mapped from particle to node + void map_vector_property_nodes( + mpm::properties::Vector property, bool update, unsigned phase, + const Eigen::Matrix& value) noexcept; + + //! Return an interpolation of vector property in particle from nodes + //! \param[in] property Name of the property to update + //! \param[in] phase Index corresponding to the phase + Eigen::Matrix interpolate_vector_property_nodes( + mpm::properties::Vector property, unsigned phase) const; + //! Compute volume of particle virtual void compute_volume() noexcept = 0; @@ -282,6 +349,13 @@ class ParticleBase { mpm::dense_map state_variables_; //! Vector of particle neighbour ids std::vector neighbours_; + //! Shape functions + Eigen::VectorXd shapefn_; + //! Scalar properties + fc::vector_map scalar_properties_; + //! Vector properties + fc::vector_map> + vector_properties_; }; // ParticleBase class } // namespace mpm diff --git a/include/particles/particle_base.tcc b/include/particles/particle_base.tcc index a75e8ca0b..f67c45ebf 100644 --- a/include/particles/particle_base.tcc +++ b/include/particles/particle_base.tcc @@ -15,3 +15,106 @@ mpm::ParticleBase::ParticleBase(Index id, const VectorDim& coord, : mpm::ParticleBase::ParticleBase(id, coord) { status_ = status; } + +//! Update scalar property at particle +template +void mpm::ParticleBase::update_scalar_property( + mpm::properties::Scalar property, bool update, double value) noexcept { + // Decide to update or assign + const double factor = (update == true) ? 1. : 0.; + scalar_properties_.at(property) = + scalar_properties_.at(property) * factor + value; +} + +//! Update scalar property at particle +template +double mpm::ParticleBase::scalar_property( + mpm::properties::Scalar property) const { + return scalar_properties_.at(property); +} + +//! Map scalar property to nodes +template +void mpm::ParticleBase::map_scalar_property_nodes( + mpm::properties::Scalar property, bool update, unsigned phase) noexcept { + // Check if particle property is set + assert(scalar_properties_.at(property) != std::numeric_limits::max()); + + // Map scalar property to nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + nodes_[i]->update_scalar_property( + property, update, phase, scalar_properties_.at(property) * shapefn_[i]); +} + +//! Map an arbitrary scalar value to nodal scalar property +template +void mpm::ParticleBase::map_scalar_property_nodes( + mpm::properties::Scalar property, bool update, unsigned phase, + double value) noexcept { + // Map scalar value to nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + nodes_[i]->update_scalar_property(property, update, phase, + value * shapefn_[i]); +} + +//! Interpolate scalar property from nodes +template +double mpm::ParticleBase::interpolate_scalar_property_nodes( + mpm::properties::Scalar property, unsigned phase) const { + double value = 0.; + // Interpolate scalar property from nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + value += nodes_[i]->scalar_property(property, phase) * shapefn_[i]; + return value; +} + +//! Update vector property at particle +template +void mpm::ParticleBase::update_vector_property( + mpm::properties::Vector property, bool update, + const Eigen::Matrix& value) noexcept { + // Decide to update or assign + const double factor = (update == true) ? 1. : 0.; + vector_properties_.at(property) = + vector_properties_.at(property) * factor + value; +} + +//! Update vector property at particle +template +Eigen::Matrix mpm::ParticleBase::vector_property( + mpm::properties::Vector property) const { + return vector_properties_.at(property); +} + +//! Map vector property to nodes +template +void mpm::ParticleBase::map_vector_property_nodes( + mpm::properties::Vector property, bool update, unsigned phase) noexcept { + // Map vector property to nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + nodes_[i]->update_vector_property( + property, update, phase, vector_properties_.at(property) * shapefn_[i]); +} + +//! Map an arbitrary vector value to nodal vector property +template +void mpm::ParticleBase::map_vector_property_nodes( + mpm::properties::Vector property, bool update, unsigned phase, + const Eigen::Matrix& value) noexcept { + // Map vector property to nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + nodes_[i]->update_vector_property(property, update, phase, + value * shapefn_[i]); +} + +//! Interpolate vector property from nodes +template +Eigen::Matrix + mpm::ParticleBase::interpolate_vector_property_nodes( + mpm::properties::Vector property, unsigned phase) const { + Eigen::Matrix value = Eigen::Matrix::Zero(); + // Interpolate vector property from nodes + for (unsigned i = 0; i < nodes_.size(); ++i) + value += nodes_[i]->vector_property(property, phase) * shapefn_[i]; + return value; +} From da1682e4e3332c3f8fc373d39a28951b01063130 Mon Sep 17 00:00:00 2001 From: Nanda Date: Tue, 21 Jul 2020 12:07:54 -0700 Subject: [PATCH 50/55] :construction: cosmetic changes as suggested --- include/node.h | 2 ++ include/node_base.h | 2 ++ include/particles/particle_base.h | 29 +++++++++++++++++------------ include/particles/particle_base.tcc | 12 ++++++------ 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/include/node.h b/include/node.h index f7c8e8b0e..19bea7512 100644 --- a/include/node.h +++ b/include/node.h @@ -76,6 +76,7 @@ class Node : public NodeBase { //! Return property at a given node for a given phase //! \param[in] property Name of the property to return //! \param[in] phase Index corresponding to the phase + //! \retval scalar property at the designated phase double scalar_property(mpm::properties::Scalar property, unsigned phase) const override; @@ -91,6 +92,7 @@ class Node : public NodeBase { //! Return property at a given node for a given phase //! \param[in] property Name of the property to return //! \param[in] phase Index corresponding to the phase + //! \retval vector property at the designated phase virtual Eigen::Matrix vector_property( mpm::properties::Vector property, unsigned phase) const override; diff --git a/include/node_base.h b/include/node_base.h index d751d7688..24abdde58 100644 --- a/include/node_base.h +++ b/include/node_base.h @@ -84,6 +84,7 @@ class NodeBase { //! Return property at a given node for a given phase //! \param[in] property Name of the property to return //! \param[in] phase Index corresponding to the phase + //! \retval scalar property at the designated phase virtual double scalar_property(mpm::properties::Scalar property, unsigned phase) const = 0; @@ -99,6 +100,7 @@ class NodeBase { //! Return property at a given node for a given phase //! \param[in] property Name of the property to return //! \param[in] phase Index corresponding to the phase + //! \retval vector property at the designated phase virtual Eigen::Matrix vector_property( mpm::properties::Vector property, unsigned phase) const = 0; diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index ced7b4f1f..ab4df0019 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -131,30 +131,33 @@ class ParticleBase { void update_scalar_property(mpm::properties::Scalar property, bool update, double value) noexcept; - //! Return property + //! Return scalar property //! \param[in] phase Index corresponding to the phase + //! \retval scalar property at particle double scalar_property(mpm::properties::Scalar property) const; //! Map scalar property to the nodes //! \param[in] property Name of the property to update //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase - void map_scalar_property_nodes(mpm::properties::Scalar property, bool update, - unsigned phase) noexcept; + void map_scalar_property_to_nodes(mpm::properties::Scalar property, + bool update, unsigned phase) noexcept; //! Map an arbitrary scalar value to nodal scalar property //! \param[in] property Name of the property to update //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase //! \param[in] value Scalar value to be mapped from particle to node - void map_scalar_property_nodes(mpm::properties::Scalar property, bool update, - unsigned phase, double value) noexcept; + void map_scalar_property_to_nodes(mpm::properties::Scalar property, + bool update, unsigned phase, + double value) noexcept; //! Return an interpolation of scalar property in particle from nodes //! \param[in] property Name of the property to update //! \param[in] phase Index corresponding to the phase - double interpolate_scalar_property_nodes(mpm::properties::Scalar property, - unsigned phase) const; + //! \retval interpolated scalar property at particle + double interpolate_scalar_property_from_nodes( + mpm::properties::Scalar property, unsigned phase) const; //! Update vector property at the particle //! \param[in] property Name of the property to update @@ -164,8 +167,9 @@ class ParticleBase { mpm::properties::Vector property, bool update, const Eigen::Matrix& value) noexcept; - //! Return property + //! Return vector property //! \param[in] phase Index corresponding to the phase + //! \retval vector property at particle Eigen::Matrix vector_property( mpm::properties::Vector property) const; @@ -173,22 +177,23 @@ class ParticleBase { //! \param[in] property Name of the property to update //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase - void map_vector_property_nodes(mpm::properties::Vector property, bool update, - unsigned phase) noexcept; + void map_vector_property_to_nodes(mpm::properties::Vector property, + bool update, unsigned phase) noexcept; //! Map an arbitrary vector value to nodal vector property //! \param[in] property Name of the property to update //! \param[in] update A boolean to update (true) or assign (false) //! \param[in] phase Index corresponding to the phase //! \param[in] value Vector value to be mapped from particle to node - void map_vector_property_nodes( + void map_vector_property_to_nodes( mpm::properties::Vector property, bool update, unsigned phase, const Eigen::Matrix& value) noexcept; //! Return an interpolation of vector property in particle from nodes //! \param[in] property Name of the property to update //! \param[in] phase Index corresponding to the phase - Eigen::Matrix interpolate_vector_property_nodes( + //! \retval interpolated vector property at particle + Eigen::Matrix interpolate_vector_property_from_nodes( mpm::properties::Vector property, unsigned phase) const; //! Compute volume of particle diff --git a/include/particles/particle_base.tcc b/include/particles/particle_base.tcc index f67c45ebf..03175fbcb 100644 --- a/include/particles/particle_base.tcc +++ b/include/particles/particle_base.tcc @@ -35,7 +35,7 @@ double mpm::ParticleBase::scalar_property( //! Map scalar property to nodes template -void mpm::ParticleBase::map_scalar_property_nodes( +void mpm::ParticleBase::map_scalar_property_to_nodes( mpm::properties::Scalar property, bool update, unsigned phase) noexcept { // Check if particle property is set assert(scalar_properties_.at(property) != std::numeric_limits::max()); @@ -48,7 +48,7 @@ void mpm::ParticleBase::map_scalar_property_nodes( //! Map an arbitrary scalar value to nodal scalar property template -void mpm::ParticleBase::map_scalar_property_nodes( +void mpm::ParticleBase::map_scalar_property_to_nodes( mpm::properties::Scalar property, bool update, unsigned phase, double value) noexcept { // Map scalar value to nodes @@ -59,7 +59,7 @@ void mpm::ParticleBase::map_scalar_property_nodes( //! Interpolate scalar property from nodes template -double mpm::ParticleBase::interpolate_scalar_property_nodes( +double mpm::ParticleBase::interpolate_scalar_property_from_nodes( mpm::properties::Scalar property, unsigned phase) const { double value = 0.; // Interpolate scalar property from nodes @@ -88,7 +88,7 @@ Eigen::Matrix mpm::ParticleBase::vector_property( //! Map vector property to nodes template -void mpm::ParticleBase::map_vector_property_nodes( +void mpm::ParticleBase::map_vector_property_to_nodes( mpm::properties::Vector property, bool update, unsigned phase) noexcept { // Map vector property to nodes for (unsigned i = 0; i < nodes_.size(); ++i) @@ -98,7 +98,7 @@ void mpm::ParticleBase::map_vector_property_nodes( //! Map an arbitrary vector value to nodal vector property template -void mpm::ParticleBase::map_vector_property_nodes( +void mpm::ParticleBase::map_vector_property_to_nodes( mpm::properties::Vector property, bool update, unsigned phase, const Eigen::Matrix& value) noexcept { // Map vector property to nodes @@ -110,7 +110,7 @@ void mpm::ParticleBase::map_vector_property_nodes( //! Interpolate vector property from nodes template Eigen::Matrix - mpm::ParticleBase::interpolate_vector_property_nodes( + mpm::ParticleBase::interpolate_vector_property_from_nodes( mpm::properties::Vector property, unsigned phase) const { Eigen::Matrix value = Eigen::Matrix::Zero(); // Interpolate vector property from nodes From 3e1f9ff6331c925540d8f3582fb7851b58e39d7b Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 22 Jul 2020 13:51:20 -0700 Subject: [PATCH 51/55] :construction: add boolean property --- include/mpm_properties.h | 2 ++ include/node.h | 13 +++++++++++++ include/node.tcc | 16 ++++++++++++++++ include/node_base.h | 11 +++++++++++ include/particles/particle_base.h | 18 ++++++++++++++++-- include/particles/particle_base.tcc | 14 ++++++++++++++ 6 files changed, 72 insertions(+), 2 deletions(-) diff --git a/include/mpm_properties.h b/include/mpm_properties.h index c582c8ff4..1046c6873 100644 --- a/include/mpm_properties.h +++ b/include/mpm_properties.h @@ -3,6 +3,8 @@ namespace mpm { namespace properties { +//! Boolean Properties +enum Boolean : unsigned int { FreeSurface }; //! Scalar Properties enum Scalar : unsigned int { Mass, diff --git a/include/node.h b/include/node.h index 19bea7512..bc4afd326 100644 --- a/include/node.h +++ b/include/node.h @@ -65,6 +65,17 @@ class Node : public NodeBase { //! Return status bool status() const override { return status_; } + //! Assign boolean property at the nodes + //! \param[in] property Name of the property to assign + //! \param[in] boolean Property boolean (true/false) of the node + void assign_boolean_property(mpm::properties::Boolean property, + bool boolean) noexcept override; + + //! Return boolean property + //! \param[in] property Name of the property to update + //! \retval boolean property at node + bool boolean_property(mpm::properties::Boolean property) const override; + //! Update scalar property at the nodes //! \param[in] property Name of the property to update //! \param[in] update A boolean to update (true) or assign (false) @@ -309,6 +320,8 @@ class Node : public NodeBase { unsigned dof_{std::numeric_limits::max()}; //! Status bool status_{false}; + //! Boolean properties + fc::vector_map boolean_properties_; //! Scalar properties fc::vector_map> scalar_properties_; diff --git a/include/node.tcc b/include/node.tcc index 2b3f2bdc5..c3584b5f0 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -46,6 +46,22 @@ void mpm::Node::initialise_property_handle( this->prop_id_ = prop_id; } +//! Assign boolean property at the nodes +template +void mpm::Node::assign_boolean_property( + mpm::properties::Boolean property, bool boolean) noexcept { + // Update/assign value + std::lock_guard guard(node_mutex_); + boolean_properties_.at(property) = boolean; +} + +//! Return boolean property +template +bool mpm::Node::boolean_property( + mpm::properties::Boolean property) const { + return boolean_properties_.at(property); +} + //! Update scalar property at the nodes from particle template void mpm::Node::update_scalar_property( diff --git a/include/node_base.h b/include/node_base.h index 24abdde58..b0eb77888 100644 --- a/include/node_base.h +++ b/include/node_base.h @@ -72,6 +72,17 @@ class NodeBase { //! Return status virtual bool status() const = 0; + //! Assign boolean property at the nodes + //! \param[in] property Name of the property to assign + //! \param[in] boolean Property boolean (true/false) of the node + virtual void assign_boolean_property(mpm::properties::Boolean property, + bool boolean) noexcept = 0; + + //! Return boolean property + //! \param[in] property Name of the property to update + //! \retval boolean property at node + virtual bool boolean_property(mpm::properties::Boolean property) const = 0; + //! Update scalar property at the nodes //! \param[in] property Name of the property to update //! \param[in] update A boolean to update (true) or assign (false) diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index ab4df0019..51a12415d 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -124,6 +124,18 @@ class ParticleBase { //! Return size of particle in natural coordinates virtual VectorDim natural_size() const = 0; + //! Assign boolean property at the particle + //! \param[in] property Name of the property to assign + //! \param[in] boolean Property boolean (true/false) of the particles in a + //! cell + void assign_boolean_property(mpm::properties::Boolean property, + bool boolean) noexcept; + + //! Return boolean property + //! \param[in] property Name of the property to update + //! \retval boolean property at particle + bool boolean_property(mpm::properties::Boolean property) const; + //! Update scalar property at the particle //! \param[in] property Name of the property to update //! \param[in] update A boolean to update (true) or assign (false) @@ -132,7 +144,7 @@ class ParticleBase { double value) noexcept; //! Return scalar property - //! \param[in] phase Index corresponding to the phase + //! \param[in] property Name of the property to return //! \retval scalar property at particle double scalar_property(mpm::properties::Scalar property) const; @@ -168,7 +180,7 @@ class ParticleBase { const Eigen::Matrix& value) noexcept; //! Return vector property - //! \param[in] phase Index corresponding to the phase + //! \param[in] property Name of the property to return //! \retval vector property at particle Eigen::Matrix vector_property( mpm::properties::Vector property) const; @@ -356,6 +368,8 @@ class ParticleBase { std::vector neighbours_; //! Shape functions Eigen::VectorXd shapefn_; + //! Boolean properties + fc::vector_map boolean_properties_; //! Scalar properties fc::vector_map scalar_properties_; //! Vector properties diff --git a/include/particles/particle_base.tcc b/include/particles/particle_base.tcc index 03175fbcb..092340f25 100644 --- a/include/particles/particle_base.tcc +++ b/include/particles/particle_base.tcc @@ -16,6 +16,20 @@ mpm::ParticleBase::ParticleBase(Index id, const VectorDim& coord, status_ = status; } +//! Assign boolean property at the particle +template +void mpm::ParticleBase::assign_boolean_property( + mpm::properties::Boolean property, bool boolean) noexcept { + boolean_properties_.at(property) = boolean; +} + +//! Return boolean property +template +bool mpm::ParticleBase::boolean_property( + mpm::properties::Boolean property) const { + return boolean_properties_.at(property); +} + //! Update scalar property at particle template void mpm::ParticleBase::update_scalar_property( From 47c5f48803327ba2ab587b3b5528a31d2b563c20 Mon Sep 17 00:00:00 2001 From: Nanda Date: Wed, 22 Jul 2020 15:55:27 -0700 Subject: [PATCH 52/55] :wrench: modify function calls --- include/particles/particle.tcc | 7 ++++--- include/particles/particle_functions.tcc | 26 ++++++++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 0d5d3c680..58ba73ac8 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -700,14 +700,15 @@ void mpm::Particle::compute_updated_position( // Check if particle has a valid cell ptr assert(cell_ != nullptr); // Get interpolated nodal velocity - const auto& nodal_velocity = this->interpolate_vector_property_nodes( + const auto& nodal_velocity = this->interpolate_vector_property_from_nodes( mpm::properties::Vector::Velocity, mpm::ParticlePhase::Solid); // Acceleration update if (!velocity_update) { // Get interpolated nodal acceleration - const auto& nodal_acceleration = this->interpolate_vector_property_nodes( - mpm::properties::Vector::Acceleration, mpm::ParticlePhase::Solid); + const auto& nodal_acceleration = + this->interpolate_vector_property_from_nodes( + mpm::properties::Vector::Acceleration, mpm::ParticlePhase::Solid); // Update particle velocity from interpolated nodal acceleration vector_properties_.at(mpm::properties::Vector::Velocity) += diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc index 7134f0c89..40646005c 100644 --- a/include/particles/particle_functions.tcc +++ b/include/particles/particle_functions.tcc @@ -57,11 +57,11 @@ void map_mass_momentum_to_nodes( assert(particle->mass() != std::numeric_limits::max()); // Map mass and momentum to nodes - particle->map_scalar_property_nodes(mpm::properties::Scalar::Mass, true, - mpm::ParticlePhase::Solid); - particle->map_vector_property_nodes(mpm::properties::Vector::Momentum, true, - mpm::ParticlePhase::Solid, - particle->mass() * particle->velocity()); + particle->map_scalar_property_to_nodes(mpm::properties::Scalar::Mass, true, + mpm::ParticlePhase::Solid); + particle->map_vector_property_to_nodes( + mpm::properties::Vector::Momentum, true, mpm::ParticlePhase::Solid, + particle->mass() * particle->velocity()); } //! Map particle pressure to nodes @@ -74,7 +74,7 @@ void map_mass_pressure_to_nodes( // Check if state variable pressure is found if (particle->pressure() != std::numeric_limits::quiet_NaN()) { // Map particle pressure to nodes - particle->map_scalar_property_nodes( + particle->map_scalar_property_to_nodes( mpm::properties::Scalar::MassPressure, true, mpm::ParticlePhase::Solid, particle->mass() * particle->pressure()); } @@ -85,9 +85,9 @@ template void map_body_force(std::shared_ptr> particle, const Eigen::Matrix& pgravity) noexcept { // Compute nodal body forces - particle->map_vector_property_nodes(mpm::properties::Vector::ExternalForce, - true, mpm::ParticlePhase::Solid, - pgravity * particle->mass()); + particle->map_vector_property_to_nodes(mpm::properties::Vector::ExternalForce, + true, mpm::ParticlePhase::Solid, + pgravity * particle->mass()); } //! Map traction force @@ -96,9 +96,9 @@ void map_traction_force( std::shared_ptr> particle) noexcept { if (particle->set_traction()) { // Map particle traction forces to nodes - particle->map_vector_property_nodes(mpm::properties::Vector::ExternalForce, - true, mpm::ParticlePhase::Solid, - particle->traction()); + particle->map_vector_property_to_nodes( + mpm::properties::Vector::ExternalForce, true, mpm::ParticlePhase::Solid, + particle->traction()); } } @@ -111,7 +111,7 @@ void compute_pressure_smoothing( // Check if particle has pressure if (particle->pressure() != std::numeric_limits::quiet_NaN()) { - double pressure = particle->interpolate_scalar_property_nodes( + double pressure = particle->interpolate_scalar_property_from_nodes( mpm::properties::Scalar::Pressure, mpm::ParticlePhase::Solid); particle->assign_state_variable("pressure", pressure); } From b8a39327d291f2451fec8e4c353db6d6bf44f819 Mon Sep 17 00:00:00 2001 From: Nanda Date: Fri, 24 Jul 2020 19:45:58 -0700 Subject: [PATCH 53/55] :construction: :dart: add set_traction to boolean_properties and add testing --- include/mpm_properties.h | 2 +- include/particles/particle.h | 5 ----- include/particles/particle.tcc | 7 +++++-- include/particles/particle_base.h | 3 --- include/particles/particle_functions.tcc | 2 +- tests/particle_test.cc | 16 ++++++++++++++++ 6 files changed, 23 insertions(+), 12 deletions(-) diff --git a/include/mpm_properties.h b/include/mpm_properties.h index b280561f1..fa7eddfae 100644 --- a/include/mpm_properties.h +++ b/include/mpm_properties.h @@ -4,7 +4,7 @@ namespace mpm { namespace properties { //! Boolean Properties -enum Boolean : unsigned int { FreeSurface }; +enum Boolean : unsigned int { SetTraction }; //! Scalar Properties enum Scalar : unsigned int { Mass, diff --git a/include/particles/particle.h b/include/particles/particle.h index e254d8dc7..a33e624c8 100644 --- a/include/particles/particle.h +++ b/include/particles/particle.h @@ -224,9 +224,6 @@ class Particle : public ParticleBase { //! \param[in] phase Index corresponding to the phase VectorDim traction() const override { return traction_; } - //! Return set traction bool - bool set_traction() const override { return set_traction_; } - //! Compute updated position of the particle //! \param[in] dt Analysis time step //! \param[in] velocity_update Update particle velocity from nodal vel @@ -342,8 +339,6 @@ class Particle : public ParticleBase { Eigen::Matrix dstrain_; //! Particle velocity constraints std::map particle_velocity_constraints_; - //! Set traction - bool set_traction_{false}; //! Surface Traction (given as a stress; force/area) Eigen::Matrix traction_; //! dN/dX diff --git a/include/particles/particle.tcc b/include/particles/particle.tcc index 58ba73ac8..57f69a061 100644 --- a/include/particles/particle.tcc +++ b/include/particles/particle.tcc @@ -230,7 +230,6 @@ template void mpm::Particle::initialise() { dstrain_.setZero(); natural_size_.setZero(); - set_traction_ = false; size_.setZero(); strain_rate_.setZero(); strain_.setZero(); @@ -238,6 +237,10 @@ void mpm::Particle::initialise() { traction_.setZero(); volumetric_strain_centroid_ = 0.; + // Initialize boolean properties + boolean_properties_.emplace( + std::make_pair(mpm::properties::Boolean::SetTraction, false)); + // Initialize scalar properties scalar_properties_.emplace( std::make_pair(mpm::properties::Scalar::Mass, double(0.))); @@ -685,7 +688,7 @@ bool mpm::Particle::assign_traction(unsigned direction, double traction) { // Assign traction traction_(direction) = traction * this->volume() / this->size_(direction); status = true; - this->set_traction_ = true; + this->assign_boolean_property(mpm::properties::Boolean::SetTraction, true); } catch (std::exception& exception) { console_->error("{} #{}: {}\n", __FILE__, __LINE__, exception.what()); status = false; diff --git a/include/particles/particle_base.h b/include/particles/particle_base.h index 9f8a67c2a..c73ed2f3c 100644 --- a/include/particles/particle_base.h +++ b/include/particles/particle_base.h @@ -296,9 +296,6 @@ class ParticleBase { //! Return traction virtual VectorDim traction() const = 0; - //! Return set traction bool - virtual bool set_traction() const = 0; - //! Compute updated position virtual void compute_updated_position( double dt, bool velocity_update = false) noexcept = 0; diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc index 40646005c..f502f96fc 100644 --- a/include/particles/particle_functions.tcc +++ b/include/particles/particle_functions.tcc @@ -94,7 +94,7 @@ void map_body_force(std::shared_ptr> particle, template void map_traction_force( std::shared_ptr> particle) noexcept { - if (particle->set_traction()) { + if (particle->boolean_property(mpm::properties::Boolean::SetTraction)) { // Map particle traction forces to nodes particle->map_vector_property_to_nodes( mpm::properties::Vector::ExternalForce, true, mpm::ParticlePhase::Solid, diff --git a/tests/particle_test.cc b/tests/particle_test.cc index 3663d2889..b8e25107a 100644 --- a/tests/particle_test.cc +++ b/tests/particle_test.cc @@ -234,6 +234,14 @@ TEST_CASE("Particle is checked for 1D case", "[particle][1D]") { else REQUIRE(particle->traction()(i) == Approx(0.).epsilon(Tolerance)); } + + // Check for boolean property assignment and return + REQUIRE(particle->boolean_property(mpm::properties::Boolean::SetTraction) == + true); + particle->assign_boolean_property(mpm::properties::Boolean::SetTraction, + false); + REQUIRE(particle->boolean_property(mpm::properties::Boolean::SetTraction) == + false); } SECTION("Check initialise particle HDF5") { @@ -1368,6 +1376,14 @@ TEST_CASE("Particle is checked for 2D case", "[particle][2D]") { else REQUIRE(particle->traction()(i) == Approx(0.).epsilon(Tolerance)); } + + // Check for boolean property assignment and return + REQUIRE(particle->boolean_property(mpm::properties::Boolean::SetTraction) == + true); + particle->assign_boolean_property(mpm::properties::Boolean::SetTraction, + false); + REQUIRE(particle->boolean_property(mpm::properties::Boolean::SetTraction) == + false); } // Check initialise particle from HDF5 file From 68506c12947895371ae62f1ae5e7e2f09786358f Mon Sep 17 00:00:00 2001 From: Nanda Date: Sat, 25 Jul 2020 10:39:08 -0700 Subject: [PATCH 54/55] :wrench: fix particle_function for pressure smoothing --- include/particles/particle_functions.tcc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/include/particles/particle_functions.tcc b/include/particles/particle_functions.tcc index f502f96fc..fb53fc1ea 100644 --- a/include/particles/particle_functions.tcc +++ b/include/particles/particle_functions.tcc @@ -67,16 +67,17 @@ void map_mass_momentum_to_nodes( //! Map particle pressure to nodes template void map_mass_pressure_to_nodes( - std::shared_ptr> particle) noexcept { + std::shared_ptr> particle, + unsigned phase = mpm::ParticlePhase::Solid) noexcept { // Mass is initialized assert(particle->mass() != std::numeric_limits::max()); // Check if state variable pressure is found - if (particle->pressure() != std::numeric_limits::quiet_NaN()) { + if (particle->pressure(phase) != std::numeric_limits::quiet_NaN()) { // Map particle pressure to nodes particle->map_scalar_property_to_nodes( - mpm::properties::Scalar::MassPressure, true, mpm::ParticlePhase::Solid, - particle->mass() * particle->pressure()); + mpm::properties::Scalar::MassPressure, true, phase, + particle->mass() * particle->pressure(phase)); } } @@ -105,15 +106,16 @@ void map_traction_force( // Compute pressure smoothing of the particle based on nodal pressure template void compute_pressure_smoothing( - std::shared_ptr> particle) noexcept { + std::shared_ptr> particle, + unsigned phase = mpm::ParticlePhase::Solid) noexcept { // Assert assert(particle->cell_ptr()); // Check if particle has pressure - if (particle->pressure() != std::numeric_limits::quiet_NaN()) { + if (particle->pressure(phase) != std::numeric_limits::quiet_NaN()) { double pressure = particle->interpolate_scalar_property_from_nodes( - mpm::properties::Scalar::Pressure, mpm::ParticlePhase::Solid); - particle->assign_state_variable("pressure", pressure); + mpm::properties::Scalar::Pressure, phase); + particle->assign_state_variable("pressure", pressure, phase); } } From 0666b0a345a717d44b257ecfc2e9a1f178ab60d9 Mon Sep 17 00:00:00 2001 From: Nanda Date: Sat, 25 Jul 2020 11:01:25 -0700 Subject: [PATCH 55/55] :construction: :dart: refactor and add test for nodal boolean properties --- include/mpm_properties.h | 2 +- include/node.h | 6 +-- include/node.tcc | 22 ++++++---- tests/node_test.cc | 88 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 13 deletions(-) diff --git a/include/mpm_properties.h b/include/mpm_properties.h index fa7eddfae..170c2bec6 100644 --- a/include/mpm_properties.h +++ b/include/mpm_properties.h @@ -4,7 +4,7 @@ namespace mpm { namespace properties { //! Boolean Properties -enum Boolean : unsigned int { SetTraction }; +enum Boolean : unsigned int { SetTraction, Friction, GenericBC }; //! Scalar Properties enum Scalar : unsigned int { Mass, diff --git a/include/node.h b/include/node.h index f0fa1c065..993abca0b 100644 --- a/include/node.h +++ b/include/node.h @@ -271,7 +271,7 @@ class Node : public NodeBase { void assign_rotation_matrix( const Eigen::Matrix& rotation_matrix) override { rotation_matrix_ = rotation_matrix; - generic_boundary_constraints_ = true; + this->assign_boolean_property(mpm::properties::Boolean::GenericBC, true); } //! Add material id from material points to list of materials in materials_ @@ -348,11 +348,7 @@ class Node : public NodeBase { Eigen::Matrix rotation_matrix_; //! Material ids whose information was passed to this node std::set material_ids_; - //! A general velocity (non-Cartesian/inclined) constraint is specified at the - //! node - bool generic_boundary_constraints_{false}; //! Frictional constraints - bool friction_{false}; std::tuple friction_constraint_; //! Concentrated force Eigen::Matrix concentrated_force_; diff --git a/include/node.tcc b/include/node.tcc index 9b91f7bf4..6fb6f5bad 100644 --- a/include/node.tcc +++ b/include/node.tcc @@ -18,6 +18,14 @@ mpm::Node::Node( velocity_constraints_.clear(); concentrated_force_.setZero(); + // Initialize boolean properties + // Friction + boolean_properties_.emplace( + std::make_pair(mpm::properties::Boolean::Friction, false)); + // GenericBC + boolean_properties_.emplace( + std::make_pair(mpm::properties::Boolean::GenericBC, false)); + // Initialize scalar properties // Mass scalar_properties_.emplace( @@ -417,7 +425,7 @@ void mpm::Node::apply_velocity_constraints() { // Phase: Integer value of division (dir / Tdim) const auto phase = static_cast(dir / Tdim); - if (!generic_boundary_constraints_) { + if (!this->boolean_property(mpm::properties::Boolean::GenericBC)) { // Velocity constraints are applied on Cartesian boundaries vector_properties_.at(mpm::properties::Vector::Velocity)( direction, phase) = constraint.second; @@ -460,7 +468,7 @@ bool mpm::Node::assign_friction_constraint( this->friction_constraint_ = std::make_tuple(static_cast(dir), static_cast(sign_n), static_cast(friction)); - this->friction_ = true; + this->assign_boolean_property(mpm::properties::Boolean::Friction, true); } else throw std::runtime_error("Constraint direction is out of bounds"); @@ -474,7 +482,7 @@ bool mpm::Node::assign_friction_constraint( //! Apply friction constraints template void mpm::Node::apply_friction_constraints(double dt) { - if (friction_) { + if (this->boolean_property(mpm::properties::Boolean::Friction)) { auto sign = [](double value) { return (value > 0.) ? 1. : -1.; }; // Set friction constraint @@ -496,7 +504,7 @@ void mpm::Node::apply_friction_constraints(double dt) { // tangential direction to boundary const unsigned dir_t = (Tdim - 1) - dir_n; - if (!generic_boundary_constraints_) { + if (!this->boolean_property(mpm::properties::Boolean::GenericBC)) { // Cartesian case // Normal and tangential acceleration acc_n = vector_properties_.at(mpm::properties::Vector::Acceleration)( @@ -540,7 +548,7 @@ void mpm::Node::apply_friction_constraints(double dt) { acc_t -= sign(acc_t) * mu * std::abs(acc_n); } - if (!generic_boundary_constraints_) { + if (!this->boolean_property(mpm::properties::Boolean::GenericBC)) { // Cartesian case vector_properties_.at(mpm::properties::Vector::Acceleration)( dir_t, phase) = acc_t; @@ -568,7 +576,7 @@ void mpm::Node::apply_friction_constraints(double dt) { const unsigned dir_t1 = dir(dir_n, 1); Eigen::Matrix acc, vel; - if (!generic_boundary_constraints_) { + if (!this->boolean_property(mpm::properties::Boolean::GenericBC)) { // Cartesian case acc = this->acceleration(phase); vel = this->velocity(phase); @@ -617,7 +625,7 @@ void mpm::Node::apply_friction_constraints(double dt) { } } - if (!generic_boundary_constraints_) { + if (!this->boolean_property(mpm::properties::Boolean::GenericBC)) { // Cartesian case vector_properties_.at(mpm::properties::Vector::Acceleration) .col(phase) = acc; diff --git a/tests/node_test.cc b/tests/node_test.cc index 9bd3584c7..3a610136a 100644 --- a/tests/node_test.cc +++ b/tests/node_test.cc @@ -341,12 +341,20 @@ TEST_CASE("Node is checked for 1D case", "[node][1D]") { REQUIRE(node->velocity(Nphase)(i) == Approx(velocity(i)).epsilon(Tolerance)); + // Check boolean properties Friction, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::Friction) == + false); + // Apply friction constraints REQUIRE(node->assign_friction_constraint(0, 1., 0.5) == true); // Apply friction constraints REQUIRE(node->assign_friction_constraint(-1, 1., 0.5) == false); REQUIRE(node->assign_friction_constraint(3, 1., 0.5) == false); + // Check boolean properties Friction, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::Friction) == + true); + // Test acceleration with constraints acceleration[0] = 0.5 * acceleration[0]; for (unsigned i = 0; i < acceleration.size(); ++i) @@ -979,6 +987,10 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { // Apply velocity constraints REQUIRE(node->assign_velocity_constraint(0, -12.5) == true); + // Check boolean properties GenericBC, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + false); + // Apply rotation matrix with Euler angles alpha = 10 deg, beta = 30 deg Eigen::Matrix euler_angles; euler_angles << 10. * M_PI / 180, 30. * M_PI / 180; @@ -987,6 +999,10 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { node->assign_rotation_matrix(rotation_matrix); const auto inverse_rotation_matrix = rotation_matrix.inverse(); + // Check boolean properties GenericBC, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + true); + // Apply inclined velocity constraints node->apply_velocity_constraints(); @@ -1016,6 +1032,10 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { REQUIRE(node->assign_velocity_constraint(0, -12.5) == true); REQUIRE(node->assign_velocity_constraint(1, 7.5) == true); + // Check boolean properties GenericBC, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + false); + // Apply rotation matrix with Euler angles alpha = -10 deg, beta = 30 // deg Eigen::Matrix euler_angles; @@ -1025,6 +1045,10 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { node->assign_rotation_matrix(rotation_matrix); const auto inverse_rotation_matrix = rotation_matrix.inverse(); + // Check boolean properties GenericBC, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + true); + // Apply inclined velocity constraints node->apply_velocity_constraints(); @@ -1054,11 +1078,19 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { } SECTION("Check Cartesian friction constraints") { + // Check boolean properties Friction, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::Friction) == + false); + // Apply friction constraints REQUIRE(node->assign_friction_constraint(1, 1, 0.2) == true); // Check out of bounds condition REQUIRE(node->assign_friction_constraint(2, 1, 0.2) == false); + // Check boolean properties Friction, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::Friction) == + true); + // Apply friction constraints node->apply_friction_constraints(dt); @@ -1070,9 +1102,21 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { } SECTION("Check general friction constraints in 1 direction") { + // Check boolean properties Friction, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::Friction) == + false); + // Apply friction constraints REQUIRE(node->assign_friction_constraint(1, 1, 0.2) == true); + // Check boolean properties Friction, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::Friction) == + true); + + // Check boolean properties GenericBC, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + false); + // Apply rotation matrix with Euler angles alpha = 10 deg, beta = 30 deg Eigen::Matrix euler_angles; euler_angles << 10. * M_PI / 180, 30. * M_PI / 180; @@ -1084,6 +1128,10 @@ TEST_CASE("Node is checked for 2D case", "[node][2D]") { // Apply general friction constraints node->apply_friction_constraints(dt); + // Check boolean properties GenericBC, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + true); + // Check applied constraints on acceleration in the global coordinates acceleration << 4.905579787672637, 4.920772034660430; for (unsigned i = 0; i < Dim; ++i) @@ -1563,6 +1611,10 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { REQUIRE(node->assign_velocity_constraint(0, 10.5) == true); REQUIRE(node->assign_velocity_constraint(2, -12.5) == true); + // Check boolean properties GenericBC, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + false); + // Apply rotation matrix with Euler angles alpha = 10 deg, beta = 20 deg // and gamma = 30 deg Eigen::Matrix euler_angles; @@ -1572,6 +1624,10 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { node->assign_rotation_matrix(rotation_matrix); const auto inverse_rotation_matrix = rotation_matrix.inverse(); + // Check boolean properties GenericBC, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + true); + // Apply constraints node->apply_velocity_constraints(); @@ -1607,6 +1663,10 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { REQUIRE(node->assign_velocity_constraint(1, -12.5) == true); REQUIRE(node->assign_velocity_constraint(2, 7.5) == true); + // Check boolean properties GenericBC, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + false); + // Apply rotation matrix with Euler angles alpha = -10 deg, beta = 20 // deg and gamma = -30 deg Eigen::Matrix euler_angles; @@ -1616,6 +1676,10 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { node->assign_rotation_matrix(rotation_matrix); const auto inverse_rotation_matrix = rotation_matrix.inverse(); + // Check boolean properties GenericBC, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + true); + // Apply constraints node->apply_velocity_constraints(); @@ -1649,11 +1713,19 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { } SECTION("Check Cartesian friction constraints") { + // Check boolean properties Friction, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::Friction) == + false); + // Apply friction constraints REQUIRE(node->assign_friction_constraint(2, 2, 0.3) == true); // Check out of bounds condition REQUIRE(node->assign_friction_constraint(4, 1, 0.2) == false); + // Check boolean properties Friction, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::Friction) == + true); + // Apply constraints node->apply_friction_constraints(dt); @@ -1665,9 +1737,21 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { } SECTION("Check general friction constraints in 1 direction") { + // Check boolean properties Friction, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::Friction) == + false); + // Apply friction constraints REQUIRE(node->assign_friction_constraint(2, 2, 0.3) == true); + // Check boolean properties Friction, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::Friction) == + true); + + // Check boolean properties GenericBC, should be false + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + false); + // Apply rotation matrix with Euler angles alpha = 10 deg, beta = 20 deg // and gamma = 30 deg Eigen::Matrix euler_angles; @@ -1680,6 +1764,10 @@ TEST_CASE("Node is checked for 3D case", "[node][3D]") { // Apply inclined velocity constraints node->apply_friction_constraints(dt); + // Check boolean properties GenericBC, should be true + REQUIRE(node->boolean_property(mpm::properties::Boolean::GenericBC) == + true); + // Check applied constraints on acceleration in the global coordinates acceleration << 4.602895052828914, 4.492575657560740, 4.751301246937935; for (unsigned i = 0; i < Dim; ++i)