From 5b00cb7a6e774fb25016bea5b5a1e7a1bdc1184b Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Mon, 24 Feb 2025 12:45:04 +0100 Subject: [PATCH 01/16] feat(core): New flexible states --- source/Body.hpp | 9 + source/IO.cpp | 31 +- source/IO.hpp | 55 +- source/Line.cpp | 27 +- source/Line.hpp | 17 +- source/Misc.cpp | 13 + source/Misc.hpp | 33 +- source/MoorDyn2.cpp | 17 +- source/MoorDyn2.hpp | 6 +- source/Rod.hpp | 9 + source/State.cpp | 1213 +++++++++------------------ source/State.hpp | 846 +++++++++++-------- source/Time.cpp | 658 ++++++++------- source/Time.hpp | 838 ++++++++---------- source/Util/CFL.hpp | 6 +- source/Waves.cpp | 2 +- source/Waves.hpp | 9 +- tests/CMakeLists.txt | 1 + tests/Mooring/WD0050_Chain.txt.ic | Bin 2049 -> 2721 bytes tests/Mooring/WD0200_Chain.txt.ic | Bin 3729 -> 4961 bytes tests/Mooring/WD0600_Chain.txt.ic | Bin 8241 -> 10977 bytes tests/Mooring/lines.txt.ic | Bin 3089 -> 4065 bytes tests/c_api.c | 10 - tests/state_tests.cpp | 339 ++++++++ tests/time_schemes.cpp | 12 +- wrappers/python/moordyn/moorpyic.py | 71 +- 26 files changed, 2179 insertions(+), 2043 deletions(-) create mode 100644 tests/state_tests.cpp diff --git a/source/Body.hpp b/source/Body.hpp index 96908a20..09c3ed95 100644 --- a/source/Body.hpp +++ b/source/Body.hpp @@ -378,9 +378,18 @@ class Body final : public io::IO, public SuperCFL /** @brief Set the states to the body to the position r and velocity rd * @param r The position * @param rd The velocity + * @{ */ void DECLDIR setState(XYZQuat r, vec6 rd); + inline void setState(vec7 r, vec6 rd) + { + setState(XYZQuat::fromVec7(r), rd); + } + /** + * @} + */ + /** @brief calculate the forces and state derivatives of the body * * This function is only meant for free bodies diff --git a/source/IO.cpp b/source/IO.cpp index 532bf734..96c7af4e 100644 --- a/source/IO.cpp +++ b/source/IO.cpp @@ -196,7 +196,7 @@ IO::IO(moordyn::Log* log) : LogUser(log) , _is_big_endian(false) , _min_major_version(2) - , _min_minor_version(2) + , _min_minor_version(4) { _is_big_endian = is_big_endian(); if (_min_major_version <= MOORDYN_MAJOR_VERSION) { @@ -206,8 +206,6 @@ IO::IO(moordyn::Log* log) } } -IO::~IO() {} - void IO::Save(const std::string filepath) { @@ -400,6 +398,18 @@ IO::Serialize(const XYZQuat& m) return data; } +std::vector +IO::Serialize(const list& m) +{ + std::vector data; + const uint64_t n = m.rows(); + data.reserve(1 + n); + data.push_back(Serialize(n)); + for (uint64_t i = 0; i < n; i++) + data.push_back(Serialize(m(i))); + return data; +} + std::vector IO::Serialize(const std::vector& l) { @@ -551,6 +561,21 @@ IO::Deserialize(const uint64_t* in, XYZQuat& out) return remaining; } +uint64_t* +IO::Deserialize(const uint64_t* in, list& out) +{ + uint64_t n; + uint64_t* remaining = Deserialize(in, n); + if (out.rows() != n) + out.resize(n); + for (unsigned int i = 0; i < n; i++) { + real v; + remaining = Deserialize(remaining, v); + out(i) = v; + } + return remaining; +} + uint64_t* IO::Deserialize(const uint64_t* in, std::vector& out) { diff --git a/source/IO.hpp b/source/IO.hpp index 0440400a..ce9c546b 100644 --- a/source/IO.hpp +++ b/source/IO.hpp @@ -72,7 +72,7 @@ class IO : public LogUser /** @brief Destructor */ - ~IO(); + virtual ~IO() = default; /** @brief Save the entity into a file * @@ -177,6 +177,12 @@ class IO : public LogUser */ std::vector Serialize(const XYZQuat& m); + /** @brief Pack a list to make it writable + * @param m The list + * @return The packed list + */ + std::vector Serialize(const list& m); + /** @brief Pack a list of floating point numbers to make it writable * @param l The list * @return The packed list @@ -207,6 +213,25 @@ class IO : public LogUser */ std::vector Serialize(const std::vector& l); + /** @brief Pack an arbitrarily large vector + * @param l The list + * @return The packed list + */ + template + inline std::vector Serialize( + const Eigen::Matrix& l) + { + std::vector data; + const uint64_t n = l.rows(); + data.push_back(Serialize(n)); + for (uint64_t i = 0; i < n; i++) { + std::vector subdata = Serialize(l(i)); + data.insert(data.end(), subdata.begin(), subdata.end()); + } + return data; + + } + /** @brief Pack a list of lists to make it writable * This function might act recursively * @param l The list @@ -219,7 +244,7 @@ class IO : public LogUser const uint64_t n = l.size(); data.push_back(Serialize(n)); for (auto v : l) { - auto subdata = Serialize(v); + std::vector subdata = Serialize(v); data.insert(data.end(), subdata.begin(), subdata.end()); } return data; @@ -288,6 +313,13 @@ class IO : public LogUser */ uint64_t* Deserialize(const uint64_t* in, XYZQuat& out); + /** @brief Unpack a loaded list + * @param in The pointer to the next unread value + * @param out The unpacked value + * @return The new pointer to the remaining data to be read + */ + uint64_t* Deserialize(const uint64_t* in, list& out); + /** @brief Unpack a loaded list of floating point numbers * @param in The pointer to the next unread value * @param out The unpacked value @@ -323,6 +355,25 @@ class IO : public LogUser */ uint64_t* Deserialize(const uint64_t* in, std::vector& out); + /** @brief Unpack an arbitrarily large vector + * @param in The pointer to the next unread value + * @param out The unpacked vector + * @return The new pointer to the remaining data to be read + */ + template + uint64_t* Deserialize(const uint64_t* in, + Eigen::Matrix& out) + { + uint64_t n; + uint64_t* remaining = Deserialize(in, n); + if (out.rows() != n) + out.resize(n); + for (unsigned int i = 0; i < n; i++) { + remaining = Deserialize(remaining, out(i)); + } + return remaining; + } + /** @brief Unpack a loaded list of lists * * This function might works recursively diff --git a/source/Line.cpp b/source/Line.cpp index 547919cc..64ecda48 100644 --- a/source/Line.cpp +++ b/source/Line.cpp @@ -678,6 +678,25 @@ Line::setState(const std::vector& pos, const std::vector& vel) std::copy(vel.begin(), vel.end(), rd.begin() + 1); } +void +Line::setState(const StateVarRef pos, const StateVarRef vel) +{ + if ((pos.rows() != N - 1) || (vel.rows() != N - 1)) { + LOGERR << "Invalid input size" << endl; + throw moordyn::invalid_value_error("Invalid input size"); + } + for (unsigned int i = 1; i < N; i++) { + if ((pos(i - 1).rows() != 3) || (vel(i - 1).rows() != 3)) { + LOGERR << "Invalid point input size on line " << number + << " node " << i << ". pos size is " << pos(i - 1).rows() + << " and vel size is " << vel(i - 1).rows() << endl; + throw moordyn::invalid_value_error("Invalid input size"); + } + r[i] = pos(i - 1); + rd[i] = vel(i - 1); + } +} + void Line::setEndKinematics(vec pos, vec vel, EndPoints end_point) { @@ -768,7 +787,7 @@ Line::getEndSegmentMoment(EndPoints end_point, EndPoints rod_end_point) const } void -Line::getStateDeriv(std::vector& vel, std::vector& acc) +Line::getStateDeriv(StateVarRef vel, StateVarRef acc) { // NOTE: // Jose Luis Cercos-Pita: This is by far the most consuming function of the @@ -1177,14 +1196,12 @@ Line::getStateDeriv(std::vector& vel, std::vector& acc) } // loop through internal nodes and compute the accelerations - vel.reserve(N - 1); - acc.reserve(N - 1); for (unsigned int i = 1; i < N; i++) { // For small systems it is usually faster to compute the inverse // of the matrix. See // https://eigen.tuxfamily.org/dox/group__TutorialLinearAlgebra.html - acc[i - 1] = M[i].inverse() * Fnet[i]; - vel[i - 1] = rd[i]; + acc(i - 1) = M[i].inverse() * Fnet[i]; + vel(i - 1) = rd[i]; } }; diff --git a/source/Line.hpp b/source/Line.hpp index fdfdf13a..1677e5e3 100644 --- a/source/Line.hpp +++ b/source/Line.hpp @@ -51,6 +51,7 @@ namespace moordyn { class Waves; typedef std::shared_ptr WavesRef; +typedef Eigen::Ref> StateVarRef; /** @class Line Line.hpp * @brief A mooring line @@ -815,9 +816,15 @@ class Line final : public io::IO, public NatFreqCFL * @note This method is not affecting the line end points * @see moordyn::Line::setEndState * @throws invalid_value_error If either @p r or @p u have wrong sizes + * @{ */ void setState(const std::vector& r, const std::vector& u); + void setState(const StateVarRef r, const StateVarRef u); + /** + * @} + */ + /** @brief Set the position and velocity of an end point * @param r Position * @param rd Velocity @@ -834,8 +841,8 @@ class Line final : public io::IO, public NatFreqCFL * @param q The direction unit vector * @param end_point Either ENDPOINT_B or ENDPOINT_A * @param rod_end_point Either ENDPOINT_B or ENDPOINT_A - * @throws invalid_value_error If either @p end_point or @p end_point are - * not valid end point qualifiers + * @throws invalid_value_error If either @p end_point or @p rod_end_point + * are not valid end point qualifiers */ void setEndOrientation(vec q, EndPoints end_point, EndPoints rod_end_point); @@ -850,8 +857,8 @@ class Line final : public io::IO, public NatFreqCFL * @param end_point Either ENDPOINT_B or ENDPOINT_A * @param rod_end_point Either ENDPOINT_B or ENDPOINT_A * @return The moment vector - * @throws invalid_value_error If either @p end_point or @p end_point are - * not valid end point qualifiers + * @throws invalid_value_error If either @p end_point or @p rod_end_point + * are not valid end point qualifiers */ vec getEndSegmentMoment(EndPoints end_point, EndPoints rod_end_point) const; @@ -860,7 +867,7 @@ class Line final : public io::IO, public NatFreqCFL * @param acc Where to store the accelerations of the internal nodes * @throws nan_error If nan values are detected in any node position */ - void getStateDeriv(std::vector& vel, std::vector& acc); + void getStateDeriv(StateVarRef vel, StateVarRef acc); // void initiateStep(vector &rFairIn, vector &rdFairIn, // double time); diff --git a/source/Misc.cpp b/source/Misc.cpp index 2b1a8c28..4c86108a 100644 --- a/source/Misc.cpp +++ b/source/Misc.cpp @@ -187,6 +187,13 @@ XYZQuat::operator-(const XYZQuat& visitor) const result.quat = this->quat.coeffs() - visitor.quat.coeffs(); return result; } +XYZQuat& +XYZQuat::operator*=(const real& visitor) +{ + this->pos *= visitor; + this->quat.coeffs() *= visitor; + return *this; +} XYZQuat XYZQuat::operator*(const real& visitor) const { @@ -360,6 +367,12 @@ GetCurvature(moordyn::real length, const vec& q1, const vec& q2) } // ::moordyn +moordyn::quaternion operator*(const moordyn::real& k, + const moordyn::quaternion& v) +{ + return moordyn::quaternion(k * v.coeffs()); +} + /* References diff --git a/source/Misc.hpp b/source/Misc.hpp index c106fb5b..b62ab2c4 100644 --- a/source/Misc.hpp +++ b/source/Misc.hpp @@ -72,13 +72,19 @@ round(T val) using namespace std; namespace Eigen { -// Eigen does not provide 6 components objects out of the box +// Eigen does not provide 6 or 7 components objects out of the box typedef Matrix Vector6f; typedef Matrix Matrix6f; typedef Matrix Vector6d; typedef Matrix Matrix6d; typedef Matrix Vector6i; typedef Matrix Matrix6i; +typedef Matrix Vector7f; +typedef Matrix Matrix7f; +typedef Matrix Vector7d; +typedef Matrix Matrix7d; +typedef Matrix Vector7i; +typedef Matrix Matrix7i; // It is also convenient for us to define a generic Eigen dynamic matrix class #ifdef MOORDYN_SINGLEPRECISSION typedef MatrixXf MatrixXr; @@ -97,13 +103,16 @@ typedef Eigen::Vector2f vec2; typedef Eigen::Vector3f vec3; typedef Eigen::Vector4f vec4; typedef Eigen::Vector6f vec6; +typedef Eigen::Vector7f vec7; typedef vec3 vec; typedef Eigen::Matrix2f mat2; typedef Eigen::Matrix3f mat3; typedef Eigen::Matrix4f mat4; typedef Eigen::Matrix6f mat6; +typedef Eigen::Matrix7f mat7; typedef mat3 mat; typedef Eigen::Quaternionf quaternion; +typedef Eigen::Matrix list; #else /// Real numbers wrapper. It is either double or float typedef double real; @@ -115,6 +124,8 @@ typedef Eigen::Vector3d vec3; typedef Eigen::Vector4d vec4; /// 6-D vector of real numbers typedef Eigen::Vector6d vec6; +/// 7-D vector of real numbers +typedef Eigen::Vector7d vec7; /// vec3 renaming typedef vec3 vec; /// 2x2 matrix of real numbers @@ -125,10 +136,14 @@ typedef Eigen::Matrix3d mat3; typedef Eigen::Matrix4d mat4; /// 6x6 matrix of real numbers typedef Eigen::Matrix6d mat6; +/// 7x7 matrix of real numbers +typedef Eigen::Matrix7d mat7; /// mat3 renaming typedef mat3 mat; /// Quaternion of real numbers typedef Eigen::Quaterniond quaternion; +/// A resizable list of reals +typedef Eigen::Matrix list; #endif /// 2-D vector of integers typedef Eigen::Vector2i ivec2; @@ -138,6 +153,8 @@ typedef Eigen::Vector3i ivec3; typedef Eigen::Vector4i ivec4; /// 6-D vector of integers typedef Eigen::Vector6i ivec6; +/// 7-D vector of integers +typedef Eigen::Vector7i ivec7; /// Renaming of ivec3 typedef ivec3 ivec; @@ -302,9 +319,13 @@ struct XYZQuat out.tail<3>() = Quat2Euler(this->quat); return out; } - Eigen::Vector toVec7() const + static XYZQuat fromVec7(const vec7& vec) { - Eigen::Vector out; + return XYZQuat{ vec.head<3>(), quaternion(vec.tail<4>()) }; + } + vec7 toVec7() const + { + vec7 out; out.head<3>() = pos; out.tail<4>() = quat.coeffs(); return out; @@ -312,6 +333,7 @@ struct XYZQuat XYZQuat operator+(const XYZQuat& visitor) const; XYZQuat operator-(const XYZQuat& visitor) const; + XYZQuat& operator*=(const real& visitor); XYZQuat operator*(const real& visitor) const; }; @@ -566,6 +588,8 @@ MAKE_EXCEPTION(mem_error) MAKE_EXCEPTION(invalid_value_error) /// Exception thrown when invalid values are found MAKE_EXCEPTION(non_implemented_error) +/// Exception thrown when an invalid type is asked +MAKE_EXCEPTION(invalid_type_error) /// Exception thrown for other uhandled errors MAKE_EXCEPTION(unhandled_error) @@ -1037,6 +1061,9 @@ typedef struct _FailProps } // ::moordyn +moordyn::quaternion operator*(const moordyn::real& k, + const moordyn::quaternion& v); + const int nCoef = 30; // maximum number of entries to allow in nonlinear // coefficient lookup tables diff --git a/source/MoorDyn2.cpp b/source/MoorDyn2.cpp index 62d3d636..11573ddf 100644 --- a/source/MoorDyn2.cpp +++ b/source/MoorDyn2.cpp @@ -351,7 +351,7 @@ moordyn::MoorDyn::icStationary() LOGMSG << "Finalizing ICs using static solve" << endl; - StationaryScheme t_integrator(_log, waves); + time::StationaryScheme t_integrator(_log, waves); t_integrator.SetGround(GroundBody); for (auto obj : BodyList) t_integrator.AddBody(obj); @@ -1644,7 +1644,7 @@ moordyn::MoorDyn::ReadInFile() string err_msg; if (!_t_integrator) { try { - _t_integrator = create_time_scheme("RK2", _log, waves); + _t_integrator = time::create_time_scheme("RK2", _log, waves); } MOORDYN_CATCHER(err, err_msg); if (err != MOORDYN_SUCCESS) { @@ -2147,7 +2147,7 @@ moordyn::MoorDyn::readOptionsLine(vector& in_txt, int i) moordyn::error_id err = MOORDYN_SUCCESS; string err_msg; try { - _t_integrator = create_time_scheme(value, _log, waves); + _t_integrator = time::create_time_scheme(value, _log, waves); } MOORDYN_CATCHER(err, err_msg); if (err != MOORDYN_SUCCESS) { @@ -2717,7 +2717,7 @@ int DECLDIR MoorDyn_GetTimeScheme(MoorDyn system, char* name, size_t* name_len) { CHECK_SYSTEM(system); - moordyn::TimeScheme* tscheme = ((moordyn::MoorDyn*)system)->GetTimeScheme(); + auto tscheme = ((moordyn::MoorDyn*)system)->GetTimeScheme(); std::string out = tscheme->GetName(); if (name_len) *name_len = out.size() + 1; @@ -2735,9 +2735,10 @@ MoorDyn_SetTimeScheme(MoorDyn system, const char* name) moordyn::error_id err = MOORDYN_SUCCESS; string err_msg; moordyn::MoorDyn* sys = (moordyn::MoorDyn*)system; - moordyn::TimeScheme* tscheme; + moordyn::time::Scheme* tscheme; try { - tscheme = create_time_scheme(name, sys->GetLogger(), sys->GetWaves()); + tscheme = moordyn::time::create_time_scheme( + name, sys->GetLogger(), sys->GetWaves()); } MOORDYN_CATCHER(err, err_msg); if (err != MOORDYN_SUCCESS) { @@ -2759,7 +2760,7 @@ MoorDyn_SaveState(MoorDyn system, const char* filepath) moordyn::error_id err = MOORDYN_SUCCESS; string err_msg; try { - moordyn::TimeScheme* t = ((moordyn::MoorDyn*)system)->GetTimeScheme(); + auto t = ((moordyn::MoorDyn*)system)->GetTimeScheme(); t->SaveState(filepath); } MOORDYN_CATCHER(err, err_msg); @@ -2777,7 +2778,7 @@ MoorDyn_LoadState(MoorDyn system, const char* filepath) moordyn::error_id err = MOORDYN_SUCCESS; string err_msg; try { - moordyn::TimeScheme* t = ((moordyn::MoorDyn*)system)->GetTimeScheme(); + auto t = ((moordyn::MoorDyn*)system)->GetTimeScheme(); t->LoadState(filepath); } MOORDYN_CATCHER(err, err_msg); diff --git a/source/MoorDyn2.hpp b/source/MoorDyn2.hpp index 28e53968..d81c08ba 100644 --- a/source/MoorDyn2.hpp +++ b/source/MoorDyn2.hpp @@ -370,12 +370,12 @@ class MoorDyn final : public io::IO /** @brief Get the current time integrator * @return The time integrator */ - inline TimeScheme* GetTimeScheme() const { return _t_integrator; } + inline time::Scheme* GetTimeScheme() const { return _t_integrator; } /** @brief Set the current time integrator * @return The time integrator */ - inline void SetTimeScheme(TimeScheme* tscheme) { + inline void SetTimeScheme(time::Scheme* tscheme) { if (_t_integrator) delete _t_integrator; _t_integrator = tscheme; _t_integrator->SetGround(GroundBody); @@ -572,7 +572,7 @@ class MoorDyn final : public io::IO real dtOut; /// The time integration scheme - TimeScheme* _t_integrator; + time::Scheme* _t_integrator; /// General options of the Mooring system EnvCondRef env; diff --git a/source/Rod.hpp b/source/Rod.hpp index 9851a404..e882b115 100644 --- a/source/Rod.hpp +++ b/source/Rod.hpp @@ -466,9 +466,18 @@ class Rod final : public io::IO, public SuperCFL * pinned rods) * @throws invalid_value_error If the rod is not of type FREE, CPLDPIN or * PINNED + * @{ */ void setState(XYZQuat pos, vec6 vel); + inline void setState(vec7 r, vec6 rd) + { + setState(XYZQuat::fromVec7(r), rd); + } + /** + * @} + */ + /** @brief Called at the beginning of each coupling step to update the * boundary conditions (rod kinematics) for the proceeding time steps * @param r The input position diff --git a/source/State.cpp b/source/State.cpp index ea42afb4..0da381dd 100644 --- a/source/State.cpp +++ b/source/State.cpp @@ -34,900 +34,477 @@ using namespace std; namespace moordyn { -template<> -string -StateVar::AsString() const -{ - stringstream s; - s << "pos = [" << pos.transpose() << "]; "; - s << "vel = [" << vel.transpose() << "]" << endl; - return s.str(); -} - -template<> -string -StateVar::AsString() const -{ - stringstream s; - s << "pos = [" << pos.transpose() << "]; "; - s << "vel = [" << vel.transpose() << "]" << endl; - return s.str(); -} - -template<> -string -StateVar::AsString() const -{ - stringstream s; - s << "pos = [" << pos.toVec7().transpose() << "]; "; - s << "vel = [" << vel.transpose() << "]" << endl; - return s.str(); -} - -template<> -string -StateVar>::AsString() const -{ - stringstream s; - s << "pos = ["; - for (auto v : pos) - s << "[" << v.transpose() << "], "; - s << "]" << endl; - s << "vel = ["; - for (auto v : vel) - s << "[" << v.transpose() << "], "; - s << "]" << endl; - return s.str(); -} - -template<> -StateVar -StateVar::operator+(const StateVar& rhs) -{ - StateVar out; - out.pos = pos + rhs.pos; - out.vel = vel + rhs.vel; - return out; -} - -template<> -StateVar -StateVar::operator+(const StateVar& rhs) -{ - StateVar out; - out.pos = pos + rhs.pos; - out.vel = vel + rhs.vel; - return out; -} - -template<> -StateVar -StateVar::operator+(const StateVar& rhs) -{ - StateVar out; - out.pos = pos + rhs.pos; - out.vel = vel + rhs.vel; - return out; -} - -template<> -StateVar -StateVar::operator-(const StateVar& rhs) -{ - StateVar out; - out.pos = pos + rhs.pos; - out.vel = vel + rhs.vel; - return out; -} +namespace state { -template<> -StateVar> -StateVar>::operator+(const StateVar>& rhs) +void +State::addLine(Line* obj) { - if ((pos.size() != rhs.pos.size()) || (vel.size() != rhs.vel.size())) - throw moordyn::invalid_value_error("Invalid input size"); - StateVar> out; - out.pos.reserve(pos.size()); - out.vel.reserve(vel.size()); - // NOTE: At this point we are assuming that both pos and vel have the same - // length - for (unsigned int i = 0; i < pos.size(); i++) { - out.pos.push_back(pos[i] + rhs.pos[i]); - out.vel.push_back(vel[i] + rhs.vel[i]); + if (std::find(lines.begin(), lines.end(), obj) != lines.end()) { + throw moordyn::invalid_value_error("Repeated line"); } - return out; -} - -template<> -StateVar -StateVar::operator-(const StateVar& rhs) -{ - StateVar out; - out.pos = pos - rhs.pos; - out.vel = vel - rhs.vel; - return out; + lines.push_back(obj); + resize(); } -template<> -StateVar -StateVar::operator-(const StateVar& rhs) +unsigned int +State::removeLine(Line* obj) { - StateVar out; - out.pos = pos - rhs.pos; - out.vel = vel - rhs.vel; - return out; -} - -template<> -StateVar> -StateVar>::operator-(const StateVar>& rhs) -{ - if ((pos.size() != rhs.pos.size()) || (vel.size() != rhs.vel.size())) - throw moordyn::invalid_value_error("Invalid input size"); - StateVar> out; - out.pos.reserve(pos.size()); - out.vel.reserve(vel.size()); - // NOTE: At this point we are assuming that both pos and vel have the same - // length - for (unsigned int i = 0; i < pos.size(); i++) { - out.pos.push_back(pos[i] - rhs.pos[i]); - out.vel.push_back(vel[i] - rhs.vel[i]); + auto it = std::find(lines.begin(), lines.end(), obj); + if (it == lines.end()) { + throw moordyn::invalid_value_error("Missing line"); } - return out; -} - -template<> -void -StateVar::Mix(const StateVar& rhs, const real& f) -{ - pos = pos * (1.0 - f) + rhs.pos * f; - vel = vel * (1.0 - f) + rhs.vel * f; + const unsigned int i = std::distance(lines.begin(), it); + lines.erase(it); + resize(); + return i; } -template<> void -StateVar::Mix(const StateVar& rhs, const real& f) +State::addPoint(Point* obj) { - pos = pos * (1.0 - f) + rhs.pos * f; - vel = vel * (1.0 - f) + rhs.vel * f; + if (std::find(points.begin(), points.end(), obj) != points.end()) { + throw moordyn::invalid_value_error("Repeated point"); + } + points.push_back(obj); + resize(); } -template<> -void -StateVar::Mix(const StateVar& rhs, const real& f) +unsigned int +State::removePoint(Point* obj) { - pos = pos * (1.0 - f) + rhs.pos * f; - vel = vel * (1.0 - f) + rhs.vel * f; + auto it = std::find(points.begin(), points.end(), obj); + if (it == points.end()) { + throw moordyn::invalid_value_error("Missing point"); + } + const unsigned int i = std::distance(points.begin(), it); + points.erase(it); + resize(); + return i; } -template<> void -StateVar>::Mix(const StateVar>& rhs, const real& f) +State::addRod(Rod* obj) { - for (unsigned int i = 0; i < pos.size(); i++) { - pos[i] = pos[i] * (1.0 - f) + rhs.pos[i] * f; - vel[i] = vel[i] * (1.0 - f) + rhs.vel[i] * f; + if (std::find(rods.begin(), rods.end(), obj) != rods.end()) { + throw moordyn::invalid_value_error("Repeated rod"); } + rods.push_back(obj); + resize(); } -template<> -string -StateVarDeriv::AsString() const -{ - stringstream s; - s << "vel = [" << vel.transpose() << "]; "; - s << "acc = [" << acc.transpose() << "]" << endl; - return s.str(); -} - -template<> -string -StateVarDeriv::AsString() const +unsigned int +State::removeRod(Rod* obj) { - stringstream s; - s << "vel = [" << vel.transpose() << "]; "; - s << "acc = [" << acc.transpose() << "]" << endl; - return s.str(); -} - -template<> -string -StateVarDeriv::AsString() const -{ - stringstream s; - s << "vel = [" << vel.toVec7().transpose() << "]; "; - s << "acc = [" << acc.transpose() << "]" << endl; - return s.str(); -} - -template<> -string -StateVarDeriv>::AsString() const -{ - stringstream s; - s << "vel = ["; - for (auto v : vel) - s << "[" << v.transpose() << "], "; - s << "]" << endl; - s << "acc = ["; - for (auto v : acc) - s << "[" << v.transpose() << "], "; - s << "]" << endl; - return s.str(); -} - -template<> -StateVar -StateVarDeriv::operator*(const real& dt) -{ - StateVar out; - out.pos = vel * dt; - out.vel = acc * dt; - return out; -} - -template<> -StateVar -StateVarDeriv::operator*(const real& dt) -{ - StateVar out; - out.pos = vel * dt; - out.vel = acc * dt; - return out; -} - -template<> -StateVar -StateVarDeriv::operator*(const real& dt) -{ - StateVar out; - out.pos = vel * dt; - out.vel = acc * dt; - return out; -} - -template<> -StateVar> -StateVarDeriv>::operator*(const real& dt) -{ - StateVar> out; - out.pos.reserve(vel.size()); - out.vel.reserve(acc.size()); - // NOTE: At this point we are assuming that both vel and acc have the same - // length - for (unsigned int i = 0; i < vel.size(); i++) { - out.pos.push_back(vel[i] * dt); - out.vel.push_back(acc[i] * dt); + auto it = std::find(rods.begin(), rods.end(), obj); + if (it == rods.end()) { + throw moordyn::invalid_value_error("Missing rod"); } - return out; -} - -template<> -StateVarDeriv -StateVarDeriv::operator+(const StateVarDeriv& rhs) -{ - StateVarDeriv out; - out.vel = vel + rhs.vel; - out.acc = acc + rhs.acc; - return out; -} - -template<> -StateVarDeriv -StateVarDeriv::operator+(const StateVarDeriv& rhs) -{ - StateVarDeriv out; - out.vel = vel + rhs.vel; - out.acc = acc + rhs.acc; - return out; -} - -template<> -StateVarDeriv -StateVarDeriv::operator+(const StateVarDeriv& rhs) -{ - StateVarDeriv out; - out.vel = vel + rhs.vel; - out.acc = acc + rhs.acc; - return out; + const unsigned int i = std::distance(rods.begin(), it); + rods.erase(it); + resize(); + return i; } -template<> -StateVarDeriv> -StateVarDeriv>::operator+( - const StateVarDeriv>& rhs) +void +State::addBody(Body* obj) { - if ((vel.size() != rhs.vel.size()) || (acc.size() != rhs.acc.size())) - throw moordyn::invalid_value_error("Invalid input size"); - StateVarDeriv> out; - out.vel.reserve(vel.size()); - out.acc.reserve(acc.size()); - // NOTE: At this point we are assuming that both vel and acc have the same - // length - for (unsigned int i = 0; i < vel.size(); i++) { - out.vel.push_back(vel[i] + rhs.vel[i]); - out.acc.push_back(acc[i] + rhs.acc[i]); + if (std::find(bodies.begin(), bodies.end(), obj) != bodies.end()) { + throw moordyn::invalid_value_error("Repeated body"); } - return out; -} - -template<> -StateVarDeriv -StateVarDeriv::operator-(const StateVarDeriv& rhs) -{ - StateVarDeriv out; - out.vel = vel - rhs.vel; - out.acc = acc - rhs.acc; - return out; + bodies.push_back(obj); + resize(); } -template<> -StateVarDeriv -StateVarDeriv::operator-(const StateVarDeriv& rhs) +unsigned int +State::removeBody(Body* obj) { - StateVarDeriv out; - out.vel = vel - rhs.vel; - out.acc = acc - rhs.acc; - return out; -} - -template<> -StateVarDeriv -StateVarDeriv::operator-(const StateVarDeriv& rhs) -{ - StateVarDeriv out; - out.vel = vel - rhs.vel; - out.acc = acc - rhs.acc; - return out; -} - -template<> -StateVarDeriv> -StateVarDeriv>::operator-( - const StateVarDeriv>& rhs) -{ - if ((vel.size() != rhs.vel.size()) || (acc.size() != rhs.acc.size())) - throw moordyn::invalid_value_error("Invalid input size"); - StateVarDeriv> out; - out.vel.reserve(vel.size()); - out.acc.reserve(acc.size()); - // NOTE: At this point we are assuming that both vel and acc have the same - // length - for (unsigned int i = 0; i < vel.size(); i++) { - out.vel.push_back(vel[i] - rhs.vel[i]); - out.acc.push_back(acc[i] - rhs.acc[i]); + auto it = std::find(bodies.begin(), bodies.end(), obj); + if (it == bodies.end()) { + throw moordyn::invalid_value_error("Missing body"); } - return out; -} + const unsigned int i = std::distance(bodies.begin(), it); + bodies.erase(it); + resize(); + return i; +} + +#define TYPE_GETTER(T, TDEF) \ +template <> \ +VarBase::types \ +State::getType() \ +{ \ + return VarBase::types::TDEF; \ +} + +TYPE_GETTER(real, REAL) +TYPE_GETTER(VarScalar, REAL) +TYPE_GETTER(vec, VEC) +TYPE_GETTER(VarVec, VEC) +TYPE_GETTER(vec6, VEC6) +TYPE_GETTER(VarVec6, VEC6) +TYPE_GETTER(XYZQuat, QUAT) +TYPE_GETTER(VarQuat, QUAT) +TYPE_GETTER(list, LIST) +TYPE_GETTER(VarList, LIST) + +#define STATE_ADDER(T, TBASE) \ +template <> \ +void \ +State::addVar(const char* name) \ +{ \ + TBASE* var = new TBASE(); \ + var->resize(ndof()); \ + vars[name] = var; \ + types[name] = getType(); \ +} + + +STATE_ADDER(VarScalar, VarScalar) +STATE_ADDER(real, VarScalar) +STATE_ADDER(VarVec, VarVec) +STATE_ADDER(vec, VarVec) +STATE_ADDER(VarVec6, VarVec6) +STATE_ADDER(vec6, VarVec6) +STATE_ADDER(VarQuat, VarQuat) +STATE_ADDER(XYZQuat, VarQuat) +STATE_ADDER(VarList, VarList) +STATE_ADDER(list, VarList) -template<> -real StateVarDeriv::MakeStationary(const real &dt) -{ - real ret = acc.norm(); - vel = 0.5 * dt * acc; - acc = vec::Zero(); - return ret; -} - -template<> -real StateVarDeriv::MakeStationary(const real &dt) -{ - real ret = acc.head<3>().norm(); - vel = 0.5 * dt * acc; - acc = vec6::Zero(); - return ret; -} - -template<> -real StateVarDeriv::MakeStationary(const real &dt) -{ - real ret = acc.head<3>().norm(); - vel = XYZQuat::fromVec6(0.5 * dt * acc); - acc = vec6::Zero(); - return ret; -} - -template<> -real StateVarDeriv>::MakeStationary(const real &dt) -{ - real ret = 0.0; - for (unsigned int i = 0; i < vel.size(); i++) { - ret += acc[i].norm(); - vel[i] = 0.5 * dt * acc[i]; - acc[i] = vec::Zero(); +void +State::addVar(const char* name, VarBase::types t) +{ + switch (t) { + case VarBase::types::REAL: + addVar(name); + break; + case VarBase::types::VEC: + addVar(name); + break; + case VarBase::types::VEC6: + addVar(name); + break; + case VarBase::types::QUAT: + addVar(name); + break; + case VarBase::types::LIST: + addVar(name); + break; + default: + throw moordyn::invalid_type_error("Unrecognized variable type"); } - return ret; } -template<> -StateVarDeriv -StateVarDeriv::Newmark( - const StateVarDeriv& visitor, const real& dt, - real gamma, real beta) -{ - StateVarDeriv ret; - const vec acc_gamma = (1 - gamma) * acc + gamma * visitor.acc; - const vec acc_beta = (0.5 - beta) * acc + beta * visitor.acc; - ret.vel = vel + dt * acc_beta; - ret.acc = acc_gamma; - return ret; -} - -template<> -StateVarDeriv -StateVarDeriv::Newmark( - const StateVarDeriv& visitor, const real& dt, - real gamma, real beta) -{ - StateVarDeriv ret; - const vec6 acc_gamma = (1 - gamma) * acc + gamma * visitor.acc; - const vec6 acc_beta = (0.5 - beta) * acc + beta * visitor.acc; - ret.vel = vel + dt * acc_beta; - ret.acc = acc_gamma; - return ret; -} - -template<> -StateVarDeriv -StateVarDeriv::Newmark( - const StateVarDeriv& visitor, const real& dt, - real gamma, real beta) -{ - StateVarDeriv ret; - const vec6 acc_gamma = (1 - gamma) * acc + gamma * visitor.acc; - const vec6 acc_beta = (0.5 - beta) * acc + beta * visitor.acc; - ret.vel = vel + XYZQuat::fromVec6(dt * acc_beta); - ret.acc = acc_gamma; - return ret; -} - -template<> -StateVarDeriv> -StateVarDeriv>::Newmark( - const StateVarDeriv>& visitor, const real& dt, - real gamma, real beta) +void +State::setListLength(const char* name, size_t n, void* obj) { - StateVarDeriv> ret; - ret.vel.reserve(vel.size()); - ret.acc.reserve(acc.size()); - for (unsigned int i = 0; i < vel.size(); i++) { - const vec acc_gamma = (1 - gamma) * acc[i] + gamma * visitor.acc[i]; - const vec acc_beta = (0.5 - beta) * acc[i] + beta * visitor.acc[i]; - ret.vel.push_back(vel[i] + dt * acc_beta); - ret.acc.push_back(acc_gamma); + checkVar(name); + std::pair ids = obj ? + indexes[obj] : std::make_pair((size_t)0, ndof()); + for (size_t i = ids.first; i < ids.second; i++) { + ((VarList*)vars[name])->operator()(i).resize(n); } - return ret; } -template<> -StateVarDeriv -StateVarDeriv::Wilson( - const StateVarDeriv& visitor, const real& tau, const real& dt) -{ - StateVarDeriv ret; - const real f = tau / dt; - ret.acc = (1 - 0.5 * f) * acc + 0.5 * f * visitor.acc; - ret.vel = vel + 0.5 * dt * ( - (1 - 1.0 / 3.0 * f) * acc + 1.0 / 3.0 * f * visitor.acc); - return ret; -} - -template<> -StateVarDeriv -StateVarDeriv::Wilson( - const StateVarDeriv& visitor, const real& tau, const real& dt) -{ - StateVarDeriv ret; - const real f = tau / dt; - ret.acc = (1 - 0.5 * f) * acc + 0.5 * f * visitor.acc; - ret.vel = vel + 0.5 * dt * ( - (1 - 1.0 / 3.0 * f) * acc + 1.0 / 3.0 * f * visitor.acc); - return ret; -} - -template<> -StateVarDeriv -StateVarDeriv::Wilson( - const StateVarDeriv& visitor, - const real& tau, - const real& dt) -{ - StateVarDeriv ret; - const real f = tau / dt; - ret.acc = (1 - 0.5 * f) * acc + 0.5 * f * visitor.acc; - ret.vel = vel + XYZQuat::fromVec6(0.5 * dt * ( - (1 - 1.0 / 3.0 * f) * acc + 1.0 / 3.0 * f * visitor.acc)); - return ret; -} - -template<> -StateVarDeriv> -StateVarDeriv>::Wilson( - const StateVarDeriv>& visitor, - const real& tau, - const real& dt) -{ - StateVarDeriv> ret; - ret.vel.reserve(vel.size()); - ret.acc.reserve(acc.size()); - const real f = tau / dt; - const real f2 = 0.5 * f; - const real f3 = 1.0 / 3.0 * f; - for (unsigned int i = 0; i < vel.size(); i++) { - ret.acc.push_back((1 - f2) * acc[i] + f2 * visitor.acc[i]); - ret.vel.push_back(vel[i] + 0.5 * dt * ( - (1 - f3) * acc[i] + f3 * visitor.acc[i])); +#define STATE_SETTER(T, TBASE) \ +template <> \ +void \ +State::set(const char* name, \ + Eigen::Matrix v) \ +{ \ + checkVar(name); \ + TBASE* var = (TBASE*)vars[name]; \ + if(var->rows() != v.rows()) \ + throw moordyn::invalid_value_error("Inconsistent lengths"); \ + var->asMatrix() = v; \ +} + +STATE_SETTER(real, VarScalar) +STATE_SETTER(vec, VarVec) +STATE_SETTER(vec6, VarVec6) +STATE_SETTER(XYZQuat, VarQuat) +STATE_SETTER(list, VarList) + +#define STATE_OBJ_SETTER(T, TBASE) \ +template <> \ +void \ +State::set(const char* name, \ + void* obj, \ + Eigen::Matrix v) \ +{ \ + checkVar(name); \ + TBASE* var = (TBASE*)vars[name]; \ + auto ids = indexes[obj]; \ + if((ids.second - ids.first) != v.rows()) \ + throw moordyn::invalid_value_error("Inconsistent lengths"); \ + var->operator()(Eigen::seq(ids.first, ids.second - 1)) = v; \ +} + +STATE_OBJ_SETTER(real, VarScalar) +STATE_OBJ_SETTER(vec, VarVec) +STATE_OBJ_SETTER(vec6, VarVec6) +STATE_OBJ_SETTER(XYZQuat, VarQuat) +STATE_OBJ_SETTER(list, VarList) + +std::vector +State::Serialize(void) +{ + std::vector data, subdata; + + for (const auto& [key, var] : vars) { + switch (var->inner_type()) { + case VarBase::types::REAL: + subdata = io::IO::Serialize(((VarScalar*)var)->asMatrix()); + break; + case VarBase::types::VEC: + subdata = io::IO::Serialize(((VarVec*)var)->asMatrix()); + break; + case VarBase::types::VEC6: + subdata = io::IO::Serialize(((VarVec6*)var)->asMatrix()); + break; + case VarBase::types::QUAT: + subdata = io::IO::Serialize(((VarQuat*)var)->asMatrix()); + break; + case VarBase::types::LIST: + subdata = io::IO::Serialize(((VarList*)var)->asMatrix()); + break; + default: + throw moordyn::invalid_type_error("Unhandled variable type"); + } + data.insert(data.end(), subdata.begin(), subdata.end()); } - return ret; -} - -template<> -void -StateVarDeriv::Mix(const StateVarDeriv& rhs, const real& f) -{ - vel = vel * (1.0 - f) + rhs.vel * f; - acc = acc * (1.0 - f) + rhs.acc * f; + return data; +} + +uint64_t* +State::Deserialize(const uint64_t* data) +{ + uint64_t* ptr = (uint64_t*)data; + for (const auto& [key, var] : vars) { + switch (var->inner_type()) { + case VarBase::types::REAL: + ptr = io::IO::Deserialize(ptr, ((VarScalar*)var)->asMatrix()); + break; + case VarBase::types::VEC: + ptr = io::IO::Deserialize(ptr, ((VarVec*)var)->asMatrix()); + break; + case VarBase::types::VEC6: + ptr = io::IO::Deserialize(ptr, ((VarVec6*)var)->asMatrix()); + break; + case VarBase::types::QUAT: + ptr = io::IO::Deserialize(ptr, ((VarQuat*)var)->asMatrix()); + break; + case VarBase::types::LIST: + ptr = io::IO::Deserialize(ptr, ((VarList*)var)->asMatrix()); + break; + default: + throw moordyn::invalid_type_error("Unhandled variable type"); + } + } + return ptr; } -template<> -void -StateVarDeriv::Mix(const StateVarDeriv& rhs, const real& f) -{ - vel = vel * (1.0 - f) + rhs.vel * f; - acc = acc * (1.0 - f) + rhs.acc * f; +#define STATE_GETTER(T, TBASE) \ +template <> \ +Eigen::Matrix& \ +State::getRef(const char* name) \ +{ \ + checkVar(name); \ + TBASE* var = (TBASE*)vars[name]; \ + return var->asMatrix(); \ } -template<> -void -StateVarDeriv::Mix(const StateVarDeriv& rhs, const real& f) -{ - vel = vel * (1.0 - f) + rhs.vel * f; - acc = acc * (1.0 - f) + rhs.acc * f; -} +STATE_GETTER(real, VarScalar) +STATE_GETTER(vec, VarVec) +STATE_GETTER(vec6, VarVec6) +STATE_GETTER(XYZQuat, VarQuat) +STATE_GETTER(list, VarList) -template<> void -StateVarDeriv>::Mix(const StateVarDeriv>& rhs, const real& f) -{ - for (unsigned int i = 0; i < vel.size(); i++) { - vel[i] = vel[i] * (1.0 - f) + rhs.vel[i] * f; - acc[i] = acc[i] * (1.0 - f) + rhs.acc[i] * f; - } -} - -string -MoorDynState::AsString() const -{ - stringstream s; - for (unsigned int i = 0; i < lines.size(); i++) { - s << "Line " << i << ":" << endl; - s << lines[i].AsString(); - } - for (unsigned int i = 0; i < points.size(); i++) { - s << "Point " << i << ":" << endl; - s << points[i].AsString(); - } - for (unsigned int i = 0; i < rods.size(); i++) { - s << "Rod " << i << ":" << endl; - s << rods[i].AsString(); +State::clear() +{ + for (auto& [key, value] : vars) { + switch (value->inner_type()) { + case VarBase::types::REAL: + delete (VarScalar*)value; + break; + case VarBase::types::VEC: + delete (VarVec*)value; + break; + case VarBase::types::VEC6: + delete (VarVec6*)value; + break; + case VarBase::types::QUAT: + delete (VarQuat*)value; + break; + case VarBase::types::LIST: + delete (VarList*)value; + break; + default: + break; + } } - for (unsigned int i = 0; i < bodies.size(); i++) { - s << "Body " << i << ":" << endl; - s << bodies[i].AsString(); - } - s << endl; - return s.str(); -} - -MoorDynState& -MoorDynState::operator=(const MoorDynState& rhs) -{ + vars.clear(); + types.clear(); lines.clear(); - lines.reserve(rhs.lines.size()); - for (auto l : rhs.lines) - lines.push_back(l); points.clear(); - points.reserve(rhs.points.size()); - for (auto l : rhs.points) - points.push_back(l); rods.clear(); - rods.reserve(rhs.rods.size()); - for (auto l : rhs.rods) - rods.push_back(l); bodies.clear(); - bodies.reserve(rhs.bodies.size()); - for (auto l : rhs.bodies) - bodies.push_back(l); - - return *this; -} - -MoorDynState -MoorDynState::operator+(const MoorDynState& rhs) -{ - MoorDynState out; - - if (lines.size() != rhs.lines.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.lines.reserve(lines.size()); - for (unsigned int i = 0; i < lines.size(); i++) - out.lines.push_back(lines[i] + rhs.lines[i]); - if (points.size() != rhs.points.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.points.reserve(points.size()); - for (unsigned int i = 0; i < points.size(); i++) - out.points.push_back(points[i] + rhs.points[i]); - if (rods.size() != rhs.rods.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.rods.reserve(rods.size()); - for (unsigned int i = 0; i < rods.size(); i++) - out.rods.push_back(rods[i] + rhs.rods[i]); - if (bodies.size() != rhs.bodies.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.bodies.reserve(bodies.size()); - for (unsigned int i = 0; i < bodies.size(); i++) - out.bodies.push_back(bodies[i] + rhs.bodies[i]); - - return out; -} - -MoorDynState -MoorDynState::operator-(const MoorDynState& rhs) -{ - MoorDynState out; - - if (lines.size() != rhs.lines.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.lines.reserve(lines.size()); - for (unsigned int i = 0; i < lines.size(); i++) - out.lines.push_back(lines[i] - rhs.lines[i]); - if (points.size() != rhs.points.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.points.reserve(points.size()); - for (unsigned int i = 0; i < points.size(); i++) - out.points.push_back(points[i] - rhs.points[i]); - if (rods.size() != rhs.rods.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.rods.reserve(rods.size()); - for (unsigned int i = 0; i < rods.size(); i++) - out.rods.push_back(rods[i] - rhs.rods[i]); - if (bodies.size() != rhs.bodies.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.bodies.reserve(bodies.size()); - for (unsigned int i = 0; i < bodies.size(); i++) - out.bodies.push_back(bodies[i] - rhs.bodies[i]); - - return out; + indexes.clear(); } void -MoorDynState::Mix(const MoorDynState& visitor, const real& f) +State::copy(const State& rhs) { - for (unsigned int i = 0; i < lines.size(); i++) - lines[i].Mix(visitor.lines[i], f); - for (unsigned int i = 0; i < points.size(); i++) - points[i].Mix(visitor.points[i], f); - for (unsigned int i = 0; i < rods.size(); i++) - rods[i].Mix(visitor.rods[i], f); - for (unsigned int i = 0; i < bodies.size(); i++) - bodies[i].Mix(visitor.bodies[i], f); -} + clear(); -string -DMoorDynStateDt::AsString() const -{ - stringstream s; - for (unsigned int i = 0; i < lines.size(); i++) { - s << "Line " << i << ":" << endl; - s << lines[i].AsString(); - } - for (unsigned int i = 0; i < points.size(); i++) { - s << "Point " << i << ":" << endl; - s << points[i].AsString(); - } - for (unsigned int i = 0; i < rods.size(); i++) { - s << "Rod " << i << ":" << endl; - s << rods[i].AsString(); - } - for (unsigned int i = 0; i < bodies.size(); i++) { - s << "Body " << i << ":" << endl; - s << bodies[i].AsString(); - } - s << endl; - return s.str(); -} - -DMoorDynStateDt& -DMoorDynStateDt::operator=(const DMoorDynStateDt& rhs) -{ - lines.clear(); lines.reserve(rhs.lines.size()); for (auto l : rhs.lines) - lines.push_back(l); - points.clear(); + addLine(l); points.reserve(rhs.points.size()); for (auto l : rhs.points) - points.push_back(l); - rods.clear(); + addPoint(l); rods.reserve(rhs.rods.size()); for (auto l : rhs.rods) - rods.push_back(l); - bodies.clear(); + addRod(l); bodies.reserve(rhs.bodies.size()); for (auto l : rhs.bodies) - bodies.push_back(l); - - return *this; + addBody(l); + + for (const auto& [key, var] : rhs.vars) { + addVar(key.c_str(), var->inner_type()); + switch (var->inner_type()) { + case VarBase::types::REAL: + *((VarScalar*)vars[key]) = *((VarScalar*)var); + break; + case VarBase::types::VEC: + *((VarVec*)vars[key]) = *((VarVec*)var); + break; + case VarBase::types::VEC6: + *((VarVec6*)vars[key]) = *((VarVec6*)var); + break; + case VarBase::types::QUAT: + *((VarQuat*)vars[key]) = *((VarQuat*)var); + break; + case VarBase::types::LIST: + *((VarList*)vars[key]) = *((VarList*)var); + break; + default: + throw moordyn::invalid_type_error("Unhandled variable type"); + } + } } -MoorDynState -DMoorDynStateDt::operator*(const real& dt) -{ - MoorDynState out; - - out.lines.reserve(lines.size()); - for (unsigned int i = 0; i < lines.size(); i++) - out.lines.push_back(lines[i] * dt); - out.points.reserve(points.size()); - for (unsigned int i = 0; i < points.size(); i++) - out.points.push_back(points[i] * dt); - out.rods.reserve(rods.size()); - for (unsigned int i = 0; i < rods.size(); i++) - out.rods.push_back(rods[i] * dt); - out.bodies.reserve(bodies.size()); - for (unsigned int i = 0; i < bodies.size(); i++) - out.bodies.push_back(bodies[i] * dt); - - return out; +#define STATE_TYPE_CHECKER(T, TDEF) \ +template <> \ +bool \ +State::checkType(const char* name) \ +{ \ + return types[name] == VarBase::types::TDEF; \ } -DMoorDynStateDt -DMoorDynStateDt::operator+(const DMoorDynStateDt& rhs) -{ - DMoorDynStateDt out; - - if (lines.size() != rhs.lines.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.lines.reserve(lines.size()); - for (unsigned int i = 0; i < lines.size(); i++) - out.lines.push_back(lines[i] + rhs.lines[i]); - if (points.size() != rhs.points.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.points.reserve(points.size()); - for (unsigned int i = 0; i < points.size(); i++) - out.points.push_back(points[i] + rhs.points[i]); - if (rods.size() != rhs.rods.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.rods.reserve(rods.size()); - for (unsigned int i = 0; i < rods.size(); i++) - out.rods.push_back(rods[i] + rhs.rods[i]); - if (bodies.size() != rhs.bodies.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.bodies.reserve(bodies.size()); - for (unsigned int i = 0; i < bodies.size(); i++) - out.bodies.push_back(bodies[i] + rhs.bodies[i]); - - return out; -} +STATE_TYPE_CHECKER(VarScalar, REAL) +STATE_TYPE_CHECKER(real, REAL) +STATE_TYPE_CHECKER(VarVec, VEC) +STATE_TYPE_CHECKER(vec, VEC) +STATE_TYPE_CHECKER(VarVec6, VEC6) +STATE_TYPE_CHECKER(vec6, VEC6) +STATE_TYPE_CHECKER(VarQuat, QUAT) +STATE_TYPE_CHECKER(XYZQuat, QUAT) +STATE_TYPE_CHECKER(VarList, LIST) +STATE_TYPE_CHECKER(list, LIST) -DMoorDynStateDt -DMoorDynStateDt::operator-(const DMoorDynStateDt& rhs) +void State::resize() { - DMoorDynStateDt out; - - if (lines.size() != rhs.lines.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.lines.reserve(lines.size()); - for (unsigned int i = 0; i < lines.size(); i++) - out.lines.push_back(lines[i] - rhs.lines[i]); - if (points.size() != rhs.points.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.points.reserve(points.size()); - for (unsigned int i = 0; i < points.size(); i++) - out.points.push_back(points[i] - rhs.points[i]); - if (rods.size() != rhs.rods.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.rods.reserve(rods.size()); - for (unsigned int i = 0; i < rods.size(); i++) - out.rods.push_back(rods[i] - rhs.rods[i]); - if (bodies.size() != rhs.bodies.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.bodies.reserve(bodies.size()); - for (unsigned int i = 0; i < bodies.size(); i++) - out.bodies.push_back(bodies[i] - rhs.bodies[i]); - - return out; -} + auto indexes_old = indexes; + size_t n_old = 0; + for (const auto& [key, value] : indexes_old) { + n_old = value.second > n_old ? value.second : n_old; + } -real -DMoorDynStateDt::MakeStationary(const real &dt) -{ - real ret = 0.0; - for (unsigned int i = 0; i < lines.size(); i++) - ret += lines[i].MakeStationary(dt); - for (unsigned int i = 0; i < points.size(); i++) - ret += points[i].MakeStationary(dt); - for (unsigned int i = 0; i < rods.size(); i++) - ret += rods[i].MakeStationary(dt); - for (unsigned int i = 0; i < bodies.size(); i++) - ret += bodies[i].MakeStationary(dt); - return ret; + size_t n = ndof(); + + if (n > n_old) { + // A new entity has been added + std::pair ids; + for (const auto& [key, value] : indexes) { + if (indexes_old.find(key) == indexes_old.end()) { + ids = value; + break; + } + } + for (const auto& [key, value] : vars) { + grow(value, n, ids); + } + } else { + // An entity has been removed, find its indexes + std::pair ids; + for (const auto& [key, value] : indexes_old) { + if (indexes.find(key) == indexes.end()) { + ids = value; + break; + } + } + for (const auto& [key, value] : vars) { + shrink(value, n, ids); + } + } } -DMoorDynStateDt -DMoorDynStateDt::Newmark(const DMoorDynStateDt& rhs, - const real& dt, - real gamma, - real beta) -{ - DMoorDynStateDt out; - - if (lines.size() != rhs.lines.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.lines.reserve(lines.size()); - for (unsigned int i = 0; i < lines.size(); i++) - out.lines.push_back(lines[i].Newmark(rhs.lines[i], dt, gamma, beta)); - if (points.size() != rhs.points.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.points.reserve(points.size()); - for (unsigned int i = 0; i < points.size(); i++) - out.points.push_back(points[i].Newmark(rhs.points[i], dt, gamma, beta)); - if (rods.size() != rhs.rods.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.rods.reserve(rods.size()); - for (unsigned int i = 0; i < rods.size(); i++) - out.rods.push_back(rods[i].Newmark(rhs.rods[i], dt, gamma, beta)); - if (bodies.size() != rhs.bodies.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.bodies.reserve(bodies.size()); - for (unsigned int i = 0; i < bodies.size(); i++) - out.bodies.push_back(bodies[i].Newmark(rhs.bodies[i], dt, gamma, beta)); - - return out; +void State::grow(VarBase* var, size_t n, std::pair ids) +{ + switch (var->inner_type()) { + case VarBase::types::REAL: + grow(((VarScalar*)var)->asMatrix(), n, ids); + break; + case VarBase::types::VEC: + grow(((VarVec*)var)->asMatrix(), n, ids); + break; + case VarBase::types::VEC6: + grow(((VarVec6*)var)->asMatrix(), n, ids); + break; + case VarBase::types::QUAT: + grow(((VarQuat*)var)->asMatrix(), n, ids); + break; + case VarBase::types::LIST: + grow(((VarList*)var)->asMatrix(), n, ids); + break; + default: + throw moordyn::invalid_type_error("Unrecognized variable type"); + } } -DMoorDynStateDt -DMoorDynStateDt::Wilson(const DMoorDynStateDt& rhs, - const real& tau, - const real& dt) -{ - DMoorDynStateDt out; - - if (lines.size() != rhs.lines.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.lines.reserve(lines.size()); - for (unsigned int i = 0; i < lines.size(); i++) - out.lines.push_back(lines[i].Wilson(rhs.lines[i], tau, dt)); - if (points.size() != rhs.points.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.points.reserve(points.size()); - for (unsigned int i = 0; i < points.size(); i++) - out.points.push_back(points[i].Wilson(rhs.points[i], tau, dt)); - if (rods.size() != rhs.rods.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.rods.reserve(rods.size()); - for (unsigned int i = 0; i < rods.size(); i++) - out.rods.push_back(rods[i].Wilson(rhs.rods[i], tau, dt)); - if (bodies.size() != rhs.bodies.size()) - throw moordyn::invalid_value_error("Invalid input size"); - out.bodies.reserve(bodies.size()); - for (unsigned int i = 0; i < bodies.size(); i++) - out.bodies.push_back(bodies[i].Wilson(rhs.bodies[i], tau, dt)); - - return out; +void State::shrink(VarBase* var, size_t n, std::pair ids) +{ + switch (var->inner_type()) { + case VarBase::types::REAL: + shrink(((VarScalar*)var)->asMatrix(), n, ids); + break; + case VarBase::types::VEC: + shrink(((VarVec*)var)->asMatrix(), n, ids); + break; + case VarBase::types::VEC6: + shrink(((VarVec6*)var)->asMatrix(), n, ids); + break; + case VarBase::types::QUAT: + shrink(((VarQuat*)var)->asMatrix(), n, ids); + break; + case VarBase::types::LIST: + shrink(((VarList*)var)->asMatrix(), n, ids); + break; + default: + throw moordyn::invalid_type_error("Unrecognized variable type"); + } } -void -DMoorDynStateDt::Mix(const DMoorDynStateDt& visitor, const real& f) -{ - for (unsigned int i = 0; i < lines.size(); i++) - lines[i].Mix(visitor.lines[i], f); - for (unsigned int i = 0; i < points.size(); i++) - points[i].Mix(visitor.points[i], f); - for (unsigned int i = 0; i < rods.size(); i++) - rods[i].Mix(visitor.rods[i], f); - for (unsigned int i = 0; i < bodies.size(); i++) - bodies[i].Mix(visitor.bodies[i], f); -} +} // ::state } // ::moordyn + +moordyn::state::VarListBase operator*(const moordyn::real& k, + moordyn::state::VarListBase v) +{ + for (unsigned int i = 0; i < v.rows(); i++) { + v(i) *= k; + } + return v; +} diff --git a/source/State.hpp b/source/State.hpp index acc735a9..5de794d8 100644 --- a/source/State.hpp +++ b/source/State.hpp @@ -35,457 +35,581 @@ #pragma once #include "Misc.hpp" -#include +#include "Line.hpp" +#include "IO.hpp" +#include "Point.hpp" +#include "Rod.hpp" +#include "Body.hpp" +#include +#include #include +#include #include namespace moordyn { -/** @class StateVar Time.hpp - * @brief Generic state variables - * - * This is holding the position and velocities - */ -template -class StateVar -{ - public: - /// The position - T pos; - /// The velocity - V vel; +namespace state { - /** @brief Give a string representation of the state variables - * - * Useful for debugging purposes - * @return A string representation - */ - string AsString() const; - - /** @brief Copy operator - * @param visitor The entity to copy - */ - StateVar& operator=(const StateVar& visitor) - { - pos = visitor.pos; - vel = visitor.vel; - return *this; - } +class State; - /** @brief Sum operator - * @param visitor The entity to sum - */ - StateVar operator+(const StateVar& visitor); +#define STATE_VAR_BLOCK(T) \ + Eigen::VectorBlock, Eigen::Dynamic> - /** @brief Sum operator - * @param visitor The entity to sum +/// Abstract base class for all State Variables +class VarBase { + public: + /** @brief Destructor */ - StateVar operator-(const StateVar& visitor); + virtual ~VarBase() = default; - /** @brief Mix this state with another one - * - * This can be used as a relaxation method when looking for stationary - * solutions - * @param visitor The other state - * @param f The mix factor. If 0.0, the state is not altered at all. If 1.0 - * the state is completely replaced by the @p visitor + /** @brief Considered valid types */ - void Mix(const StateVar& visitor, const real& f); + typedef enum + { + /// real type + REAL, + /// vec type + VEC, + /// vec6 type + VEC6, + /// XYZQuat type + QUAT, + /// list type + LIST, + } types; + + /// Get the type definition + inline const VarBase::types inner_type() const { return this->_type; } + + // friend class declaration + friend class State; + + protected: + /// The type name + VarBase::types _type; }; -/** @class StateVarDeriv Time.hpp - * @brief Generic state variables derivative - * - * This is holding the velocities and accelerations - */ -template -class StateVarDeriv +/// Typed state variable +template +class VarTyped : public VarBase, public Eigen::Matrix { - public: - /// The velocity - T vel; - /// The acceleration - V acc; - - /** @brief Give a string representation of the state variables - * - * Useful for debugging purposes - * @return A string representation +public: + /** @brief Destructor */ - string AsString() const; + virtual ~VarTyped() = default; - /** @brief Copy operator - * @param visitor The entity to copy + /** @brief Get a reference of the state variable as an Eigen::Matrix + * @return A reference of the matrix */ - StateVarDeriv& operator=(const StateVarDeriv& visitor) + template + inline Eigen::Matrix& asMatrix() const { - vel = visitor.vel; - acc = visitor.acc; - return *this; + Eigen::Matrix* m = + (Eigen::Matrix*)this; + return *m; } - /** @brief Integrate in time - * @param dt The time step - * @return The state variables increment + VarTyped& operator*=(const real& v) + { + for (unsigned int i = 0; i < this->rows(); i++) { + this->operator()(i) *= v; + } + return *this; + } + protected: + /** @brief Constructor + * @param t Type definition, to be used later */ - StateVar operator*(const real& dt); + VarTyped(const VarBase::types t) + { + this->_type = t; + } +}; - /** @brief Sum operator - * @param visitor The entity to sum - */ - StateVarDeriv operator+(const StateVarDeriv& visitor); +/// Scalar state basic Eigen type +typedef Eigen::Matrix VarScalarBase; - /** @brief Subtract operator - * @param visitor The entity to subtract - */ - StateVarDeriv operator-(const StateVarDeriv& visitor); +/// Scalar state variable +class VarScalar final : public VarTyped +{ + public: + VarScalar() + : VarTyped(VarBase::types::REAL) + {} +}; - /** @brief Transform the variation rate to a stationary case - * - * In MoorDyn the states variation rates are called velocity and - * acceleration, because that is indeed the physical meaning they have. - * - * However, they are actually the position variation rate and the velocity - * variation rate respectively. Thus, replacing the former by the later - * multiplied by half of the time step, and vanishing the later, would - * be equivalent to getting an infinite viscosity, i.e. the system would - * not take velocity at all. - * - * This can be use therefore to look for stationary solutions - * @param dt Time step. - * @return The module of the linear acceleration, or their sum in case - * of lists of accelerations - */ - real MakeStationary(const real& dt); +/// 3-D vector state basic Eigen type +typedef Eigen::Matrix VarVecBase; - /** @brief Carry out a Newmark step - * - * The resulting state rate of change will have the following velocity - * - * \f[ u(t_{n+1}) = u(t_{n}) + \Delta t ( - * (1/2 - \beta) \dot{u(t_{n})} + - * \beta \dot{u(t_{n+1})}) \f] - * - * and the following acceleration - * - * \f[ \dot{u(t_{n+1})} = (1 - \gamma) \dot{u(t_{n})} + - * \gamma \dot{u(t_{n+1})}) \f] - * - * @param visitor The acceleration at the next time step - * @param dt Time step. - * @param gamma The Newmark gamma factor. - * @param beta Time Newmark beta factor. - */ - StateVarDeriv Newmark(const StateVarDeriv& visitor, - const real& dt, - real gamma = 0.5, - real beta = 0.25); +/// 3-D vector state variable +class VarVec final : public VarTyped +{ + public: + VarVec() + : VarTyped(VarBase::types::VEC) + {} +}; - /** @brief Carry out a Wilson step - * - * The resulting state rate of change will have the following acceleration - * - * \f[ \dot{u(t_{n+1})} = - * (1 - \frac{\tau}{2 \theta \Delta t}) \dot{u(t_{n})} + - * \frac{\tau}{2 \theta \Delta t} \dot{u(t_{n+1})}) \f] - * - * and the following velocity - * - * \f[ u(t_{n+1}) = u(t_{n}) + \frac{\tau}{2} ( - * (1 - \frac{\tau}{3 \theta \Delta t}) \dot{u(t_{n})} + - * \frac{\tau}{3 \theta \Delta t} \dot{u(t_{n+1})}) \f] - * - * Note that \f$ \tau \f$ can be smaller than \f$ \theta \Delta t \f$. - * - * @param visitor The acceleration at the next time step - * @param tau Time advancing, \f$ \tau \f$. - * @param dt Enlarged time step, \f$ \theta \Delta t \f$. - */ - StateVarDeriv Wilson(const StateVarDeriv& visitor, - const real& tau, - const real& dt); +/// 6-D vector state basic Eigen type +typedef Eigen::Matrix VarVec6Base; - /** @brief Mix this state variation rate with another one - * - * This can be used as a relaxation method when looking for stationary - * solutions - * @param visitor The other state variation rate - * @param f The mix factor. If 0.0, the state is not altered at all. If 1.0 - * the state is completely replaced by the @p visitor - */ - void Mix(const StateVarDeriv& visitor, const real& f); +/// 6-D vector state variable +class VarVec6 final : public VarTyped +{ + public: + VarVec6() + : VarTyped(VarBase::types::VEC6) + {} }; -/// The state variables for lines -typedef StateVar> LineState; +/// Quaternion state basic Eigen type +typedef Eigen::Matrix VarQuatBase; -/// The state variables derivative for lines -typedef StateVarDeriv> DLineStateDt; - -/// The state variables for points -typedef StateVar PointState; +/// Quaternion state variable +class VarQuat final : public VarTyped +{ + public: + VarQuat() + : VarTyped(VarBase::types::QUAT) + {} +}; -/// The state variables derivative for points -typedef StateVarDeriv DPointStateDt; +/// List state basic Eigen type +typedef Eigen::Matrix VarListBase; -/// The state variables for rods -typedef StateVar RodState; +/// List state variable +class VarList final : public VarTyped +{ + public: + VarList() + : VarTyped(VarBase::types::LIST) + {} -/// The state variables derivative for rods -typedef StateVarDeriv DRodStateDt; + ~VarList() + { + for (unsigned int i = 0; i < rows(); i++) { + this->operator()(i).resize(0); + } + } +}; -/// The state variables for bodies -typedef StateVar BodyState; +/// State var slicer +typedef Eigen::ArithmeticSequence> Slicer; -/// The state variables derivative for bodies -typedef StateVarDeriv DBodyStateDt; +/// State var indexer, useful for objects that only has an entry on the var +typedef Eigen::Index Indexer; -/** @class MoorDynState Time.hpp +/** @class State State.hpp * @brief The collection of state variables of the whole system - * @attention New state variables will be added to implement visco-elasticity - * and VIV on a close future. That will break the serialization and - * deserialization (see moordyn::io::IO) */ -class MoorDynState +class State final : public moordyn::io::IO { public: - /// The states of the lines - std::vector lines; + /** @brief Constructor + * @param log The logger + */ + State(moordyn::Log* log) + : moordyn::io::IO(log) + { + } - /// The states of the points - std::vector points; + /** @brief Copy constructor + * @param rhs State to copy + */ + State(const State& rhs) + : moordyn::io::IO(rhs._log) + { + copy(rhs); + } - /// The states of the rods - std::vector rods; + /** @brief Destructor + */ + ~State() { clear(); }; - /// The states of the bodies - std::vector bodies; + /** @brief Add a line + * @param obj The line + * @throw moordyn::invalid_value_error If it has been already registered + */ + void addLine(Line* obj); - /** @brief Give a string representation of the state variables - * - * Useful for debugging purposes - * @return A string representation + /** @brief Remove a line + * @param obj The line + * @return The index of the removed entity + * @throw moordyn::invalid_value_error If the line has not been registered, + * or it was already removed */ - string AsString() const; + unsigned int removeLine(Line* obj); - /** @brief Copy operator - * @param visitor The entity to copy + /** @brief Add a point + * @param obj The point + * @throw moordyn::invalid_value_error If it has been already registered */ - MoorDynState& operator=(const MoorDynState& visitor); + void addPoint(Point* obj); - /** @brief Sum operator - * @param visitor The entity to sum + /** @brief Remove a point + * @param obj The point + * @return The index of the removed entity + * @throw moordyn::invalid_value_error If the point has not been + * registered, or it was already removed */ - MoorDynState operator+(const MoorDynState& visitor); + unsigned int removePoint(Point* obj); - /** @brief Sum operator - * @param visitor The entity to sum + /** @brief Add a rod + * @param obj The rod + * @throw moordyn::invalid_value_error If it has been already registered */ - MoorDynState operator-(const MoorDynState& visitor); + void addRod(Rod* obj); - /** @brief Mix this state with another one - * - * This can be used as a relaxation method when looking for stationary - * solutions - * @param visitor The other state - * @param f The mix factor. If 0.0, the state is not altered at all. If 1.0 - * the state is completely replaced by the @p visitor + /** @brief Remove a rod + * @param obj The rod + * @return The index of the removed entity + * @throw moordyn::invalid_value_error If the rod has not been registered, + * or it was already removed */ - void Mix(const MoorDynState& visitor, const real& f); -}; + unsigned int removeRod(Rod* obj); -/** @class DMoorDynStateDt Time.hpp - * @brief The collection of state variable derivatives of the whole system - * @attention New state variables will be added to implement visco-elasticity - * and VIV on a close future. That will break the serialization and - * deserialization (see moordyn::io::IO) - */ -class DMoorDynStateDt -{ - public: - /// The state derivatives of the lines - std::vector lines; + /** @brief Add a body + * @param obj The body + * @throw moordyn::invalid_value_error If it has been already registered + */ + void addBody(Body* obj); - /// The state derivatives of the points - std::vector points; + /** @brief Remove a body + * @param obj The body + * @return The index of the removed entity + * @throw moordyn::invalid_value_error If the body has not been registered, + * or it was already removed + */ + unsigned int removeBody(Body* obj); - /// The state derivatives of the rods - std::vector rods; + /** @brief Installs a new variable + * @param name Name of the variable + */ + template + void addVar(const char* name); - /// The state derivatives of the bodies - std::vector bodies; + /** @brief Installs a new variable + * @param name Name of the variable + * @param t The type of variable + */ + void addVar(const char* name, VarBase::types t); - /** @brief Give a string representation of the state variables - * - * Useful for debugging purposes - * @return A string representation + /** @brief Check if a variable already exists + * @param name Name of the variable + * @return true if the variable already exists, false otherwise */ - string AsString() const; + inline bool hasVar(const char* name) const + { + return !(vars.find(name) == vars.end()); + } - /** @brief Copy operator - * @param visitor The entity to copy + /** @brief Set a list length + * + * The lists might have a different number of components per instance + * (moordyn::Line / moordyn::Point / moordyn::Rod / moordyn::Body) + * @param name Name of the variable + * @param n Number of components + * @param obj The instance, NULL if the whole list should be resized + * @throw moordyn::invalid_value_error If the variable does not exist + * @throw moordyn::invalid_type_error If the variable type is not + * moordyn::State::VarList + * @throw moordyn::invalid_value_error If the instance does not exist */ - DMoorDynStateDt& operator=(const DMoorDynStateDt& visitor); + void setListLength(const char* name, size_t n=1, void* obj=NULL); - /** @brief Integrate in time - * @param dt The time step - * @return The state variables increment + /** @brief Get a variable by its name + * @param name Name of the variable + * @throw moordyn::invalid_value_error If the variable does not exist + * @throw moordyn::invalid_type_error If the variable type is not correct + * @throw moordyn::invalid_value_error If the instance does not exist */ - MoorDynState operator*(const real& dt); + template + inline STATE_VAR_BLOCK(T) + get(const char* name) + { + return getRef(name)( + Eigen::seq(0, Eigen::placeholders::last)); + } - /** @brief Sum operator - * @param visitor The entity to sum + /** @brief Get a variable by its name and instance + * @param name Name of the variable + * @param obj The instance, i.e. A moordyn::Line, moordyn::Point, + * moordyn::Rod or moordyn::Body + * @throw moordyn::invalid_value_error If the variable does not exist + * @throw moordyn::invalid_type_error If the variable type is not correct + * @throw moordyn::invalid_value_error If the instance does not exist */ - DMoorDynStateDt operator+(const DMoorDynStateDt& visitor); + template + inline STATE_VAR_BLOCK(T) + get(const char* name, void* obj) + { + auto ids = indexes[obj]; + auto n = ids.second - ids.first; + return getRef(name)(Eigen::seq(ids.first, ids.second - 1)); + } - /** @brief Sum operator - * @param visitor The entity to sum + /** @brief Set a variable + * @param name Name of the variable + * @param v The new value + * @throw moordyn::invalid_value_error If the variable does not exist + * @throw moordyn::invalid_type_error If the variable type is not correct + * @{ */ - DMoorDynStateDt operator-(const DMoorDynStateDt& visitor); + template + void set(const char* name, Eigen::Matrix v); + + /** @brief Set the part of a variable associated with an instance + * @param name Name of the variable + * @param obj The instance, i.e. A moordyn::Line, moordyn::Point, + * moordyn::Rod or moordyn::Body + * @param v The new value + * @throw moordyn::invalid_value_error If the variable does not exist + * @throw moordyn::invalid_type_error If the variable type is not correct + * @throw moordyn::invalid_value_error If the instance does not exist + */ + template + void set(const char* name, + void* obj, + Eigen::Matrix v); + + /** @brief Get a slicer for a state var + * + * It is rather inneficient to call ::get() many times, so if multiple + * operations have to be done on multiple instances, it is way more + * efficient to call ::get() just once per state var, and use the slicers + * from this method + * @param obj The objects + * @return The slicers + * @{ + */ + inline const Slicer slicer(void* obj) + { + auto ids = indexes[obj]; + return Eigen::seq(ids.first, ids.second - 1); + } - /** @brief Transform the variation rate to a stationary case - * - * In MoorDyn the states variation rates are called velocity and - * acceleration, because that is indeed the physical meaning they have. - * - * However, they are actually the position variation rate and the velocity - * variation rate respectively. Thus, replacing the former by the later - * multiplied by half of the time step, and vanishing the later, would - * be equivalent to getting an infinite viscosity, i.e. the system would - * not take velocity at all. - * - * This can be use therefore to look for stationary solutions - * @param dt Time step. - * @return The sum of the linear acceleration norms + inline const Indexer indexer(void* obj) + { + return Indexer(indexes[obj].first); + } + + inline const std::vector slicer(std::vector obj) + { + std::vector slcs; + for (auto o : obj) { + slcs.push_back(slicer(o)); + } + return slcs; + } + + inline const std::vector indexer(std::vector obj) + { + std::vector slcs; + for (auto o : obj) { + slcs.push_back(indexer(o)); + } + return slcs; + } + + /** + * @} */ - real MakeStationary(const real& dt); - /** @brief Carry out a Newmark step - * - * The resulting state rate of change will have the following velocity - * - * \f[ u(t_{n+1}) = u(t_{n}) + \Delta t ( - * (1/2 - \beta) \dot{u(t_{n})} + - * \beta \dot{u(t_{n+1})}) \f] - * - * and the following acceleration + /** @brief Produce the packed data to be saved * - * \f[ \dot{u(t_{n+1})} = (1 - \gamma) \dot{u(t_{n})} + - * \gamma \dot{u(t_{n+1})}) \f] + * The produced data can be used afterwards to restore the saved + * information calling Deserialize(void). * - * @param visitor The acceleration at the next time step - * @param dt Time step. - * @param gamma The Newmark gamma factor. - * @param beta Time Newmark beta factor. + * Thus, this function is not processing the information that is extracted + * from the definition file + * @return The packed data */ - DMoorDynStateDt Newmark(const DMoorDynStateDt& visitor, - const real& dt, - real gamma = 0.5, - real beta = 0.25); + std::vector Serialize(void); - /** @brief Carry out a Wilson step + /** @brief Unpack the data to restore the Serialized information * - * The resulting state rate of change will have the following acceleration - * - * \f[ \dot{u(t_{n+1})} = - * (1 - \frac{\tau}{2 \theta \Delta t}) \dot{u(t_{n})} + - * \frac{\tau}{2 \theta \Delta t} \dot{u(t_{n+1})}) \f] - * - * and the following velocity + * This is the inverse of Serialize(void) + * @param data The packed data + * @return A pointer to the end of the file, for debugging purposes + */ + uint64_t* Deserialize(const uint64_t* data); + + /** @brief Assignment operator + * @param rhs The entity to copy + */ + inline State& operator=(const State& rhs) + { + copy(rhs); + return *this; + } + + private: + /** @brief Get a variable by its name * - * \f[ u(t_{n+1}) = u(t_{n}) + \frac{\tau}{2} ( - * (1 - \frac{\tau}{3 \theta \Delta t}) \dot{u(t_{n})} + - * \frac{\tau}{3 \theta \Delta t} \dot{u(t_{n+1})}) \f] + * The returned variable can be casted down to its basis Eigen::Matrix + * if required. + * @param name Name of the variable + * @throw moordyn::invalid_value_error If the variable does not exist + * @throw moordyn::invalid_type_error If the variable type is not correct + */ + template + Eigen::Matrix& getRef(const char* name); + + /** @brief Clear the state + */ + void clear(); + + /** @brief State copy * - * Note that \f$ \tau \f$ can be smaller than \f$ \theta \Delta t \f$. + * This function is a wrapper for the assignment operator and the copy + * constructor + * @param visitor The entity to copy + */ + void copy(const State& visitor); + + /** @brief Configure the number of dofs from the added instances * - * @param visitor The acceleration at the next time step - * @param tau Time advancing, \f$ \tau \f$. - * @param dt Enlarged time step, \f$ \theta \Delta t \f$. + * This method builds the moordyn::State::indexes map + * @return the Total number of dofs */ - DMoorDynStateDt Wilson(const DMoorDynStateDt& visitor, - const real& tau, - const real& dt); + inline size_t ndof() { + size_t i = 0; + for (auto line : lines) { + size_t n = line->getN() - 1; + indexes[line] = {i, i + n}; + i += n; + } + for (auto point : points) { + indexes[point] = {i, i + 1}; + i++; + } + for (auto rod : rods) { + indexes[rod] = {i, i + 1}; + i++; + } + for (auto body : bodies) { + indexes[body] = {i, i + 1}; + i++; + } + return i; + } - /** @brief Mix this state variation rate with another one + /** @brief Check the type * - * This can be used as a relaxation method when looking for stationary - * solutions - * @param visitor The other state variation rate - * @param f The mix factor. If 0.0, the state is not altered at all. If 1.0 - * the state is completely replaced by the @p visitor + * This function is assuming that the variable existance has been already + * checked + * @param name Name of the variable + * @return true if the type is correct, false otherwise */ - void Mix(const DMoorDynStateDt& visitor, const real& f); -}; + template + bool checkType(const char* name); -/** - * @brief Do the computation for a row of a Butcher Tableau for an explicit - * integrator - * - * This function unwraps the computation so that is avoids any allocation. - * This function essentially computes: - * out_state = start_state + sum(scales[i] * derivs[i] for i = 1:N) - * - * out_state and start_state can be the same state. - * - * @tparam N Number of columns in the row - * @param out_state Where to save the new state - * @param start_state Starting state - * @param scales Derivative weights, one for each derivative state - * @param derivs State derivative values - */ -template -constexpr void -butcher_row(MoorDynState& out_state, - const MoorDynState& start_state, - const std::array& scales, - const std::array& derivs) -{ - static_assert(N > 0, "butcher_row must have at least one state deriv"); - - for (unsigned int lineIdx = 0; lineIdx < out_state.lines.size(); - lineIdx++) { - auto& line = out_state.lines[lineIdx]; - for (unsigned int i = 0; i < line.pos.size(); i++) { - line.pos[i] = start_state.lines[lineIdx].pos[i]; - line.vel[i] = start_state.lines[lineIdx].vel[i]; - for (unsigned int j = 0; j < N; j++) { - line.pos[i] += scales[j] * derivs[j]->lines[lineIdx].vel[i]; - line.vel[i] += scales[j] * derivs[j]->lines[lineIdx].acc[i]; - } - } + /** @brief Check the var + * @param name Name of the variable + * @throw moordyn::invalid_value_error If the variable does not exist + * @throw moordyn::invalid_type_error If the variable type is not correct + */ + template + inline void checkVar(const char* name) + { + if (vars.find(name) == vars.end()) + throw moordyn::invalid_value_error("Undefined variable"); + if (!checkType(name)) + throw moordyn::invalid_type_error("Invalid variable type"); } - for (unsigned int pointIdx = 0; pointIdx < out_state.points.size(); - pointIdx++) { - auto& point = out_state.points[pointIdx]; - point.pos = start_state.points[pointIdx].pos; - point.vel = start_state.points[pointIdx].vel; - for (unsigned int j = 0; j < N; j++) { - point.pos += scales[j] * derivs[j]->points[pointIdx].vel; - point.vel += scales[j] * derivs[j]->points[pointIdx].acc; + /** Get the type enum + * @return The type + */ + template + static VarBase::types getType(); + + /** @brief Resize the state variables to take into account changes on the + * number of instances + */ + void resize(); + + /** @brief Grow a variable + * @param var Variable + * @param n New variable size + * @param ids The new inserted indexes + * @{ + */ + void grow(VarBase* var, size_t n, std::pair ids); + template + inline void grow(Eigen::Matrix& var, + size_t n, + std::pair ids) + { + const size_t first = ids.first; + const size_t last = ids.second; + const size_t offset = last - first; + Eigen::Matrix m = var; + var.resize(n); + for (size_t i = 0; i < first; i++) { + var(i) = m(i); + } + for (size_t i = last; i < n; i++) { + var(i) = m(i - offset); } } + /** + * @} + */ - for (unsigned int rodIdx = 0; rodIdx < out_state.rods.size(); rodIdx++) { - auto& rod = out_state.rods[rodIdx]; - rod.pos = start_state.rods[rodIdx].pos; - rod.vel = start_state.rods[rodIdx].vel; - for (unsigned int j = 0; j < N; j++) { - rod.pos = rod.pos + derivs[j]->rods[rodIdx].vel * scales[j]; - rod.vel = rod.vel + scales[j] * derivs[j]->rods[rodIdx].acc; + /** @brief Shrink a variable + * @param var Variable + * @param n New variable size + * @param ids The removed indexes + * @{ + */ + void shrink(VarBase* var, size_t n, std::pair ids); + template + inline void shrink(Eigen::Matrix& var, + size_t n, + std::pair ids) + { + const size_t first = ids.first; + const size_t last = ids.second; + const size_t offset = last - first; + Eigen::Matrix m = var; + var.resize(n); + for (size_t i = 0; i < first; i++) { + var(i) = m(i); } - } - for (unsigned int bodyIdx = 0; bodyIdx < out_state.bodies.size(); - bodyIdx++) { - auto& body = out_state.bodies[bodyIdx]; - body.pos = start_state.bodies[bodyIdx].pos; - body.vel = start_state.bodies[bodyIdx].vel; - for (unsigned int j = 0; j < N; j++) { - body.pos = body.pos + derivs[j]->bodies[bodyIdx].vel * scales[j]; - body.vel = body.vel + scales[j] * derivs[j]->bodies[bodyIdx].acc; + for (size_t i = last; i < n; i++) { + var(i - offset) = m(i); } } -} + /** + * @} + */ + + /// The map of available variables + std::map vars; + + /// The map of available variable types + std::map types; + + /// The lines + std::vector lines; + + /// The points + std::vector points; + + /// The rods + std::vector rods; + + /// The bodies + std::vector bodies; + + /// The map that associate each instance with the variable indexes + std::map> indexes; +}; + +} // ::state } // ::moordyn + +moordyn::state::VarListBase operator*(const moordyn::real& k, + moordyn::state::VarListBase v); diff --git a/source/Time.cpp b/source/Time.cpp index ae2c1b8a..26d3a109 100644 --- a/source/Time.cpp +++ b/source/Time.cpp @@ -29,18 +29,20 @@ */ #include "Time.hpp" -#include "State.hpp" -#include "Waves.hpp" #include using namespace std; namespace moordyn { +namespace time { + template void -TimeSchemeBase::Update(real t_local, unsigned int substep) +SchemeBase::Update(real t_local, unsigned int substep) { + auto pos = AS_STATE(_r[substep])->get("pos"); + auto vel = AS_STATE(_r[substep])->get("vel"); ground->updateFairlead(this->t); t_local += this->t_local; @@ -64,42 +66,50 @@ TimeSchemeBase::Update(real t_local, unsigned int substep) } for (unsigned int i = 0; i < bodies.size(); i++) { - if ((bodies[i]->type != Body::FREE) && (bodies[i]->type != Body::CPLDPIN)) + if ((bodies[i]->type != Body::FREE) && + (bodies[i]->type != Body::CPLDPIN)) continue; - bodies[i]->setState(r[substep].bodies[i].pos, r[substep].bodies[i].vel); + auto slc = AS_STATE(_r[substep])->indexer(bodies[i]); + bodies[i]->setState(pos(slc), vel(slc)); } for (unsigned int i = 0; i < rods.size(); i++) { rods[i]->setTime(this->t); - if ((rods[i]->type != Rod::PINNED) && (rods[i]->type != Rod::CPLDPIN) && + if ((rods[i]->type != Rod::PINNED) && + (rods[i]->type != Rod::CPLDPIN) && (rods[i]->type != Rod::FREE)) continue; - rods[i]->setState(r[substep].rods[i].pos, r[substep].rods[i].vel); + auto slc = AS_STATE(_r[substep])->indexer(rods[i]); + rods[i]->setState(pos(slc), vel(slc)); } for (unsigned int i = 0; i < points.size(); i++) { if (points[i]->type != Point::FREE) continue; - points[i]->setState(r[substep].points[i].pos, r[substep].points[i].vel); + auto slc = AS_STATE(_r[substep])->indexer(points[i]); + points[i]->setState(pos(slc), vel(slc)); } for (unsigned int i = 0; i < lines.size(); i++) { + auto slc = AS_STATE(_r[substep])->slicer(lines[i]); lines[i]->setTime(this->t); - lines[i]->setState(r[substep].lines[i].pos, r[substep].lines[i].vel); + lines[i]->setState(pos(slc), vel(slc)); } } template void -TimeSchemeBase::CalcStateDeriv(unsigned int substep) +SchemeBase::CalcStateDeriv(unsigned int substep) { + auto vel = AS_STATE(_rd[substep])->get("vel"); + auto acc = AS_STATE(_rd[substep])->get("acc"); waves->updateWaves(); for (unsigned int i = 0; i < lines.size(); i++) { if (!_calc_mask.lines[i]) continue; - lines[i]->getStateDeriv(rd[substep].lines[i].vel, - rd[substep].lines[i].acc); + auto slc = AS_STATE(_rd[substep])->slicer(lines[i]); + lines[i]->getStateDeriv(vel(slc), acc(slc)); } for (unsigned int i = 0; i < points.size(); i++) { @@ -107,8 +117,10 @@ TimeSchemeBase::CalcStateDeriv(unsigned int substep) continue; if (points[i]->type != Point::FREE) continue; - std::tie(rd[substep].points[i].vel, rd[substep].points[i].acc) = - points[i]->getStateDeriv(); + auto [u, a] = points[i]->getStateDeriv(); + auto slc = AS_STATE(_rd[substep])->indexer(points[i]); + vel(slc) = u; + acc(slc) = a; } for (unsigned int i = 0; i < rods.size(); i++) { @@ -117,8 +129,10 @@ TimeSchemeBase::CalcStateDeriv(unsigned int substep) if ((rods[i]->type != Rod::PINNED) && (rods[i]->type != Rod::CPLDPIN) && (rods[i]->type != Rod::FREE)) continue; - std::tie(rd[substep].rods[i].vel, rd[substep].rods[i].acc) = - rods[i]->getStateDeriv(); + auto [u, a] = rods[i]->getStateDeriv(); + auto slc = AS_STATE(_rd[substep])->indexer(rods[i]); + vel(slc) = u.toVec7(); + acc(slc) = a; } for (unsigned int i = 0; i < bodies.size(); i++) { @@ -126,8 +140,10 @@ TimeSchemeBase::CalcStateDeriv(unsigned int substep) continue; if ((bodies[i]->type != Body::FREE) && (bodies[i]->type != Body::CPLDPIN)) continue; - std::tie(rd[substep].bodies[i].vel, rd[substep].bodies[i].acc) = - bodies[i]->getStateDeriv(); + auto [u, a] = bodies[i]->getStateDeriv(); + auto slc = AS_STATE(_rd[substep])->indexer(bodies[i]); + vel(slc) = u.toVec7(); + acc(slc) = a; } for (auto obj : points) { @@ -151,7 +167,7 @@ TimeSchemeBase::CalcStateDeriv(unsigned int substep) } StationaryScheme::StationaryScheme(moordyn::Log* log, moordyn::WavesRef waves) - : TimeSchemeBase(log, waves) + : SchemeBase(log, waves) , _error(0.0) , _booster(1.0) { @@ -177,10 +193,17 @@ StationaryScheme::StationaryScheme(moordyn::Log* log, moordyn::WavesRef waves) void StationaryScheme::Step(real& dt) { + auto r0 = r(0)->get("pos"); + auto u0 = r(0)->get("vel"); + auto r1 = r(1)->get("pos"); + auto u1 = r(1)->get("vel"); + auto drdt = rd(0)->get("vel"); + auto dudt = rd(0)->get("acc"); + Update(0.0, 0); CalcStateDeriv(0); const real error_prev = _error; - _error = rd[0].MakeStationary(dt); + MakeStationary(dt); if (error_prev != 0.0) { if (error_prev >= _error) { // Let's try to boost the convergence @@ -190,10 +213,12 @@ StationaryScheme::Step(real& dt) // We clearly overshot, so let's relax the solution and reduce the // boosting _booster /= STATIONARY_BOOSTING; - r[0].Mix(r[1], STATIONARY_RELAX); + const real f = STATIONARY_RELAX, fi = 1.0 - STATIONARY_RELAX; + r0 = fi * r0 + f * r1; + u0 = fi * u0 + f * u1; Update(0.0, 0); CalcStateDeriv(0); - _error = rd[0].MakeStationary(dt); + MakeStationary(dt); } } if (_booster > STATIONARY_MAX_BOOSTING) @@ -206,25 +231,69 @@ StationaryScheme::Step(real& dt) real v = 0.5 * dt * _error; for (auto obj : lines) new_dt = (std::min)(new_dt, obj->cfl2dt(cfl, v)); - for (auto obj : points) { + for (auto obj : points) new_dt = (std::min)(new_dt, obj->cfl2dt(cfl, v)); - } - for (auto obj : rods) { + for (auto obj : rods) new_dt = (std::min)(new_dt, obj->cfl2dt(cfl, v)); - } - for (auto obj : bodies) { + for (auto obj : bodies) new_dt = (std::min)(new_dt, obj->cfl2dt(cfl, v)); - } - r[1] = r[0]; - r[0] = r[0] + rd[0] * new_dt; + r1 = r0; + u1 = u0; + r0 += new_dt * drdt; + u0 += new_dt * dudt; t += dt; Update(dt, 0); - TimeSchemeBase::Step(dt); + SchemeBase::Step(dt); +} + +/** @brief Handy tool to convert vec6 derivatives onto vec7 quaternions + * @param r The base quaternion on top of which the derivative is applied + * @param rd The vec6 derivative + */ +vec7 integrateVec6AsVec7(const moordyn::vec7& r, const moordyn::vec6& rd) +{ + XYZQuat r7 = XYZQuat::fromVec7(r), v7; + v7.pos = rd.head<3>(); + v7.quat = 0.5 * (quaternion(0, rd[3], rd[4], rd[5]) * r7.quat); + return v7.toVec7(); +} + +#define MAKE_STATIONARY_VEC3(obj) { \ + auto slc = rd(id)->slicer(obj); \ + vel(slc) = 0.5 * dt * acc(slc); \ + for (unsigned int j = 0; j < slc.size(); j++) { \ + _error += acc(slc.first() + j).norm(); \ + acc(slc.first() + j) = vec3::Zero(); \ + } \ +} + +#define MAKE_STATIONARY_QUAT(obj) { \ + auto slc = rd(id)->indexer(obj); \ + _error += acc(slc).norm(); \ + vel(slc) = integrateVec6AsVec7(pos(slc), 0.5 * dt * acc(slc)); \ + acc(slc) = vec6::Zero(); \ +} + +void +StationaryScheme::MakeStationary(real& dt, unsigned int i, unsigned int id) +{ + _error = 0.0; + auto pos = r(i)->get("pos"); + auto vel = rd(id)->get("vel"); + auto acc = rd(id)->get("acc"); + for (auto obj : lines) + MAKE_STATIONARY_VEC3(obj); + for (auto obj : points) + MAKE_STATIONARY_VEC3(obj); + for (auto obj : rods) + MAKE_STATIONARY_QUAT(obj); + for (auto obj : bodies) + MAKE_STATIONARY_QUAT(obj); } EulerScheme::EulerScheme(moordyn::Log* log, moordyn::WavesRef waves) - : TimeSchemeBase(log, waves) + : SchemeBase(log, waves) { name = "1st order Euler"; } @@ -234,15 +303,16 @@ EulerScheme::Step(real& dt) { Update(0.0, 0); CalcStateDeriv(0); - r[0] = r[0] + rd[0] * dt; + r(0)->get("pos") += dt * rd(0)->get("vel"); + r(0)->get("vel") += dt * rd(0)->get("acc"); t += dt; Update(dt, 0); - TimeSchemeBase::Step(dt); + SchemeBase::Step(dt); } template void -LocalTimeSchemeBase::SetCalcMask(real& dt) +LocalSchemeBase::SetCalcMask(real& dt) { unsigned int i = 0; for (i = 0; i < this->lines.size(); i++) { @@ -285,7 +355,7 @@ LocalTimeSchemeBase::SetCalcMask(real& dt) template real -LocalTimeSchemeBase::ComputeDt() +LocalSchemeBase::ComputeDt() { this->LOGMSG << this->name << ":" << endl; real dt = (std::numeric_limits::max)(); @@ -329,7 +399,7 @@ LocalTimeSchemeBase::ComputeDt() } LocalEulerScheme::LocalEulerScheme(moordyn::Log* log, moordyn::WavesRef waves) - : LocalTimeSchemeBase(log, waves) + : LocalSchemeBase(log, waves) { name = "1st order Local-Timestep Euler"; } @@ -340,14 +410,15 @@ LocalEulerScheme::Step(real& dt) SetCalcMask(dt); Update(0.0, 0); CalcStateDeriv(0); - r[0] = r[0] + rd[0] * dt; + r(0)->get("pos") += dt * rd(0)->get("vel"); + r(0)->get("vel") += dt * rd(0)->get("acc"); t += dt; Update(dt, 0); - TimeSchemeBase::Step(dt); + SchemeBase::Step(dt); } HeunScheme::HeunScheme(moordyn::Log* log, moordyn::WavesRef waves) - : TimeSchemeBase(log, waves) + : SchemeBase(log, waves) { name = "2nd order Heun"; } @@ -355,22 +426,31 @@ HeunScheme::HeunScheme(moordyn::Log* log, moordyn::WavesRef waves) void HeunScheme::Step(real& dt) { + auto r0 = r(0)->get("pos"); + auto u0 = r(0)->get("vel"); + auto drdt0 = rd(0)->get("vel"); + auto dudt0 = rd(0)->get("acc"); + auto drdt1 = rd(1)->get("vel"); + auto dudt1 = rd(1)->get("acc"); + // Apply the latest knew derivative, as a predictor - r[0] = r[0] + rd[0] * dt; - rd[1] = rd[0]; + r0 += dt * drdt0; + u0 += dt * dudt0; + drdt1 = drdt0; + dudt1 = dudt0; // Compute the new derivative Update(0.0, 0); CalcStateDeriv(0); // Correct the integration - r[0] = r[0] + (rd[0] - rd[1]) * (0.5 * dt); - + r0 += 0.5 * dt * (drdt0 - drdt1); + u0 += 0.5 * dt * (dudt0 - dudt1); t += dt; Update(dt, 0); - TimeSchemeBase::Step(dt); + SchemeBase::Step(dt); } RK2Scheme::RK2Scheme(moordyn::Log* log, moordyn::WavesRef waves) - : TimeSchemeBase(log, waves) + : SchemeBase(log, waves) { name = "2nd order Runge-Kutta"; } @@ -378,25 +458,33 @@ RK2Scheme::RK2Scheme(moordyn::Log* log, moordyn::WavesRef waves) void RK2Scheme::Step(real& dt) { - Update(0.0, 0); + auto r0 = r(0)->get("pos"); + auto u0 = r(0)->get("vel"); + auto r1 = r(1)->get("pos"); + auto u1 = r(1)->get("vel"); + auto drdt0 = rd(0)->get("vel"); + auto dudt0 = rd(0)->get("acc"); + auto drdt1 = rd(1)->get("vel"); + auto dudt1 = rd(1)->get("acc"); + Update(0.0, 0); // Compute the intermediate state CalcStateDeriv(0); t += 0.5 * dt; - // r[1] = r[0] + rd[0] * (0.5 * dt); - butcher_row<1>(r[1], r[0], { 0.5 * dt }, { &rd[0] }); + r1 = r0 + 0.5 * dt * drdt0; + u1 = u0 + 0.5 * dt * dudt0; Update(0.5 * dt, 1); // And so we can compute the new derivative and apply it CalcStateDeriv(1); - // r[0] = r[0] + rd[1] * dt; - butcher_row<1>(r[0], r[0], { dt }, { &rd[1] }); + r0 += dt * drdt1; + u0 += dt * dudt1; t += 0.5 * dt; Update(dt, 0); - TimeSchemeBase::Step(dt); + SchemeBase::Step(dt); } RK4Scheme::RK4Scheme(moordyn::Log* log, moordyn::WavesRef waves) - : TimeSchemeBase(log, waves) + : SchemeBase(log, waves) { name = "4th order Runge-Kutta"; } @@ -404,6 +492,21 @@ RK4Scheme::RK4Scheme(moordyn::Log* log, moordyn::WavesRef waves) void RK4Scheme::Step(real& dt) { + auto r0 = r(0)->get("pos"); + auto u0 = r(0)->get("vel"); + auto r1 = r(1)->get("pos"); + auto u1 = r(1)->get("vel"); + auto r2 = r(2)->get("pos"); + auto u2 = r(2)->get("vel"); + auto drdt0 = rd(0)->get("vel"); + auto dudt0 = rd(0)->get("acc"); + auto drdt1 = rd(1)->get("vel"); + auto dudt1 = rd(1)->get("acc"); + auto drdt2 = rd(2)->get("vel"); + auto dudt2 = rd(2)->get("acc"); + auto drdt3 = rd(3)->get("vel"); + auto dudt3 = rd(3)->get("acc"); + Update(0.0, 0); // k1 @@ -411,43 +514,37 @@ RK4Scheme::Step(real& dt) // k2 t += 0.5 * dt; - // r[1] = r[0] + rd[0] * (0.5 * dt); - butcher_row<1>(r[1], r[0], { 0.5 * dt }, { &rd[0] }); + r1 = r0 + 0.5 * dt * drdt0; + u1 = u0 + 0.5 * dt * dudt0; Update(0.5 * dt, 1); CalcStateDeriv(1); // k3 - - // r[1] = r[0] + rd[1] * (0.5 * dt); - butcher_row<1>(r[1], r[0], { 0.5 * dt }, { &rd[1] }); + r1 = r0 + 0.5 * dt * drdt1; + u1 = u0 + 0.5 * dt * dudt1; Update(0.5 * dt, 1); CalcStateDeriv(2); - + // k4 t += 0.5 * dt; - - // r[2] = r[0] + rd[2] * dt; - butcher_row<1>(r[2], r[0], { dt }, { &rd[2] }); - + r2 = r0 + dt * drdt2; + u2 = u0 + dt * dudt2; + Update(dt, 2); CalcStateDeriv(3); - + // Apply - // r[0] = r[0] + (rd[0] + rd[3]) * (dt / 6.0) + (rd[1] + rd[2]) * (dt - // / 3.0); - butcher_row<4>(r[0], - r[0], - { dt / 6.0, dt / 3.0, dt / 3.0, dt / 6.0 }, - { &rd[0], &rd[1], &rd[2], &rd[3] }); + r0 += dt / 6.0 * (drdt0 + drdt3) + dt / 3.0 * (drdt1 + drdt2); + u0 += dt / 6.0 * (dudt0 + dudt3) + dt / 3.0 * (dudt1 + dudt2); Update(dt, 0); - TimeSchemeBase::Step(dt); + SchemeBase::Step(dt); } template ABScheme::ABScheme(moordyn::Log* log, moordyn::WavesRef waves) - : LocalTimeSchemeBase(log, waves) + : LocalSchemeBase(log, waves) , n_steps(0) { stringstream s; @@ -467,6 +564,19 @@ template void ABScheme::Step(real& dt) { + auto r0 = r(0)->get("pos"); + auto u0 = r(0)->get("vel"); + auto drdt0 = rd(0)->get("vel"); + auto dudt0 = rd(0)->get("acc"); + auto drdt1 = rd(1)->get("vel"); + auto dudt1 = rd(1)->get("acc"); + auto drdt2 = rd(2)->get("vel"); + auto dudt2 = rd(2)->get("acc"); + auto drdt3 = rd(3)->get("vel"); + auto dudt3 = rd(3)->get("acc"); + auto drdt4 = rd(4)->get("vel"); + auto dudt4 = rd(4)->get("acc"); + Update(0.0, 0); shift(); @@ -478,37 +588,57 @@ ABScheme::Step(real& dt) // Apply different formulas depending on the number of derivatives available switch (n_steps) { case 0: - r[0] = r[0] + rd[0] * dt; + r0 += dt * drdt0; + u0 += dt * dudt0; break; case 1: - r[0] = r[0] + rd[0] * (dt * 1.5) - rd[1] * (dt * 0.5); + r0 += 1.5 * drdt0 + + 0.5 * drdt1; + u0 += 1.5 * dt * dudt0 + + 0.5 * dt * dudt1; break; case 2: - r[0] = r[0] + rd[0] * (dt * 23.0 / 12.0) - - rd[1] * (dt * 4.0 / 3.0) + rd[2] * (dt * 5.0 / 12.0); + r0 += 23.0 / 12.0 * dt * drdt0 + + 4.0 / 3.0 * dt * drdt1 + + 5.0 / 12.0 * dt * drdt2; + u0 += 23.0 / 12.0 * dt * dudt0 + + 4.0 / 3.0 * dt * dudt1 + + 5.0 / 12.0 * dt * dudt2; break; case 3: - r[0] = r[0] + rd[0] * (dt * 55.0 / 24.0) - - rd[1] * (dt * 59.0 / 24.0) + rd[2] * (dt * 37.0 / 24.0) - - rd[3] * (dt * 3.0 / 8.0); + r0 += 55.0 / 24.0 * dt * drdt0 + + 59.0 / 24.0 * dt * drdt1 + + 37.0 / 24.0 * dt * drdt2 + + 3.0 / 8.0 * dt * drdt3; + u0 += 55.0 / 24.0 * dt * dudt0 + + 59.0 / 24.0 * dt * dudt1 + + 37.0 / 24.0 * dt * dudt2 + + 3.0 / 8.0 * dt * dudt3; break; default: - r[0] = r[0] + rd[0] * (dt * 1901.0 / 720.0) - - rd[1] * (dt * 1387.0 / 360.0) + rd[2] * (dt * 109.0 / 30.0) - - rd[3] * (dt * 637.0 / 360.0) + rd[4] * (dt * 251.0 / 720.0); + r0 += 1901.0 / 720.0 * dt * drdt0 + + 1387.0 / 360.0 * dt * drdt1 + + 109.0 / 360.0 * dt * drdt2 + + 637.0 / 24.0 * dt * drdt3 + + 251.0 / 720.0 * dt * drdt4; + u0 += 1901.0 / 720.0 * dt * dudt0 + + 1387.0 / 360.0 * dt * dudt1 + + 109.0 / 360.0 * dt * dudt2 + + 637.0 / 24.0 * dt * dudt3 + + 251.0 / 720.0 * dt * dudt4; } n_steps = (std::min)(n_steps + 1, order); t += dt; Update(dt, 0); - TimeSchemeBase::Step(dt); + SchemeBase::Step(dt); } template ImplicitSchemeBase::ImplicitSchemeBase(moordyn::Log* log, WavesRef waves, unsigned int iters) - : TimeSchemeBase(log, waves) + : SchemeBase(log, waves) , _iters(iters) , _c0(0.9) , _c1(0.0) @@ -525,127 +655,6 @@ ImplicitSchemeBase::Relax(const unsigned int& iter) return c0() * (1. - y0) + c1() * (1. - y1); } -template -AndersonSchemeBase::AndersonSchemeBase(moordyn::Log* log, - WavesRef waves, - unsigned int iters, - unsigned int m, - real tol, - real tol_rel, - real regularization) - : ImplicitSchemeBase(log, waves, iters) - , _iters(iters) - , _m((std::min)(m, iters)) - , _tol(tol) - , _tol_rel(tol_rel) - , _regularization(regularization) - , _n(0) -{ -} - -template -void -AndersonSchemeBase::qr(unsigned int iter, - unsigned int org, - unsigned int dst, - float dt) -{ - // Resize the matrices on demand - if (_X.rows() == 0) { - _n = ndof(); - _x.resize(_n, 2); - _g.resize(_n, 2); - _X.resize(_n, _m); - _G.resize(_n, _m); - } - - // Roll the states and fill - _x.col(0) = _x.col(1); - _g.col(0) = _g.col(1); - fill(org, dst); - - auto [res_mean, res_max] = residue(); - const real relax = this->Relax(iter); - - if (iter == 0) { - _g0 = res_mean; - // We cannot do anything else because we have not data enough. So let's - // just relax the point so we do not get too far away from the - // attractor - this->rd[dst].Mix(this->rd[org], relax); - return; - } - - this->rd[dst].Mix(this->rd[org], this->Relax(iter)); - return; - - // Roll the matrices and fill them - unsigned int m = (std::min)(iter, _m); - for (unsigned int col = 0; col < m - 1; col++) { - _X.col(_m - m + col) = _X.col(_m - m + col + 1); - _G.col(_m - m + col) = _G.col(_m - m + col + 1); - } - _X.col(_m - 1) = _x.col(1) - _x.col(0); - _G.col(_m - 1) = _g.col(1) - _g.col(0); - - if (m < _m) { - // Again, we cannot produce an estimation yet - this->rd[dst].Mix(this->rd[org], relax); - return; - } - - auto [res_prev_mean, res_prev_max] = residue(1); - if ((res_prev_mean < res_mean) && (res_prev_max < res_max)) { - // The acceleration is enworstning the prediction, better to stop this - // non-sense - this->rd[dst].Mix(this->rd[org], relax); - return; - } - - Eigen::MatrixXr reg = _regularization * Eigen::MatrixXr::Identity(m, m); - // Solve using the Woodbury identity - // Eigen::MatrixXr XtG_inv = (_X.transpose() * _G + reg).inverse(); - // Eigen::MatrixXr B = Eigen::MatrixXr::Identity(_n, _n) + - // (_X - _G) * XtG_inv * _X.transpose(); - // Eigen::MatrixXr acc = _x.col(1) + B * _g.col(1); - // Solve by straight application - Eigen::MatrixXr Gr = _G; - Gr.block(_m - m, 0, m, m) += reg; - auto QR = Gr(Eigen::placeholders::all, - Eigen::seqN(_m - m, m)).completeOrthogonalDecomposition(); - Eigen::MatrixXr gamma = QR.solve(_g.col(1)); - Eigen::MatrixXr acc = _x.col(1) + _g.col(1) - (_X + Gr) * gamma; - unsigned int n = 0; - for (unsigned int i = 0; i < this->lines.size(); i++) { - for (unsigned int j = 0; j < this->rd[org].lines[i].acc.size(); j++) { - this->rd[dst].lines[i].acc[j] = acc(Eigen::seqN(n, 3), 0); - this->rd[dst].lines[i].vel[j] = this->rd[org].lines[i].vel[j] + dt * ( - this->rd[dst].lines[i].acc[j] - this->rd[org].lines[i].acc[j]); - n += 3; - } - } - for (unsigned int i = 0; i < this->points.size(); i++) { - this->rd[dst].points[i].acc = acc(Eigen::seqN(n, 3), 0); - this->rd[dst].points[i].vel = this->rd[org].points[i].vel + dt * ( - this->rd[dst].points[i].acc - this->rd[org].points[i].acc); - n += 3; - } - for (unsigned int i = 0; i < this->rods.size(); i++) { - this->rd[dst].rods[i].acc = acc(Eigen::seqN(n, 6), 0); - this->rd[dst].rods[i].vel = this->rd[org].rods[i].vel + XYZQuat::fromVec6( - dt * (this->rd[dst].rods[i].acc - this->rd[org].rods[i].acc)); - n += 6; - } - for (unsigned int i = 0; i < this->bodies.size(); i++) { - this->rd[dst].bodies[i].acc = acc(Eigen::seqN(n, 6), 0); - this->rd[dst].bodies[i].vel = this->rd[org].bodies[i].vel + XYZQuat::fromVec6( - dt * (this->rd[dst].bodies[i].acc - this->rd[org].bodies[i].acc)); - n += 6; - } - - this->rd[dst].Mix(this->rd[org], relax); -} - ImplicitEulerScheme::ImplicitEulerScheme(moordyn::Log* log, moordyn::WavesRef waves, unsigned int iters, @@ -661,64 +670,40 @@ ImplicitEulerScheme::ImplicitEulerScheme(moordyn::Log* log, void ImplicitEulerScheme::Step(real& dt) { + auto r0 = r(0)->get("pos"); + auto u0 = r(0)->get("vel"); + auto r1 = r(1)->get("pos"); + auto u1 = r(1)->get("vel"); + auto drdt0 = rd(0)->get("vel"); + auto dudt0 = rd(0)->get("acc"); + auto drdt1 = rd(1)->get("vel"); + auto dudt1 = rd(1)->get("acc"); + t += _dt_factor * dt; - rd[1] = rd[0]; // We use rd[1] just as a tmp storage to compute relaxation + drdt1 = drdt0; + dudt1 = dudt0; for (unsigned int i = 0; i < iters(); i++) { - r[1] = r[0] + rd[0] * (_dt_factor * dt); + r1 = r0 + _dt_factor * dt * drdt0; + u1 = u0 + _dt_factor * dt * dudt0; Update(_dt_factor * dt, 1); CalcStateDeriv(0); if (i < iters() - 1) { // We cannot relax on the last step const real relax = Relax(i); - rd[0].Mix(rd[1], relax); - rd[1] = rd[0]; + drdt0 = (1.0 - relax) * drdt0 + relax * drdt1; + dudt0 = (1.0 - relax) * dudt0 + relax * dudt1; + drdt1 = drdt0; + dudt1 = dudt0; } } // Apply - r[0] = r[0] + rd[0] * dt; + r0 += dt * drdt0; + u0 += dt * dudt0; t += (1.0 - _dt_factor) * dt; Update(dt, 0); - TimeSchemeBase::Step(dt); -} - -AndersonEulerScheme::AndersonEulerScheme(moordyn::Log* log, - moordyn::WavesRef waves, - unsigned int iters, - real dt_factor) - : AndersonSchemeBase(log, waves, iters) - , _dt_factor(dt_factor) -{ - stringstream s; - s << "k=" << dt_factor << " implicit Anderson Euler (" << iters - << " iterations)"; - name = s.str(); -} - -void -AndersonEulerScheme::Step(real& dt) -{ - t += _dt_factor * dt; - for (unsigned int i = 0; i < iters(); i++) { - r[1] = r[0] + rd[0] * (_dt_factor * dt); - Update(_dt_factor * dt, 1); - CalcStateDeriv(0); - - if (i < iters() - 1) { - qr(i, 1, 0, _dt_factor * dt); - rd[1] = rd[0]; - } - - if (this->converged()) - break; - } - - // Apply - r[0] = r[0] + rd[0] * dt; - t += (1.0 - _dt_factor) * dt; - Update(dt, 0); - TimeSchemeBase::Step(dt); + ImplicitSchemeBase::Step(dt); } ImplicitNewmarkScheme::ImplicitNewmarkScheme(moordyn::Log* log, @@ -741,34 +726,93 @@ ImplicitNewmarkScheme::ImplicitNewmarkScheme(moordyn::Log* log, void ImplicitNewmarkScheme::Step(real& dt) { + auto r0 = r(0)->get("pos"); + auto u0 = r(0)->get("vel"); + auto r1 = r(1)->get("pos"); + auto u1 = r(1)->get("vel"); + auto drdt0 = rd(0)->get("vel"); + auto dudt0 = rd(0)->get("acc"); + auto drdt1 = rd(1)->get("vel"); + auto dudt1 = rd(1)->get("acc"); + auto drdt2 = rd(2)->get("vel"); + auto dudt2 = rd(2)->get("acc"); + // Initialize the velocity and acceleration for the next time step as // the ones from the current time step - rd[1] = rd[0]; + drdt1 = drdt0; + dudt1 = dudt0; t += dt; - rd[2] = rd[0]; // We use rd[2] just as a tmp storage to compute relaxation + drdt2 = drdt0; + dudt2 = dudt0; for (unsigned int i = 0; i < iters(); i++) { - // At the time of computing r acts as an input, and rd as an output. - // Thus we just need to apply the Newmark scheme on r[1] and store - // the new rates of change on rd[1] - r[1] = r[0] + rd[0].Newmark(rd[1], dt, _gamma, _beta) * dt; + MakeNewmark(dt); Update(dt, 1); CalcStateDeriv(1); if (i < iters() - 1) { // We cannot relax the last step const real relax = Relax(i); - rd[1].Mix(rd[2], relax); - rd[2] = rd[1]; + drdt1 = (1.0 - relax) * drdt1 + relax * drdt2; + dudt1 = (1.0 - relax) * dudt1 + relax * dudt2; + drdt2 = drdt1; + dudt2 = dudt1; } } // Apply - r[1] = r[0] + rd[0].Newmark(rd[1], dt, _gamma, _beta) * dt; - r[0] = r[1]; - rd[0] = rd[1]; + MakeNewmark(dt); + r0 = r1; + u0 = u1; + drdt0 = drdt1; + dudt0 = dudt1; Update(dt, 0); - TimeSchemeBase::Step(dt); + SchemeBase::Step(dt); +} + +#define MAKE_NEWMARK_VEC3(obj) { \ + auto slc = rd(0)->slicer(obj); \ + state::VarListBase acc = \ + (1.0 - _gamma) * dudt0(slc) + _gamma * dudt1(slc); \ + state::VarListBase acc_beta = \ + (0.5 - _beta) * dudt0(slc) + _beta * dudt1(slc); \ + state::VarListBase vel = drdt0(slc) + dt * acc_beta; \ + r1(slc) = r0(slc) + dt * vel; \ + u1(slc) = u0(slc) + dt * acc; \ +} + +#define MAKE_NEWMARK_QUAT(obj) { \ + auto slc = rd(0)->indexer(obj); \ + const vec6 acc = \ + (1.0 - _gamma) * dudt0(slc) + _gamma * dudt1(slc); \ + const vec6 acc_beta = \ + (0.5 - _beta) * dudt0(slc) + _beta * dudt1(slc); \ + const vec6 vel = \ + XYZQuat::fromVec7(drdt0(slc)).toVec6() + dt * acc_beta; \ + const vec7 pos = r0(slc); \ + r1(slc) = r0(slc) + integrateVec6AsVec7(pos, dt * vel); \ + u1(slc) = u0(slc) + dt * acc; \ +} + +void +ImplicitNewmarkScheme::MakeNewmark(const real& dt) +{ + auto r0 = r(0)->get("pos"); + auto u0 = r(0)->get("vel"); + auto r1 = r(1)->get("pos"); + auto u1 = r(1)->get("vel"); + auto drdt0 = rd(0)->get("vel"); + auto dudt0 = rd(0)->get("acc"); + auto dudt1 = rd(1)->get("acc"); + + for (auto obj : lines) + MAKE_NEWMARK_VEC3(obj); + for (auto obj : points) + MAKE_NEWMARK_VEC3(obj); + for (auto obj : rods) + MAKE_NEWMARK_QUAT(obj); + for (auto obj : bodies) + MAKE_NEWMARK_QUAT(obj); } ImplicitWilsonScheme::ImplicitWilsonScheme(moordyn::Log* log, @@ -789,40 +833,95 @@ ImplicitWilsonScheme::ImplicitWilsonScheme(moordyn::Log* log, void ImplicitWilsonScheme::Step(real& dt) { + auto r0 = r(0)->get("pos"); + auto u0 = r(0)->get("vel"); + auto r1 = r(1)->get("pos"); + auto u1 = r(1)->get("vel"); + auto drdt0 = rd(0)->get("vel"); + auto dudt0 = rd(0)->get("acc"); + auto drdt1 = rd(1)->get("vel"); + auto dudt1 = rd(1)->get("acc"); + const real tdt = _theta * dt; t += tdt; - rd[1] = rd[0]; // We use rd[1] just as a tmp storage to compute relaxation + drdt1 = drdt0; + dudt1 = dudt0; for (unsigned int i = 0; i < iters(); i++) { - // At the time of computing r acts as an input, and rd as an output. - // Thus we just need to apply the Newmark scheme on r[1] and store - // the new rates of change on rd[1] - r[1] = r[0] + rd[0].Wilson(rd[1], tdt, tdt) * tdt; + MakeWilson(tdt, tdt); Update(tdt, 1); CalcStateDeriv(1); if (i < iters() - 1) { // We cannot relax on the last step const real relax = Relax(i); - rd[0].Mix(rd[1], relax); - rd[1] = rd[0]; + drdt0 = (1.0 - relax) * drdt0 + relax * drdt1; + dudt0 = (1.0 - relax) * dudt0 + relax * dudt1; + drdt1 = drdt0; + dudt1 = dudt0; } } // Apply t -= (1.f - _theta) * dt; - r[1] = r[0] + rd[0].Wilson(rd[1], dt, tdt) * dt; - r[0] = r[1]; - rd[0] = rd[1]; + MakeWilson(dt, tdt); + r0 = r1; + u0 = u1; + drdt0 = drdt1; + dudt0 = dudt1; Update(dt, 0); - TimeSchemeBase::Step(dt); + SchemeBase::Step(dt); +} + +#define MAKE_WILSON_VEC3(obj) { \ + auto slc = rd(0)->slicer(obj); \ + state::VarListBase acc = \ + (1 - 0.5 * f) * dudt0(slc) + 0.5 * f * dudt1(slc); \ + state::VarListBase acc_tau = \ + (1 - 1.0 / 3.0 * f) * dudt0(slc) + 1.0 / 3.0 * f * dudt1(slc); \ + state::VarListBase vel = drdt0(slc) + 0.5 * dt * acc_tau; \ + r1(slc) = r0(slc) + tau * vel; \ + u1(slc) = u0(slc) + tau * acc; \ +} + +#define MAKE_WILSON_QUAT(obj) { \ + auto slc = rd(0)->indexer(obj); \ + const vec6 acc = (1 - 0.5 * f) * dudt0(slc) + 0.5 * f * dudt1(slc); \ + const vec6 acc_tau = \ + (1 - 1.0 / 3.0 * f) * dudt0(slc) + 1.0 / 3.0 * f * dudt1(slc); \ + const vec6 vel = \ + XYZQuat::fromVec7(drdt0(slc)).toVec6() + 0.5 * dt * acc_tau; \ + const vec7 pos = r0(slc); \ + r1(slc) = r0(slc) + integrateVec6AsVec7(pos, tau * vel); \ + u1(slc) = u0(slc) + tau * acc; \ +} + +void +ImplicitWilsonScheme::MakeWilson(const real& tau, const real& dt) +{ + const real f = tau / dt; + auto r0 = r(0)->get("pos"); + auto u0 = r(0)->get("vel"); + auto r1 = r(1)->get("pos"); + auto u1 = r(1)->get("vel"); + auto drdt0 = rd(0)->get("vel"); + auto dudt0 = rd(0)->get("acc"); + auto dudt1 = rd(1)->get("acc"); + for (auto obj : lines) + MAKE_WILSON_VEC3(obj); + for (auto obj : points) + MAKE_WILSON_VEC3(obj); + for (auto obj : rods) + MAKE_WILSON_QUAT(obj); + for (auto obj : bodies) + MAKE_WILSON_QUAT(obj); } -TimeScheme* +Scheme* create_time_scheme(const std::string& name, moordyn::Log* log, moordyn::WavesRef waves) { - TimeScheme* out = NULL; + Scheme* out = NULL; if (str::lower(name) == "euler") { out = new EulerScheme(log, waves); } else if (str::lower(name) == "leuler") { @@ -863,15 +962,6 @@ create_time_scheme(const std::string& name, s << "Invalid Midpoint name format '" << name << "'"; throw moordyn::invalid_value_error(s.str().c_str()); } - } else if (str::startswith(str::lower(name), "anderson")) { - try { - unsigned int iters = std::stoi(name.substr(8)); - out = new AndersonEulerScheme(log, waves, iters, 0.5); - } catch (std::invalid_argument) { - stringstream s; - s << "Invalid Anderson name format '" << name << "'"; - throw moordyn::invalid_value_error(s.str().c_str()); - } } else if (str::startswith(str::lower(name), "aca")) { try { unsigned int iters = std::stoi(name.substr(3)); @@ -902,4 +992,6 @@ create_time_scheme(const std::string& name, return out; } +} // ::time + } // ::moordyn diff --git a/source/Time.hpp b/source/Time.hpp index 7ca359d4..5d45987b 100644 --- a/source/Time.hpp +++ b/source/Time.hpp @@ -41,22 +41,25 @@ #include "Point.hpp" #include "Rod.hpp" #include "Body.hpp" +#include "Waves.hpp" #include #include namespace moordyn { -/** @class TimeScheme Time.hpp +namespace time { + +/** @class Scheme Time.hpp * @brief Time scheme abstraction * * This class is helping mooring::MoorDyn to can consider every single time * scheme in the very same way */ -class TimeScheme : public io::IO +class Scheme : public io::IO { public: /// @brief Destructor - virtual ~TimeScheme() {} + virtual ~Scheme() {} /** @brief Set the ground body * @param obj The ground body @@ -207,7 +210,7 @@ class TimeScheme : public io::IO /** @brief Set the simulation time * @param time The time - * @note This method is also calling to TimeScheme::Next() + * @note This method is also calling to Scheme::Next() */ inline void SetTime(const real& time) { @@ -227,7 +230,7 @@ class TimeScheme : public io::IO /** @brief Prepare everything for the next outer time step * - * Always call this method before start calling TimeScheme::Step() + * Always call this method before start calling Scheme::Step() */ inline void Next() { @@ -248,7 +251,7 @@ class TimeScheme : public io::IO * * This function is the one that must be specialized on each time scheme, * but remember to call it at the end of the inherited function to increment - * TimeScheme::t_local + * Scheme::t_local * @param dt Time step */ virtual void Step(real& dt) { t_local += dt; }; @@ -257,16 +260,14 @@ class TimeScheme : public io::IO * @param i The index of the state variable to take * @return The state variable */ - inline virtual MoorDynState GetState(unsigned int i=0) - { - return MoorDynState(); - } + virtual moordyn::state::State GetState(unsigned int i=0) = 0; /** @brief Resume the simulation from the stationary solution * @param state The stationary solution * @param i The index of the state variable to take */ - inline virtual void SetState(const MoorDynState& state, unsigned int i=0) + inline virtual void SetState(const moordyn::state::State& state, + unsigned int i=0) {}; /** @brief Save the system state on a file @@ -281,7 +282,6 @@ class TimeScheme : public io::IO /** @brief Load the system state from a file * @param filepath The output file path * @param i The index of the state variable to overwrite - * @see ::DeserializeState() */ inline virtual void LoadState(const std::string filepath, unsigned int i=0) { @@ -291,7 +291,7 @@ class TimeScheme : public io::IO /** @brief Constructor * @param log Logging handler */ - TimeScheme(moordyn::Log* log) + Scheme(moordyn::Log* log) : io::IO(log) , name("None") , t(0.0) @@ -325,21 +325,32 @@ class TimeScheme : public io::IO real cfl; }; -// Forward declare waves -class Waves; -typedef std::shared_ptr WavesRef; +/** Definition to explicitely let the compiler know the type inside the + * std::array. + * + * This is required to avoid problems with the templates + */ +#define AS_STATE(R) ((moordyn::state::State*)R) -/** @class TimeSchemeBase Time.hpp +/** @class SchemeBase Time.hpp * @brief A generic abstract integration scheme * * This class can be later overloaded to implement a plethora of time schemes */ template -class TimeSchemeBase : public TimeScheme +class SchemeBase : public Scheme { public: /// @brief Destructor - virtual ~TimeSchemeBase() {} + virtual ~SchemeBase() + { + for (unsigned int substep = 0; substep < NSTATE; substep++) { + delete _r[substep]; + } + for (unsigned int substep = 0; substep < NDERIV; substep++) { + delete _rd[substep]; + } + } /** @brief Add a line * @param obj The line @@ -348,23 +359,42 @@ class TimeSchemeBase : public TimeScheme virtual void AddLine(Line* obj) { try { - TimeScheme::AddLine(obj); + Scheme::AddLine(obj); } catch (...) { throw; } - // Build up the states and states derivatives - unsigned int n = obj->getN() - 1; - LineState state; - state.pos.assign(n, vec::Zero()); - state.vel.assign(n, vec::Zero()); - for (unsigned int i = 0; i < r.size(); i++) { - r[i].lines.push_back(state); + // Build up the states and state derivatives + AS_STATE(_r[0])->addLine(obj); + AS_STATE(_r[0])->setListLength("pos", 3, obj); + AS_STATE(_r[0])->setListLength("vel", 3, obj); + for (unsigned int i = 0; i < obj->getN() - 1; i++) { + AS_STATE(_r[0])->get("pos", obj)(i) = vec::Zero(); + AS_STATE(_r[0])->get("vel", obj)(i) = vec::Zero(); + } + for (unsigned int i = 1; i < NSTATE; i++) { + AS_STATE(_r[i])->addLine(obj); + AS_STATE(_r[i])->setListLength("pos", 3, obj); + AS_STATE(_r[i])->setListLength("vel", 3, obj); + AS_STATE(_r[i])->get("pos", obj) = + AS_STATE(_r[0])->get("pos", obj); + AS_STATE(_r[i])->get("vel", obj) = + AS_STATE(_r[0])->get("vel", obj); + } + AS_STATE(_rd[0])->addLine(obj); + AS_STATE(_rd[0])->setListLength("vel", 3, obj); + AS_STATE(_rd[0])->setListLength("acc", 3, obj); + for (unsigned int i = 0; i < obj->getN() - 1; i++) { + AS_STATE(_rd[0])->get("vel", obj)(i) = vec::Zero(); + AS_STATE(_rd[0])->get("acc", obj)(i) = vec::Zero(); } - DLineStateDt dstate; - dstate.vel.assign(n, vec::Zero()); - dstate.acc.assign(n, vec::Zero()); - for (unsigned int i = 0; i < rd.size(); i++) { - rd[i].lines.push_back(dstate); + for (unsigned int i = 1; i < NDERIV; i++) { + AS_STATE(_rd[i])->addLine(obj); + AS_STATE(_rd[i])->setListLength("vel", 3, obj); + AS_STATE(_rd[i])->setListLength("acc", 3, obj); + AS_STATE(_rd[i])->get("vel", obj) = + AS_STATE(_rd[0])->get("vel", obj); + AS_STATE(_rd[i])->get("acc", obj) = + AS_STATE(_rd[0])->get("acc", obj); } // Add the mask value _calc_mask.lines.push_back(true); @@ -380,15 +410,16 @@ class TimeSchemeBase : public TimeScheme { unsigned int i; try { - i = TimeScheme::RemoveLine(obj); + i = Scheme::RemoveLine(obj); } catch (...) { throw; } - for (unsigned int i = 0; i < r.size(); i++) - r[i].lines.erase(r[i].lines.begin() + i); - for (unsigned int i = 0; i < rd.size(); i++) - rd[i].lines.erase(rd[i].lines.begin() + i); - _calc_mask.lines.erase(_calc_mask.lines.begin() + i); + for (unsigned int i = 0; i < NSTATE; i++) { + AS_STATE(_r[i])->removeLine(obj); + } + for (unsigned int i = 0; i < NDERIV; i++) { + AS_STATE(_rd[i])->removeLine(obj); + } return i; } @@ -399,22 +430,24 @@ class TimeSchemeBase : public TimeScheme virtual void AddPoint(Point* obj) { try { - TimeScheme::AddPoint(obj); + Scheme::AddPoint(obj); } catch (...) { throw; } - // Build up the states and states derivatives - PointState state; - state.pos = vec::Zero(); - state.vel = vec::Zero(); - for (unsigned int i = 0; i < r.size(); i++) { - r[i].points.push_back(state); + // Build up the states and state derivatives + for (unsigned int i = 0; i < NSTATE; i++) { + AS_STATE(_r[i])->addPoint(obj); + AS_STATE(_r[i])->setListLength("pos", 3, obj); + AS_STATE(_r[i])->setListLength("vel", 3, obj); + AS_STATE(_r[i])->get("pos", obj)(0) = vec::Zero(); + AS_STATE(_r[i])->get("vel", obj)(0) = vec::Zero(); } - DPointStateDt dstate; - dstate.vel = vec::Zero(); - dstate.acc = vec::Zero(); - for (unsigned int i = 0; i < rd.size(); i++) { - rd[i].points.push_back(dstate); + for (unsigned int i = 0; i < NDERIV; i++) { + AS_STATE(_rd[i])->addPoint(obj); + AS_STATE(_rd[i])->setListLength("vel", 3, obj); + AS_STATE(_rd[i])->setListLength("acc", 3, obj); + AS_STATE(_rd[i])->get("vel", obj)(0) = vec::Zero(); + AS_STATE(_rd[i])->get("acc", obj)(0) = vec::Zero(); } // Add the mask value _calc_mask.points.push_back(true); @@ -430,14 +463,16 @@ class TimeSchemeBase : public TimeScheme { unsigned int i; try { - i = TimeScheme::RemovePoint(obj); + i = Scheme::RemovePoint(obj); } catch (...) { throw; } - for (unsigned int i = 0; i < r.size(); i++) - r[i].points.erase(r[i].points.begin() + i); - for (unsigned int i = 0; i < rd.size(); i++) - rd[i].points.erase(rd[i].points.begin() + i); + for (unsigned int i = 0; i < NSTATE; i++) { + AS_STATE(_r[i])->removePoint(obj); + } + for (unsigned int i = 0; i < NDERIV; i++) { + AS_STATE(_rd[i])->removePoint(obj); + } _calc_mask.points.erase(_calc_mask.points.begin() + i); return i; } @@ -449,22 +484,24 @@ class TimeSchemeBase : public TimeScheme virtual void AddRod(Rod* obj) { try { - TimeScheme::AddRod(obj); + Scheme::AddRod(obj); } catch (...) { throw; } - // Build up the states and states derivatives - RodState state; - state.pos = XYZQuat::Zero(); - state.vel = vec6::Zero(); - for (unsigned int i = 0; i < r.size(); i++) { - r[i].rods.push_back(state); + // Build up the states and state derivatives + for (unsigned int i = 0; i < NSTATE; i++) { + AS_STATE(_r[i])->addRod(obj); + AS_STATE(_r[i])->setListLength("pos", 7, obj); + AS_STATE(_r[i])->setListLength("vel", 6, obj); + AS_STATE(_r[i])->get("pos", obj)(0) = vec7::Zero(); + AS_STATE(_r[i])->get("vel", obj)(0) = vec6::Zero(); } - DRodStateDt dstate; - dstate.vel = XYZQuat::Zero(); - dstate.acc = vec6::Zero(); - for (unsigned int i = 0; i < rd.size(); i++) { - rd[i].rods.push_back(dstate); + for (unsigned int i = 0; i < NDERIV; i++) { + AS_STATE(_rd[i])->addRod(obj); + AS_STATE(_rd[i])->setListLength("vel", 7, obj); + AS_STATE(_rd[i])->setListLength("acc", 6, obj); + AS_STATE(_rd[i])->get("vel", obj)(0) = vec7::Zero(); + AS_STATE(_rd[i])->get("acc", obj)(0) = vec6::Zero(); } // Add the mask value _calc_mask.rods.push_back(true); @@ -480,14 +517,16 @@ class TimeSchemeBase : public TimeScheme { unsigned int i; try { - i = TimeScheme::RemoveRod(obj); + i = Scheme::RemoveRod(obj); } catch (...) { throw; } - for (unsigned int i = 0; i < r.size(); i++) - r[i].rods.erase(r[i].rods.begin() + i); - for (unsigned int i = 0; i < rd.size(); i++) - rd[i].rods.erase(rd[i].rods.begin() + i); + for (unsigned int i = 0; i < NSTATE; i++) { + AS_STATE(_r[i])->removeRod(obj); + } + for (unsigned int i = 0; i < NDERIV; i++) { + AS_STATE(_rd[i])->removeRod(obj); + } _calc_mask.rods.erase(_calc_mask.rods.begin() + i); return i; } @@ -499,22 +538,24 @@ class TimeSchemeBase : public TimeScheme virtual void AddBody(Body* obj) { try { - TimeScheme::AddBody(obj); + Scheme::AddBody(obj); } catch (...) { throw; } - // Build up the states and states derivatives - BodyState state; - state.pos = XYZQuat::Zero(); - state.vel = vec6::Zero(); - for (unsigned int i = 0; i < r.size(); i++) { - r[i].bodies.push_back(state); + // Build up the states and state derivatives + for (unsigned int i = 0; i < NSTATE; i++) { + AS_STATE(_r[i])->addBody(obj); + AS_STATE(_r[i])->setListLength("pos", 7, obj); + AS_STATE(_r[i])->setListLength("vel", 6, obj); + AS_STATE(_r[i])->get("pos", obj)(0) = vec7::Zero(); + AS_STATE(_r[i])->get("vel", obj)(0) = vec6::Zero(); } - DBodyStateDt dstate; - dstate.vel = XYZQuat::Zero(); - dstate.acc = vec6::Zero(); - for (unsigned int i = 0; i < rd.size(); i++) { - rd[i].bodies.push_back(dstate); + for (unsigned int i = 0; i < NDERIV; i++) { + AS_STATE(_rd[i])->addBody(obj); + AS_STATE(_rd[i])->setListLength("vel", 7, obj); + AS_STATE(_rd[i])->setListLength("acc", 6, obj); + AS_STATE(_rd[i])->get("vel", obj)(0) = vec7::Zero(); + AS_STATE(_rd[i])->get("acc", obj)(0) = vec6::Zero(); } // Add the mask value _calc_mask.bodies.push_back(true); @@ -530,14 +571,16 @@ class TimeSchemeBase : public TimeScheme { unsigned int i; try { - i = TimeScheme::RemoveBody(obj); + i = Scheme::RemoveBody(obj); } catch (...) { throw; } - for (unsigned int i = 0; i < r.size(); i++) - r[i].bodies.erase(r[i].bodies.begin() + i); - for (unsigned int i = 0; i < rd.size(); i++) - rd[i].bodies.erase(rd[i].bodies.begin() + i); + for (unsigned int i = 0; i < NSTATE; i++) { + AS_STATE(_r[i])->removeBody(obj); + } + for (unsigned int i = 0; i < NDERIV; i++) { + AS_STATE(_rd[i])->removeBody(obj); + } _calc_mask.bodies.erase(_calc_mask.bodies.begin() + i); return i; } @@ -554,29 +597,37 @@ class TimeSchemeBase : public TimeScheme // types (mutate) without needing to micromanage them in the time // scheme for (unsigned int i = 0; i < bodies.size(); i++) { - if ((bodies[i]->type != Body::FREE) && (bodies[i]->type != Body::CPLDPIN)) // Only fully coupled bodies are intialized in MD2.cpp + // Only fully coupled bodies are intialized in MD2.cpp + if ((bodies[i]->type != Body::FREE) && + (bodies[i]->type != Body::CPLDPIN)) continue; - std::tie(r[0].bodies[i].pos, r[0].bodies[i].vel) = - bodies[i]->initialize(); + auto [pos, vel] = bodies[i]->initialize(); + AS_STATE(_r[0])->get("pos", bodies[i])(0) = pos.toVec7(); + AS_STATE(_r[0])->get("vel", bodies[i])(0) = vel; } for (unsigned int i = 0; i < rods.size(); i++) { if ((rods[i]->type != Rod::FREE) && (rods[i]->type != Rod::PINNED)) continue; - std::tie(r[0].rods[i].pos, r[0].rods[i].vel) = - rods[i]->initialize(); + auto [pos, vel] = rods[i]->initialize(); + AS_STATE(_r[0])->get("pos", rods[i])(0) = pos.toVec7(); + AS_STATE(_r[0])->get("vel", rods[i])(0) = vel; } for (unsigned int i = 0; i < points.size(); i++) { if (points[i]->type != Point::FREE) continue; - std::tie(r[0].points[i].pos, r[0].points[i].vel) = - points[i]->initialize(); + auto [pos, vel] = points[i]->initialize(); + AS_STATE(_r[0])->get("pos", points[i])(0) = pos; + AS_STATE(_r[0])->get("vel", points[i])(0) = vel; } for (unsigned int i = 0; i < lines.size(); i++) { - std::tie(r[0].lines[i].pos, r[0].lines[i].vel) = - lines[i]->initialize(); + auto [pos, vel] = lines[i]->initialize(); + for (unsigned int j = 0; j < pos.size(); j++) { + AS_STATE(_r[0])->get("pos", lines[i])(j) = pos[j]; + AS_STATE(_r[0])->get("vel", lines[i])(j) = vel[j]; + } } } @@ -584,101 +635,70 @@ class TimeSchemeBase : public TimeScheme * * This function is the one that must be specialized on each time scheme, * but remember to call it at the end of the inherited function to increment - * TimeScheme::t_local + * Scheme::t_local * @param dt Time step */ - virtual void Step(real& dt) { TimeScheme::Step(dt); }; + virtual void Step(real& dt) { Scheme::Step(dt); }; - /** @brief Get the state variable + /** @brief Get the state * @param i The index of the state variable to take * @return The state variable + * @throws moordyn::invalid_value_error if the @p i index is greater or + * equal than the number of states + * @{ */ - inline MoorDynState GetState(unsigned int i=0) + inline state::State* r(unsigned int i=0) { - return r[i]; + if (i >= NSTATE) { + LOGERR << "State " << i << " cannot be got on a '" << name + << "' scheme that has " << NSTATE << "states" + << endl; + throw moordyn::invalid_value_error("Invalid state"); + } + return _r[i]; } - /** @brief Resume the simulation from the stationary solution - * @param state The stationary solution - * @param i The index of the state variable to take - */ - inline virtual void SetState(const MoorDynState& state, unsigned int i=0) + inline state::State GetState(unsigned int i=0) { - r[i] = state; + return *r(i); } - - /** @brief Pack the system state - * @param i The index of the state variable to serialize - * @return The serialized state variable, including positions and - * velocities + /** + * @} */ - inline virtual std::vector SerializeState(unsigned int i=0) - { - std::vector data, subdata; - for (unsigned int j = 0; j < bodies.size(); j++) { - subdata = io::IO::Serialize(r[i].bodies[j].pos); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(r[i].bodies[j].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - } - for (unsigned int j = 0; j < rods.size(); j++) { - subdata = io::IO::Serialize(r[i].rods[j].pos); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(r[i].rods[j].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - } - for (unsigned int j = 0; j < points.size(); j++) { - subdata = io::IO::Serialize(r[i].points[j].pos); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(r[i].points[j].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - } - for (unsigned int j = 0; j < lines.size(); j++) { - subdata = io::IO::Serialize(r[i].lines[j].pos); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(r[i].lines[j].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - } - return data; - } - /** @brief Unpack and set the system state - * @param data The packed system state - * @param i The index of the state variable to overwrite - * @return A pointer to the end of the file, for debugging purposes + /** @brief Resume the simulation from the stationary solution + * @param state The stationary solution + * @param i The index of the state variable to take + * @throws moordyn::invalid_value_error if the @p i index is greater or + * equal than the number of states */ - inline virtual uint64_t* DeserializeState(const uint64_t* data, - unsigned int i=0) + inline virtual void SetState(const state::State& state, unsigned int i=0) { - uint64_t* ptr = (uint64_t*)data; - for (unsigned int j = 0; j < bodies.size(); j++) { - ptr = io::IO::Deserialize(ptr, r[i].bodies[j].pos); - ptr = io::IO::Deserialize(ptr, r[i].bodies[j].vel); + if (i >= NSTATE) { + LOGERR << "State " << i << " cannot be setted on a '" << name + << "' scheme that has " << NSTATE << "states" + << endl; + throw moordyn::invalid_value_error("Invalid state"); } - for (unsigned int j = 0; j < rods.size(); j++) { - ptr = io::IO::Deserialize(ptr, r[i].rods[j].pos); - ptr = io::IO::Deserialize(ptr, r[i].rods[j].vel); - } - for (unsigned int j = 0; j < points.size(); j++) { - ptr = io::IO::Deserialize(ptr, r[i].points[j].pos); - ptr = io::IO::Deserialize(ptr, r[i].points[j].vel); - } - for (unsigned int j = 0; j < lines.size(); j++) { - ptr = io::IO::Deserialize(ptr, r[i].lines[j].pos); - ptr = io::IO::Deserialize(ptr, r[i].lines[j].vel); - } - return ptr; + *_r[i] = state; } /** @brief Save the system state on a file * @param filepath The output file path * @param i The index of the state variable to serialize - * @see ::SerializeState() + * @throws moordyn::invalid_value_error if the @p i index is greater or + * equal than the number of states */ inline virtual void SaveState(const std::string filepath, unsigned int i=0) { auto f = MakeFile(filepath); - std::vector data = SerializeState(i); + if (i >= NSTATE) { + LOGERR << "State " << i << " cannot be saved on a '" << name + << "' scheme that has " << NSTATE << "states" + << endl; + throw moordyn::invalid_value_error("Invalid state"); + } + std::vector data = AS_STATE(_r[i])->Serialize(); const uint64_t size = data.size(); f.write((char*)&size, sizeof(uint64_t)); for (auto v : data) { @@ -690,23 +710,50 @@ class TimeSchemeBase : public TimeScheme /** @brief Load the system state from a file * @param filepath The output file path * @param i The index of the state variable to overwrite - * @see ::DeserializeState() + * @throws moordyn::invalid_value_error if the @p i index is greater or + * equal than the number of states + * @throws moordyn::mem_error if the expected size of the serialized data + * is not met */ inline virtual void LoadState(const std::string filepath, unsigned int i=0) { auto [length, data] = LoadFile(filepath); - const uint64_t* end = DeserializeState(data, i); + if (i >= NSTATE) { + LOGERR << "State " << i << " cannot be loaded on a '" << name + << "' scheme that has " << NSTATE << "states" + << endl; + throw moordyn::invalid_value_error("Invalid state"); + } + const uint64_t* end = AS_STATE(_r[i])->Deserialize(data); if (data + length != end) { const uint64_t l = end - data; - LOGERR << l * sizeof(uint64_t) << " bytes (vs. " << length + LOGERR << l * sizeof(uint64_t) << " bytes (vs. " + << length * sizeof(uint64_t) << " bytes expected) unpacked from '" << filepath << "'" << endl; - throw moordyn::mem_error("Allocation error"); + throw moordyn::mem_error("Mismatching data size"); } free(data); } + /** @brief Get the state derivative + * @param i The index of the state derivative to take + * @return The state derivative + * @throws moordyn::invalid_value_error if the @p i index is greater or + * equal than the number of state derivatives + */ + inline state::State* rd(unsigned int i=0) + { + if (i >= NDERIV) { + LOGERR << "State derivative " << i << " cannot be got on a '" + << name << "' scheme that has " << NDERIV + << "state derivatives" << endl; + throw moordyn::invalid_value_error("Invalid state derivative"); + } + return _rd[i]; + } + /** @brief Produce the packed data to be saved * * The produced data can be used afterwards to restore the saved information @@ -725,56 +772,12 @@ class TimeSchemeBase : public TimeScheme // number of lines, rods and so on. That information is already // collected from the definition file for (unsigned int substep = 0; substep < NSTATE; substep++) { - for (unsigned int i = 0; i < bodies.size(); i++) { - subdata = io::IO::Serialize(r[substep].bodies[i].pos); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(r[substep].bodies[i].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - } - for (unsigned int i = 0; i < rods.size(); i++) { - subdata = io::IO::Serialize(r[substep].rods[i].pos); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(r[substep].rods[i].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - } - for (unsigned int i = 0; i < points.size(); i++) { - subdata = io::IO::Serialize(r[substep].points[i].pos); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(r[substep].points[i].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - } - for (unsigned int i = 0; i < lines.size(); i++) { - subdata = io::IO::Serialize(r[substep].lines[i].pos); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(r[substep].lines[i].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - } + subdata = _r[substep]->Serialize(); + data.insert(data.end(), subdata.begin(), subdata.end()); } for (unsigned int substep = 0; substep < NDERIV; substep++) { - for (unsigned int i = 0; i < bodies.size(); i++) { - subdata = io::IO::Serialize(rd[substep].bodies[i].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(rd[substep].bodies[i].acc); - data.insert(data.end(), subdata.begin(), subdata.end()); - } - for (unsigned int i = 0; i < rods.size(); i++) { - subdata = io::IO::Serialize(rd[substep].rods[i].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(rd[substep].rods[i].acc); - data.insert(data.end(), subdata.begin(), subdata.end()); - } - for (unsigned int i = 0; i < points.size(); i++) { - subdata = io::IO::Serialize(rd[substep].points[i].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(rd[substep].points[i].acc); - data.insert(data.end(), subdata.begin(), subdata.end()); - } - for (unsigned int i = 0; i < lines.size(); i++) { - subdata = io::IO::Serialize(rd[substep].lines[i].vel); - data.insert(data.end(), subdata.begin(), subdata.end()); - subdata = io::IO::Serialize(rd[substep].lines[i].acc); - data.insert(data.end(), subdata.begin(), subdata.end()); - } + subdata = _rd[substep]->Serialize(); + data.insert(data.end(), subdata.begin(), subdata.end()); } return data; @@ -797,40 +800,10 @@ class TimeSchemeBase : public TimeScheme // Along the same line, we did not save information about the number of // lines, rods and so on for (unsigned int substep = 0; substep < NSTATE; substep++) { - for (unsigned int i = 0; i < bodies.size(); i++) { - ptr = io::IO::Deserialize(ptr, r[substep].bodies[i].pos); - ptr = io::IO::Deserialize(ptr, r[substep].bodies[i].vel); - } - for (unsigned int i = 0; i < rods.size(); i++) { - ptr = io::IO::Deserialize(ptr, r[substep].rods[i].pos); - ptr = io::IO::Deserialize(ptr, r[substep].rods[i].vel); - } - for (unsigned int i = 0; i < points.size(); i++) { - ptr = io::IO::Deserialize(ptr, r[substep].points[i].pos); - ptr = io::IO::Deserialize(ptr, r[substep].points[i].vel); - } - for (unsigned int i = 0; i < lines.size(); i++) { - ptr = io::IO::Deserialize(ptr, r[substep].lines[i].pos); - ptr = io::IO::Deserialize(ptr, r[substep].lines[i].vel); - } + ptr = _r[substep]->Deserialize(ptr); } for (unsigned int substep = 0; substep < NDERIV; substep++) { - for (unsigned int i = 0; i < bodies.size(); i++) { - ptr = io::IO::Deserialize(ptr, rd[substep].bodies[i].vel); - ptr = io::IO::Deserialize(ptr, rd[substep].bodies[i].acc); - } - for (unsigned int i = 0; i < rods.size(); i++) { - ptr = io::IO::Deserialize(ptr, rd[substep].rods[i].vel); - ptr = io::IO::Deserialize(ptr, rd[substep].rods[i].acc); - } - for (unsigned int i = 0; i < points.size(); i++) { - ptr = io::IO::Deserialize(ptr, rd[substep].points[i].vel); - ptr = io::IO::Deserialize(ptr, rd[substep].points[i].acc); - } - for (unsigned int i = 0; i < lines.size(); i++) { - ptr = io::IO::Deserialize(ptr, rd[substep].lines[i].vel); - ptr = io::IO::Deserialize(ptr, rd[substep].lines[i].acc); - } + ptr = _rd[substep]->Deserialize(ptr); } return ptr; @@ -842,33 +815,50 @@ class TimeSchemeBase : public TimeScheme * @param waves The simulation waves object, needed so that we can tell it * about substeps */ - TimeSchemeBase(moordyn::Log* log, moordyn::WavesRef waves) - : TimeScheme(log) + SchemeBase(moordyn::Log* log, moordyn::WavesRef waves) + : Scheme(log) , waves(waves) { + // Register some basic variables that we know are always present + // The position, and its associated derivative, have several different + // types depending on the instance (e.g. moordyn::vec for lines and + // points, moordyn::XYZQuat for rods and bodies), so better using lists + // that we can resize on demand + for (unsigned int substep = 0; substep < NSTATE; substep++) { + moordyn::state::State* state = new moordyn::state::State(log); + state->addVar("pos"); + state->addVar("vel"); + _r[substep] = state; + } + for (unsigned int substep = 0; substep < NDERIV; substep++) { + moordyn::state::State* state = new moordyn::state::State(log); + state->addVar("vel"); + state->addVar("acc"); + _rd[substep] = state; + } } /** @brief Update all the entities to set the state - * @param substep The index within moordyn::TimeSchemeBase::r that will be + * @param substep The index within moordyn::SchemeBase::r that will be * applied to set the states * @param t_local The local time, within the inner time step (from 0 to dt) * @note This is only affecting to the free entities - * @see TimeScheme::Next() - * @see TimeScheme::Step() + * @see Scheme::Next() + * @see Scheme::Step() */ void Update(real t_local, unsigned int substep = 0); /** @brief Compute the time derivatives and store them - * @param substep The index within moordyn::TimeSchemeBase::rd where the + * @param substep The index within moordyn::SchemeBase::rd where the * info will be saved */ void CalcStateDeriv(unsigned int substep = 0); /// The list of states - std::array r; + std::array _r; /// The list of state derivatives - std::array rd; + std::array _rd; /// The waves instance std::shared_ptr waves; @@ -888,7 +878,7 @@ class TimeSchemeBase : public TimeScheme std::vector bodies; } mask; - /// The TimeSchemeBase::CalcStateDeriv() mask + /// The SchemeBase::CalcStateDeriv() mask mask _calc_mask; }; @@ -898,7 +888,7 @@ class TimeSchemeBase : public TimeScheme * The stationary solution is featured by the lack of velocity on the system, * i.e. the system positions are integrating directly from the accelerations */ -class StationaryScheme : public TimeSchemeBase<2, 1> +class StationaryScheme final : public SchemeBase<2, 1> { public: /** @brief Constructor @@ -934,11 +924,21 @@ class StationaryScheme : public TimeSchemeBase<2, 1> inline unsigned int NStates() const { unsigned int n = bodies.size() + rods.size() + points.size(); for (unsigned int i = 0; i < lines.size(); i++) - n += r[0].lines[i].pos.size(); + n += lines[i]->getN() - 1; return n; } private: + /** @brief Make the state derivative stationary + * + * Making the derivative stationary means replacing the velocity by half + * the acceleration by the time step + * @param dt The time step + * @param i The index of the state + * @param id The index of the state derivative + */ + void MakeStationary(real& dt, unsigned int i=0, unsigned int id=0); + /** The last computed acceleration module * @see DMoorDynStateDt::MakeStationary() * @see StationaryScheme::Error() @@ -955,7 +955,7 @@ class StationaryScheme : public TimeSchemeBase<2, 1> * This time scheme is strongly discourage, and use only for testing/debugging * purposes */ -class EulerScheme : public TimeSchemeBase<1, 1> +class EulerScheme final : public SchemeBase<1, 1> { public: /** @brief Constructor @@ -975,17 +975,17 @@ class EulerScheme : public TimeSchemeBase<1, 1> virtual void Step(real& dt); }; -/** @class LocalTimeSchemeBase Time.hpp +/** @class LocalSchemeBase Time.hpp * @brief A generic abstract integration scheme * * This class can be later overloaded to implement a plethora of time schemes */ template -class LocalTimeSchemeBase : public TimeSchemeBase +class LocalSchemeBase : public SchemeBase { public: /// @brief Destructor - virtual ~LocalTimeSchemeBase() {} + virtual ~LocalSchemeBase() {} /** @brief Create an initial state for all the entities * @note Just the first state is written. None of the following states, nor @@ -994,7 +994,7 @@ class LocalTimeSchemeBase : public TimeSchemeBase */ inline void Init() { - TimeSchemeBase::Init(); + SchemeBase::Init(); ComputeDt(); } @@ -1002,9 +1002,9 @@ class LocalTimeSchemeBase : public TimeSchemeBase * @param state The stationary solution * @param i The index of the state variable to take */ - inline void SetState(const MoorDynState& state, unsigned int i=0) + inline virtual void SetState(const state::State& state, unsigned int i=0) { - TimeSchemeBase::SetState(state, i); + SchemeBase::SetState(state, i); ComputeDt(); } @@ -1014,8 +1014,8 @@ class LocalTimeSchemeBase : public TimeSchemeBase * @param waves The simulation waves object, needed so that we can tell it * about substeps */ - LocalTimeSchemeBase(moordyn::Log* log, moordyn::WavesRef waves) - : TimeSchemeBase(log, waves) + LocalSchemeBase(moordyn::Log* log, moordyn::WavesRef waves) + : SchemeBase(log, waves) { } @@ -1063,7 +1063,7 @@ class LocalTimeSchemeBase : public TimeSchemeBase * Thus, the derivatives recomputation is delayed until those time steps are * fulfilled */ -class LocalEulerScheme : public LocalTimeSchemeBase<1, 1> +class LocalEulerScheme final : public LocalSchemeBase<1, 1> { public: /** @brief Constructor @@ -1088,7 +1088,7 @@ class LocalEulerScheme : public LocalTimeSchemeBase<1, 1> * performance, since it is only requiring a single derivative per time step, * while rendering similar results to other 2nd order schemes */ -class HeunScheme : public TimeSchemeBase<1, 2> +class HeunScheme final : public SchemeBase<1, 2> { public: /** @brief Constructor @@ -1113,7 +1113,7 @@ class HeunScheme : public TimeSchemeBase<1, 2> * * This was the traditionally applied time scheme in MoorDyn v1 */ -class RK2Scheme : public TimeSchemeBase<2, 2> +class RK2Scheme final : public SchemeBase<2, 2> { public: /** @brief Constructor @@ -1141,7 +1141,7 @@ class RK2Scheme : public TimeSchemeBase<2, 2> * other hand, it might be possible to increase the time step size, compensating * such a drawback */ -class RK4Scheme : public TimeSchemeBase<5, 4> +class RK4Scheme final : public SchemeBase<5, 4> { public: /** @brief Constructor @@ -1172,7 +1172,7 @@ class RK4Scheme : public TimeSchemeBase<5, 4> * Euler's and Heun's ones */ template -class ABScheme : public LocalTimeSchemeBase<1, 5> +class ABScheme final : public LocalSchemeBase<1, 5> { public: /** @brief Constructor @@ -1199,7 +1199,7 @@ class ABScheme : public LocalTimeSchemeBase<1, 5> */ virtual std::vector Serialize(void) { - std::vector data = TimeSchemeBase::Serialize(); + std::vector data = SchemeBase::Serialize(); // We append the number of available steps data.push_back(io::IO::Serialize((uint64_t)n_steps)); @@ -1215,7 +1215,7 @@ class ABScheme : public LocalTimeSchemeBase<1, 5> */ virtual uint64_t* Deserialize(const uint64_t* data) { - uint64_t* ptr = TimeSchemeBase::Deserialize(data); + uint64_t* ptr = SchemeBase::Deserialize(data); uint64_t n; ptr = io::IO::Deserialize(ptr, n); n_steps = n; @@ -1236,30 +1236,38 @@ class ABScheme : public LocalTimeSchemeBase<1, 5> for (unsigned int i = 0; i < lines.size(); i++) { if (!_calc_mask.lines[i]) continue; - rd[dst].lines[i].vel = rd[org].lines[i].vel; - rd[dst].lines[i].acc = rd[org].lines[i].acc; + rd(dst)->get("vel", lines[i]) = + rd(org)->get("vel", lines[i]); + rd(dst)->get("acc", lines[i]) = + rd(org)->get("acc", lines[i]); } for (unsigned int i = 0; i < points.size(); i++) { if (!_calc_mask.points[i] && (points[i]->type == Point::FREE)) continue; - rd[dst].points[i].vel = rd[org].points[i].vel; - rd[dst].points[i].acc = rd[org].points[i].acc; + rd(dst)->get("vel", points[i]) = + rd(org)->get("vel", points[i]); + rd(dst)->get("acc", points[i]) = + rd(org)->get("acc", points[i]); } for (unsigned int i = 0; i < rods.size(); i++) { if (!_calc_mask.rods[i] && ((rods[i]->type != Rod::FREE) || (rods[i]->type != Rod::PINNED))) continue; - rd[dst].rods[i].vel = rd[org].rods[i].vel; - rd[dst].rods[i].acc = rd[org].rods[i].acc; + rd(dst)->get("vel", rods[i]) = + rd(org)->get("vel", rods[i]); + rd(dst)->get("acc", rods[i]) = + rd(org)->get("acc", rods[i]); } for (unsigned int i = 0; i < bodies.size(); i++) { if (!_calc_mask.bodies[i] && (bodies[i]->type == Body::FREE)) continue; - rd[dst].bodies[i].vel = rd[org].bodies[i].vel; - rd[dst].bodies[i].acc = rd[org].bodies[i].acc; + rd(dst)->get("vel", bodies[i]) = + rd(org)->get("vel", bodies[i]); + rd(dst)->get("acc", bodies[i]) = + rd(org)->get("acc", bodies[i]); } } @@ -1267,7 +1275,7 @@ class ABScheme : public LocalTimeSchemeBase<1, 5> */ inline void shift() { - for (unsigned int i = 0; i < rd.size() - 1; i++) + for (unsigned int i = 0; i < _rd.size() - 1; i++) shift(i); } }; @@ -1278,7 +1286,7 @@ class ABScheme : public LocalTimeSchemeBase<1, 5> * This class can be later overloaded to implement a plethora of time schemes */ template -class ImplicitSchemeBase : public TimeSchemeBase +class ImplicitSchemeBase : public SchemeBase { public: /** @brief Constructor @@ -1338,171 +1346,6 @@ class ImplicitSchemeBase : public TimeSchemeBase real _c1; }; -/** @class AndersonSchemeBase Time.hpp - * @brief A generic abstract implicit scheme accelerated with Anderson scheme - * - * This class can be later overloaded to implement a plethora of time schemes - */ -template -class AndersonSchemeBase : public ImplicitSchemeBase -{ - public: - /** @brief Constructor - * @param log Logging handler - * @param waves Waves instance - * @param iters The number of inner iterations to find the derivative - * @param m The number of points to compute Anderson's acceleration - * @param tol Minimum residue to consider that the solution has converged - * @param tol_rel Relative residue reduction to consider that the solution - * has converged - */ - AndersonSchemeBase(moordyn::Log* log, - WavesRef waves, - unsigned int iters = 10, - unsigned int m = 4, - real tol = 1.e-2, - real tol_rel = 1.e-2, - real regularization = 1.e-10); - - /// @brief Destructor - virtual ~AndersonSchemeBase() {} - - /** @brief Get the residual tolerance - * - * When the maximum residue falls below this value the inner iteration - * is stopped - * @return The tolerance - */ - inline real tol() const { return _tol; } - - /** @brief Set the residual tolerance - * - * When the maximum residue falls below this value the inner iteration - * is stopped - * @param t The tolerance - */ - inline void tol(const real t) { _tol = t; } - - protected: - /** @brief Get the number of subiterations - * @return The number of iterations - */ - inline unsigned int iters() const { return _iters; } - - /** @brief Produce a new estimation - * @param iter The current iteration - * @param org The input point, x - * @param dst The input eval, f(x), as well as the output - * @param dt The time step to integrate the acceleration as the velocity - */ - void qr(unsigned int iter, unsigned int org, unsigned int dst, float dt); - - /** @brief Check if the iterator has converged - * @return true if the maximum residue has fallen below the tolerance, - * false otherwise - */ - inline bool converged() const - { - const real g = _g.col(1).cwiseAbs().mean(); - return (g < _tol) || (g / _g0 < _tol_rel); - } - - /** @brief Get the stats of the residues - * @param ago Either 0 to get the latests residue or 1 to get the previous - * one. - * @return The average and maximum residue - */ - inline const std::tuple residue(unsigned int ago=0) const - { - return { _g.col(1 - ago).cwiseAbs().mean(), - _g.col(1 - ago).cwiseAbs().maxCoeff() }; - } - - private: - /// The number of iterations - unsigned int _iters; - - /// The number of points to compute Anderson's acceleration - unsigned int _m; - - /// Minimum residue to consider that the solution has converged - real _tol; - - /// Relative residue reduction to consider that the solution has converged - real _tol_rel; - - /// Regularization factor - real _regularization; - - /// Number of dofs - real _n; - - /// Initial residue - real _g0; - - /// The evaluation points list - Eigen::Matrix _x; - - /// The residues list - Eigen::Matrix _g; - - /// The evaluation points variation matrix - Eigen::MatrixXr _X; - - /// The residues variation matrix - Eigen::MatrixXr _G; - - /** @brief Compute the number of acceleration DOFs - * @return The number of acceleration DOFs - */ - inline unsigned int ndof() const { - unsigned int n = 3 * this->points.size() + 6 * (this->bodies.size() + this->rods.size()); - for (unsigned int i = 0; i < this->lines.size(); i++) - n += 3 * this->rd[0].lines[i].acc.size(); - return n; - } - - /** @brief Fill the last column of the X matrix - * @param org The point, x - * @param dst The eval, f(x) - * @note This function is assuming that the matrix is already resized - */ - inline void fill(unsigned int org, unsigned int dst) - { - unsigned int i, j, n = 0; - for (i = 0; i < this->lines.size(); i++) { - for (j = 0; j < this->rd[org].lines[i].acc.size(); j++) { - const vec x = this->rd[org].lines[i].acc[j]; - const vec fx = this->rd[dst].lines[i].acc[j]; - _x(Eigen::seqN(n, 3), 1) = x; - _g(Eigen::seqN(n, 3), 1) = fx - x; - n += 3; - } - } - for (i = 0; i < this->points.size(); i++) { - const vec x = this->rd[org].points[i].acc; - const vec fx = this->rd[dst].points[i].acc; - _x(Eigen::seqN(n, 3), 1) = x; - _g(Eigen::seqN(n, 3), 1) = fx - x; - n += 3; - } - for (i = 0; i < this->rods.size(); i++) { - const vec6 x = this->rd[org].rods[i].acc; - const vec6 fx = this->rd[dst].rods[i].acc; - _x(Eigen::seqN(n, 6), 1) = x; - _g(Eigen::seqN(n, 6), 1) = fx - x; - n += 6; - } - for (i = 0; i < this->bodies.size(); i++) { - const vec6 x = this->rd[org].bodies[i].acc; - const vec6 fx = this->rd[dst].bodies[i].acc; - _x(Eigen::seqN(n, 6), 1) = x; - _g(Eigen::seqN(n, 6), 1) = fx - x; - n += 6; - } - } -}; - /** @class ImplicitEulerScheme Time.hpp * @brief Implicit 1st order Euler time scheme * @@ -1510,7 +1353,7 @@ class AndersonSchemeBase : public ImplicitSchemeBase * evaluated somewhere inside the time step. Obviously, since that point depends * on the derivative itself, a fixed point problem shall be solved */ -class ImplicitEulerScheme : public ImplicitSchemeBase<2, 2> +class ImplicitEulerScheme final : public ImplicitSchemeBase<2, 2> { public: /** @brief Constructor @@ -1540,43 +1383,6 @@ class ImplicitEulerScheme : public ImplicitSchemeBase<2, 2> real _dt_factor; }; -/** @class AndersonEulerScheme Time.hpp - * @brief Implicit 1st order Euler time scheme - * - * The implicit Euler method is an implicit method where the derivative is - * evaluated somewhere inside the time step. Obviously, since that point depends - * on the derivative itself, a fixed point problem shall be solved - */ -class AndersonEulerScheme : public AndersonSchemeBase<2, 2> -{ - public: - /** @brief Constructor - * @param log Logging handler - * @param waves Waves instance - * @param iters The number of inner iterations to find the derivative - * @param dt_factor The inner evaluation point factor. 0.5 for the midpoint - * method, 1.0 for the backward Euler method - */ - AndersonEulerScheme(moordyn::Log* log, - WavesRef waves, - unsigned int iters = 10, - real dt_factor = 0.5); - - /// @brief Destructor - virtual ~AndersonEulerScheme() {} - - /** @brief Run a time step - * - * This function is the one that must be specialized on each time scheme - * @param dt Time step - */ - virtual void Step(real& dt); - - private: - /// The evaluation point - real _dt_factor; -}; - /** @class ImplicitNewmarkScheme Time.hpp * @brief Implicit Newmark Scheme * @@ -1585,7 +1391,7 @@ class AndersonEulerScheme : public AndersonSchemeBase<2, 2> * and solids, specifically on its Average Constant Acceleration incarnation * @see https://en.wikipedia.org/wiki/Newmark-beta_method */ -class ImplicitNewmarkScheme : public ImplicitSchemeBase<2, 3> +class ImplicitNewmarkScheme final : public ImplicitSchemeBase<2, 3> { public: /** @brief Constructor @@ -1612,6 +1418,25 @@ class ImplicitNewmarkScheme : public ImplicitSchemeBase<2, 3> virtual void Step(real& dt); private: + /** @brief Make the gamma,beta-Newmark step + * + * The resulting state rate of change will have the following velocity + * + * \f[ u(t_{n+1}) = u(t_{n}) + \Delta t ( + * (1/2 - \beta) \dot{u(t_{n})} + + * \beta \dot{u(t_{n+1})}) \f] + * + * and the following acceleration + * + * \f[ \dot{u(t_{n+1})} = (1 - \gamma) \dot{u(t_{n})} + + * \gamma \dot{u(t_{n+1})}) \f] + * + * ::r(0) and ::rd(0) are the input state and derivative, while ::r(1) is + * the output state. + * @param dt Time step. + */ + void MakeNewmark(const real& dt); + /// Alpha factor real _gamma; /// Beta factor @@ -1630,7 +1455,7 @@ class ImplicitNewmarkScheme : public ImplicitSchemeBase<2, 3> * * @see https://www.academia.edu/download/59040594/wilson197220190426-49259-kipdfs.pdf */ -class ImplicitWilsonScheme : public ImplicitSchemeBase<2, 3> +class ImplicitWilsonScheme final : public ImplicitSchemeBase<2, 3> { public: /** @brief Constructor @@ -1656,6 +1481,27 @@ class ImplicitWilsonScheme : public ImplicitSchemeBase<2, 3> virtual void Step(real& dt); private: + /** @brief Make the Wilson step + * + * \f[ \dot{u(t_{n+1})} = + * (1 - \frac{\tau}{2 \theta \Delta t}) \dot{u(t_{n})} + + * \frac{\tau}{2 \theta \Delta t} \dot{u(t_{n+1})}) \f] + * + * and the following velocity + * + * \f[ u(t_{n+1}) = u(t_{n}) + \frac{\tau}{2} ( + * (1 - \frac{\tau}{3 \theta \Delta t}) \dot{u(t_{n})} + + * \frac{\tau}{3 \theta \Delta t} \dot{u(t_{n+1})}) \f] + * + * Note that \f$ \tau \f$ can be smaller than \f$ \theta \Delta t \f$. + * + * ::r(0) and ::rd(0) are the input state and derivative, while ::r(1) is + * the output state. + * @param tau Time advancing, \f$ \tau \f$. + * @param dt Enlarged time step, \f$ \theta \Delta t \f$. + */ + void MakeWilson(const real& tau, const real& dt); + /// Theta factor real _theta; }; @@ -1671,7 +1517,9 @@ class ImplicitWilsonScheme : public ImplicitSchemeBase<2, 3> * @throw moordyn::mem_error If the time scheme memory cannot be allocated * @note Remember to delete the returned time scheme at some point */ -TimeScheme* +Scheme* create_time_scheme(const std::string& name, moordyn::Log* log, WavesRef waves); +} // ::time + } // ::moordyn diff --git a/source/Util/CFL.hpp b/source/Util/CFL.hpp index 5a7b3e6d..9a9b48fe 100644 --- a/source/Util/CFL.hpp +++ b/source/Util/CFL.hpp @@ -62,7 +62,7 @@ class CFL /** @brief Destructor */ - virtual ~CFL() {}; + virtual ~CFL() = default; /** @brief Get the timestep from a CFL factor * @param cfl CFL factor @@ -118,7 +118,7 @@ class NatFreqCFL : public CFL /** @brief Destructor */ - virtual ~NatFreqCFL() {}; + virtual ~NatFreqCFL() = default; /** @brief Get the timestep from a CFL factor * @param cfl CFL factor @@ -200,7 +200,7 @@ class SuperCFL : public CFL /** @brief Destructor */ - virtual ~SuperCFL() {}; + virtual ~SuperCFL() = default; /** @brief Get the timestep from a CFL factor * @param cfl CFL factor diff --git a/source/Waves.cpp b/source/Waves.cpp index ee63edbb..ef7ec589 100644 --- a/source/Waves.cpp +++ b/source/Waves.cpp @@ -256,7 +256,7 @@ Waves::~Waves() {} void Waves::setup(EnvCondRef env_in, SeafloorRef seafloor, - TimeScheme* t, + time::Scheme* t, const char* folder) { // make sure to reset the kinematics if setup gets called multiple times diff --git a/source/Waves.hpp b/source/Waves.hpp index 118a9ab6..4411b12d 100644 --- a/source/Waves.hpp +++ b/source/Waves.hpp @@ -45,7 +45,10 @@ namespace moordyn { -class TimeScheme; +namespace time { +class Scheme; +} + class Seafloor; typedef std::shared_ptr SeafloorRef; @@ -629,7 +632,7 @@ class Waves : public LogUser real rho_w; /// The time integration scheme - moordyn::TimeScheme* _t_integrator; + moordyn::time::Scheme* _t_integrator; // ------------ from Line object... ----------- // new additions for handling waves in-object and precalculating them (not @@ -672,7 +675,7 @@ class Waves : public LogUser */ void setup(EnvCondRef env, SeafloorRef seafloor, - TimeScheme* t, + time::Scheme* t, const char* folder = "Mooring/"); }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cf75eb26..16e17fe5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,6 +39,7 @@ set(CPP_TESTS set(CATCH2_TESTS math_tests + state_tests testDecomposeString bodies beam diff --git a/tests/Mooring/WD0050_Chain.txt.ic b/tests/Mooring/WD0050_Chain.txt.ic index 36791eaedafe5f2d40d3eb762b66610089180902..df6467c60c59003edddf8a2eed0c3beeea5fea18 100644 GIT binary patch literal 2721 zcmeax&o6SR%wu8+Vq^dVEeOTH45h(TLWKio@y-3~`LEePggpTf*yRtTJN!;NyL#Fe8SL^6Y0&VM!!GZT>JU5i#NwW9 z^4R4WQlR0lfFa+?eR%@go+Jk@&V|KAc8a#v!i}!l-#p;yKWV*Rj7d$RL;1`7>#x02 zw0%`66d=DBAulyoVvCV&f&+VI%d_M2lx*LedJs?*h>%~?y>|cKf3Xg`R8#*m_$k|} z9GY}`&t`YH`6aLW<&@i_9hg~Cv+VyU+kRtu$GYH&8(dx|OLEID;Yf!)@xMPiuT-&} zJKylJ;8Rz)d_|ks`&moF9A36_Rb38IwauP6ZFwiqA$sfco_)*IY>(Zu{J8GCBV2yPhUZe#So|G$E??>1^yhqMybOI;t-ZQpXo>#nJ^fy=*`dm%dhtgC}#uDcG$Bn{iJ z&QA)Pb1mTV89WA_2fQ5}Eb2q{OBia}s-`SG+9qlOm;a!*H+9DjONUzi_m`HQ)wJC+ z|A>y=OFg(egWnM=sp|6d)=B8`eb}ab1^tp^GT>iml%eIv_6dVLQ75hH@)3S|7 zk+^bUrYKw3Ca~=k@g%{948^l^C?#!(q!i=Vluxi)u+Nt5vn8*0qRexga7TqoSch2699s pWK=YC$c`Kl2^kd)9pcFmk&sc*&>=x({?>cD$AA9(?GJCVe*>bJoLK+> diff --git a/tests/Mooring/WD0200_Chain.txt.ic b/tests/Mooring/WD0200_Chain.txt.ic index 29d544ff4ecaffe579962880bb42c1feb2188d49..1b11a8d5bec3b26d37396b0aabded655a6a3b47b 100644 GIT binary patch literal 4961 zcmeI!eKeF=90%~(ByT01kkj-sB5#q`VMp%n IY%4j4_qYP2RqQ+=qyriuVr?axP z)%0RQWm>(VOm3EAH`4$#kjPr*cHqXAa}uhxg%h@G$IYtUfS| zcTu`>{Wo}DoeuSljgq5>#_&!VU!Q`o-*+($3YT@(mV2w=JvdGI{wa9ZOM|T^Tdox} zYvR2sRr!57c#la1>-YE0=C0MnJ1te2j~?EeQh;|sUSByl1Mk8V<^E>k-7W>%WU}hZ zCHi=m3zhpf#Cwhq7}~AZi(_Wt9fZn!jPNc`h9h4;xFm9zjdw^^=10Z5C<(^G8qVd7 z%)vV)Nty3Fya(?F_K$U^Gq29adzC<$KMn6R0d#eDd%L?C<6XE*c|Kooc_K{bcdc+c zwgBG`iOTac!MlhLpx3e7<^30&!dITJ>3{m=x+~+PK?y)_YJf${ckNhM@S#qnHSkk<-zE#IQ7aJGeqsk5zJSRzOP@S)K0Jy z24?1DcgC9|tM#$&vU<{gE%SNP*1iLl{cs`Xl&J-pn+TQE^Q6!Ak{UioJ0N6@AnWBr z3&dgBcnqbJ-bv*}2~Nbr6|csC%c+*=YH6rR2#fR=nyj*uU&VvD24m=wWMo3M1Y>_o3#IhK(`sq9bALVaLVD%2n?2Xa(slf}?$u>}Yt!++Cz4 zTa1i^zFyLvP@>-Y5q};oM>A0M`zQuGco0Wpau2U9&0zC4F0=aOzFaxoU@Q>GZ8gokqHN zhY?S=Is$r}ee`)#>`||O-O8E!LWusimwJ~@I~5Kd9cdRVS@!5n$w&j`!)C(o3oA}m zq-_C{%;uN#3ha?}!+OowXwn7w?1LuZVc@T5@M6BQNB2jwzs-KKiRkygciWZe8w%mE zNL&pLsE(r{769prl4^Y>gA1P7;;^VJ2Q>Riq$~GiFww8rHPUNk76Lyd?BC+^*a4Ml ztNFM7#UZ>VsX!q&*bD~L4S#Y?9Z^=gU1V(0M#8z40rh;%P4J+0Ww~U#BT|#;FnWF1 zgx67Qx?YZPfNl1=$)?N^ZA>0gPkbIkI8D;}OVuD7s==_hlj4N9pI|zA zG@~VS)u12g!oS74bo_uBE6Jdrp(9s=cQfMr)(~#dndek)x*GZ;*4Q$i(NX)3x}ozS zzJ$l*dfal`y9!J*q^QW!8R?gd%;6fWB0Tu2(avw0JYmmMQ2=9`GfH3&ze%d_B%F5I z@BrI%Ipp~>J=D)TqYY^b=0`D?6F!_-YLb-Z4wCf3x4MJQs4&Gm|A^e3a9U9bPhGYI z44-AiuUq7T$|M6nch$HN9?Wzv&#!TU4?!s%JrOR5meaK2bSa(is+&3V=NXHjy~;N7 z(peW|cbqmo!_bCsA$Oft7@G-{3{DqIjHaI)`nG?(y?_Z9gb_;ha>fBx%!HC|R3 zvG+6;AD@19Pa7|)j1#UB24*^u4<;0q(nLNgVPK{c`Cvj(DNW>~5(Z{Ekq;&mmC{5$ SDq&!z6Zv35Q7Qf3`TPSWdcmmx delta 1142 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 Y6j&&*P++0JLV<+>3k4Pm{Ld8lKlbmy1^@s6 diff --git a/tests/Mooring/lines.txt.ic b/tests/Mooring/lines.txt.ic index 92406e7b7294215f8657e6ed3cab941fb8d33562..9570251efe3c21e898a925cddcf2b9260df31766 100644 GIT binary patch literal 4065 zcmeIz`!if;7{Kuro6MFNDN@8tNtbpL?IJE+)0qR?B++d@@nQT|)*$_qt%?OMwoOEdB*ME8@^>t7pN+^@VXf@j_S=^D;o>ojbA z*KKUEa}(yF$&Qf7?F|OlvBAIjD=SI(^VK1(ZSAyNj=r7|w zsmMkXo=4y6vNXre&5HZZ{G{qJan|-(EGeon-L^fg`X$WL%UU~Lr0O^3F47-$OVHy* z|4VGJ!ZICsrDo5mx=uh;<017ePhMwqxgdGgxgH){O|RZaRqQvIol2e#N!;SY3~ zD<6NJIg)8=tS>@}H}4ky#@&Z~67}uiPyr9`lB>xVO#}iI_s5;YkS=>{vFTBfSKJ49 zw_G^SZ;&5Dx22<>Iqi4Em77?6R>o3P4%kL_d8C6c0B-^ytsw@`obGd%} z`K#}u3t61>1uYil8*0SbJsXX8zJGSTb8$G*{y0b~3UkJy1Xag7&5q(d)%f8sV?G!e z+A5lIYF)5Do9BnRJ-K)<9u>3l4GtigZs)k+k}K9=IB4fY4aNH?X1JH(BZghnfa@;z za?yG}ll@IE_zZstZX+IB8?WSS=qVgq*F(Y-m@2jXhRe@J4}u%HL|;{TGW`KsGSo`X zHXM0figxU_3D`^UHQjM|S+6n)I(%0YWB*7HZQ1_I0)nrd`ZpX)SVD*ik^7hz9Y+&K z+Gt?}Uu`2lmuUC!A$^A(vzMNM%zJ{)>JWT=eNc8tF~WoLFZy>)eV>M|G4>R^Oz`F5 zCMq_uSp7d+9_NRIrJ{gvtBgp3uO945MQm(5Tw|77tgIxXfa-L50l`<{kK&1*=Jz0I z!l|;RItdMVo0aAfd`(d@?lbkH!FckF$Rt95PGhWz27<4<(FcW0K@@zY7`Kx<@{k@a z@1YsNSAFsOC+^6@f#Yy#-$Q(=CHUg^ z9PWO;CK#xJYj6TIXv|tbLc1x+oOY`PhGf zkLlE5=357{syW{i?Y(Ta|@bhtB+&#X!R_P!bsqjJ z-*$bu8EN)arzx@k%{tBaADuc+)yx~>m8qxCtJnQYp1!gng;LEKNip)y$9EG`YhN3g z-=!`~z<#+6=4$FRv|87#Z?ba}<>2wQ;E2t2hUjSjpMs?&F;oUOx3to-u_u=oGdgL4 zvfLZnZDzzE>@DFvD$B%U9*5uPurx88f|60F*PJO3BnPV`aa*Zl)|e2`sEJZ~Zt;@IDAB?Ncaqxpsh z1)i}-z;f=apiX%d-;@u3;Vu~hPAg>gJsgmYi5y%>uvzbRW}RyZM9rfIiTNes+n z`wC_+zm3mja8u{B*vKcuh`nt(3Iu+cwa$fMSo_03xg^vXMG7m9wVNG=WaImT%-JAJ zw^cP{RlA_y8)pYPJ$O(!EM?~!?!gM(_EGwRE2^XS+c{A~VD&sT$GYA<<6mI)Jcki| z2h&>$TdDq6UG8LK^=nCBytm;6@4@%LZODCN{iUqO^(3yX>j6;`OcdLG#S>)WdqMTQ zWuFx}3jID>B2>#y*ByFYjt#ci`0pTmPPQFf)GHT5o6nMJLf6cLo zErRG^rMG#(F+6stg%(QqY#I8oNV`h_sRnkeZblk5?+QGvL-_o1pW=XOh!3To^=_T` zHU(c{ZqIv}@agU*DKxP-0@_UvbAv;Zv45CVS_I*<3tcLUiHU ze4!e(Q#$go9xdm-8R4_G@ZICLlwrVixUh3WQygYozmb+q_?#D?bm+Vo0^FKAKBI3% z<4L{N;f;h(LD#{~=c|H%8o=32*NMdV#9;g;!sm=l>q2y)FLd-c^)b%8iJ4PgMM}bF zh;lBQGwTWPXbUG}Zvci2EmZ^I6AyD#pBg#>{IREozuNj9u5s<-llTO6 za*fZ&^(TA+iBBN$2_!yzvW1QMS>;uA=G0*Ox`@d+e8fy5_}_yiK4K;jcfe8RfV Kwg2}2q4GB +#include + +#include "Misc.hpp" +#include "Log.hpp" +#include "Point.hpp" +#include "Line.hpp" +#include "State.hpp" +#include +#include "catch2/matchers/catch_matchers_templated.hpp" +#include "util.h" + +// md::real is faster to type +namespace md = moordyn; + +md::Point* +CreatePoint(md::Log* log, EnvCondRef env) +{ + md::Point* obj = new md::Point(log, 0); + obj->setup(0, + md::Point::FIXED, + md::vec::Zero(), + 0, + 0, + md::vec::Zero(), + 0.0, + 0.0, + env); + return obj; +} + +md::Line* +CreateLine(md::Log* log, EnvCondRef env, unsigned int n, + shared_ptr outfile) +{ + md::Line* obj = new md::Line(log, 0); + LineProps props; + props.type = "main"; + props.d = 0.1; + props.w = 100.; + props.EA = 3.e8; + props.EI = 0.0; + props.c = -1.0; + props.Cdn = 1.0; + props.Can = 1.0; + props.Cdt = 0.0; + props.Cat = 0.0; + props.nEApoints = 0; + props.nCpoints = 0; + props.nEIpoints = 0; + obj->setup(0, + &props, + 1.e3, + n, + env, + outfile, + ""); + return obj; +} + +#define SYS_STARTER \ + md::Log *log = new md::Log(MOORDYN_DBG_LEVEL); \ + EnvCondRef env = std::make_shared(); \ + env->g = 9.81; \ + env->WtrDpth = 0.; \ + env->rho_w = 1025.; \ + env->kb = 3.0e6; \ + env->cb = 3.0e5; \ + env->WriteUnits = 1; \ + env->writeLog = 0; \ + env->FrictionCoefficient = 0.0; \ + env->FricDamp = 200.0; \ + env->StatDynFricScale = 1.0; \ + shared_ptr outfile = make_shared("state_tests.out"); + +#define SYS_KILLER \ + outfile->close(); \ + delete log; + +TEST_CASE("Scalar state var generation") +{ + SYS_STARTER + + auto p1 = CreatePoint(log, env); + auto p2 = CreatePoint(log, env); + auto l = CreateLine(log, env, 20, outfile); + + md::state::State state(log); + state.addPoint(p1); + state.addPoint(p2); + state.addLine(l); + state.addVar("scalar_by_type"); + state.addVar("scalar_by_enum", md::state::VarBase::types::REAL); + + REQUIRE(state.get("scalar_by_type").rows() == 21); + REQUIRE(state.get("scalar_by_enum").rows() == 21); + + bool catched = false; + try { + state.get("invalid_name"); + } catch (const md::invalid_value_error& e) { + catched = true; + } + REQUIRE(catched); + catched = false; + try { + state.get("scalar_by_type"); + } catch (const md::invalid_type_error& e) { + catched = true; + } + REQUIRE(catched); + + delete p1; + delete p2; + delete l; + SYS_KILLER +} + +TEST_CASE("State var setting") +{ + SYS_STARTER + + auto p1 = CreatePoint(log, env); + auto p2 = CreatePoint(log, env); + auto l = CreateLine(log, env, 20, outfile); + + md::state::State state(log); + state.addPoint(p1); + state.addPoint(p2); + state.addLine(l); + state.addVar("a"); + state.addVar("b"); + + // Set the full state vars + Eigen::Matrix ma; + Eigen::Matrix mb; + for (unsigned int i = 0; i < 21; i++) { + ma(i) = md::vec::Zero(); + mb(i) = md::vec(i, 2*i, 4*i); + } + // They can be set with the set method: + state.set("a", mb); + state.set("b", ma); + REQUIRE(state.get("a").rows() == mb.rows()); + REQUIRE(state.get("b").rows() == ma.rows()); + // Comparisons should be made element by element + for (unsigned int i = 0; i < mb.rows(); i++) { + REQUIRE(allclose(state.get("a")(i), mb(i))); + } + for (unsigned int i = 0; i < ma.rows(); i++) { + REQUIRE(allclose(state.get("b")(i), ma(i))); + } + // Or they can be set with the = operator + state.get("a") = ma; + state.get("b") = mb; + REQUIRE(state.get("a").rows() == ma.rows()); + REQUIRE(state.get("b").rows() == mb.rows()); + for (unsigned int i = 0; i < ma.rows(); i++) { + REQUIRE(allclose(state.get("a")(i), ma(i))); + } + for (unsigned int i = 0; i < mb.rows(); i++) { + REQUIRE(allclose(state.get("b")(i), mb(i))); + } + + // The same can be done with just a subpart of the state var: + state.set("a", l, state.get("b", l)); + // Internally, the state var is storing first the lines and then the points + for (unsigned int i = 0; i < 19; i++) { + REQUIRE(allclose(state.get("a")(i), mb(i))); + } + for (unsigned int i = 19; i < 21; i++) { + REQUIRE(allclose(state.get("a")(i), ma(i))); + } + // If we modify a, b should remain the same + state.get("a")(1) = md::vec::Zero(); + REQUIRE(!allclose(state.get("a")(1), state.get("b")(1))); + // And again, we can use the = operator to set state var subparts: + state.get("a", l) = state.get("b", l); + for (unsigned int i = 0; i < 19; i++) { + REQUIRE(allclose(state.get("a")(i), mb(i))); + } + for (unsigned int i = 19; i < 21; i++) { + REQUIRE(allclose(state.get("a")(i), ma(i))); + } + + delete p1; + delete p2; + delete l; + SYS_KILLER +} + +TEST_CASE("State var operations") +{ + SYS_STARTER + + auto p1 = CreatePoint(log, env); + auto p2 = CreatePoint(log, env); + auto l = CreateLine(log, env, 20, outfile); + + md::state::State state(log); + state.addPoint(p1); + state.addPoint(p2); + state.addLine(l); + state.addVar("a"); + state.addVar("b"); + state.addVar("c"); + + Eigen::Matrix ma; + Eigen::Matrix mb; + for (unsigned int i = 0; i < 21; i++) { + ma(i) = md::vec(0.5 * i, i, 2 * i); + mb(i) = md::vec(i, 2 * i, 4 * i); + } + state.set("a", ma); + state.get("b") = mb; + + REQUIRE(state.get("a").rows() == ma.rows()); + REQUIRE(state.get("b").rows() == mb.rows()); + + state.set("c", state.get("a") + state.get("b")); + for (unsigned int i = 0; i < ma.rows(); i++) { + REQUIRE(allclose(state.get("c")(i), ma(i) + mb(i))); + } + + delete p1; + delete p2; + delete l; + SYS_KILLER +} + +TEST_CASE("List var") +{ + SYS_STARTER + + auto p1 = CreatePoint(log, env); + auto p2 = CreatePoint(log, env); + auto l = CreateLine(log, env, 20, outfile); + + md::state::State state(log); + state.addPoint(p1); + state.addPoint(p2); + state.addLine(l); + state.addVar("a"); + state.addVar("b"); + state.addVar("c"); + + // We just fill the lists for the line, with 4 elements each + Eigen::Matrix ma; + Eigen::Matrix mb; + for (unsigned int i = 0; i < 19; i++) { + ma(i).resize(4); + ma(i) = Eigen::Matrix(0.5 * i, i, 2 * i, 4 * i); + mb(i).resize(4); + mb(i) = Eigen::Matrix(i, 2 * i, 4 * i, 8 * i); + } + + state.setListLength("a", 4, l); + state.setListLength("b", 4, l); + state.setListLength("c", 4, l); + state.get("a", l) = ma; + state.get("b", l) = mb; + + REQUIRE(state.get("a").rows() == state.get("b").rows()); + + state.get("c") = + state.get("a") + state.get("b"); + for (unsigned int i = 0; i < 19; i++) { + REQUIRE(allclose(state.get("c")(i), ma(i) + mb(i))); + } + + delete p1; + delete p2; + delete l; + SYS_KILLER +} + +TEST_CASE("Serialization") +{ + SYS_STARTER + + auto p1 = CreatePoint(log, env); + auto p2 = CreatePoint(log, env); + auto l = CreateLine(log, env, 20, outfile); + + md::state::State state_in(log), state_out(log); + state_in.addPoint(p1); + state_in.addPoint(p2); + state_in.addLine(l); + state_out.addPoint(p1); + state_out.addPoint(p2); + state_out.addLine(l); + + state_in.addVar("a"); + state_in.setListLength("a", 4, l); + state_in.addVar("b"); + state_out.addVar("a"); + state_out.setListLength("a", 4, l); + state_out.addVar("b"); + + // We just fill the lists for the line, with 4 elements each + Eigen::Matrix va_in; + Eigen::Matrix va_out; + for (unsigned int i = 0; i < 19; i++) { + va_in(i).resize(4); + va_in(i) = Eigen::Matrix(0.5 * i, i, 2 * i, 4 * i); + va_out(i).resize(4); + va_out(i) = Eigen::Matrix::Zero(); + } + state_in.get("a", l) = va_in; + state_out.get("a", l) = va_out; + + for (unsigned int i = 0; i < 21; i++) { + state_in.get("b")(i) = md::vec6( + i, 2 * i, 4 * i, 8 * i, 16 * i, 32 * i); + state_out.get("b")(i) = md::vec6::Zero(); + } + + auto data_saved = state_in.Serialize(); + state_out.Deserialize(data_saved.data()); + + REQUIRE(state_out.get("a").rows() == + state_in.get("a").rows()); + REQUIRE(state_out.get("b").rows() == + state_in.get("b").rows()); + + for (unsigned int i = 0; i < 21; i++) { + REQUIRE(allclose(state_out.get("a")(i), + state_in.get("a")(i))); + } + for (unsigned int i = 0; i < 21; i++) { + REQUIRE(allclose(state_out.get("b")(i), + state_in.get("b")(i))); + } + + delete p1; + delete p2; + delete l; + SYS_KILLER +} diff --git a/tests/time_schemes.cpp b/tests/time_schemes.cpp index 4874a0ee..ec37e43a 100644 --- a/tests/time_schemes.cpp +++ b/tests/time_schemes.cpp @@ -54,9 +54,9 @@ static std::vector schemes({ "Euler", "Heun", "RK2", "RK4", - "AB2", - "AB3", - "AB4", + // "AB2", + // "AB3", + // "AB4", "BEuler5", "BEuler10", "BEuler15", @@ -74,9 +74,9 @@ static std::vector dts({ "1.5E-4", // Euler "1.8E-4", // Heun "2.6E-4", // RK2 "4.9E-4", // RK4 - "1.4E-4", // AB2 - "1.1E-4", // AB3 - "1.0E-4", // AB5 + // "1.4E-4", // AB2 + // "1.1E-4", // AB3 + // "1.0E-4", // AB5 "6.0E-4", // BEuler5 "1.0E-3", // BEuler10 "1.3E-3", // BEuler15 diff --git a/wrappers/python/moordyn/moorpyic.py b/wrappers/python/moordyn/moorpyic.py index 069092fe..b009fc5e 100644 --- a/wrappers/python/moordyn/moorpyic.py +++ b/wrappers/python/moordyn/moorpyic.py @@ -48,17 +48,25 @@ def moorpy_export(moorpy_system, filename, out.write(b'MoorDyn') out.write(struct.pack(' 0.0: k = (rB - rA) / L Rmat = np.array(rotationMatrix( @@ -79,28 +84,26 @@ def moorpy_export(moorpy_system, filename, r = R.from_matrix(Rmat) else: r = R.from_matrix(np.eye(3)) - data += struct.pack(' Date: Mon, 24 Feb 2025 14:15:18 +0100 Subject: [PATCH 02/16] fix(core): CLang buildings --- source/Time.cpp | 24 ++++++++++++------------ source/Time.hpp | 32 ++++++++++++++++---------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/source/Time.cpp b/source/Time.cpp index 26d3a109..ce74059e 100644 --- a/source/Time.cpp +++ b/source/Time.cpp @@ -564,18 +564,18 @@ template void ABScheme::Step(real& dt) { - auto r0 = r(0)->get("pos"); - auto u0 = r(0)->get("vel"); - auto drdt0 = rd(0)->get("vel"); - auto dudt0 = rd(0)->get("acc"); - auto drdt1 = rd(1)->get("vel"); - auto dudt1 = rd(1)->get("acc"); - auto drdt2 = rd(2)->get("vel"); - auto dudt2 = rd(2)->get("acc"); - auto drdt3 = rd(3)->get("vel"); - auto dudt3 = rd(3)->get("acc"); - auto drdt4 = rd(4)->get("vel"); - auto dudt4 = rd(4)->get("acc"); + auto r0 = AS_STATE(r(0))->get("pos"); + auto u0 = AS_STATE(r(0))->get("vel"); + auto drdt0 = AS_STATE(rd(0))->get("vel"); + auto dudt0 = AS_STATE(rd(0))->get("acc"); + auto drdt1 = AS_STATE(rd(1))->get("vel"); + auto dudt1 = AS_STATE(rd(1))->get("acc"); + auto drdt2 = AS_STATE(rd(2))->get("vel"); + auto dudt2 = AS_STATE(rd(2))->get("acc"); + auto drdt3 = AS_STATE(rd(3))->get("vel"); + auto dudt3 = AS_STATE(rd(3))->get("acc"); + auto drdt4 = AS_STATE(rd(4))->get("vel"); + auto dudt4 = AS_STATE(rd(4))->get("acc"); Update(0.0, 0); shift(); diff --git a/source/Time.hpp b/source/Time.hpp index 5d45987b..16f8d973 100644 --- a/source/Time.hpp +++ b/source/Time.hpp @@ -1236,38 +1236,38 @@ class ABScheme final : public LocalSchemeBase<1, 5> for (unsigned int i = 0; i < lines.size(); i++) { if (!_calc_mask.lines[i]) continue; - rd(dst)->get("vel", lines[i]) = - rd(org)->get("vel", lines[i]); - rd(dst)->get("acc", lines[i]) = - rd(org)->get("acc", lines[i]); + AS_STATE(rd(dst))->get("vel", lines[i]) = + AS_STATE(rd(org))->get("vel", lines[i]); + AS_STATE(rd(dst))->get("acc", lines[i]) = + AS_STATE(rd(org))->get("acc", lines[i]); } for (unsigned int i = 0; i < points.size(); i++) { if (!_calc_mask.points[i] && (points[i]->type == Point::FREE)) continue; - rd(dst)->get("vel", points[i]) = - rd(org)->get("vel", points[i]); - rd(dst)->get("acc", points[i]) = - rd(org)->get("acc", points[i]); + AS_STATE(rd(dst))->get("vel", points[i]) = + AS_STATE(rd(org))->get("vel", points[i]); + AS_STATE(rd(dst))->get("acc", points[i]) = + AS_STATE(rd(org))->get("acc", points[i]); } for (unsigned int i = 0; i < rods.size(); i++) { if (!_calc_mask.rods[i] && ((rods[i]->type != Rod::FREE) || (rods[i]->type != Rod::PINNED))) continue; - rd(dst)->get("vel", rods[i]) = - rd(org)->get("vel", rods[i]); - rd(dst)->get("acc", rods[i]) = - rd(org)->get("acc", rods[i]); + AS_STATE(rd(dst))->get("vel", rods[i]) = + AS_STATE(rd(org))->get("vel", rods[i]); + AS_STATE(rd(dst))->get("acc", rods[i]) = + AS_STATE(rd(org))->get("acc", rods[i]); } for (unsigned int i = 0; i < bodies.size(); i++) { if (!_calc_mask.bodies[i] && (bodies[i]->type == Body::FREE)) continue; - rd(dst)->get("vel", bodies[i]) = - rd(org)->get("vel", bodies[i]); - rd(dst)->get("acc", bodies[i]) = - rd(org)->get("acc", bodies[i]); + AS_STATE(rd(dst))->get("vel", bodies[i]) = + AS_STATE(rd(org))->get("vel", bodies[i]); + AS_STATE(rd(dst))->get("acc", bodies[i]) = + AS_STATE(rd(org))->get("acc", bodies[i]); } } From be0dcbf47865106a729bb132cbdaf618196f63ba Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Tue, 25 Feb 2025 06:24:35 +0100 Subject: [PATCH 03/16] fix(core): Removed unused operator --- source/State.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/source/State.hpp b/source/State.hpp index 5de794d8..9b33ae1c 100644 --- a/source/State.hpp +++ b/source/State.hpp @@ -108,14 +108,6 @@ class VarTyped : public VarBase, public Eigen::Matrix (Eigen::Matrix*)this; return *m; } - - VarTyped& operator*=(const real& v) - { - for (unsigned int i = 0; i < this->rows(); i++) { - this->operator()(i) *= v; - } - return *this; - } protected: /** @brief Constructor * @param t Type definition, to be used later From 9cba098acb990c79dfdbd9b3caf88a947965199a Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Tue, 25 Feb 2025 06:40:14 +0100 Subject: [PATCH 04/16] fix(core): VSCode warnings --- source/Body.cpp | 4 ++-- source/Point.cpp | 2 +- source/Rod.cpp | 4 ++-- source/Seafloor.cpp | 2 +- source/Time.hpp | 6 +++--- source/Waves/WaveSpectrum.cpp | 2 +- source/Waves/WaveSpectrum.hpp | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/source/Body.cpp b/source/Body.cpp index 0a68ced6..b36d9da8 100644 --- a/source/Body.cpp +++ b/source/Body.cpp @@ -273,7 +273,7 @@ Body::setDependentStates() // pass above to the point and get it to calculate the forces try { attachedP[i]->setKinematics(rPoint, rdPoint); - } catch (moordyn::invalid_value_error& exception) { + } catch (moordyn::invalid_value_error) { // Just rethrow the exception throw; } @@ -313,7 +313,7 @@ Body::setDependentStates() // rdRod = [baseDX, baseDY, baseDZ, endDX, endDY, endDZ] try { attachedR[i]->setKinematics(rRod, rdRod); - } catch (moordyn::invalid_value_error& exception) { + } catch (moordyn::invalid_value_error) { // Just rethrow the exception throw; } diff --git a/source/Point.cpp b/source/Point.cpp index 1016252a..7925ae9d 100644 --- a/source/Point.cpp +++ b/source/Point.cpp @@ -550,7 +550,7 @@ int DECLDIR MoorDyn_GetPointNAttached(MoorDynPoint point, unsigned int* n) { CHECK_POINT(point); - *n = ((moordyn::Point*)point)->getLines().size(); + *n = (unsigned int)((moordyn::Point*)point)->getLines().size(); return MOORDYN_SUCCESS; } diff --git a/source/Rod.cpp b/source/Rod.cpp index d111f627..b2464cba 100644 --- a/source/Rod.cpp +++ b/source/Rod.cpp @@ -1512,8 +1512,8 @@ Rod::getVTK() const { auto points = vtkSmartPointer::New(); auto cells = vtkSmartPointer::New(); - auto vtk_rd = io::vtk_farray("rd", 3, r.size()); - auto vtk_Fnet = io::vtk_farray("Fnet", 3, r.size()); + auto vtk_rd = io::vtk_farray("rd", 3, (unsigned int)r.size()); + auto vtk_Fnet = io::vtk_farray("Fnet", 3, (unsigned int)r.size()); if (N) { auto line = vtkSmartPointer::New(); line->GetPointIds()->SetNumberOfIds(r.size()); diff --git a/source/Seafloor.cpp b/source/Seafloor.cpp index 8c64515e..3fcb41dc 100644 --- a/source/Seafloor.cpp +++ b/source/Seafloor.cpp @@ -51,7 +51,7 @@ Seafloor::setup(EnvCondRef env, const string& filepath) try { fLines = moordyn::fileIO::fileToLines(filepath); - } catch (std::exception& err) { + } catch (std::exception) { LOGERR << "Cannot read the file " << filepath << '\n'; throw moordyn::input_file_error("Failure reading depths file"); } diff --git a/source/Time.hpp b/source/Time.hpp index 16f8d973..92f410fc 100644 --- a/source/Time.hpp +++ b/source/Time.hpp @@ -921,9 +921,9 @@ class StationaryScheme final : public SchemeBase<2, 1> * no matter if the state is a scalar, a vector or a quaternion, it is * considered as a single entry */ - inline unsigned int NStates() const { - unsigned int n = bodies.size() + rods.size() + points.size(); - for (unsigned int i = 0; i < lines.size(); i++) + inline size_t NStates() const { + size_t n = bodies.size() + rods.size() + points.size(); + for (size_t i = 0; i < lines.size(); i++) n += lines[i]->getN() - 1; return n; } diff --git a/source/Waves/WaveSpectrum.cpp b/source/Waves/WaveSpectrum.cpp index c28bd9a3..54f66e5f 100644 --- a/source/Waves/WaveSpectrum.cpp +++ b/source/Waves/WaveSpectrum.cpp @@ -127,7 +127,7 @@ spectrumFromFile(const std::string& path, moordyn::Log* _log) } std::vector -ParametricSpectrum::getAmplitudes(real minOmega, real maxOmega, real numComps) +ParametricSpectrum::getAmplitudes(real minOmega, real maxOmega, size_t numComps) { real dw = (maxOmega - minOmega) / (numComps - 1); auto omegas = Eigen::ArrayX::LinSpaced(numComps, minOmega, maxOmega); diff --git a/source/Waves/WaveSpectrum.hpp b/source/Waves/WaveSpectrum.hpp index fb31392c..8cefeb29 100644 --- a/source/Waves/WaveSpectrum.hpp +++ b/source/Waves/WaveSpectrum.hpp @@ -135,7 +135,7 @@ class ParametricSpectrum virtual real getSpectrum(real f) = 0; std::vector getAmplitudes(real minOmega, real maxOmega, - real numComps); + size_t numComps); }; class PMSpectrum : ParametricSpectrum From 6778f19f9935f590712e0dfca21c146756bcfc28 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Tue, 25 Feb 2025 06:49:39 +0100 Subject: [PATCH 05/16] fix(core): Export some more symbols for state tests --- source/Body.hpp | 2 +- source/IO.hpp | 2 +- source/Line.hpp | 2 +- source/Log.hpp | 2 +- source/Point.hpp | 2 +- source/Rod.hpp | 2 +- source/State.hpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/Body.hpp b/source/Body.hpp index 09c3ed95..3957fd7e 100644 --- a/source/Body.hpp +++ b/source/Body.hpp @@ -73,7 +73,7 @@ class Rod; * moordyn::Body extends the io::IO class, allowing it to perform input/output * in a consistent manner. */ -class Body final : public io::IO, public SuperCFL +class DECLDIR Body final : public io::IO, public SuperCFL { public: /** @brief Costructor diff --git a/source/IO.hpp b/source/IO.hpp index ce9c546b..2aa20f56 100644 --- a/source/IO.hpp +++ b/source/IO.hpp @@ -62,7 +62,7 @@ namespace io { * @note Since this class already publicly inherits moordyn::LogUser, all the * classes inheriting this class do not require inheriting also LogUser */ -class IO : public LogUser +class DECLDIR IO : public LogUser { public: /** @brief Costructor diff --git a/source/Line.hpp b/source/Line.hpp index 1677e5e3..f2dc01f0 100644 --- a/source/Line.hpp +++ b/source/Line.hpp @@ -70,7 +70,7 @@ typedef Eigen::Ref> StateVarRef; * The integration time step (moordyn::MoorDyn.dtM0) should be smaller than * this natural period to avoid numerical instabilities */ -class Line final : public io::IO, public NatFreqCFL +class DECLDIR Line final : public io::IO, public NatFreqCFL { public: /** @brief Constructor diff --git a/source/Log.hpp b/source/Log.hpp index a3be77e0..d39b590a 100644 --- a/source/Log.hpp +++ b/source/Log.hpp @@ -145,7 +145,7 @@ operator<<(MultiStream& st, T val) * This class is a sensible replacement for std::cout and std::cerr ofstream * entities, providing the appropriate streaming channel to each entity */ -class Log +class DECLDIR Log { public: /** @brief Constructor diff --git a/source/Point.hpp b/source/Point.hpp index 6374d8f2..9f26b402 100644 --- a/source/Point.hpp +++ b/source/Point.hpp @@ -67,7 +67,7 @@ typedef std::shared_ptr WavesRef; * weight or float via the point's mass and volume parameters * - Coupled: The point position and velocity is externally imposed */ -class Point final : public io::IO, public SuperCFL +class DECLDIR Point final : public io::IO, public SuperCFL { public: /** @brief Constructor diff --git a/source/Rod.hpp b/source/Rod.hpp index e882b115..50d6e67d 100644 --- a/source/Rod.hpp +++ b/source/Rod.hpp @@ -63,7 +63,7 @@ class Line; * Each end point of the rod can be fixed or pinned to another object, let free * or control it externally */ -class Rod final : public io::IO, public SuperCFL +class DECLDIR Rod final : public io::IO, public SuperCFL { public: /** @brief Costructor diff --git a/source/State.hpp b/source/State.hpp index 9b33ae1c..0c2c4d46 100644 --- a/source/State.hpp +++ b/source/State.hpp @@ -194,7 +194,7 @@ typedef Eigen::Index Indexer; /** @class State State.hpp * @brief The collection of state variables of the whole system */ -class State final : public moordyn::io::IO +class DECLDIR State final : public moordyn::io::IO { public: /** @brief Constructor From 5420f8ed5209e4800a2a74df49378e2bc1e6142e Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Tue, 25 Feb 2025 07:12:23 +0100 Subject: [PATCH 06/16] fix(core): Export some more symbols for state tests --- source/State.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/State.cpp b/source/State.cpp index 0da381dd..398145a4 100644 --- a/source/State.cpp +++ b/source/State.cpp @@ -149,7 +149,7 @@ TYPE_GETTER(VarList, LIST) #define STATE_ADDER(T, TBASE) \ template <> \ -void \ +void DECLDIR \ State::addVar(const char* name) \ { \ TBASE* var = new TBASE(); \ @@ -207,7 +207,7 @@ State::setListLength(const char* name, size_t n, void* obj) #define STATE_SETTER(T, TBASE) \ template <> \ -void \ +void DECLDIR \ State::set(const char* name, \ Eigen::Matrix v) \ { \ @@ -226,7 +226,7 @@ STATE_SETTER(list, VarList) #define STATE_OBJ_SETTER(T, TBASE) \ template <> \ -void \ +void DECLDIR \ State::set(const char* name, \ void* obj, \ Eigen::Matrix v) \ @@ -305,7 +305,7 @@ State::Deserialize(const uint64_t* data) #define STATE_GETTER(T, TBASE) \ template <> \ -Eigen::Matrix& \ +Eigen::Matrix& DECLDIR \ State::getRef(const char* name) \ { \ checkVar(name); \ From f640fa7fd1bb1f554fb156a64865aa8062b2f6a0 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Tue, 25 Feb 2025 07:24:32 +0100 Subject: [PATCH 07/16] fix(core): VS Code invalid exports --- source/Body.hpp | 2 +- source/State.cpp | 32 ++++++++++++++++---------------- source/State.hpp | 22 +++++++++++----------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/source/Body.hpp b/source/Body.hpp index 3957fd7e..f980da8c 100644 --- a/source/Body.hpp +++ b/source/Body.hpp @@ -380,7 +380,7 @@ class DECLDIR Body final : public io::IO, public SuperCFL * @param rd The velocity * @{ */ - void DECLDIR setState(XYZQuat r, vec6 rd); + void setState(XYZQuat r, vec6 rd); inline void setState(vec7 r, vec6 rd) { diff --git a/source/State.cpp b/source/State.cpp index 398145a4..dc876f27 100644 --- a/source/State.cpp +++ b/source/State.cpp @@ -205,6 +205,22 @@ State::setListLength(const char* name, size_t n, void* obj) } } +#define STATE_GETTER(T, TBASE) \ +template <> \ +Eigen::Matrix& DECLDIR \ +State::getRef(const char* name) \ +{ \ + checkVar(name); \ + TBASE* var = (TBASE*)vars[name]; \ + return var->asMatrix(); \ +} + +STATE_GETTER(real, VarScalar) +STATE_GETTER(vec, VarVec) +STATE_GETTER(vec6, VarVec6) +STATE_GETTER(XYZQuat, VarQuat) +STATE_GETTER(list, VarList) + #define STATE_SETTER(T, TBASE) \ template <> \ void DECLDIR \ @@ -303,22 +319,6 @@ State::Deserialize(const uint64_t* data) return ptr; } -#define STATE_GETTER(T, TBASE) \ -template <> \ -Eigen::Matrix& DECLDIR \ -State::getRef(const char* name) \ -{ \ - checkVar(name); \ - TBASE* var = (TBASE*)vars[name]; \ - return var->asMatrix(); \ -} - -STATE_GETTER(real, VarScalar) -STATE_GETTER(vec, VarVec) -STATE_GETTER(vec6, VarVec6) -STATE_GETTER(XYZQuat, VarQuat) -STATE_GETTER(list, VarList) - void State::clear() { diff --git a/source/State.hpp b/source/State.hpp index 0c2c4d46..64fe111b 100644 --- a/source/State.hpp +++ b/source/State.hpp @@ -309,6 +309,17 @@ class DECLDIR State final : public moordyn::io::IO */ void setListLength(const char* name, size_t n=1, void* obj=NULL); + /** @brief Get a variable by its name + * + * If you want to get a editable view of the state variable, ::get() is + * in general preferred. + * @param name Name of the variable + * @throw moordyn::invalid_value_error If the variable does not exist + * @throw moordyn::invalid_type_error If the variable type is not correct + */ + template + Eigen::Matrix& getRef(const char* name); + /** @brief Get a variable by its name * @param name Name of the variable * @throw moordyn::invalid_value_error If the variable does not exist @@ -436,17 +447,6 @@ class DECLDIR State final : public moordyn::io::IO } private: - /** @brief Get a variable by its name - * - * The returned variable can be casted down to its basis Eigen::Matrix - * if required. - * @param name Name of the variable - * @throw moordyn::invalid_value_error If the variable does not exist - * @throw moordyn::invalid_type_error If the variable type is not correct - */ - template - Eigen::Matrix& getRef(const char* name); - /** @brief Clear the state */ void clear(); From 0c9d331fdea7ffae05d406738bdfe70e0b21f160 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Tue, 25 Feb 2025 07:44:44 +0100 Subject: [PATCH 08/16] fix(core): VS Code invalid exports --- source/State.cpp | 32 ++++++++++++++++---------------- source/State.hpp | 19 ++++++++----------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/source/State.cpp b/source/State.cpp index dc876f27..499696b4 100644 --- a/source/State.cpp +++ b/source/State.cpp @@ -205,22 +205,6 @@ State::setListLength(const char* name, size_t n, void* obj) } } -#define STATE_GETTER(T, TBASE) \ -template <> \ -Eigen::Matrix& DECLDIR \ -State::getRef(const char* name) \ -{ \ - checkVar(name); \ - TBASE* var = (TBASE*)vars[name]; \ - return var->asMatrix(); \ -} - -STATE_GETTER(real, VarScalar) -STATE_GETTER(vec, VarVec) -STATE_GETTER(vec6, VarVec6) -STATE_GETTER(XYZQuat, VarQuat) -STATE_GETTER(list, VarList) - #define STATE_SETTER(T, TBASE) \ template <> \ void DECLDIR \ @@ -319,6 +303,22 @@ State::Deserialize(const uint64_t* data) return ptr; } +#define STATE_GETTER(T, TBASE) \ +template <> \ +DECLDIR Eigen::Matrix& \ +State::getRef(const char* name) \ +{ \ + checkVar(name); \ + TBASE* var = (TBASE*)vars[name]; \ + return var->asMatrix(); \ +} + +STATE_GETTER(real, VarScalar) +STATE_GETTER(vec, VarVec) +STATE_GETTER(vec6, VarVec6) +STATE_GETTER(XYZQuat, VarQuat) +STATE_GETTER(list, VarList) + void State::clear() { diff --git a/source/State.hpp b/source/State.hpp index 64fe111b..3ead4ad5 100644 --- a/source/State.hpp +++ b/source/State.hpp @@ -309,17 +309,6 @@ class DECLDIR State final : public moordyn::io::IO */ void setListLength(const char* name, size_t n=1, void* obj=NULL); - /** @brief Get a variable by its name - * - * If you want to get a editable view of the state variable, ::get() is - * in general preferred. - * @param name Name of the variable - * @throw moordyn::invalid_value_error If the variable does not exist - * @throw moordyn::invalid_type_error If the variable type is not correct - */ - template - Eigen::Matrix& getRef(const char* name); - /** @brief Get a variable by its name * @param name Name of the variable * @throw moordyn::invalid_value_error If the variable does not exist @@ -447,6 +436,14 @@ class DECLDIR State final : public moordyn::io::IO } private: + /** @brief Get a variable by its name + * @param name Name of the variable + * @throw moordyn::invalid_value_error If the variable does not exist + * @throw moordyn::invalid_type_error If the variable type is not correct + */ + template + Eigen::Matrix& getRef(const char* name); + /** @brief Clear the state */ void clear(); From 557f010e03f659bde5973355bb406628412696e4 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Tue, 25 Feb 2025 07:46:47 +0100 Subject: [PATCH 09/16] fix(core): Add subclasses to the DLL interface --- source/Log.hpp | 2 +- source/Util/CFL.hpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/Log.hpp b/source/Log.hpp index d39b590a..f5f28eff 100644 --- a/source/Log.hpp +++ b/source/Log.hpp @@ -219,7 +219,7 @@ class DECLDIR Log * Inheriting this class you can grant the class will have everything required * to use the macros LOGGER, LOGDBG, LOGMSG, LOGWRN and LOGERR */ -class LogUser +class DECLDIR LogUser { public: /** @brief Constructor diff --git a/source/Util/CFL.hpp b/source/Util/CFL.hpp index 9a9b48fe..e7ce7324 100644 --- a/source/Util/CFL.hpp +++ b/source/Util/CFL.hpp @@ -53,7 +53,7 @@ namespace moordyn { * Thus can be used for entitites which does not impose any kind of limitation * on the timestep */ -class CFL +class DECLDIR CFL { public: /** @brief Constructor @@ -109,7 +109,7 @@ class CFL /** @class NatFreqCFL CFL.hpp * @brief CFL for objects based on a natural frequency */ -class NatFreqCFL : public CFL +class DECLDIR NatFreqCFL : public CFL { public: /** @brief Constructor @@ -191,7 +191,7 @@ class NatFreqCFL : public CFL * Some objects has not an actual CFL definition, but they instead compute it * from the entities attached to it. */ -class SuperCFL : public CFL +class DECLDIR SuperCFL : public CFL { public: /** @brief Constructor From bc47faccc3fdd6fedff0879a55596b766c957c8d Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Tue, 25 Feb 2025 09:35:39 +0100 Subject: [PATCH 10/16] fix(ci): Drop Ubuntu from MATLAB tests (for the time being) --- .github/workflows/matlab.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/matlab.yml b/.github/workflows/matlab.yml index d12c4b07..e652cea1 100644 --- a/.github/workflows/matlab.yml +++ b/.github/workflows/matlab.yml @@ -20,7 +20,8 @@ jobs: fail-fast: false matrix: # MATLAB requires an old version of Windows - os: [ubuntu-latest, macos-latest] + # Ubuntu dropped because of libcstd++ incompatibilities + os: [macos-latest] steps: - name: Check out repository From b79f2c5c0c6822e9df5c7dcdf1beedb8fb62b965 Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Fri, 28 Feb 2025 06:34:11 +0100 Subject: [PATCH 11/16] feat(core): Create a base class for the instances which provides a unique identifier --- source/Body.cpp | 2 +- source/Body.hpp | 7 +--- source/CMakeLists.txt | 2 + source/Instance.cpp | 31 +++++++++++++++ source/Instance.hpp | 92 +++++++++++++++++++++++++++++++++++++++++++ source/Line.cpp | 2 +- source/Line.hpp | 4 +- source/Point.cpp | 2 +- source/Point.hpp | 4 +- source/Rod.cpp | 2 +- source/Rod.hpp | 4 +- 11 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 source/Instance.cpp create mode 100644 source/Instance.hpp diff --git a/source/Body.cpp b/source/Body.cpp index b36d9da8..1146e481 100644 --- a/source/Body.cpp +++ b/source/Body.cpp @@ -39,7 +39,7 @@ using namespace std; namespace moordyn { Body::Body(moordyn::Log* log, size_t id) - : io::IO(log) + : Instance(log) , bodyId(id) { #ifdef USE_VTK diff --git a/source/Body.hpp b/source/Body.hpp index f980da8c..22be1fbf 100644 --- a/source/Body.hpp +++ b/source/Body.hpp @@ -40,7 +40,7 @@ #pragma once #include "Misc.hpp" -#include "IO.hpp" +#include "Instance.hpp" #include "Util/CFL.hpp" #include #include @@ -69,11 +69,8 @@ class Rod; * In the configuration file the options are: * * Name/ID, X0, Y0, Z0, Xcg, Ycg, Zcg, M, V, IX, IY, IZ, CdA-x,y,z Ca-x,y,z - * - * moordyn::Body extends the io::IO class, allowing it to perform input/output - * in a consistent manner. */ -class DECLDIR Body final : public io::IO, public SuperCFL +class DECLDIR Body final : public Instance, public SuperCFL { public: /** @brief Costructor diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 248a6cbe..bf110e09 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1,6 +1,7 @@ set(MOORDYN_SRCS Body.cpp Point.cpp + Instance.cpp IO.cpp Line.cpp Log.cpp @@ -23,6 +24,7 @@ set(MOORDYN_SRCS set(MOORDYN_HEADERS Body.hpp Point.hpp + Instance.hpp IO.hpp Line.hpp Log.hpp diff --git a/source/Instance.cpp b/source/Instance.cpp new file mode 100644 index 00000000..a740aae1 --- /dev/null +++ b/source/Instance.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Matt Hall + * + * This file is part of MoorDyn. MoorDyn is free software: you can redistribute + * it and/or modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * MoorDyn is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License + * along with MoorDyn. If not, see . + */ + +#include "Instance.hpp" +#include + +namespace moordyn { + +std::atomic __instances_counter(0); + +Instance::Instance(moordyn::Log* log) + : io::IO(log) +{ + _id = __instances_counter++; +} + +} // ::moordyn diff --git a/source/Instance.hpp b/source/Instance.hpp new file mode 100644 index 00000000..502cd21c --- /dev/null +++ b/source/Instance.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022, Matt Hall + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** @file Instance.hpp + * Base class for the moordyn::Body, moordyn::Line, moordyn::Point and + * moordyn::Rod objects. + */ + +#pragma once + +#include "IO.hpp" +#include "Log.hpp" +#include + +namespace moordyn { + +class Waves; +typedef std::shared_ptr WavesRef; + +class Point; +class Rod; + +/** @class Instance Instance.hpp + * @brief A generic instance + */ +class DECLDIR Instance : public io::IO +{ + public: + /** @brief Costructor + * @param log Logging handler defining where/how results should be logged. + */ + Instance(moordyn::Log* log); + + /** @brief Destructor + */ + virtual ~Instance() = default; + + /** @brief Get the unique identifier of this instance + * + * The unique identifier is a growing index which identifies every single + * created instance. Even if an instance is removed, the index will remain + * taken. + * @return The unique identifier + */ + inline const size_t Id() const { return _id; } + + /** @brief Get the number of state variables required by this instance + * + * This can be seen as the number of rows on the state variables holder. + * @return The number of state variables + */ + virtual inline const size_t state_n() const { return 1; } + + /** @brief Get the dimension of the state variable. + * + * This can be seen as the number of columns on the state variables holder. + * @return The dimension of the state variable + */ + virtual inline const size_t state_dims() const { return 6; } + private: + /// Unique identifier of this instance + size_t _id; +}; + +} // ::moordyn diff --git a/source/Line.cpp b/source/Line.cpp index 64ecda48..b3d24b1a 100644 --- a/source/Line.cpp +++ b/source/Line.cpp @@ -51,7 +51,7 @@ namespace moordyn { using namespace waves; Line::Line(moordyn::Log* log, size_t lineId) - : io::IO(log) + : Instance(log) , lineId(lineId) , isPb(false) { diff --git a/source/Line.hpp b/source/Line.hpp index f2dc01f0..546beb96 100644 --- a/source/Line.hpp +++ b/source/Line.hpp @@ -35,7 +35,7 @@ #pragma once #include "Misc.hpp" -#include "IO.hpp" +#include "Instance.hpp" #include "Seafloor.hpp" #include "Util/CFL.hpp" #include @@ -70,7 +70,7 @@ typedef Eigen::Ref> StateVarRef; * The integration time step (moordyn::MoorDyn.dtM0) should be smaller than * this natural period to avoid numerical instabilities */ -class DECLDIR Line final : public io::IO, public NatFreqCFL +class DECLDIR Line final : public Instance, public NatFreqCFL { public: /** @brief Constructor diff --git a/source/Point.cpp b/source/Point.cpp index 7925ae9d..cbc687d5 100644 --- a/source/Point.cpp +++ b/source/Point.cpp @@ -46,7 +46,7 @@ namespace moordyn { Point::Point(moordyn::Log* log, size_t id) - : io::IO(log) + : Instance(log) , seafloor(nullptr) , pointId(id) { diff --git a/source/Point.hpp b/source/Point.hpp index 9f26b402..2b20ec31 100644 --- a/source/Point.hpp +++ b/source/Point.hpp @@ -35,7 +35,7 @@ #pragma once #include "Misc.hpp" -#include "IO.hpp" +#include "Instance.hpp" #include "Seafloor.hpp" #include "Util/CFL.hpp" #include @@ -67,7 +67,7 @@ typedef std::shared_ptr WavesRef; * weight or float via the point's mass and volume parameters * - Coupled: The point position and velocity is externally imposed */ -class DECLDIR Point final : public io::IO, public SuperCFL +class DECLDIR Point final : public Instance, public SuperCFL { public: /** @brief Constructor diff --git a/source/Rod.cpp b/source/Rod.cpp index b2464cba..8679abe8 100644 --- a/source/Rod.cpp +++ b/source/Rod.cpp @@ -54,7 +54,7 @@ namespace moordyn { // ... --- seg n-2 --- [node n-1] --- seg n-1 --- [point (node N)] Rod::Rod(moordyn::Log* log, size_t rodId) - : io::IO(log) + : Instance(log) , seafloor(nullptr) , rodId(rodId) { diff --git a/source/Rod.hpp b/source/Rod.hpp index 50d6e67d..9c4d86cc 100644 --- a/source/Rod.hpp +++ b/source/Rod.hpp @@ -35,7 +35,7 @@ #pragma once #include "Misc.hpp" -#include "IO.hpp" +#include "Instance.hpp" #include "Seafloor.hpp" #include "Util/CFL.hpp" #include @@ -63,7 +63,7 @@ class Line; * Each end point of the rod can be fixed or pinned to another object, let free * or control it externally */ -class DECLDIR Rod final : public io::IO, public SuperCFL +class DECLDIR Rod final : public Instance, public SuperCFL { public: /** @brief Costructor From 6a84cf6a7772ca9c0d4edb984a63cf7c9b73810a Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Wed, 5 Mar 2025 14:11:18 +0100 Subject: [PATCH 12/16] feat(core)!: Less flexible but fast extensible state variables --- source/Body.cpp | 18 +- source/Body.hpp | 24 +- source/IO.cpp | 27 -- source/IO.hpp | 51 ++- source/Instance.cpp | 5 + source/Instance.hpp | 26 +- source/Line.cpp | 22 +- source/Line.hpp | 28 +- source/Misc.hpp | 11 +- source/MoorDyn2.cpp | 8 + source/Point.cpp | 7 +- source/Point.hpp | 27 +- source/Rod.cpp | 7 +- source/Rod.hpp | 22 +- source/State.cpp | 473 +++---------------------- source/State.hpp | 515 ++++------------------------ source/Time.cpp | 365 ++++++++------------ source/Time.hpp | 203 ++++------- tests/Mooring/WD0050_Chain.txt.ic | Bin 2721 -> 2089 bytes tests/Mooring/WD0200_Chain.txt.ic | Bin 4961 -> 3769 bytes tests/Mooring/WD0600_Chain.txt.ic | Bin 10977 -> 8281 bytes tests/Mooring/lines.txt.ic | Bin 4065 -> 3193 bytes tests/state_tests.cpp | 274 ++++----------- wrappers/python/moordyn/moorpyic.py | 71 ++-- 24 files changed, 605 insertions(+), 1579 deletions(-) diff --git a/source/Body.cpp b/source/Body.cpp index 1146e481..852e7888 100644 --- a/source/Body.cpp +++ b/source/Body.cpp @@ -472,14 +472,16 @@ Body::setState(XYZQuat pos, vec6 vel) setDependentStates(); } -std::pair -Body::getStateDeriv() +void +Body::getStateDeriv(InstanceStateVarView drdt) { if ((type != FREE) && (type != CPLDPIN)) { LOGERR << "getStateDeriv called for non-free body" << endl; throw moordyn::invalid_value_error("Invalid body type"); } + XYZQuat u7; + // Get contributions from attached points (and lines attached to // them) and store in FNet: doRHS(); @@ -488,10 +490,10 @@ Body::getStateDeriv() a6 = solveMat6(M, F6net); // NOTE; is the above still valid even though it includes rotational DOFs? - dPos.pos = v6.head<3>(); + u7.pos = v6.head<3>(); // this assumes that the angular velocity is about the global coordinates // which is true for bodies - dPos.quat = 0.5 * (quaternion(0.0, v6[3], v6[4], v6[5]) * r7.quat).coeffs(); + u7.quat = 0.5 * (quaternion(0.0, v6[3], v6[4], v6[5]) * r7.quat).coeffs(); } else { // Pinned body // account for moment in response to acceleration due to inertial coupling (off-diagonal sub-matrix terms) @@ -504,10 +506,12 @@ Body::getStateDeriv() a6(Eigen::seqN(3, 3)) = M_out3.inverse() * Fnet_out3; // dxdt = V (velocities) - dPos.pos = vec::Zero(); - dPos.quat = 0.5 * (quaternion(0.0, v6[3], v6[4], v6[5]) * r7.quat).coeffs(); + u7.pos = vec::Zero(); + u7.quat = 0.5 * (quaternion(0.0, v6[3], v6[4], v6[5]) * r7.quat).coeffs(); } - return std::make_pair(dPos, a6); + + drdt.row(0).head<7>() = u7.toVec7(); + drdt.row(0).tail<6>() = a6; }; const vec6 diff --git a/source/Body.hpp b/source/Body.hpp index 22be1fbf..d111e4e2 100644 --- a/source/Body.hpp +++ b/source/Body.hpp @@ -123,8 +123,6 @@ class DECLDIR Body final : public Instance, public SuperCFL // vec6 r6; /// body 6dof velocity[x/y/z] vec6 v6; - /// body quaternion position derivative - XYZQuat dPos; /// body 6dof acceleration[x/y/z] vec6 a6; @@ -379,9 +377,9 @@ class DECLDIR Body final : public Instance, public SuperCFL */ void setState(XYZQuat r, vec6 rd); - inline void setState(vec7 r, vec6 rd) + inline void setState(const InstanceStateVarView r) { - setState(XYZQuat::fromVec7(r), rd); + setState(XYZQuat::fromVec7(r.row(0).head<7>()), r.row(0).tail<6>()); } /** * @} @@ -390,12 +388,12 @@ class DECLDIR Body final : public Instance, public SuperCFL /** @brief calculate the forces and state derivatives of the body * * This function is only meant for free bodies - * @return The states derivatives, i.e. the velocity (first) and the - * acceleration (second) + * @param drdt The states derivatives, i.e. the velocity and the + * acceleration * @throw moordyn::invalid_value_error If the body is of type * moordyn::Body::FREE */ - std::pair getStateDeriv(); + void getStateDeriv(InstanceStateVarView drdt); /** @brief calculates the forces on the body * @throw moordyn::invalid_value_error If the body is of type @@ -405,6 +403,18 @@ class DECLDIR Body final : public Instance, public SuperCFL void Output(real time); + /** @brief Get the number of state variables required by this instance + * @return 1 + */ + inline const size_t stateN() const { return 1; } + + /** @brief Get the dimension of the state variable + * @return 7 components for position quaternion and 6 components for + * linear and angular velocities, i.e. 13 components + * @warning This function shall be called after ::setup() + */ + inline const size_t stateDims() const { return 13; } + /** @brief Produce the packed data to be saved * * The produced data can be used afterwards to restore the saved information diff --git a/source/IO.cpp b/source/IO.cpp index 96c7af4e..cc9f2779 100644 --- a/source/IO.cpp +++ b/source/IO.cpp @@ -398,18 +398,6 @@ IO::Serialize(const XYZQuat& m) return data; } -std::vector -IO::Serialize(const list& m) -{ - std::vector data; - const uint64_t n = m.rows(); - data.reserve(1 + n); - data.push_back(Serialize(n)); - for (uint64_t i = 0; i < n; i++) - data.push_back(Serialize(m(i))); - return data; -} - std::vector IO::Serialize(const std::vector& l) { @@ -561,21 +549,6 @@ IO::Deserialize(const uint64_t* in, XYZQuat& out) return remaining; } -uint64_t* -IO::Deserialize(const uint64_t* in, list& out) -{ - uint64_t n; - uint64_t* remaining = Deserialize(in, n); - if (out.rows() != n) - out.resize(n); - for (unsigned int i = 0; i < n; i++) { - real v; - remaining = Deserialize(remaining, v); - out(i) = v; - } - return remaining; -} - uint64_t* IO::Deserialize(const uint64_t* in, std::vector& out) { diff --git a/source/IO.hpp b/source/IO.hpp index 2aa20f56..1490daee 100644 --- a/source/IO.hpp +++ b/source/IO.hpp @@ -177,12 +177,6 @@ class DECLDIR IO : public LogUser */ std::vector Serialize(const XYZQuat& m); - /** @brief Pack a list to make it writable - * @param m The list - * @return The packed list - */ - std::vector Serialize(const list& m); - /** @brief Pack a list of floating point numbers to make it writable * @param l The list * @return The packed list @@ -213,23 +207,25 @@ class DECLDIR IO : public LogUser */ std::vector Serialize(const std::vector& l); - /** @brief Pack an arbitrarily large vector - * @param l The list + /** @brief Pack an arbitrarily large matrix + * @param l The matrix * @return The packed list */ - template inline std::vector Serialize( - const Eigen::Matrix& l) + const Eigen::Matrix& l) { std::vector data; const uint64_t n = l.rows(); + const uint64_t m = l.cols(); + data.reserve(2 + n * m); data.push_back(Serialize(n)); + data.push_back(Serialize(m)); for (uint64_t i = 0; i < n; i++) { - std::vector subdata = Serialize(l(i)); - data.insert(data.end(), subdata.begin(), subdata.end()); + for (uint64_t j = 0; j < m; j++) { + data.push_back(Serialize(l(i, j))); + } } return data; - } /** @brief Pack a list of lists to make it writable @@ -313,13 +309,6 @@ class DECLDIR IO : public LogUser */ uint64_t* Deserialize(const uint64_t* in, XYZQuat& out); - /** @brief Unpack a loaded list - * @param in The pointer to the next unread value - * @param out The unpacked value - * @return The new pointer to the remaining data to be read - */ - uint64_t* Deserialize(const uint64_t* in, list& out); - /** @brief Unpack a loaded list of floating point numbers * @param in The pointer to the next unread value * @param out The unpacked value @@ -355,21 +344,25 @@ class DECLDIR IO : public LogUser */ uint64_t* Deserialize(const uint64_t* in, std::vector& out); - /** @brief Unpack an arbitrarily large vector + /** @brief Unpack an arbitrarily large matrix * @param in The pointer to the next unread value * @param out The unpacked vector * @return The new pointer to the remaining data to be read */ - template - uint64_t* Deserialize(const uint64_t* in, - Eigen::Matrix& out) + uint64_t* Deserialize( + const uint64_t* in, + Eigen::Matrix& out) { - uint64_t n; - uint64_t* remaining = Deserialize(in, n); - if (out.rows() != n) - out.resize(n); + uint64_t* remaining; + uint64_t n, m; + remaining = Deserialize(in, n); + remaining = Deserialize(remaining, m); + if ((out.rows() != n) || (out.cols() != m)) + out.resize(n, m); for (unsigned int i = 0; i < n; i++) { - remaining = Deserialize(remaining, out(i)); + for (unsigned int j = 0; j < m; j++) { + remaining = Deserialize(remaining, out(i, j)); + } } return remaining; } diff --git a/source/Instance.cpp b/source/Instance.cpp index a740aae1..eacd4cd9 100644 --- a/source/Instance.cpp +++ b/source/Instance.cpp @@ -28,4 +28,9 @@ Instance::Instance(moordyn::Log* log) _id = __instances_counter++; } +void reset_instance_ids() +{ + __instances_counter = 0; +} + } // ::moordyn diff --git a/source/Instance.hpp b/source/Instance.hpp index 502cd21c..609b0ec0 100644 --- a/source/Instance.hpp +++ b/source/Instance.hpp @@ -35,6 +35,7 @@ #pragma once +#include "Misc.hpp" #include "IO.hpp" #include "Log.hpp" #include @@ -67,26 +68,45 @@ class DECLDIR Instance : public io::IO * The unique identifier is a growing index which identifies every single * created instance. Even if an instance is removed, the index will remain * taken. + * That applies even for multiple instances generation. * @return The unique identifier + * @warning It cannot be assumed that the first taken index will be 0 + * @warning It cannot be assumed that consequtive ids will be assigned */ - inline const size_t Id() const { return _id; } + inline const size_t id() const { return _id; } + + /** @brief Set the state + * @param r The state variable + */ + virtual void setState(const InstanceStateVarView r) = 0; + + /** @brief Calculate forces and get the derivative of the states + * @param drdt Output state derivatives + * @throws nan_error If nan values are detected in any node position + */ + virtual void getStateDeriv(InstanceStateVarView drdt) = 0; /** @brief Get the number of state variables required by this instance * * This can be seen as the number of rows on the state variables holder. * @return The number of state variables */ - virtual inline const size_t state_n() const { return 1; } + virtual inline const size_t stateN() const { return 1; } /** @brief Get the dimension of the state variable. * * This can be seen as the number of columns on the state variables holder. * @return The dimension of the state variable */ - virtual inline const size_t state_dims() const { return 6; } + virtual inline const size_t stateDims() const { return 6; } private: /// Unique identifier of this instance size_t _id; }; +/** @brief Reset the instances Ids, so they will be assigned again starting + * from 0 + */ +void reset_instance_ids(); + } // ::moordyn diff --git a/source/Line.cpp b/source/Line.cpp index b3d24b1a..b0347988 100644 --- a/source/Line.cpp +++ b/source/Line.cpp @@ -679,21 +679,11 @@ Line::setState(const std::vector& pos, const std::vector& vel) } void -Line::setState(const StateVarRef pos, const StateVarRef vel) +Line::setState(const InstanceStateVarView state) { - if ((pos.rows() != N - 1) || (vel.rows() != N - 1)) { - LOGERR << "Invalid input size" << endl; - throw moordyn::invalid_value_error("Invalid input size"); - } for (unsigned int i = 1; i < N; i++) { - if ((pos(i - 1).rows() != 3) || (vel(i - 1).rows() != 3)) { - LOGERR << "Invalid point input size on line " << number - << " node " << i << ". pos size is " << pos(i - 1).rows() - << " and vel size is " << vel(i - 1).rows() << endl; - throw moordyn::invalid_value_error("Invalid input size"); - } - r[i] = pos(i - 1); - rd[i] = vel(i - 1); + r[i] = state.row(i - 1).head<3>(); + rd[i] = state.row(i - 1).segment<3>(3); } } @@ -787,7 +777,7 @@ Line::getEndSegmentMoment(EndPoints end_point, EndPoints rod_end_point) const } void -Line::getStateDeriv(StateVarRef vel, StateVarRef acc) +Line::getStateDeriv(InstanceStateVarView drdt) { // NOTE: // Jose Luis Cercos-Pita: This is by far the most consuming function of the @@ -1200,8 +1190,8 @@ Line::getStateDeriv(StateVarRef vel, StateVarRef acc) // For small systems it is usually faster to compute the inverse // of the matrix. See // https://eigen.tuxfamily.org/dox/group__TutorialLinearAlgebra.html - acc(i - 1) = M[i].inverse() * Fnet[i]; - vel(i - 1) = rd[i]; + drdt.row(i - 1).head<3>() = rd[i]; + drdt.row(i - 1).segment<3>(3) = M[i].inverse() * Fnet[i]; } }; diff --git a/source/Line.hpp b/source/Line.hpp index 546beb96..313a5f8b 100644 --- a/source/Line.hpp +++ b/source/Line.hpp @@ -811,16 +811,14 @@ class DECLDIR Line final : public Instance, public NatFreqCFL inline void setTime(real time) { t = time; } /** @brief Set the line state - * @param r The moordyn::Line::getN() - 1 positions - * @param u The moordyn::Line::getN() - 1 velocities + * @param r The positions and velocities on the internal nodes * @note This method is not affecting the line end points * @see moordyn::Line::setEndState - * @throws invalid_value_error If either @p r or @p u have wrong sizes * @{ */ - void setState(const std::vector& r, const std::vector& u); + void setState(const std::vector& pos, const std::vector& vel); - void setState(const StateVarRef r, const StateVarRef u); + void setState(const InstanceStateVarView r); /** * @} */ @@ -863,17 +861,31 @@ class DECLDIR Line final : public Instance, public NatFreqCFL vec getEndSegmentMoment(EndPoints end_point, EndPoints rod_end_point) const; /** @brief Calculate forces and get the derivative of the line's states - * @param vel Where to store the velocities of the internal nodes - * @param acc Where to store the accelerations of the internal nodes + * @param drdt Output state derivatives * @throws nan_error If nan values are detected in any node position */ - void getStateDeriv(StateVarRef vel, StateVarRef acc); + void getStateDeriv(InstanceStateVarView drdt); // void initiateStep(vector &rFairIn, vector &rdFairIn, // double time); void Output(real); + /** @brief Get the number of state variables required by this instance + * @return The number of internal nodes + * @warning This function shall be called after ::setup() + */ + inline const size_t stateN() const { return getN() - 1; } + + /** @brief Get the dimension of the state variable + * @return 3 components for positions and 3 components for velocities, i.e. + * 6 components + * @note In future developments this function might return different + * numbers depending on the line configuration. e.g. the viscoelasticity. + * @warning This function shall be called after ::setup() + */ + inline const size_t stateDims() const { return 6; } + /** @brief Produce the packed data to be saved * * The produced data can be used afterwards to restore the saved information diff --git a/source/Misc.hpp b/source/Misc.hpp index b62ab2c4..1211c6a7 100644 --- a/source/Misc.hpp +++ b/source/Misc.hpp @@ -161,6 +161,15 @@ typedef ivec3 ivec; /// Complex numbers typedef std::complex complex; +/// State variables for a particular instance +typedef Eigen::Matrix InstanceStateVar; +/// View of the State variables for a particular instance +typedef Eigen::Block InstanceStateVarView; +/// State variable +typedef Eigen::Matrix StateVar; +/// View of the State variable +typedef Eigen::VectorBlock StateVarView; + /** @brief This function compares two real numbers and determines if they are * "almost" equal * @@ -586,7 +595,7 @@ MAKE_EXCEPTION(nan_error) MAKE_EXCEPTION(mem_error) /// Exception thrown when invalid values are found MAKE_EXCEPTION(invalid_value_error) -/// Exception thrown when invalid values are found +/// Exception thrown when something is not implemented yet MAKE_EXCEPTION(non_implemented_error) /// Exception thrown when an invalid type is asked MAKE_EXCEPTION(invalid_type_error) diff --git a/source/MoorDyn2.cpp b/source/MoorDyn2.cpp index 11573ddf..3b2f2ea0 100644 --- a/source/MoorDyn2.cpp +++ b/source/MoorDyn2.cpp @@ -34,6 +34,7 @@ #include "Misc.hpp" #include "MoorDyn2.hpp" #include "Rod.hpp" +#include #ifdef LINUX #include @@ -76,6 +77,8 @@ const char* UnitList[] = { "(Nm) ", "(frac) " }; +std::atomic __systems_counter(0); + moordyn::MoorDyn::MoorDyn(const char* infilename, int log_level) : io::IO(NULL) , _filepath("Mooring/lines.txt") @@ -100,6 +103,7 @@ moordyn::MoorDyn::MoorDyn(const char* infilename, int log_level) , nXtra(0) , npW(0) { + ++__systems_counter; SetLogger(new Log(log_level)); if (infilename && (strlen(infilename) > 0)) { @@ -184,6 +188,10 @@ moordyn::MoorDyn::~MoorDyn() delete obj; delete GetLogger(); + + if (--__systems_counter == 0) { + reset_instance_ids(); + } } moordyn::error_id diff --git a/source/Point.cpp b/source/Point.cpp index cbc687d5..3b4d663b 100644 --- a/source/Point.cpp +++ b/source/Point.cpp @@ -276,8 +276,8 @@ Point::setState(vec pos, vec vel) a.line->setEndKinematics(r, rd, a.end_point); } -std::pair -Point::getStateDeriv() +void +Point::getStateDeriv(InstanceStateVarView drdt) { // the RHS is only relevant (there are only states to worry about) if it is // a Point type of Point @@ -295,7 +295,8 @@ Point::getStateDeriv() acc = M.inverse() * Fnet; // update states - return std::make_pair(rd, acc); + drdt.row(0).head<3>() = rd; + drdt.row(0).tail<3>() = acc; }; void diff --git a/source/Point.hpp b/source/Point.hpp index 2b20ec31..d4952364 100644 --- a/source/Point.hpp +++ b/source/Point.hpp @@ -353,15 +353,24 @@ class DECLDIR Point final : public Instance, public SuperCFL * @param pos Position * @param vel Velocity * @throws moordyn::invalid_value_error If it is not a FREE point + * @{ */ void setState(vec pos, vec vel); + inline void setState(const InstanceStateVarView r) + { + setState(r.row(0).head<3>(), r.row(0).tail<3>()); + } + /** + * @} + */ + /** @brief Calculate the forces and state derivatives of the point - * @return The states derivatives, i.e. the velocity (first) and the - * acceleration (second) + * @param drdt The states derivatives, i.e. the velocity and the + * acceleration * @throws moordyn::invalid_value_error If it is not a FREE point */ - std::pair getStateDeriv(); + void getStateDeriv(InstanceStateVarView drdt); /** @brief Calculate the force and mass contributions of the point on the * parent body @@ -382,6 +391,18 @@ class DECLDIR Point final : public Instance, public SuperCFL */ moordyn::error_id doRHS(); + /** @brief Get the number of state variables required by this instance + * @return 1 + */ + inline const size_t stateN() const { return 1; } + + /** @brief Get the dimension of the state variable + * @return 3 components for positions and 3 components for velocities, i.e. + * 6 components + * @warning This function shall be called after ::setup() + */ + inline const size_t stateDims() const { return 6; } + /** @brief Produce the packed data to be saved * * The produced data can be used afterwards to restore the saved information diff --git a/source/Rod.cpp b/source/Rod.cpp index 8679abe8..62cacb47 100644 --- a/source/Rod.cpp +++ b/source/Rod.cpp @@ -601,8 +601,8 @@ Rod::setDependentStates() attached.line->setEndOrientation(q, attached.end_point, ENDPOINT_B); } -std::pair -Rod::getStateDeriv() +void +Rod::getStateDeriv(InstanceStateVarView drdt) { // attempting error handling <<<<<<<< for (unsigned int i = 0; i <= N; i++) { @@ -683,7 +683,8 @@ Rod::getStateDeriv() 0.5 * (quaternion(0.0, v6[3], v6[4], v6[5]) * r7.quat).coeffs(); } - return std::make_pair(vel7, acc6); + drdt.row(0).head<7>() = vel7.toVec7(); + drdt.row(0).tail<6>() = acc6; } const vec6 diff --git a/source/Rod.hpp b/source/Rod.hpp index 9c4d86cc..afed1c01 100644 --- a/source/Rod.hpp +++ b/source/Rod.hpp @@ -470,9 +470,9 @@ class DECLDIR Rod final : public Instance, public SuperCFL */ void setState(XYZQuat pos, vec6 vel); - inline void setState(vec7 r, vec6 rd) + inline void setState(const InstanceStateVarView r) { - setState(XYZQuat::fromVec7(r), rd); + setState(XYZQuat::fromVec7(r.row(0).head<7>()), r.row(0).tail<6>()); } /** * @} @@ -528,13 +528,13 @@ class DECLDIR Rod final : public Instance, public SuperCFL void setDependentStates(); /** @brief calculate the forces and state derivatives of the rod - * @return The linear and angular velocity (first), and the linear and - * angular accelerations (second) + * @param drdt The velocity quaternion and the linear and angular + * accelerations * @throws nan_error If nan values are detected in any node position * @note The returned linear velocity and accelerations for pinned rods * should be ignored */ - std::pair getStateDeriv(); + void getStateDeriv(InstanceStateVarView drdt); /** @brief Get the net force on rod (and possibly moment at end A if it's * not pinned) @@ -580,6 +580,18 @@ class DECLDIR Rod final : public Instance, public SuperCFL void Output(real); + /** @brief Get the number of state variables required by this instance + * @return 1 + */ + inline const size_t stateN() const { return 1; } + + /** @brief Get the dimension of the state variable + * @return 7 components for position quaternion and 6 components for + * linear and angular velocities, i.e. 13 components + * @warning This function shall be called after ::setup() + */ + inline const size_t stateDims() const { return 13; } + /** @brief Produce the packed data to be saved * * The produced data can be used afterwards to restore the saved information diff --git a/source/State.cpp b/source/State.cpp index 499696b4..149d80f1 100644 --- a/source/State.cpp +++ b/source/State.cpp @@ -37,239 +37,51 @@ namespace moordyn { namespace state { void -State::addLine(Line* obj) +State::addInstance(moordyn::Instance* obj) { - if (std::find(lines.begin(), lines.end(), obj) != lines.end()) { - throw moordyn::invalid_value_error("Repeated line"); + if (std::find(_objs.begin(), _objs.end(), obj) != _objs.end()) { + throw moordyn::invalid_value_error("Repeated instance"); } - lines.push_back(obj); - resize(); -} - -unsigned int -State::removeLine(Line* obj) -{ - auto it = std::find(lines.begin(), lines.end(), obj); - if (it == lines.end()) { - throw moordyn::invalid_value_error("Missing line"); - } - const unsigned int i = std::distance(lines.begin(), it); - lines.erase(it); - resize(); - return i; -} - -void -State::addPoint(Point* obj) -{ - if (std::find(points.begin(), points.end(), obj) != points.end()) { - throw moordyn::invalid_value_error("Repeated point"); - } - points.push_back(obj); - resize(); -} - -unsigned int -State::removePoint(Point* obj) -{ - auto it = std::find(points.begin(), points.end(), obj); - if (it == points.end()) { - throw moordyn::invalid_value_error("Missing point"); - } - const unsigned int i = std::distance(points.begin(), it); - points.erase(it); - resize(); - return i; -} - -void -State::addRod(Rod* obj) -{ - if (std::find(rods.begin(), rods.end(), obj) != rods.end()) { - throw moordyn::invalid_value_error("Repeated rod"); + StateVar new_var(_objs.size() + 1); + for (size_t i = 0; i < _objs.size(); i++) { + new_var(i) = _var(i); } - rods.push_back(obj); - resize(); + InstanceStateVar obj_var(obj->stateN(), obj->stateDims()); + new_var(_objs.size()) = obj_var; + _var = new_var; + _objs.push_back(obj); + _indexes = make_indexes(); } unsigned int -State::removeRod(Rod* obj) -{ - auto it = std::find(rods.begin(), rods.end(), obj); - if (it == rods.end()) { - throw moordyn::invalid_value_error("Missing rod"); - } - const unsigned int i = std::distance(rods.begin(), it); - rods.erase(it); - resize(); - return i; -} - -void -State::addBody(Body* obj) +State::removeInstance(moordyn::Instance* obj) { - if (std::find(bodies.begin(), bodies.end(), obj) != bodies.end()) { - throw moordyn::invalid_value_error("Repeated body"); + auto it = std::find(_objs.begin(), _objs.end(), obj); + if (it == _objs.end()) { + throw moordyn::invalid_value_error("Missing instance"); } - bodies.push_back(obj); - resize(); -} - -unsigned int -State::removeBody(Body* obj) -{ - auto it = std::find(bodies.begin(), bodies.end(), obj); - if (it == bodies.end()) { - throw moordyn::invalid_value_error("Missing body"); + const unsigned int removed = std::distance(_objs.begin(), it); + StateVar new_var(_objs.size() - 1); + for (size_t i = 0; i < removed; i++) { + new_var(i) = _var(i); } - const unsigned int i = std::distance(bodies.begin(), it); - bodies.erase(it); - resize(); - return i; -} - -#define TYPE_GETTER(T, TDEF) \ -template <> \ -VarBase::types \ -State::getType() \ -{ \ - return VarBase::types::TDEF; \ -} - -TYPE_GETTER(real, REAL) -TYPE_GETTER(VarScalar, REAL) -TYPE_GETTER(vec, VEC) -TYPE_GETTER(VarVec, VEC) -TYPE_GETTER(vec6, VEC6) -TYPE_GETTER(VarVec6, VEC6) -TYPE_GETTER(XYZQuat, QUAT) -TYPE_GETTER(VarQuat, QUAT) -TYPE_GETTER(list, LIST) -TYPE_GETTER(VarList, LIST) - -#define STATE_ADDER(T, TBASE) \ -template <> \ -void DECLDIR \ -State::addVar(const char* name) \ -{ \ - TBASE* var = new TBASE(); \ - var->resize(ndof()); \ - vars[name] = var; \ - types[name] = getType(); \ -} - - -STATE_ADDER(VarScalar, VarScalar) -STATE_ADDER(real, VarScalar) -STATE_ADDER(VarVec, VarVec) -STATE_ADDER(vec, VarVec) -STATE_ADDER(VarVec6, VarVec6) -STATE_ADDER(vec6, VarVec6) -STATE_ADDER(VarQuat, VarQuat) -STATE_ADDER(XYZQuat, VarQuat) -STATE_ADDER(VarList, VarList) -STATE_ADDER(list, VarList) - -void -State::addVar(const char* name, VarBase::types t) -{ - switch (t) { - case VarBase::types::REAL: - addVar(name); - break; - case VarBase::types::VEC: - addVar(name); - break; - case VarBase::types::VEC6: - addVar(name); - break; - case VarBase::types::QUAT: - addVar(name); - break; - case VarBase::types::LIST: - addVar(name); - break; - default: - throw moordyn::invalid_type_error("Unrecognized variable type"); + for (size_t i = removed; i < _objs.size() - 1; i++) { + new_var(i) = _var(i + 1); } + _var = new_var; + _objs.erase(it); + _indexes = make_indexes(); + return removed; } -void -State::setListLength(const char* name, size_t n, void* obj) -{ - checkVar(name); - std::pair ids = obj ? - indexes[obj] : std::make_pair((size_t)0, ndof()); - for (size_t i = ids.first; i < ids.second; i++) { - ((VarList*)vars[name])->operator()(i).resize(n); - } -} - -#define STATE_SETTER(T, TBASE) \ -template <> \ -void DECLDIR \ -State::set(const char* name, \ - Eigen::Matrix v) \ -{ \ - checkVar(name); \ - TBASE* var = (TBASE*)vars[name]; \ - if(var->rows() != v.rows()) \ - throw moordyn::invalid_value_error("Inconsistent lengths"); \ - var->asMatrix() = v; \ -} - -STATE_SETTER(real, VarScalar) -STATE_SETTER(vec, VarVec) -STATE_SETTER(vec6, VarVec6) -STATE_SETTER(XYZQuat, VarQuat) -STATE_SETTER(list, VarList) - -#define STATE_OBJ_SETTER(T, TBASE) \ -template <> \ -void DECLDIR \ -State::set(const char* name, \ - void* obj, \ - Eigen::Matrix v) \ -{ \ - checkVar(name); \ - TBASE* var = (TBASE*)vars[name]; \ - auto ids = indexes[obj]; \ - if((ids.second - ids.first) != v.rows()) \ - throw moordyn::invalid_value_error("Inconsistent lengths"); \ - var->operator()(Eigen::seq(ids.first, ids.second - 1)) = v; \ -} - -STATE_OBJ_SETTER(real, VarScalar) -STATE_OBJ_SETTER(vec, VarVec) -STATE_OBJ_SETTER(vec6, VarVec6) -STATE_OBJ_SETTER(XYZQuat, VarQuat) -STATE_OBJ_SETTER(list, VarList) - std::vector State::Serialize(void) { std::vector data, subdata; - - for (const auto& [key, var] : vars) { - switch (var->inner_type()) { - case VarBase::types::REAL: - subdata = io::IO::Serialize(((VarScalar*)var)->asMatrix()); - break; - case VarBase::types::VEC: - subdata = io::IO::Serialize(((VarVec*)var)->asMatrix()); - break; - case VarBase::types::VEC6: - subdata = io::IO::Serialize(((VarVec6*)var)->asMatrix()); - break; - case VarBase::types::QUAT: - subdata = io::IO::Serialize(((VarQuat*)var)->asMatrix()); - break; - case VarBase::types::LIST: - subdata = io::IO::Serialize(((VarList*)var)->asMatrix()); - break; - default: - throw moordyn::invalid_type_error("Unhandled variable type"); - } + const uint64_t n = _var.rows(); + data.push_back(io::IO::Serialize(n)); + for (unsigned int i = 0; i < n; i++) { + subdata = io::IO::Serialize(_var(i)); data.insert(data.end(), subdata.begin(), subdata.end()); } return data; @@ -279,77 +91,25 @@ uint64_t* State::Deserialize(const uint64_t* data) { uint64_t* ptr = (uint64_t*)data; - for (const auto& [key, var] : vars) { - switch (var->inner_type()) { - case VarBase::types::REAL: - ptr = io::IO::Deserialize(ptr, ((VarScalar*)var)->asMatrix()); - break; - case VarBase::types::VEC: - ptr = io::IO::Deserialize(ptr, ((VarVec*)var)->asMatrix()); - break; - case VarBase::types::VEC6: - ptr = io::IO::Deserialize(ptr, ((VarVec6*)var)->asMatrix()); - break; - case VarBase::types::QUAT: - ptr = io::IO::Deserialize(ptr, ((VarQuat*)var)->asMatrix()); - break; - case VarBase::types::LIST: - ptr = io::IO::Deserialize(ptr, ((VarList*)var)->asMatrix()); - break; - default: - throw moordyn::invalid_type_error("Unhandled variable type"); - } - } + uint64_t n; + ptr = io::IO::Deserialize(ptr, n); + if (n != _var.rows()) { + LOGERR << "A state variable with " << n << " instances cannot be " + << "deserialized into a state variable with " << _var.rows() + << " instances" << std::endl; + throw moordyn::input_error("Incorrect number of instances"); + } + for (unsigned int i = 0; i < n; i++) + ptr = io::IO::Deserialize(ptr, _var(i)); return ptr; } -#define STATE_GETTER(T, TBASE) \ -template <> \ -DECLDIR Eigen::Matrix& \ -State::getRef(const char* name) \ -{ \ - checkVar(name); \ - TBASE* var = (TBASE*)vars[name]; \ - return var->asMatrix(); \ -} - -STATE_GETTER(real, VarScalar) -STATE_GETTER(vec, VarVec) -STATE_GETTER(vec6, VarVec6) -STATE_GETTER(XYZQuat, VarQuat) -STATE_GETTER(list, VarList) - void State::clear() { - for (auto& [key, value] : vars) { - switch (value->inner_type()) { - case VarBase::types::REAL: - delete (VarScalar*)value; - break; - case VarBase::types::VEC: - delete (VarVec*)value; - break; - case VarBase::types::VEC6: - delete (VarVec6*)value; - break; - case VarBase::types::QUAT: - delete (VarQuat*)value; - break; - case VarBase::types::LIST: - delete (VarList*)value; - break; - default: - break; - } - } - vars.clear(); - types.clear(); - lines.clear(); - points.clear(); - rods.clear(); - bodies.clear(); - indexes.clear(); + _var.resize(0); + _objs.clear(); + _indexes.clear(); } void @@ -357,154 +117,13 @@ State::copy(const State& rhs) { clear(); - lines.reserve(rhs.lines.size()); - for (auto l : rhs.lines) - addLine(l); - points.reserve(rhs.points.size()); - for (auto l : rhs.points) - addPoint(l); - rods.reserve(rhs.rods.size()); - for (auto l : rhs.rods) - addRod(l); - bodies.reserve(rhs.bodies.size()); - for (auto l : rhs.bodies) - addBody(l); + _objs.reserve(rhs._objs.size()); + for (auto obj : rhs._objs) + addInstance(obj); - for (const auto& [key, var] : rhs.vars) { - addVar(key.c_str(), var->inner_type()); - switch (var->inner_type()) { - case VarBase::types::REAL: - *((VarScalar*)vars[key]) = *((VarScalar*)var); - break; - case VarBase::types::VEC: - *((VarVec*)vars[key]) = *((VarVec*)var); - break; - case VarBase::types::VEC6: - *((VarVec6*)vars[key]) = *((VarVec6*)var); - break; - case VarBase::types::QUAT: - *((VarQuat*)vars[key]) = *((VarQuat*)var); - break; - case VarBase::types::LIST: - *((VarList*)vars[key]) = *((VarList*)var); - break; - default: - throw moordyn::invalid_type_error("Unhandled variable type"); - } - } -} - -#define STATE_TYPE_CHECKER(T, TDEF) \ -template <> \ -bool \ -State::checkType(const char* name) \ -{ \ - return types[name] == VarBase::types::TDEF; \ -} - -STATE_TYPE_CHECKER(VarScalar, REAL) -STATE_TYPE_CHECKER(real, REAL) -STATE_TYPE_CHECKER(VarVec, VEC) -STATE_TYPE_CHECKER(vec, VEC) -STATE_TYPE_CHECKER(VarVec6, VEC6) -STATE_TYPE_CHECKER(vec6, VEC6) -STATE_TYPE_CHECKER(VarQuat, QUAT) -STATE_TYPE_CHECKER(XYZQuat, QUAT) -STATE_TYPE_CHECKER(VarList, LIST) -STATE_TYPE_CHECKER(list, LIST) - -void State::resize() -{ - auto indexes_old = indexes; - size_t n_old = 0; - for (const auto& [key, value] : indexes_old) { - n_old = value.second > n_old ? value.second : n_old; - } - - size_t n = ndof(); - - if (n > n_old) { - // A new entity has been added - std::pair ids; - for (const auto& [key, value] : indexes) { - if (indexes_old.find(key) == indexes_old.end()) { - ids = value; - break; - } - } - for (const auto& [key, value] : vars) { - grow(value, n, ids); - } - } else { - // An entity has been removed, find its indexes - std::pair ids; - for (const auto& [key, value] : indexes_old) { - if (indexes.find(key) == indexes.end()) { - ids = value; - break; - } - } - for (const auto& [key, value] : vars) { - shrink(value, n, ids); - } - } -} - -void State::grow(VarBase* var, size_t n, std::pair ids) -{ - switch (var->inner_type()) { - case VarBase::types::REAL: - grow(((VarScalar*)var)->asMatrix(), n, ids); - break; - case VarBase::types::VEC: - grow(((VarVec*)var)->asMatrix(), n, ids); - break; - case VarBase::types::VEC6: - grow(((VarVec6*)var)->asMatrix(), n, ids); - break; - case VarBase::types::QUAT: - grow(((VarQuat*)var)->asMatrix(), n, ids); - break; - case VarBase::types::LIST: - grow(((VarList*)var)->asMatrix(), n, ids); - break; - default: - throw moordyn::invalid_type_error("Unrecognized variable type"); - } -} - -void State::shrink(VarBase* var, size_t n, std::pair ids) -{ - switch (var->inner_type()) { - case VarBase::types::REAL: - shrink(((VarScalar*)var)->asMatrix(), n, ids); - break; - case VarBase::types::VEC: - shrink(((VarVec*)var)->asMatrix(), n, ids); - break; - case VarBase::types::VEC6: - shrink(((VarVec6*)var)->asMatrix(), n, ids); - break; - case VarBase::types::QUAT: - shrink(((VarQuat*)var)->asMatrix(), n, ids); - break; - case VarBase::types::LIST: - shrink(((VarList*)var)->asMatrix(), n, ids); - break; - default: - throw moordyn::invalid_type_error("Unrecognized variable type"); - } + _var = rhs._var; } } // ::state } // ::moordyn - -moordyn::state::VarListBase operator*(const moordyn::real& k, - moordyn::state::VarListBase v) -{ - for (unsigned int i = 0; i < v.rows(); i++) { - v(i) *= k; - } - return v; -} diff --git a/source/State.hpp b/source/State.hpp index 3ead4ad5..1be9dfba 100644 --- a/source/State.hpp +++ b/source/State.hpp @@ -35,11 +35,8 @@ #pragma once #include "Misc.hpp" -#include "Line.hpp" #include "IO.hpp" -#include "Point.hpp" -#include "Rod.hpp" -#include "Body.hpp" +#include "Instance.hpp" #include #include #include @@ -50,147 +47,6 @@ namespace moordyn { namespace state { -class State; - -#define STATE_VAR_BLOCK(T) \ - Eigen::VectorBlock, Eigen::Dynamic> - -/// Abstract base class for all State Variables -class VarBase { - public: - /** @brief Destructor - */ - virtual ~VarBase() = default; - - /** @brief Considered valid types - */ - typedef enum - { - /// real type - REAL, - /// vec type - VEC, - /// vec6 type - VEC6, - /// XYZQuat type - QUAT, - /// list type - LIST, - } types; - - /// Get the type definition - inline const VarBase::types inner_type() const { return this->_type; } - - // friend class declaration - friend class State; - - protected: - /// The type name - VarBase::types _type; -}; - -/// Typed state variable -template -class VarTyped : public VarBase, public Eigen::Matrix -{ -public: - /** @brief Destructor - */ - virtual ~VarTyped() = default; - - /** @brief Get a reference of the state variable as an Eigen::Matrix - * @return A reference of the matrix - */ - template - inline Eigen::Matrix& asMatrix() const - { - Eigen::Matrix* m = - (Eigen::Matrix*)this; - return *m; - } - protected: - /** @brief Constructor - * @param t Type definition, to be used later - */ - VarTyped(const VarBase::types t) - { - this->_type = t; - } -}; - -/// Scalar state basic Eigen type -typedef Eigen::Matrix VarScalarBase; - -/// Scalar state variable -class VarScalar final : public VarTyped -{ - public: - VarScalar() - : VarTyped(VarBase::types::REAL) - {} -}; - -/// 3-D vector state basic Eigen type -typedef Eigen::Matrix VarVecBase; - -/// 3-D vector state variable -class VarVec final : public VarTyped -{ - public: - VarVec() - : VarTyped(VarBase::types::VEC) - {} -}; - -/// 6-D vector state basic Eigen type -typedef Eigen::Matrix VarVec6Base; - -/// 6-D vector state variable -class VarVec6 final : public VarTyped -{ - public: - VarVec6() - : VarTyped(VarBase::types::VEC6) - {} -}; - -/// Quaternion state basic Eigen type -typedef Eigen::Matrix VarQuatBase; - -/// Quaternion state variable -class VarQuat final : public VarTyped -{ - public: - VarQuat() - : VarTyped(VarBase::types::QUAT) - {} -}; - -/// List state basic Eigen type -typedef Eigen::Matrix VarListBase; - -/// List state variable -class VarList final : public VarTyped -{ - public: - VarList() - : VarTyped(VarBase::types::LIST) - {} - - ~VarList() - { - for (unsigned int i = 0; i < rows(); i++) { - this->operator()(i).resize(0); - } - } -}; - -/// State var slicer -typedef Eigen::ArithmeticSequence> Slicer; - -/// State var indexer, useful for objects that only has an entry on the var -typedef Eigen::Index Indexer; - /** @class State State.hpp * @brief The collection of state variables of the whole system */ @@ -218,185 +74,58 @@ class DECLDIR State final : public moordyn::io::IO */ ~State() { clear(); }; - /** @brief Add a line - * @param obj The line + /** @brief Add an instance capable of holding state variables + * @param obj The instance * @throw moordyn::invalid_value_error If it has been already registered */ - void addLine(Line* obj); + void addInstance(moordyn::Instance* obj); - /** @brief Remove a line - * @param obj The line - * @return The index of the removed entity - * @throw moordyn::invalid_value_error If the line has not been registered, - * or it was already removed - */ - unsigned int removeLine(Line* obj); - - /** @brief Add a point - * @param obj The point - * @throw moordyn::invalid_value_error If it has been already registered - */ - void addPoint(Point* obj); - - /** @brief Remove a point - * @param obj The point - * @return The index of the removed entity - * @throw moordyn::invalid_value_error If the point has not been + /** @brief Remove an instance + * @param obj The instance + * @throw moordyn::invalid_value_error If the instance has not been * registered, or it was already removed */ - unsigned int removePoint(Point* obj); - - /** @brief Add a rod - * @param obj The rod - * @throw moordyn::invalid_value_error If it has been already registered - */ - void addRod(Rod* obj); - - /** @brief Remove a rod - * @param obj The rod - * @return The index of the removed entity - * @throw moordyn::invalid_value_error If the rod has not been registered, - * or it was already removed - */ - unsigned int removeRod(Rod* obj); - - /** @brief Add a body - * @param obj The body - * @throw moordyn::invalid_value_error If it has been already registered - */ - void addBody(Body* obj); - - /** @brief Remove a body - * @param obj The body - * @return The index of the removed entity - * @throw moordyn::invalid_value_error If the body has not been registered, - * or it was already removed - */ - unsigned int removeBody(Body* obj); - - /** @brief Installs a new variable - * @param name Name of the variable - */ - template - void addVar(const char* name); - - /** @brief Installs a new variable - * @param name Name of the variable - * @param t The type of variable - */ - void addVar(const char* name, VarBase::types t); - - /** @brief Check if a variable already exists - * @param name Name of the variable - * @return true if the variable already exists, false otherwise - */ - inline bool hasVar(const char* name) const - { - return !(vars.find(name) == vars.end()); - } - - /** @brief Set a list length - * - * The lists might have a different number of components per instance - * (moordyn::Line / moordyn::Point / moordyn::Rod / moordyn::Body) - * @param name Name of the variable - * @param n Number of components - * @param obj The instance, NULL if the whole list should be resized - * @throw moordyn::invalid_value_error If the variable does not exist - * @throw moordyn::invalid_type_error If the variable type is not - * moordyn::State::VarList - * @throw moordyn::invalid_value_error If the instance does not exist - */ - void setListLength(const char* name, size_t n=1, void* obj=NULL); + unsigned int removeInstance(moordyn::Instance* obj); - /** @brief Get a variable by its name - * @param name Name of the variable - * @throw moordyn::invalid_value_error If the variable does not exist - * @throw moordyn::invalid_type_error If the variable type is not correct - * @throw moordyn::invalid_value_error If the instance does not exist + /** @brief Get the state variables */ - template - inline STATE_VAR_BLOCK(T) - get(const char* name) + inline StateVarView get() { - return getRef(name)( - Eigen::seq(0, Eigen::placeholders::last)); + return _var(Eigen::seq(0, Eigen::placeholders::last)); } - /** @brief Get a variable by its name and instance - * @param name Name of the variable - * @param obj The instance, i.e. A moordyn::Line, moordyn::Point, - * moordyn::Rod or moordyn::Body - * @throw moordyn::invalid_value_error If the variable does not exist - * @throw moordyn::invalid_type_error If the variable type is not correct - * @throw moordyn::invalid_value_error If the instance does not exist + /** @brief Get the state variables associated to an instance + * @param obj The instance + * @throw moordyn::invalid_value_error If the instance has not been + * registered, or it was already removed */ - template - inline STATE_VAR_BLOCK(T) - get(const char* name, void* obj) + inline InstanceStateVarView get(moordyn::Instance* obj) { - auto ids = indexes[obj]; - auto n = ids.second - ids.first; - return getRef(name)(Eigen::seq(ids.first, ids.second - 1)); + const auto i = indexer(obj); + return _var(i).topLeftCorner(obj->stateN(), obj->stateDims()); } - /** @brief Set a variable - * @param name Name of the variable - * @param v The new value - * @throw moordyn::invalid_value_error If the variable does not exist - * @throw moordyn::invalid_type_error If the variable type is not correct - * @{ - */ - template - void set(const char* name, Eigen::Matrix v); - - /** @brief Set the part of a variable associated with an instance - * @param name Name of the variable - * @param obj The instance, i.e. A moordyn::Line, moordyn::Point, - * moordyn::Rod or moordyn::Body - * @param v The new value - * @throw moordyn::invalid_value_error If the variable does not exist - * @throw moordyn::invalid_type_error If the variable type is not correct - * @throw moordyn::invalid_value_error If the instance does not exist - */ - template - void set(const char* name, - void* obj, - Eigen::Matrix v); - - /** @brief Get a slicer for a state var - * - * It is rather inneficient to call ::get() many times, so if multiple - * operations have to be done on multiple instances, it is way more - * efficient to call ::get() just once per state var, and use the slicers - * from this method + /** @brief Get the index within a state var for a particular instance * @param obj The objects - * @return The slicers + * @return The indexes + * @throw moordyn::invalid_value_error If the instance has not been + * registered, or it was already removed * @{ */ - inline const Slicer slicer(void* obj) + inline const Eigen::Index indexer(moordyn::Instance* obj) { - auto ids = indexes[obj]; - return Eigen::seq(ids.first, ids.second - 1); - } - - inline const Indexer indexer(void* obj) - { - return Indexer(indexes[obj].first); - } - - inline const std::vector slicer(std::vector obj) - { - std::vector slcs; - for (auto o : obj) { - slcs.push_back(slicer(o)); + const size_t id = obj->id(); + if ((id >= _indexes.size()) || (_indexes[id] < 0)) { + throw moordyn::invalid_value_error("Missing instance"); } - return slcs; + return Eigen::Index(_indexes[id]); } - inline const std::vector indexer(std::vector obj) + inline const std::vector indexer( + std::vector obj) { - std::vector slcs; + std::vector slcs; + slcs.reserve(obj.size()); for (auto o : obj) { slcs.push_back(indexer(o)); } @@ -436,14 +165,6 @@ class DECLDIR State final : public moordyn::io::IO } private: - /** @brief Get a variable by its name - * @param name Name of the variable - * @throw moordyn::invalid_value_error If the variable does not exist - * @throw moordyn::invalid_type_error If the variable type is not correct - */ - template - Eigen::Matrix& getRef(const char* name); - /** @brief Clear the state */ void clear(); @@ -461,144 +182,60 @@ class DECLDIR State final : public moordyn::io::IO * This method builds the moordyn::State::indexes map * @return the Total number of dofs */ - inline size_t ndof() { - size_t i = 0; - for (auto line : lines) { - size_t n = line->getN() - 1; - indexes[line] = {i, i + n}; - i += n; + inline std::vector make_indexes() { + std::vector indexes; + for (size_t i = 0; i < _objs.size(); i++) { + size_t key = _objs[i]->id(); + if (indexes.size() <= key) { + indexes.resize(key + 1, -1); + } + indexes[key] = i; } - for (auto point : points) { - indexes[point] = {i, i + 1}; - i++; - } - for (auto rod : rods) { - indexes[rod] = {i, i + 1}; - i++; - } - for (auto body : bodies) { - indexes[body] = {i, i + 1}; - i++; - } - return i; + return indexes; } - /** @brief Check the type - * - * This function is assuming that the variable existance has been already - * checked - * @param name Name of the variable - * @return true if the type is correct, false otherwise - */ - template - bool checkType(const char* name); + /// The state var + StateVar _var; - /** @brief Check the var - * @param name Name of the variable - * @throw moordyn::invalid_value_error If the variable does not exist - * @throw moordyn::invalid_type_error If the variable type is not correct - */ - template - inline void checkVar(const char* name) - { - if (vars.find(name) == vars.end()) - throw moordyn::invalid_value_error("Undefined variable"); - if (!checkType(name)) - throw moordyn::invalid_type_error("Invalid variable type"); - } + /// The instances + std::vector _objs; - /** Get the type enum - * @return The type - */ - template - static VarBase::types getType(); + /// A link between the instances unique ids and the index into the var + std::vector _indexes; +}; - /** @brief Resize the state variables to take into account changes on the - * number of instances - */ - void resize(); +} // ::state - /** @brief Grow a variable - * @param var Variable - * @param n New variable size - * @param ids The new inserted indexes - * @{ - */ - void grow(VarBase* var, size_t n, std::pair ids); - template - inline void grow(Eigen::Matrix& var, - size_t n, - std::pair ids) - { - const size_t first = ids.first; - const size_t last = ids.second; - const size_t offset = last - first; - Eigen::Matrix m = var; - var.resize(n); - for (size_t i = 0; i < first; i++) { - var(i) = m(i); - } - for (size_t i = last; i < n; i++) { - var(i) = m(i - offset); - } - } - /** - * @} - */ +} // ::moordyn - /** @brief Shrink a variable - * @param var Variable - * @param n New variable size - * @param ids The removed indexes - * @{ - */ - void shrink(VarBase* var, size_t n, std::pair ids); - template - inline void shrink(Eigen::Matrix& var, - size_t n, - std::pair ids) - { - const size_t first = ids.first; - const size_t last = ids.second; - const size_t offset = last - first; - Eigen::Matrix m = var; - var.resize(n); - for (size_t i = 0; i < first; i++) { - var(i) = m(i); - } - for (size_t i = last; i < n; i++) { - var(i - offset) = m(i); - } +inline moordyn::StateVar operator*(moordyn::StateVarView v, moordyn::real f) +{ + moordyn::StateVar out; + out.resize(v.rows()); + for (unsigned int i = 0; i < v.rows(); i++) { + out(i).resize(v(i).rows(), v(i).cols()); + out(i) = v(i) * f; } - /** - * @} - */ - - /// The map of available variables - std::map vars; - - /// The map of available variable types - std::map types; - - /// The lines - std::vector lines; - - /// The points - std::vector points; - - /// The rods - std::vector rods; + return out; +} - /// The bodies - std::vector bodies; - - /// The map that associate each instance with the variable indexes - std::map> indexes; -}; - -} // ::state +inline moordyn::StateVar operator*(moordyn::real f, moordyn::StateVarView v) +{ + return v * f; +} -} // ::moordyn +inline moordyn::StateVar operator*(moordyn::StateVar v, moordyn::real f) +{ + moordyn::StateVar out; + out.resize(v.rows()); + for (unsigned int i = 0; i < v.rows(); i++) { + out(i).resize(v(i).rows(), v(i).cols()); + out(i) = v(i) * f; + } + return out; +} -moordyn::state::VarListBase operator*(const moordyn::real& k, - moordyn::state::VarListBase v); +inline moordyn::StateVar operator*(moordyn::real f, moordyn::StateVar v) +{ + return v.topLeftCorner(v.rows(), v.cols()) * f; +} diff --git a/source/Time.cpp b/source/Time.cpp index ce74059e..181d5754 100644 --- a/source/Time.cpp +++ b/source/Time.cpp @@ -41,8 +41,6 @@ template void SchemeBase::Update(real t_local, unsigned int substep) { - auto pos = AS_STATE(_r[substep])->get("pos"); - auto vel = AS_STATE(_r[substep])->get("vel"); ground->updateFairlead(this->t); t_local += this->t_local; @@ -69,8 +67,7 @@ SchemeBase::Update(real t_local, unsigned int substep) if ((bodies[i]->type != Body::FREE) && (bodies[i]->type != Body::CPLDPIN)) continue; - auto slc = AS_STATE(_r[substep])->indexer(bodies[i]); - bodies[i]->setState(pos(slc), vel(slc)); + bodies[i]->setState(AS_STATE(_r[substep])->get(bodies[i])); } for (unsigned int i = 0; i < rods.size(); i++) { @@ -79,21 +76,18 @@ SchemeBase::Update(real t_local, unsigned int substep) (rods[i]->type != Rod::CPLDPIN) && (rods[i]->type != Rod::FREE)) continue; - auto slc = AS_STATE(_r[substep])->indexer(rods[i]); - rods[i]->setState(pos(slc), vel(slc)); + rods[i]->setState(AS_STATE(_r[substep])->get(rods[i])); } for (unsigned int i = 0; i < points.size(); i++) { if (points[i]->type != Point::FREE) continue; - auto slc = AS_STATE(_r[substep])->indexer(points[i]); - points[i]->setState(pos(slc), vel(slc)); + points[i]->setState(AS_STATE(_r[substep])->get(points[i])); } for (unsigned int i = 0; i < lines.size(); i++) { - auto slc = AS_STATE(_r[substep])->slicer(lines[i]); lines[i]->setTime(this->t); - lines[i]->setState(pos(slc), vel(slc)); + lines[i]->setState(AS_STATE(_r[substep])->get(lines[i])); } } @@ -101,15 +95,12 @@ template void SchemeBase::CalcStateDeriv(unsigned int substep) { - auto vel = AS_STATE(_rd[substep])->get("vel"); - auto acc = AS_STATE(_rd[substep])->get("acc"); waves->updateWaves(); for (unsigned int i = 0; i < lines.size(); i++) { if (!_calc_mask.lines[i]) continue; - auto slc = AS_STATE(_rd[substep])->slicer(lines[i]); - lines[i]->getStateDeriv(vel(slc), acc(slc)); + lines[i]->getStateDeriv(AS_STATE(_rd[substep])->get(lines[i])); } for (unsigned int i = 0; i < points.size(); i++) { @@ -117,10 +108,7 @@ SchemeBase::CalcStateDeriv(unsigned int substep) continue; if (points[i]->type != Point::FREE) continue; - auto [u, a] = points[i]->getStateDeriv(); - auto slc = AS_STATE(_rd[substep])->indexer(points[i]); - vel(slc) = u; - acc(slc) = a; + points[i]->getStateDeriv(AS_STATE(_rd[substep])->get(points[i])); } for (unsigned int i = 0; i < rods.size(); i++) { @@ -129,10 +117,7 @@ SchemeBase::CalcStateDeriv(unsigned int substep) if ((rods[i]->type != Rod::PINNED) && (rods[i]->type != Rod::CPLDPIN) && (rods[i]->type != Rod::FREE)) continue; - auto [u, a] = rods[i]->getStateDeriv(); - auto slc = AS_STATE(_rd[substep])->indexer(rods[i]); - vel(slc) = u.toVec7(); - acc(slc) = a; + rods[i]->getStateDeriv(AS_STATE(_rd[substep])->get(rods[i])); } for (unsigned int i = 0; i < bodies.size(); i++) { @@ -140,10 +125,7 @@ SchemeBase::CalcStateDeriv(unsigned int substep) continue; if ((bodies[i]->type != Body::FREE) && (bodies[i]->type != Body::CPLDPIN)) continue; - auto [u, a] = bodies[i]->getStateDeriv(); - auto slc = AS_STATE(_rd[substep])->indexer(bodies[i]); - vel(slc) = u.toVec7(); - acc(slc) = a; + bodies[i]->getStateDeriv(AS_STATE(_rd[substep])->get(bodies[i])); } for (auto obj : points) { @@ -193,12 +175,10 @@ StationaryScheme::StationaryScheme(moordyn::Log* log, moordyn::WavesRef waves) void StationaryScheme::Step(real& dt) { - auto r0 = r(0)->get("pos"); - auto u0 = r(0)->get("vel"); - auto r1 = r(1)->get("pos"); - auto u1 = r(1)->get("vel"); - auto drdt = rd(0)->get("vel"); - auto dudt = rd(0)->get("acc"); + auto r0 = r(0)->get(); + auto r1 = r(1)->get(); + auto drdt0 = rd(0)->get(); + Update(0.0, 0); CalcStateDeriv(0); @@ -215,7 +195,6 @@ StationaryScheme::Step(real& dt) _booster /= STATIONARY_BOOSTING; const real f = STATIONARY_RELAX, fi = 1.0 - STATIONARY_RELAX; r0 = fi * r0 + f * r1; - u0 = fi * u0 + f * u1; Update(0.0, 0); CalcStateDeriv(0); MakeStationary(dt); @@ -239,9 +218,7 @@ StationaryScheme::Step(real& dt) new_dt = (std::min)(new_dt, obj->cfl2dt(cfl, v)); r1 = r0; - u1 = u0; - r0 += new_dt * drdt; - u0 += new_dt * dudt; + r0 += new_dt * drdt0; t += dt; Update(dt, 0); SchemeBase::Step(dt); @@ -260,36 +237,47 @@ vec7 integrateVec6AsVec7(const moordyn::vec7& r, const moordyn::vec6& rd) } #define MAKE_STATIONARY_VEC3(obj) { \ - auto slc = rd(id)->slicer(obj); \ - vel(slc) = 0.5 * dt * acc(slc); \ - for (unsigned int j = 0; j < slc.size(); j++) { \ - _error += acc(slc.first() + j).norm(); \ - acc(slc.first() + j) = vec3::Zero(); \ + auto drdt = rd(id)->get(obj); \ + for (unsigned int j = 0; j < drdt.rows(); j++) { \ + auto acc = drdt.row(j).segment<3>(3); \ + _error += acc.norm(); \ + drdt.row(j).head<3>() = 0.5 * dt * acc; \ + acc = vec3::Zero(); \ } \ } #define MAKE_STATIONARY_QUAT(obj) { \ - auto slc = rd(id)->indexer(obj); \ - _error += acc(slc).norm(); \ - vel(slc) = integrateVec6AsVec7(pos(slc), 0.5 * dt * acc(slc)); \ - acc(slc) = vec6::Zero(); \ + const moordyn::vec7 pos = r(i)->get(obj).row(0).head<7>(); \ + auto drdt = rd(id)->get(obj).row(0); \ + auto vel = drdt.head<7>(); \ + auto acc = drdt.tail<6>(); \ + _error += acc.head<3>().norm(); \ + vel = integrateVec6AsVec7(pos, 0.5 * dt * acc); \ + acc = vec6::Zero(); \ } void StationaryScheme::MakeStationary(real& dt, unsigned int i, unsigned int id) { _error = 0.0; - auto pos = r(i)->get("pos"); - auto vel = rd(id)->get("vel"); - auto acc = rd(id)->get("acc"); for (auto obj : lines) MAKE_STATIONARY_VEC3(obj); - for (auto obj : points) + for (auto obj : points) { + if (obj->type != Point::FREE) + continue; MAKE_STATIONARY_VEC3(obj); - for (auto obj : rods) + } + for (auto obj : rods) { + if ((obj->type != Rod::PINNED) && (obj->type != Rod::CPLDPIN) && + (obj->type != Rod::FREE)) + continue; MAKE_STATIONARY_QUAT(obj); - for (auto obj : bodies) + } + for (auto obj : bodies) { + if ((obj->type != Body::FREE) && (obj->type != Body::CPLDPIN)) + continue; MAKE_STATIONARY_QUAT(obj); + } } EulerScheme::EulerScheme(moordyn::Log* log, moordyn::WavesRef waves) @@ -303,8 +291,7 @@ EulerScheme::Step(real& dt) { Update(0.0, 0); CalcStateDeriv(0); - r(0)->get("pos") += dt * rd(0)->get("vel"); - r(0)->get("vel") += dt * rd(0)->get("acc"); + r(0)->get() += dt * rd(0)->get(); t += dt; Update(dt, 0); SchemeBase::Step(dt); @@ -410,8 +397,7 @@ LocalEulerScheme::Step(real& dt) SetCalcMask(dt); Update(0.0, 0); CalcStateDeriv(0); - r(0)->get("pos") += dt * rd(0)->get("vel"); - r(0)->get("vel") += dt * rd(0)->get("acc"); + r(0)->get() += dt * rd(0)->get(); t += dt; Update(dt, 0); SchemeBase::Step(dt); @@ -426,24 +412,18 @@ HeunScheme::HeunScheme(moordyn::Log* log, moordyn::WavesRef waves) void HeunScheme::Step(real& dt) { - auto r0 = r(0)->get("pos"); - auto u0 = r(0)->get("vel"); - auto drdt0 = rd(0)->get("vel"); - auto dudt0 = rd(0)->get("acc"); - auto drdt1 = rd(1)->get("vel"); - auto dudt1 = rd(1)->get("acc"); + auto r0 = r(0)->get(); + auto drdt0 = rd(0)->get(); + auto drdt1 = rd(1)->get(); // Apply the latest knew derivative, as a predictor r0 += dt * drdt0; - u0 += dt * dudt0; drdt1 = drdt0; - dudt1 = dudt0; // Compute the new derivative Update(0.0, 0); CalcStateDeriv(0); // Correct the integration r0 += 0.5 * dt * (drdt0 - drdt1); - u0 += 0.5 * dt * (dudt0 - dudt1); t += dt; Update(dt, 0); SchemeBase::Step(dt); @@ -458,26 +438,20 @@ RK2Scheme::RK2Scheme(moordyn::Log* log, moordyn::WavesRef waves) void RK2Scheme::Step(real& dt) { - auto r0 = r(0)->get("pos"); - auto u0 = r(0)->get("vel"); - auto r1 = r(1)->get("pos"); - auto u1 = r(1)->get("vel"); - auto drdt0 = rd(0)->get("vel"); - auto dudt0 = rd(0)->get("acc"); - auto drdt1 = rd(1)->get("vel"); - auto dudt1 = rd(1)->get("acc"); + auto r0 = r(0)->get(); + auto r1 = r(1)->get(); + auto drdt0 = rd(0)->get(); + auto drdt1 = rd(1)->get(); Update(0.0, 0); // Compute the intermediate state CalcStateDeriv(0); t += 0.5 * dt; r1 = r0 + 0.5 * dt * drdt0; - u1 = u0 + 0.5 * dt * dudt0; Update(0.5 * dt, 1); // And so we can compute the new derivative and apply it CalcStateDeriv(1); r0 += dt * drdt1; - u0 += dt * dudt1; t += 0.5 * dt; Update(dt, 0); SchemeBase::Step(dt); @@ -492,20 +466,13 @@ RK4Scheme::RK4Scheme(moordyn::Log* log, moordyn::WavesRef waves) void RK4Scheme::Step(real& dt) { - auto r0 = r(0)->get("pos"); - auto u0 = r(0)->get("vel"); - auto r1 = r(1)->get("pos"); - auto u1 = r(1)->get("vel"); - auto r2 = r(2)->get("pos"); - auto u2 = r(2)->get("vel"); - auto drdt0 = rd(0)->get("vel"); - auto dudt0 = rd(0)->get("acc"); - auto drdt1 = rd(1)->get("vel"); - auto dudt1 = rd(1)->get("acc"); - auto drdt2 = rd(2)->get("vel"); - auto dudt2 = rd(2)->get("acc"); - auto drdt3 = rd(3)->get("vel"); - auto dudt3 = rd(3)->get("acc"); + auto r0 = r(0)->get(); + auto r1 = r(1)->get(); + auto r2 = r(2)->get(); + auto drdt0 = rd(0)->get(); + auto drdt1 = rd(1)->get(); + auto drdt2 = rd(2)->get(); + auto drdt3 = rd(3)->get(); Update(0.0, 0); @@ -515,28 +482,24 @@ RK4Scheme::Step(real& dt) // k2 t += 0.5 * dt; r1 = r0 + 0.5 * dt * drdt0; - u1 = u0 + 0.5 * dt * dudt0; Update(0.5 * dt, 1); CalcStateDeriv(1); // k3 r1 = r0 + 0.5 * dt * drdt1; - u1 = u0 + 0.5 * dt * dudt1; Update(0.5 * dt, 1); CalcStateDeriv(2); // k4 t += 0.5 * dt; r2 = r0 + dt * drdt2; - u2 = u0 + dt * dudt2; Update(dt, 2); CalcStateDeriv(3); // Apply - r0 += dt / 6.0 * (drdt0 + drdt3) + dt / 3.0 * (drdt1 + drdt2); - u0 += dt / 6.0 * (dudt0 + dudt3) + dt / 3.0 * (dudt1 + dudt2); + r0 += (dt / 6.0) * (drdt0 + drdt3) + (dt / 3.0) * (drdt1 + drdt2); Update(dt, 0); SchemeBase::Step(dt); @@ -564,22 +527,16 @@ template void ABScheme::Step(real& dt) { - auto r0 = AS_STATE(r(0))->get("pos"); - auto u0 = AS_STATE(r(0))->get("vel"); - auto drdt0 = AS_STATE(rd(0))->get("vel"); - auto dudt0 = AS_STATE(rd(0))->get("acc"); - auto drdt1 = AS_STATE(rd(1))->get("vel"); - auto dudt1 = AS_STATE(rd(1))->get("acc"); - auto drdt2 = AS_STATE(rd(2))->get("vel"); - auto dudt2 = AS_STATE(rd(2))->get("acc"); - auto drdt3 = AS_STATE(rd(3))->get("vel"); - auto dudt3 = AS_STATE(rd(3))->get("acc"); - auto drdt4 = AS_STATE(rd(4))->get("vel"); - auto dudt4 = AS_STATE(rd(4))->get("acc"); - Update(0.0, 0); shift(); + auto r0 = r(0)->get(); + auto drdt0 = rd(0)->get(); + auto drdt1 = rd(1)->get(); + auto drdt2 = rd(2)->get(); + auto drdt3 = rd(3)->get(); + auto drdt4 = rd(4)->get(); + // Get the new derivative if (local && (n_steps == order)) SetCalcMask(dt); @@ -589,31 +546,21 @@ ABScheme::Step(real& dt) switch (n_steps) { case 0: r0 += dt * drdt0; - u0 += dt * dudt0; break; case 1: r0 += 1.5 * drdt0 + 0.5 * drdt1; - u0 += 1.5 * dt * dudt0 + - 0.5 * dt * dudt1; break; case 2: r0 += 23.0 / 12.0 * dt * drdt0 + 4.0 / 3.0 * dt * drdt1 + 5.0 / 12.0 * dt * drdt2; - u0 += 23.0 / 12.0 * dt * dudt0 + - 4.0 / 3.0 * dt * dudt1 + - 5.0 / 12.0 * dt * dudt2; break; case 3: r0 += 55.0 / 24.0 * dt * drdt0 + 59.0 / 24.0 * dt * drdt1 + 37.0 / 24.0 * dt * drdt2 + 3.0 / 8.0 * dt * drdt3; - u0 += 55.0 / 24.0 * dt * dudt0 + - 59.0 / 24.0 * dt * dudt1 + - 37.0 / 24.0 * dt * dudt2 + - 3.0 / 8.0 * dt * dudt3; break; default: r0 += 1901.0 / 720.0 * dt * drdt0 + @@ -621,11 +568,6 @@ ABScheme::Step(real& dt) 109.0 / 360.0 * dt * drdt2 + 637.0 / 24.0 * dt * drdt3 + 251.0 / 720.0 * dt * drdt4; - u0 += 1901.0 / 720.0 * dt * dudt0 + - 1387.0 / 360.0 * dt * dudt1 + - 109.0 / 360.0 * dt * dudt2 + - 637.0 / 24.0 * dt * dudt3 + - 251.0 / 720.0 * dt * dudt4; } n_steps = (std::min)(n_steps + 1, order); @@ -670,21 +612,15 @@ ImplicitEulerScheme::ImplicitEulerScheme(moordyn::Log* log, void ImplicitEulerScheme::Step(real& dt) { - auto r0 = r(0)->get("pos"); - auto u0 = r(0)->get("vel"); - auto r1 = r(1)->get("pos"); - auto u1 = r(1)->get("vel"); - auto drdt0 = rd(0)->get("vel"); - auto dudt0 = rd(0)->get("acc"); - auto drdt1 = rd(1)->get("vel"); - auto dudt1 = rd(1)->get("acc"); + auto r0 = r(0)->get(); + auto r1 = r(1)->get(); + auto drdt0 = rd(0)->get(); + auto drdt1 = rd(1)->get(); t += _dt_factor * dt; drdt1 = drdt0; - dudt1 = dudt0; for (unsigned int i = 0; i < iters(); i++) { r1 = r0 + _dt_factor * dt * drdt0; - u1 = u0 + _dt_factor * dt * dudt0; Update(_dt_factor * dt, 1); CalcStateDeriv(0); @@ -692,15 +628,12 @@ ImplicitEulerScheme::Step(real& dt) // We cannot relax on the last step const real relax = Relax(i); drdt0 = (1.0 - relax) * drdt0 + relax * drdt1; - dudt0 = (1.0 - relax) * dudt0 + relax * dudt1; drdt1 = drdt0; - dudt1 = dudt0; } } // Apply r0 += dt * drdt0; - u0 += dt * dudt0; t += (1.0 - _dt_factor) * dt; Update(dt, 0); ImplicitSchemeBase::Step(dt); @@ -726,25 +659,18 @@ ImplicitNewmarkScheme::ImplicitNewmarkScheme(moordyn::Log* log, void ImplicitNewmarkScheme::Step(real& dt) { - auto r0 = r(0)->get("pos"); - auto u0 = r(0)->get("vel"); - auto r1 = r(1)->get("pos"); - auto u1 = r(1)->get("vel"); - auto drdt0 = rd(0)->get("vel"); - auto dudt0 = rd(0)->get("acc"); - auto drdt1 = rd(1)->get("vel"); - auto dudt1 = rd(1)->get("acc"); - auto drdt2 = rd(2)->get("vel"); - auto dudt2 = rd(2)->get("acc"); + auto r0 = r(0)->get(); + auto r1 = r(1)->get(); + auto drdt0 = rd(0)->get(); + auto drdt1 = rd(1)->get(); + auto drdt2 = rd(2)->get(); // Initialize the velocity and acceleration for the next time step as // the ones from the current time step drdt1 = drdt0; - dudt1 = dudt0; t += dt; drdt2 = drdt0; - dudt2 = dudt0; for (unsigned int i = 0; i < iters(); i++) { MakeNewmark(dt); Update(dt, 1); @@ -754,65 +680,69 @@ ImplicitNewmarkScheme::Step(real& dt) // We cannot relax the last step const real relax = Relax(i); drdt1 = (1.0 - relax) * drdt1 + relax * drdt2; - dudt1 = (1.0 - relax) * dudt1 + relax * dudt2; drdt2 = drdt1; - dudt2 = dudt1; } } // Apply MakeNewmark(dt); r0 = r1; - u0 = u1; drdt0 = drdt1; - dudt0 = dudt1; Update(dt, 0); SchemeBase::Step(dt); } #define MAKE_NEWMARK_VEC3(obj) { \ - auto slc = rd(0)->slicer(obj); \ - state::VarListBase acc = \ - (1.0 - _gamma) * dudt0(slc) + _gamma * dudt1(slc); \ - state::VarListBase acc_beta = \ - (0.5 - _beta) * dudt0(slc) + _beta * dudt1(slc); \ - state::VarListBase vel = drdt0(slc) + dt * acc_beta; \ - r1(slc) = r0(slc) + dt * vel; \ - u1(slc) = u0(slc) + dt * acc; \ + auto r0 = r(0)->get(obj); \ + auto r1 = r(1)->get(obj); \ + auto drdt0 = rd(0)->get(obj); \ + auto drdt1 = rd(1)->get(obj); \ + InstanceStateVar acc = (1.0 - _gamma) * drdt0.middleCols<3>(3) + \ + _gamma * drdt1.middleCols<3>(3); \ + InstanceStateVar acc_beta = (0.5 - _beta) * drdt0.middleCols<3>(3) + \ + _beta * drdt1.middleCols<3>(3); \ + InstanceStateVar vel = drdt0.leftCols<3>() + dt * acc_beta; \ + r1.leftCols<3>() = r0.leftCols<3>() + vel * dt; \ + r1.middleCols<3>(3) = r0.middleCols<3>(3) + acc * dt; \ } #define MAKE_NEWMARK_QUAT(obj) { \ - auto slc = rd(0)->indexer(obj); \ - const vec6 acc = \ - (1.0 - _gamma) * dudt0(slc) + _gamma * dudt1(slc); \ - const vec6 acc_beta = \ - (0.5 - _beta) * dudt0(slc) + _beta * dudt1(slc); \ + auto r0 = r(0)->get(obj).row(0); \ + auto r1 = r(1)->get(obj).row(0); \ + auto drdt0 = rd(0)->get(obj).row(0); \ + auto drdt1 = rd(1)->get(obj).row(0); \ + const vec6 acc = (1.0 - _gamma) * drdt0.tail<6>() + \ + _gamma * drdt1.tail<6>(); \ + const vec6 acc_beta = (1.0 - _beta) * drdt0.tail<6>() + \ + _beta * drdt1.tail<6>(); \ const vec6 vel = \ - XYZQuat::fromVec7(drdt0(slc)).toVec6() + dt * acc_beta; \ - const vec7 pos = r0(slc); \ - r1(slc) = r0(slc) + integrateVec6AsVec7(pos, dt * vel); \ - u1(slc) = u0(slc) + dt * acc; \ + XYZQuat::fromVec7(drdt0.head<7>()).toVec6() + dt * acc_beta; \ + const vec7 pos = r0.head<7>(); \ + r1.head<7>() = pos + integrateVec6AsVec7(pos, dt * vel); \ + r1.tail<6>() = vec6(r0.tail<6>()) + acc * dt; \ } void ImplicitNewmarkScheme::MakeNewmark(const real& dt) { - auto r0 = r(0)->get("pos"); - auto u0 = r(0)->get("vel"); - auto r1 = r(1)->get("pos"); - auto u1 = r(1)->get("vel"); - auto drdt0 = rd(0)->get("vel"); - auto dudt0 = rd(0)->get("acc"); - auto dudt1 = rd(1)->get("acc"); - for (auto obj : lines) MAKE_NEWMARK_VEC3(obj); - for (auto obj : points) + for (auto obj : points) { + if (obj->type != Point::FREE) + continue; MAKE_NEWMARK_VEC3(obj); - for (auto obj : rods) + } + for (auto obj : rods) { + if ((obj->type != Rod::PINNED) && (obj->type != Rod::CPLDPIN) && + (obj->type != Rod::FREE)) + continue; MAKE_NEWMARK_QUAT(obj); - for (auto obj : bodies) + } + for (auto obj : bodies) { + if ((obj->type != Body::FREE) && (obj->type != Body::CPLDPIN)) + continue; MAKE_NEWMARK_QUAT(obj); + } } ImplicitWilsonScheme::ImplicitWilsonScheme(moordyn::Log* log, @@ -833,19 +763,14 @@ ImplicitWilsonScheme::ImplicitWilsonScheme(moordyn::Log* log, void ImplicitWilsonScheme::Step(real& dt) { - auto r0 = r(0)->get("pos"); - auto u0 = r(0)->get("vel"); - auto r1 = r(1)->get("pos"); - auto u1 = r(1)->get("vel"); - auto drdt0 = rd(0)->get("vel"); - auto dudt0 = rd(0)->get("acc"); - auto drdt1 = rd(1)->get("vel"); - auto dudt1 = rd(1)->get("acc"); + auto r0 = r(0)->get(); + auto r1 = r(1)->get(); + auto drdt0 = rd(0)->get(); + auto drdt1 = rd(1)->get(); const real tdt = _theta * dt; t += tdt; drdt1 = drdt0; - dudt1 = dudt0; for (unsigned int i = 0; i < iters(); i++) { MakeWilson(tdt, tdt); Update(tdt, 1); @@ -855,9 +780,7 @@ ImplicitWilsonScheme::Step(real& dt) // We cannot relax on the last step const real relax = Relax(i); drdt0 = (1.0 - relax) * drdt0 + relax * drdt1; - dudt0 = (1.0 - relax) * dudt0 + relax * dudt1; drdt1 = drdt0; - dudt1 = dudt0; } } @@ -865,55 +788,63 @@ ImplicitWilsonScheme::Step(real& dt) t -= (1.f - _theta) * dt; MakeWilson(dt, tdt); r0 = r1; - u0 = u1; drdt0 = drdt1; - dudt0 = dudt1; Update(dt, 0); SchemeBase::Step(dt); } #define MAKE_WILSON_VEC3(obj) { \ - auto slc = rd(0)->slicer(obj); \ - state::VarListBase acc = \ - (1 - 0.5 * f) * dudt0(slc) + 0.5 * f * dudt1(slc); \ - state::VarListBase acc_tau = \ - (1 - 1.0 / 3.0 * f) * dudt0(slc) + 1.0 / 3.0 * f * dudt1(slc); \ - state::VarListBase vel = drdt0(slc) + 0.5 * dt * acc_tau; \ - r1(slc) = r0(slc) + tau * vel; \ - u1(slc) = u0(slc) + tau * acc; \ + auto r0 = r(0)->get(obj); \ + auto r1 = r(1)->get(obj); \ + auto drdt0 = rd(0)->get(obj); \ + auto drdt1 = rd(1)->get(obj); \ + InstanceStateVar acc = (1 - 0.5 * f) * drdt0.middleCols<3>(3) + \ + 0.5 * f * drdt1.middleCols<3>(3); \ + InstanceStateVar acc_tau = (1 - 1.0 / 3.0 * f) * drdt0.middleCols<3>(3) + \ + 1.0 / 3.0 * f * drdt1.middleCols<3>(3); \ + InstanceStateVar vel = drdt0.leftCols<3>() + 0.5 * dt * acc_tau; \ + r1.leftCols<3>() = r0.leftCols<3>() + vel * tau; \ + r1.middleCols<3>(3) = r0.middleCols<3>(3) + acc * tau; \ } #define MAKE_WILSON_QUAT(obj) { \ - auto slc = rd(0)->indexer(obj); \ - const vec6 acc = (1 - 0.5 * f) * dudt0(slc) + 0.5 * f * dudt1(slc); \ - const vec6 acc_tau = \ - (1 - 1.0 / 3.0 * f) * dudt0(slc) + 1.0 / 3.0 * f * dudt1(slc); \ + auto r0 = r(0)->get(obj).row(0); \ + auto r1 = r(1)->get(obj).row(0); \ + auto drdt0 = rd(0)->get(obj).row(0); \ + auto drdt1 = rd(1)->get(obj).row(0); \ + const vec6 acc = (1 - 0.5 * f) * drdt0.tail<6>() + \ + 0.5 * f * drdt1.tail<6>(); \ + const vec6 acc_tau = (1 - 1.0 / 3.0 * f) * drdt0.tail<6>() + \ + 1.0 / 3.0 * f * drdt1.tail<6>(); \ const vec6 vel = \ - XYZQuat::fromVec7(drdt0(slc)).toVec6() + 0.5 * dt * acc_tau; \ - const vec7 pos = r0(slc); \ - r1(slc) = r0(slc) + integrateVec6AsVec7(pos, tau * vel); \ - u1(slc) = u0(slc) + tau * acc; \ + XYZQuat::fromVec7(drdt0.head<7>()).toVec6() + 0.5 * dt * acc_tau; \ + const vec7 pos = r0.head<7>(); \ + r1.head<7>() = pos + integrateVec6AsVec7(pos, tau * vel); \ + r1.tail<6>() = vec6(r0.tail<6>()) + acc * tau; \ } void ImplicitWilsonScheme::MakeWilson(const real& tau, const real& dt) { const real f = tau / dt; - auto r0 = r(0)->get("pos"); - auto u0 = r(0)->get("vel"); - auto r1 = r(1)->get("pos"); - auto u1 = r(1)->get("vel"); - auto drdt0 = rd(0)->get("vel"); - auto dudt0 = rd(0)->get("acc"); - auto dudt1 = rd(1)->get("acc"); for (auto obj : lines) MAKE_WILSON_VEC3(obj); - for (auto obj : points) + for (auto obj : points) { + if (obj->type != Point::FREE) + continue; MAKE_WILSON_VEC3(obj); - for (auto obj : rods) + } + for (auto obj : rods) { + if ((obj->type != Rod::PINNED) && (obj->type != Rod::CPLDPIN) && + (obj->type != Rod::FREE)) + continue; MAKE_WILSON_QUAT(obj); - for (auto obj : bodies) + } + for (auto obj : bodies) { + if ((obj->type != Body::FREE) && (obj->type != Body::CPLDPIN)) + continue; MAKE_WILSON_QUAT(obj); + } } Scheme* diff --git a/source/Time.hpp b/source/Time.hpp index 92f410fc..7644003e 100644 --- a/source/Time.hpp +++ b/source/Time.hpp @@ -363,39 +363,10 @@ class SchemeBase : public Scheme } catch (...) { throw; } - // Build up the states and state derivatives - AS_STATE(_r[0])->addLine(obj); - AS_STATE(_r[0])->setListLength("pos", 3, obj); - AS_STATE(_r[0])->setListLength("vel", 3, obj); - for (unsigned int i = 0; i < obj->getN() - 1; i++) { - AS_STATE(_r[0])->get("pos", obj)(i) = vec::Zero(); - AS_STATE(_r[0])->get("vel", obj)(i) = vec::Zero(); - } - for (unsigned int i = 1; i < NSTATE; i++) { - AS_STATE(_r[i])->addLine(obj); - AS_STATE(_r[i])->setListLength("pos", 3, obj); - AS_STATE(_r[i])->setListLength("vel", 3, obj); - AS_STATE(_r[i])->get("pos", obj) = - AS_STATE(_r[0])->get("pos", obj); - AS_STATE(_r[i])->get("vel", obj) = - AS_STATE(_r[0])->get("vel", obj); - } - AS_STATE(_rd[0])->addLine(obj); - AS_STATE(_rd[0])->setListLength("vel", 3, obj); - AS_STATE(_rd[0])->setListLength("acc", 3, obj); - for (unsigned int i = 0; i < obj->getN() - 1; i++) { - AS_STATE(_rd[0])->get("vel", obj)(i) = vec::Zero(); - AS_STATE(_rd[0])->get("acc", obj)(i) = vec::Zero(); - } - for (unsigned int i = 1; i < NDERIV; i++) { - AS_STATE(_rd[i])->addLine(obj); - AS_STATE(_rd[i])->setListLength("vel", 3, obj); - AS_STATE(_rd[i])->setListLength("acc", 3, obj); - AS_STATE(_rd[i])->get("vel", obj) = - AS_STATE(_rd[0])->get("vel", obj); - AS_STATE(_rd[i])->get("acc", obj) = - AS_STATE(_rd[0])->get("acc", obj); - } + for (unsigned int i = 0; i < NSTATE; i++) + AS_STATE(_r[i])->addInstance(obj); + for (unsigned int i = 0; i < NDERIV; i++) + AS_STATE(_rd[i])->addInstance(obj); // Add the mask value _calc_mask.lines.push_back(true); } @@ -414,12 +385,11 @@ class SchemeBase : public Scheme } catch (...) { throw; } - for (unsigned int i = 0; i < NSTATE; i++) { - AS_STATE(_r[i])->removeLine(obj); - } - for (unsigned int i = 0; i < NDERIV; i++) { - AS_STATE(_rd[i])->removeLine(obj); - } + for (unsigned int i = 0; i < NSTATE; i++) + AS_STATE(_r[i])->removeInstance(obj); + for (unsigned int i = 0; i < NDERIV; i++) + AS_STATE(_rd[i])->removeInstance(obj); + _calc_mask.lines.erase(_calc_mask.lines.begin() + i); return i; } @@ -434,21 +404,10 @@ class SchemeBase : public Scheme } catch (...) { throw; } - // Build up the states and state derivatives - for (unsigned int i = 0; i < NSTATE; i++) { - AS_STATE(_r[i])->addPoint(obj); - AS_STATE(_r[i])->setListLength("pos", 3, obj); - AS_STATE(_r[i])->setListLength("vel", 3, obj); - AS_STATE(_r[i])->get("pos", obj)(0) = vec::Zero(); - AS_STATE(_r[i])->get("vel", obj)(0) = vec::Zero(); - } - for (unsigned int i = 0; i < NDERIV; i++) { - AS_STATE(_rd[i])->addPoint(obj); - AS_STATE(_rd[i])->setListLength("vel", 3, obj); - AS_STATE(_rd[i])->setListLength("acc", 3, obj); - AS_STATE(_rd[i])->get("vel", obj)(0) = vec::Zero(); - AS_STATE(_rd[i])->get("acc", obj)(0) = vec::Zero(); - } + for (unsigned int i = 0; i < NSTATE; i++) + AS_STATE(_r[i])->addInstance(obj); + for (unsigned int i = 0; i < NDERIV; i++) + AS_STATE(_rd[i])->addInstance(obj); // Add the mask value _calc_mask.points.push_back(true); } @@ -467,12 +426,10 @@ class SchemeBase : public Scheme } catch (...) { throw; } - for (unsigned int i = 0; i < NSTATE; i++) { - AS_STATE(_r[i])->removePoint(obj); - } - for (unsigned int i = 0; i < NDERIV; i++) { - AS_STATE(_rd[i])->removePoint(obj); - } + for (unsigned int i = 0; i < NSTATE; i++) + AS_STATE(_r[i])->removeInstance(obj); + for (unsigned int i = 0; i < NDERIV; i++) + AS_STATE(_rd[i])->removeInstance(obj); _calc_mask.points.erase(_calc_mask.points.begin() + i); return i; } @@ -488,21 +445,10 @@ class SchemeBase : public Scheme } catch (...) { throw; } - // Build up the states and state derivatives - for (unsigned int i = 0; i < NSTATE; i++) { - AS_STATE(_r[i])->addRod(obj); - AS_STATE(_r[i])->setListLength("pos", 7, obj); - AS_STATE(_r[i])->setListLength("vel", 6, obj); - AS_STATE(_r[i])->get("pos", obj)(0) = vec7::Zero(); - AS_STATE(_r[i])->get("vel", obj)(0) = vec6::Zero(); - } - for (unsigned int i = 0; i < NDERIV; i++) { - AS_STATE(_rd[i])->addRod(obj); - AS_STATE(_rd[i])->setListLength("vel", 7, obj); - AS_STATE(_rd[i])->setListLength("acc", 6, obj); - AS_STATE(_rd[i])->get("vel", obj)(0) = vec7::Zero(); - AS_STATE(_rd[i])->get("acc", obj)(0) = vec6::Zero(); - } + for (unsigned int i = 0; i < NSTATE; i++) + AS_STATE(_r[i])->addInstance(obj); + for (unsigned int i = 0; i < NDERIV; i++) + AS_STATE(_rd[i])->addInstance(obj); // Add the mask value _calc_mask.rods.push_back(true); } @@ -521,12 +467,10 @@ class SchemeBase : public Scheme } catch (...) { throw; } - for (unsigned int i = 0; i < NSTATE; i++) { - AS_STATE(_r[i])->removeRod(obj); - } - for (unsigned int i = 0; i < NDERIV; i++) { - AS_STATE(_rd[i])->removeRod(obj); - } + for (unsigned int i = 0; i < NSTATE; i++) + AS_STATE(_r[i])->removeInstance(obj); + for (unsigned int i = 0; i < NDERIV; i++) + AS_STATE(_rd[i])->removeInstance(obj); _calc_mask.rods.erase(_calc_mask.rods.begin() + i); return i; } @@ -542,21 +486,10 @@ class SchemeBase : public Scheme } catch (...) { throw; } - // Build up the states and state derivatives - for (unsigned int i = 0; i < NSTATE; i++) { - AS_STATE(_r[i])->addBody(obj); - AS_STATE(_r[i])->setListLength("pos", 7, obj); - AS_STATE(_r[i])->setListLength("vel", 6, obj); - AS_STATE(_r[i])->get("pos", obj)(0) = vec7::Zero(); - AS_STATE(_r[i])->get("vel", obj)(0) = vec6::Zero(); - } - for (unsigned int i = 0; i < NDERIV; i++) { - AS_STATE(_rd[i])->addBody(obj); - AS_STATE(_rd[i])->setListLength("vel", 7, obj); - AS_STATE(_rd[i])->setListLength("acc", 6, obj); - AS_STATE(_rd[i])->get("vel", obj)(0) = vec7::Zero(); - AS_STATE(_rd[i])->get("acc", obj)(0) = vec6::Zero(); - } + for (unsigned int i = 0; i < NSTATE; i++) + AS_STATE(_r[i])->addInstance(obj); + for (unsigned int i = 0; i < NDERIV; i++) + AS_STATE(_rd[i])->addInstance(obj); // Add the mask value _calc_mask.bodies.push_back(true); } @@ -575,19 +508,18 @@ class SchemeBase : public Scheme } catch (...) { throw; } - for (unsigned int i = 0; i < NSTATE; i++) { - AS_STATE(_r[i])->removeBody(obj); - } - for (unsigned int i = 0; i < NDERIV; i++) { - AS_STATE(_rd[i])->removeBody(obj); - } + for (unsigned int i = 0; i < NSTATE; i++) + AS_STATE(_r[i])->removeInstance(obj); + for (unsigned int i = 0; i < NDERIV; i++) + AS_STATE(_rd[i])->removeInstance(obj); _calc_mask.bodies.erase(_calc_mask.bodies.begin() + i); return i; } /** @brief Create an initial state for all the entities - * @note Just the first state is written. None of the following states, nor - * the derivatives are initialized in any way. + * @note Just the first state is written. None of the following states are + * initialized in any way. + * @note All the derivatives are nullified * @note It is assumed that the coupled entities were already initialized */ virtual void Init() @@ -602,32 +534,45 @@ class SchemeBase : public Scheme (bodies[i]->type != Body::CPLDPIN)) continue; auto [pos, vel] = bodies[i]->initialize(); - AS_STATE(_r[0])->get("pos", bodies[i])(0) = pos.toVec7(); - AS_STATE(_r[0])->get("vel", bodies[i])(0) = vel; + auto r = AS_STATE(_r[0])->get(bodies[i]); + r.row(0).head<7>() = pos.toVec7(); + r.row(0).tail<6>() = vel; + for (unsigned int j = 0; j < NDERIV; j++) + AS_STATE(_rd[j])->get(bodies[i]).setZero(); } for (unsigned int i = 0; i < rods.size(); i++) { if ((rods[i]->type != Rod::FREE) && (rods[i]->type != Rod::PINNED)) continue; auto [pos, vel] = rods[i]->initialize(); - AS_STATE(_r[0])->get("pos", rods[i])(0) = pos.toVec7(); - AS_STATE(_r[0])->get("vel", rods[i])(0) = vel; + auto r = AS_STATE(_r[0])->get(rods[i]); + r.row(0).head<7>() = pos.toVec7(); + r.row(0).tail<6>() = vel; + for (unsigned int j = 0; j < NDERIV; j++) + AS_STATE(_rd[j])->get(rods[i]).setZero(); } for (unsigned int i = 0; i < points.size(); i++) { if (points[i]->type != Point::FREE) continue; auto [pos, vel] = points[i]->initialize(); - AS_STATE(_r[0])->get("pos", points[i])(0) = pos; - AS_STATE(_r[0])->get("vel", points[i])(0) = vel; + auto r = AS_STATE(_r[0])->get(points[i]); + r.row(0) << pos.transpose(), vel.transpose(); + r.row(0).head<3>() = pos; + r.row(0).tail<3>() = vel; + for (unsigned int j = 0; j < NDERIV; j++) + AS_STATE(_rd[j])->get(points[i]).setZero(); } for (unsigned int i = 0; i < lines.size(); i++) { auto [pos, vel] = lines[i]->initialize(); + auto r = AS_STATE(_r[0])->get(lines[i]); for (unsigned int j = 0; j < pos.size(); j++) { - AS_STATE(_r[0])->get("pos", lines[i])(j) = pos[j]; - AS_STATE(_r[0])->get("vel", lines[i])(j) = vel[j]; + r.row(j).head<3>() = pos[j]; + r.row(j).segment<3>(3) = vel[j]; } + for (unsigned int j = 0; j < NDERIV; j++) + AS_STATE(_rd[j])->get(lines[i]).setZero(); } } @@ -825,16 +770,10 @@ class SchemeBase : public Scheme // points, moordyn::XYZQuat for rods and bodies), so better using lists // that we can resize on demand for (unsigned int substep = 0; substep < NSTATE; substep++) { - moordyn::state::State* state = new moordyn::state::State(log); - state->addVar("pos"); - state->addVar("vel"); - _r[substep] = state; + _r[substep] = new moordyn::state::State(log); } for (unsigned int substep = 0; substep < NDERIV; substep++) { - moordyn::state::State* state = new moordyn::state::State(log); - state->addVar("vel"); - state->addVar("acc"); - _rd[substep] = state; + _rd[substep] = new moordyn::state::State(log); } } @@ -1236,38 +1175,30 @@ class ABScheme final : public LocalSchemeBase<1, 5> for (unsigned int i = 0; i < lines.size(); i++) { if (!_calc_mask.lines[i]) continue; - AS_STATE(rd(dst))->get("vel", lines[i]) = - AS_STATE(rd(org))->get("vel", lines[i]); - AS_STATE(rd(dst))->get("acc", lines[i]) = - AS_STATE(rd(org))->get("acc", lines[i]); + AS_STATE(rd(dst))->get(lines[i]) = + AS_STATE(rd(org))->get(lines[i]); } for (unsigned int i = 0; i < points.size(); i++) { if (!_calc_mask.points[i] && (points[i]->type == Point::FREE)) continue; - AS_STATE(rd(dst))->get("vel", points[i]) = - AS_STATE(rd(org))->get("vel", points[i]); - AS_STATE(rd(dst))->get("acc", points[i]) = - AS_STATE(rd(org))->get("acc", points[i]); + AS_STATE(rd(dst))->get(points[i]) = + AS_STATE(rd(org))->get(points[i]); } for (unsigned int i = 0; i < rods.size(); i++) { if (!_calc_mask.rods[i] && ((rods[i]->type != Rod::FREE) || (rods[i]->type != Rod::PINNED))) continue; - AS_STATE(rd(dst))->get("vel", rods[i]) = - AS_STATE(rd(org))->get("vel", rods[i]); - AS_STATE(rd(dst))->get("acc", rods[i]) = - AS_STATE(rd(org))->get("acc", rods[i]); + AS_STATE(rd(dst))->get(rods[i]) = + AS_STATE(rd(org))->get(rods[i]); } for (unsigned int i = 0; i < bodies.size(); i++) { if (!_calc_mask.bodies[i] && (bodies[i]->type == Body::FREE)) continue; - AS_STATE(rd(dst))->get("vel", bodies[i]) = - AS_STATE(rd(org))->get("vel", bodies[i]); - AS_STATE(rd(dst))->get("acc", bodies[i]) = - AS_STATE(rd(org))->get("acc", bodies[i]); + AS_STATE(rd(dst))->get(bodies[i]) = + AS_STATE(rd(org))->get(bodies[i]); } } diff --git a/tests/Mooring/WD0050_Chain.txt.ic b/tests/Mooring/WD0050_Chain.txt.ic index df6467c60c59003edddf8a2eed0c3beeea5fea18..0a3b209817c1b426ff13911f5edec2dacf8e5579 100644 GIT binary patch delta 492 zcmYk3F$#k)6o!p4DQ=~uAYF}Lo2t$C z|4yr=*T!q_b@V!~s%ay3YXn`yT33`aFPS&Yd*&na*}2INS$@d!nU~Hrec1Wl)fJ7* qXXhqgF8QfU>^(BD0Bf)V2XF#KwDhH51{PoqcHjU`pfJyGzTgLN3Ub8& literal 2721 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 SDq&!z6Zv35Q7Qf3`TPSWdcmmx diff --git a/tests/Mooring/WD0600_Chain.txt.ic b/tests/Mooring/WD0600_Chain.txt.ic index 80fa52c76ef2b834b6776a7bbccd8c55e12e5226..24abc77bb899dd8208417edcd2055e93e5d4668e 100644 GIT binary patch delta 1612 zcmYk+v2N2)7>42gX<~4p1zI9X7t{{Dh)x^{d1K@P4ueOE*ci{^z48^D#ZoWhEan;* z|HaqQ)Oj0!k)r2~&^H3`#<0Q{#vc;_0jw~-g9JF^>^DZecWEw%az@y z{`a<)_qu+3x&Ps6zJ2%Q=ApTGUe4Jr*)2INIW9RJvaf9ETj@r+mF}bm=}~%;u2yos zbR*qLchbY5tN!2W{N<=|;Mh?xY9lQF@ZD)^fgdBi%}O(u4FUJxNy^ zIbXVwZlycvL3*Tpb2&dJtM*=Fz=#Pm7Id^%>@i@(gc%Du+H3Y0Fk-@t1s$zsj{zel z%vjLTPS|6Z{oMaN`7}SD#Py5?9qkQ!3>YzC#)6LamOTcHm@s2OM>}PY0V5{NSkTeV z*kizm2{RUSw0G<=V8nzO3p(0+_82f?!i)tS?E`xZ7%^eSf{u319s@>9n6aRvU9iW1 c5ff%C=x86=W59?BGZu8T-QW82U;FjmC*aw@tN;K2 literal 10977 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 zkLlEclose(); \ delete log; -TEST_CASE("Scalar state var generation") +TEST_CASE("State generation") { SYS_STARTER @@ -86,141 +86,23 @@ TEST_CASE("Scalar state var generation") auto l = CreateLine(log, env, 20, outfile); md::state::State state(log); - state.addPoint(p1); - state.addPoint(p2); - state.addLine(l); - state.addVar("scalar_by_type"); - state.addVar("scalar_by_enum", md::state::VarBase::types::REAL); - - REQUIRE(state.get("scalar_by_type").rows() == 21); - REQUIRE(state.get("scalar_by_enum").rows() == 21); - - bool catched = false; - try { - state.get("invalid_name"); - } catch (const md::invalid_value_error& e) { - catched = true; - } - REQUIRE(catched); - catched = false; - try { - state.get("scalar_by_type"); - } catch (const md::invalid_type_error& e) { - catched = true; - } - REQUIRE(catched); - - delete p1; - delete p2; - delete l; - SYS_KILLER -} - -TEST_CASE("State var setting") -{ - SYS_STARTER - - auto p1 = CreatePoint(log, env); - auto p2 = CreatePoint(log, env); - auto l = CreateLine(log, env, 20, outfile); - - md::state::State state(log); - state.addPoint(p1); - state.addPoint(p2); - state.addLine(l); - state.addVar("a"); - state.addVar("b"); - - // Set the full state vars - Eigen::Matrix ma; - Eigen::Matrix mb; - for (unsigned int i = 0; i < 21; i++) { - ma(i) = md::vec::Zero(); - mb(i) = md::vec(i, 2*i, 4*i); - } - // They can be set with the set method: - state.set("a", mb); - state.set("b", ma); - REQUIRE(state.get("a").rows() == mb.rows()); - REQUIRE(state.get("b").rows() == ma.rows()); - // Comparisons should be made element by element - for (unsigned int i = 0; i < mb.rows(); i++) { - REQUIRE(allclose(state.get("a")(i), mb(i))); - } - for (unsigned int i = 0; i < ma.rows(); i++) { - REQUIRE(allclose(state.get("b")(i), ma(i))); - } - // Or they can be set with the = operator - state.get("a") = ma; - state.get("b") = mb; - REQUIRE(state.get("a").rows() == ma.rows()); - REQUIRE(state.get("b").rows() == mb.rows()); - for (unsigned int i = 0; i < ma.rows(); i++) { - REQUIRE(allclose(state.get("a")(i), ma(i))); - } - for (unsigned int i = 0; i < mb.rows(); i++) { - REQUIRE(allclose(state.get("b")(i), mb(i))); - } - - // The same can be done with just a subpart of the state var: - state.set("a", l, state.get("b", l)); - // Internally, the state var is storing first the lines and then the points - for (unsigned int i = 0; i < 19; i++) { - REQUIRE(allclose(state.get("a")(i), mb(i))); - } - for (unsigned int i = 19; i < 21; i++) { - REQUIRE(allclose(state.get("a")(i), ma(i))); - } - // If we modify a, b should remain the same - state.get("a")(1) = md::vec::Zero(); - REQUIRE(!allclose(state.get("a")(1), state.get("b")(1))); - // And again, we can use the = operator to set state var subparts: - state.get("a", l) = state.get("b", l); - for (unsigned int i = 0; i < 19; i++) { - REQUIRE(allclose(state.get("a")(i), mb(i))); - } - for (unsigned int i = 19; i < 21; i++) { - REQUIRE(allclose(state.get("a")(i), ma(i))); - } - - delete p1; - delete p2; - delete l; - SYS_KILLER -} - -TEST_CASE("State var operations") -{ - SYS_STARTER - - auto p1 = CreatePoint(log, env); - auto p2 = CreatePoint(log, env); - auto l = CreateLine(log, env, 20, outfile); - - md::state::State state(log); - state.addPoint(p1); - state.addPoint(p2); - state.addLine(l); - state.addVar("a"); - state.addVar("b"); - state.addVar("c"); - - Eigen::Matrix ma; - Eigen::Matrix mb; - for (unsigned int i = 0; i < 21; i++) { - ma(i) = md::vec(0.5 * i, i, 2 * i); - mb(i) = md::vec(i, 2 * i, 4 * i); - } - state.set("a", ma); - state.get("b") = mb; - - REQUIRE(state.get("a").rows() == ma.rows()); - REQUIRE(state.get("b").rows() == mb.rows()); - - state.set("c", state.get("a") + state.get("b")); - for (unsigned int i = 0; i < ma.rows(); i++) { - REQUIRE(allclose(state.get("c")(i), ma(i) + mb(i))); - } + state.addInstance(p1); + state.addInstance(p2); + state.addInstance(l); + + REQUIRE(state.get().rows() == 3); + REQUIRE(state.get()(0).rows() == 1); + REQUIRE(state.get()(0).cols() == 6); + REQUIRE(state.get()(1).rows() == 1); + REQUIRE(state.get()(1).cols() == 6); + REQUIRE(state.get()(2).rows() == 19); + REQUIRE(state.get()(2).cols() == 6); + REQUIRE(state.get(p1).rows() == 1); + REQUIRE(state.get(p1).cols() == 6); + REQUIRE(state.get(p2).rows() == 1); + REQUIRE(state.get(p2).cols() == 6); + REQUIRE(state.get(l).rows() == 19); + REQUIRE(state.get(l).cols() == 6); delete p1; delete p2; @@ -228,7 +110,7 @@ TEST_CASE("State var operations") SYS_KILLER } -TEST_CASE("List var") +TEST_CASE("State initializing") { SYS_STARTER @@ -236,37 +118,28 @@ TEST_CASE("List var") auto p2 = CreatePoint(log, env); auto l = CreateLine(log, env, 20, outfile); - md::state::State state(log); - state.addPoint(p1); - state.addPoint(p2); - state.addLine(l); - state.addVar("a"); - state.addVar("b"); - state.addVar("c"); - - // We just fill the lists for the line, with 4 elements each - Eigen::Matrix ma; - Eigen::Matrix mb; + md::state::State r(log); + r.addInstance(p1); + r.addInstance(p2); + r.addInstance(l); + + // Set the data + r.get(p1).row(0).head<3>() = md::vec::Zero(); + r.get(p1).row(0).tail<3>() = md::vec::Zero(); + r.get(p2).row(0).head<3>() = md::vec(1.0, 2.0, 3.0); + r.get(p2).row(0).tail<3>() = md::vec(4.0, 5.0, 6.0); + md::real norm = 0.0; for (unsigned int i = 0; i < 19; i++) { - ma(i).resize(4); - ma(i) = Eigen::Matrix(0.5 * i, i, 2 * i, 4 * i); - mb(i).resize(4); - mb(i) = Eigen::Matrix(i, 2 * i, 4 * i, 8 * i); + norm += md::vec6(i, 2*i, 4*i, 5*i, 6*i, 7*i).squaredNorm(); + r.get(l).row(i).head<3>() = md::vec(i, 2*i, 4*i); + r.get(l).row(i).tail<3>() = md::vec(5*i, 6*i, 7*i); } - - state.setListLength("a", 4, l); - state.setListLength("b", 4, l); - state.setListLength("c", 4, l); - state.get("a", l) = ma; - state.get("b", l) = mb; + norm = sqrt(norm); - REQUIRE(state.get("a").rows() == state.get("b").rows()); - - state.get("c") = - state.get("a") + state.get("b"); - for (unsigned int i = 0; i < 19; i++) { - REQUIRE(allclose(state.get("c")(i), ma(i) + mb(i))); - } + REQUIRE(r.get(p1).norm() == 0.0); + REQUIRE(isclose(r.get(p2).norm(), + md::vec6(1.0, 2.0, 3.0, 4.0, 5.0, 6.0).norm())); + REQUIRE(isclose(r.get(l).norm(), norm)); delete p1; delete p2; @@ -274,7 +147,7 @@ TEST_CASE("List var") SYS_KILLER } -TEST_CASE("Serialization") +TEST_CASE("Operating states") { SYS_STARTER @@ -282,55 +155,34 @@ TEST_CASE("Serialization") auto p2 = CreatePoint(log, env); auto l = CreateLine(log, env, 20, outfile); - md::state::State state_in(log), state_out(log); - state_in.addPoint(p1); - state_in.addPoint(p2); - state_in.addLine(l); - state_out.addPoint(p1); - state_out.addPoint(p2); - state_out.addLine(l); - - state_in.addVar("a"); - state_in.setListLength("a", 4, l); - state_in.addVar("b"); - state_out.addVar("a"); - state_out.setListLength("a", 4, l); - state_out.addVar("b"); - - // We just fill the lists for the line, with 4 elements each - Eigen::Matrix va_in; - Eigen::Matrix va_out; + md::state::State r(log), r2(log), r3(log); + r.addInstance(p1); + r.addInstance(p2); + r.addInstance(l); + r2.addInstance(p1); + r2.addInstance(p2); + r2.addInstance(l); + r3.addInstance(p1); + r3.addInstance(p2); + r3.addInstance(l); + + // Set the data + r.get(p1).row(0).head<3>() = md::vec::Zero(); + r.get(p1).row(0).tail<3>() = md::vec::Zero(); + r.get(p2).row(0).head<3>() = md::vec(1.0, 2.0, 3.0); + r.get(p2).row(0).tail<3>() = md::vec(4.0, 5.0, 6.0); for (unsigned int i = 0; i < 19; i++) { - va_in(i).resize(4); - va_in(i) = Eigen::Matrix(0.5 * i, i, 2 * i, 4 * i); - va_out(i).resize(4); - va_out(i) = Eigen::Matrix::Zero(); + r.get(l).row(i).head<3>() = md::vec(i, 2*i, 4*i); + r.get(l).row(i).tail<3>() = md::vec(5*i, 6*i, 7*i); } - state_in.get("a", l) = va_in; - state_out.get("a", l) = va_out; - for (unsigned int i = 0; i < 21; i++) { - state_in.get("b")(i) = md::vec6( - i, 2 * i, 4 * i, 8 * i, 16 * i, 32 * i); - state_out.get("b")(i) = md::vec6::Zero(); - } + r2.get() = r.get() * 2.0; + r3.get() = (r.get() - r2.get()); + r.get() += r3.get(); - auto data_saved = state_in.Serialize(); - state_out.Deserialize(data_saved.data()); - - REQUIRE(state_out.get("a").rows() == - state_in.get("a").rows()); - REQUIRE(state_out.get("b").rows() == - state_in.get("b").rows()); - - for (unsigned int i = 0; i < 21; i++) { - REQUIRE(allclose(state_out.get("a")(i), - state_in.get("a")(i))); - } - for (unsigned int i = 0; i < 21; i++) { - REQUIRE(allclose(state_out.get("b")(i), - state_in.get("b")(i))); - } + REQUIRE(r.get(p1).norm() == 0.0); + REQUIRE(r.get(p2).norm() == 0.0); + REQUIRE(r.get(l).norm() == 0.0); delete p1; delete p2; diff --git a/wrappers/python/moordyn/moorpyic.py b/wrappers/python/moordyn/moorpyic.py index b009fc5e..6b951ccd 100644 --- a/wrappers/python/moordyn/moorpyic.py +++ b/wrappers/python/moordyn/moorpyic.py @@ -48,25 +48,18 @@ def moorpy_export(moorpy_system, filename, out.write(b'MoorDyn') out.write(struct.pack(' Date: Thu, 6 Mar 2025 06:21:02 +0100 Subject: [PATCH 13/16] fix(core): Use non-static methods that are CLang templates compliant --- source/Time.hpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/source/Time.hpp b/source/Time.hpp index 7644003e..c624b14b 100644 --- a/source/Time.hpp +++ b/source/Time.hpp @@ -534,9 +534,8 @@ class SchemeBase : public Scheme (bodies[i]->type != Body::CPLDPIN)) continue; auto [pos, vel] = bodies[i]->initialize(); - auto r = AS_STATE(_r[0])->get(bodies[i]); - r.row(0).head<7>() = pos.toVec7(); - r.row(0).tail<6>() = vel; + AS_STATE(_r[0])->get(bodies[i]).row(0).head<7>() = pos.toVec7(); + AS_STATE(_r[0])->get(bodies[i]).row(0).tail<6>() = vel; for (unsigned int j = 0; j < NDERIV; j++) AS_STATE(_rd[j])->get(bodies[i]).setZero(); } @@ -545,9 +544,8 @@ class SchemeBase : public Scheme if ((rods[i]->type != Rod::FREE) && (rods[i]->type != Rod::PINNED)) continue; auto [pos, vel] = rods[i]->initialize(); - auto r = AS_STATE(_r[0])->get(rods[i]); - r.row(0).head<7>() = pos.toVec7(); - r.row(0).tail<6>() = vel; + AS_STATE(_r[0])->get(rods[i]).row(0).head<7>() = pos.toVec7(); + AS_STATE(_r[0])->get(rods[i]).row(0).tail<6>() = vel; for (unsigned int j = 0; j < NDERIV; j++) AS_STATE(_rd[j])->get(rods[i]).setZero(); } @@ -556,20 +554,17 @@ class SchemeBase : public Scheme if (points[i]->type != Point::FREE) continue; auto [pos, vel] = points[i]->initialize(); - auto r = AS_STATE(_r[0])->get(points[i]); - r.row(0) << pos.transpose(), vel.transpose(); - r.row(0).head<3>() = pos; - r.row(0).tail<3>() = vel; + AS_STATE(_r[0])->get(points[i]).row(0).head<3>() = pos; + AS_STATE(_r[0])->get(points[i]).row(0).tail<3>() = vel; for (unsigned int j = 0; j < NDERIV; j++) AS_STATE(_rd[j])->get(points[i]).setZero(); } for (unsigned int i = 0; i < lines.size(); i++) { auto [pos, vel] = lines[i]->initialize(); - auto r = AS_STATE(_r[0])->get(lines[i]); for (unsigned int j = 0; j < pos.size(); j++) { - r.row(j).head<3>() = pos[j]; - r.row(j).segment<3>(3) = vel[j]; + AS_STATE(_r[0])->get(lines[i]).row(j).head<3>() = pos[j]; + AS_STATE(_r[0])->get(lines[i]).row(j).segment<3>(3) = vel[j]; } for (unsigned int j = 0; j < NDERIV; j++) AS_STATE(_rd[j])->get(lines[i]).setZero(); From 1f0ad2e9e53af0e3decd527d5a911c228056226a Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Thu, 6 Mar 2025 06:51:59 +0100 Subject: [PATCH 14/16] fix(core): Initialize the state variables as zeroes by default --- source/State.cpp | 1 + source/Time.cpp | 3 ++- source/Time.hpp | 29 +++++++++++++++-------------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/source/State.cpp b/source/State.cpp index 149d80f1..481f2984 100644 --- a/source/State.cpp +++ b/source/State.cpp @@ -47,6 +47,7 @@ State::addInstance(moordyn::Instance* obj) new_var(i) = _var(i); } InstanceStateVar obj_var(obj->stateN(), obj->stateDims()); + obj_var.setZero(); new_var(_objs.size()) = obj_var; _var = new_var; _objs.push_back(obj); diff --git a/source/Time.cpp b/source/Time.cpp index 181d5754..27884f9a 100644 --- a/source/Time.cpp +++ b/source/Time.cpp @@ -423,7 +423,8 @@ HeunScheme::Step(real& dt) Update(0.0, 0); CalcStateDeriv(0); // Correct the integration - r0 += 0.5 * dt * (drdt0 - drdt1); + r0 += (0.5 * dt) * (drdt0 - drdt1); + t += dt; Update(dt, 0); SchemeBase::Step(dt); diff --git a/source/Time.hpp b/source/Time.hpp index c624b14b..da60d2a7 100644 --- a/source/Time.hpp +++ b/source/Time.hpp @@ -517,9 +517,8 @@ class SchemeBase : public Scheme } /** @brief Create an initial state for all the entities - * @note Just the first state is written. None of the following states are - * initialized in any way. - * @note All the derivatives are nullified + * @note Just the first state is written. None of the following states nor + * the derivatives are initialized in any way. * @note It is assumed that the coupled entities were already initialized */ virtual void Init() @@ -534,18 +533,18 @@ class SchemeBase : public Scheme (bodies[i]->type != Body::CPLDPIN)) continue; auto [pos, vel] = bodies[i]->initialize(); - AS_STATE(_r[0])->get(bodies[i]).row(0).head<7>() = pos.toVec7(); - AS_STATE(_r[0])->get(bodies[i]).row(0).tail<6>() = vel; - for (unsigned int j = 0; j < NDERIV; j++) - AS_STATE(_rd[j])->get(bodies[i]).setZero(); + auto r = AS_STATE(_r[0])->get(bodies[i]); + r.row(0).head(7) = pos.toVec7(); + r.row(0).tail(6) = vel; } for (unsigned int i = 0; i < rods.size(); i++) { if ((rods[i]->type != Rod::FREE) && (rods[i]->type != Rod::PINNED)) continue; auto [pos, vel] = rods[i]->initialize(); - AS_STATE(_r[0])->get(rods[i]).row(0).head<7>() = pos.toVec7(); - AS_STATE(_r[0])->get(rods[i]).row(0).tail<6>() = vel; + auto r = AS_STATE(_r[0])->get(rods[i]); + r.row(0).head(7) = pos.toVec7(); + r.row(0).tail(6) = vel; for (unsigned int j = 0; j < NDERIV; j++) AS_STATE(_rd[j])->get(rods[i]).setZero(); } @@ -554,17 +553,19 @@ class SchemeBase : public Scheme if (points[i]->type != Point::FREE) continue; auto [pos, vel] = points[i]->initialize(); - AS_STATE(_r[0])->get(points[i]).row(0).head<3>() = pos; - AS_STATE(_r[0])->get(points[i]).row(0).tail<3>() = vel; + auto r = AS_STATE(_r[0])->get(points[i]); + r.row(0).head(3) = pos; + r.row(0).tail(3) = vel; for (unsigned int j = 0; j < NDERIV; j++) AS_STATE(_rd[j])->get(points[i]).setZero(); } for (unsigned int i = 0; i < lines.size(); i++) { auto [pos, vel] = lines[i]->initialize(); + auto r = AS_STATE(_r[0])->get(lines[i]); for (unsigned int j = 0; j < pos.size(); j++) { - AS_STATE(_r[0])->get(lines[i]).row(j).head<3>() = pos[j]; - AS_STATE(_r[0])->get(lines[i]).row(j).segment<3>(3) = vel[j]; + r.row(j).head(3) = pos[j]; + r.row(j).segment(3, 3) = vel[j]; } for (unsigned int j = 0; j < NDERIV; j++) AS_STATE(_rd[j])->get(lines[i]).setZero(); @@ -616,7 +617,7 @@ class SchemeBase : public Scheme { if (i >= NSTATE) { LOGERR << "State " << i << " cannot be setted on a '" << name - << "' scheme that has " << NSTATE << "states" + << "' scheme that has " << NSTATE << " states" << endl; throw moordyn::invalid_value_error("Invalid state"); } From 957973efe68f9c91fc8ec44882d2cc10b8d06b3e Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Thu, 6 Mar 2025 07:14:38 +0100 Subject: [PATCH 15/16] fix(core): StateVarRef not used anymore --- source/Line.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/source/Line.hpp b/source/Line.hpp index 313a5f8b..a229f210 100644 --- a/source/Line.hpp +++ b/source/Line.hpp @@ -51,7 +51,6 @@ namespace moordyn { class Waves; typedef std::shared_ptr WavesRef; -typedef Eigen::Ref> StateVarRef; /** @class Line Line.hpp * @brief A mooring line From cceaff465aabd706c53d7bf68c1629ea6a58eaba Mon Sep 17 00:00:00 2001 From: Jose Luis Cercos-Pita Date: Mon, 17 Mar 2025 09:07:25 +0100 Subject: [PATCH 16/16] fix(core): Give a common Instance::initialize() method --- source/Body.hpp | 18 +++++++++++++++--- source/Instance.hpp | 7 +++++++ source/Line.hpp | 14 ++++++++++++++ source/Point.hpp | 11 +++++++++++ source/Rod.hpp | 11 +++++++++++ source/Time.hpp | 24 ++++++------------------ 6 files changed, 64 insertions(+), 21 deletions(-) diff --git a/source/Body.hpp b/source/Body.hpp index d111e4e2..60c61f6f 100644 --- a/source/Body.hpp +++ b/source/Body.hpp @@ -255,15 +255,27 @@ class DECLDIR Body final : public Instance, public SuperCFL */ inline vec6 getUnfreeVel() const { return rd_ves; } - /** @brief Initialize the FREE point state + /** @brief Initialize a free instance + * @param r The output state variable * @return The 6-dof position (first) and the 6-dof velocity (second) - * @throw moordyn::invalid_value_error If the body is not of type - * moordyn::Body::FREE + * @throw moordyn::invalid_value_error If the instance does not have free + * states. e.g. a coupled body controlled from outside * @throws moordyn::output_file_error If an outfile has been provided, but * it cannot be written + * @{ */ std::pair initialize(); + inline void initialize(InstanceStateVarView r) + { + const auto [pos, vel] = initialize(); + r.row(0).head<7>() = pos.toVec7(); + r.row(0).tail<6>() = vel; + } + /** + * @} + */ + /** @brief Initialize the free body * * Those are the bodies with type moordyn::Body::FREE diff --git a/source/Instance.hpp b/source/Instance.hpp index 609b0ec0..ee3a93ad 100644 --- a/source/Instance.hpp +++ b/source/Instance.hpp @@ -75,6 +75,13 @@ class DECLDIR Instance : public io::IO */ inline const size_t id() const { return _id; } + /** @brief Initialize a free instance + * @param r The output state variable + * @throw moordyn::invalid_value_error If the instance does not have free + * states. e.g. a coupled body controlled from outside + */ + virtual void initialize(InstanceStateVarView r) = 0; + /** @brief Set the state * @param r The state variable */ diff --git a/source/Line.hpp b/source/Line.hpp index a229f210..d59e9e3f 100644 --- a/source/Line.hpp +++ b/source/Line.hpp @@ -343,14 +343,28 @@ class DECLDIR Line final : public Instance, public NatFreqCFL } /** @brief Compute the stationary Initial Condition (IC) + * @param r The output state variable * @return The states, i.e. the positions of the internal nodes * (first) and the velocities of the internal nodes (second) * @throws moordyn::output_file_error If an outfile has been provided, but * it cannot be written * @throws invalid_value_error If there is no enough water depth + * @{ */ std::pair, std::vector> initialize(); + inline void initialize(InstanceStateVarView r) + { + const auto [pos, vel] = initialize(); + for (unsigned int i = 0; i < N - 1; i++) { + r.row(i).head<3>() = pos[i]; + r.row(i).segment<3>(3) = vel[i]; + } + } + /** + * @} + */ + /** @brief Number of segments * * The number of nodes can be computed as moordyn::Line::getN() + 1 diff --git a/source/Point.hpp b/source/Point.hpp index d4952364..ec999161 100644 --- a/source/Point.hpp +++ b/source/Point.hpp @@ -243,11 +243,22 @@ class DECLDIR Point final : public Instance, public SuperCFL inline std::vector getLines() const { return attached; } /** @brief Initialize the FREE point state + * @param r The output state variable * @return The position (first) and the velocity (second) * @throws moordyn::invalid_value_error If it is not a FREE point */ std::pair initialize(); + inline void initialize(InstanceStateVarView r) + { + const auto [pos, vel] = initialize(); + r.row(0).head<3>() = pos; + r.row(0).tail<3>() = vel; + } + /** + * @} + */ + /** @brief Get the point position * @return The position [x,y,z] */ diff --git a/source/Rod.hpp b/source/Rod.hpp index afed1c01..bb0b7795 100644 --- a/source/Rod.hpp +++ b/source/Rod.hpp @@ -336,6 +336,7 @@ class DECLDIR Rod final : public Instance, public SuperCFL inline void openoutput(); /** @brief Initialize the rod state + * @param r The output state variable * @return The position and orientation angles (first) and the linear and * angular velocity (second) * @note moordyn::Rod::r6 and moordyn::Rod::v6 must already be set @@ -346,6 +347,16 @@ class DECLDIR Rod final : public Instance, public SuperCFL */ std::pair initialize(); + inline void initialize(InstanceStateVarView r) + { + const auto [pos, vel] = initialize(); + r.row(0).head<7>() = pos.toVec7(); + r.row(0).tail<6>() = vel; + } + /** + * @} + */ + /** @brief Number of segments * * The number of nodes can be computed as moordyn::Rod::getN() + 1 diff --git a/source/Time.hpp b/source/Time.hpp index da60d2a7..9b06e7af 100644 --- a/source/Time.hpp +++ b/source/Time.hpp @@ -532,19 +532,15 @@ class SchemeBase : public Scheme if ((bodies[i]->type != Body::FREE) && (bodies[i]->type != Body::CPLDPIN)) continue; - auto [pos, vel] = bodies[i]->initialize(); - auto r = AS_STATE(_r[0])->get(bodies[i]); - r.row(0).head(7) = pos.toVec7(); - r.row(0).tail(6) = vel; + bodies[i]->initialize(AS_STATE(_r[0])->get(bodies[i])); + for (unsigned int j = 0; j < NDERIV; j++) + AS_STATE(_rd[j])->get(bodies[i]).setZero(); } for (unsigned int i = 0; i < rods.size(); i++) { if ((rods[i]->type != Rod::FREE) && (rods[i]->type != Rod::PINNED)) continue; - auto [pos, vel] = rods[i]->initialize(); - auto r = AS_STATE(_r[0])->get(rods[i]); - r.row(0).head(7) = pos.toVec7(); - r.row(0).tail(6) = vel; + rods[i]->initialize(AS_STATE(_r[0])->get(rods[i])); for (unsigned int j = 0; j < NDERIV; j++) AS_STATE(_rd[j])->get(rods[i]).setZero(); } @@ -552,21 +548,13 @@ class SchemeBase : public Scheme for (unsigned int i = 0; i < points.size(); i++) { if (points[i]->type != Point::FREE) continue; - auto [pos, vel] = points[i]->initialize(); - auto r = AS_STATE(_r[0])->get(points[i]); - r.row(0).head(3) = pos; - r.row(0).tail(3) = vel; + points[i]->initialize(AS_STATE(_r[0])->get(points[i])); for (unsigned int j = 0; j < NDERIV; j++) AS_STATE(_rd[j])->get(points[i]).setZero(); } for (unsigned int i = 0; i < lines.size(); i++) { - auto [pos, vel] = lines[i]->initialize(); - auto r = AS_STATE(_r[0])->get(lines[i]); - for (unsigned int j = 0; j < pos.size(); j++) { - r.row(j).head(3) = pos[j]; - r.row(j).segment(3, 3) = vel[j]; - } + lines[i]->initialize(AS_STATE(_r[0])->get(lines[i])); for (unsigned int j = 0; j < NDERIV; j++) AS_STATE(_rd[j])->get(lines[i]).setZero(); }