diff --git a/CMakeLists.txt b/CMakeLists.txt index a252cd83..5a7c7e31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10) set(MOORDYN_MAJOR_VERSION 2) -set(MOORDYN_MINOR_VERSION 6) -set(MOORDYN_PATCH_VERSION 2) +set(MOORDYN_MINOR_VERSION 7) +set(MOORDYN_PATCH_VERSION 0) set(MOORDYN_VERSION ${MOORDYN_MAJOR_VERSION}.${MOORDYN_MINOR_VERSION}) project(Moordyn VERSION ${MOORDYN_VERSION}) diff --git a/source/IO.cpp b/source/IO.cpp index fdb2ff4d..b4ef2131 100644 --- a/source/IO.cpp +++ b/source/IO.cpp @@ -278,11 +278,6 @@ IO::LoadFile(const std::string filepath) const uint8_t major, minor; f.read((char*)&major, sizeof(uint8_t)); f.read((char*)&minor, sizeof(uint8_t)); - std::cout << major << std::endl; - std::cout << minor << std::endl; - std::cout << _min_major_version << std::endl; - std::cout << _min_minor_version << std::endl; - std::cout << "number=" << 7 << std::endl; if ((major < _min_major_version) || ((major == _min_major_version) && (minor < _min_minor_version))) { LOGERR << "The file '" << filepath << "' was written by MoorDyn " diff --git a/source/MoorDyn2.cpp b/source/MoorDyn2.cpp index 5bd39417..5dd27159 100644 --- a/source/MoorDyn2.cpp +++ b/source/MoorDyn2.cpp @@ -795,6 +795,41 @@ moordyn::MoorDyn::Step(const double* x, return MOORDYN_SUCCESS; } +void +MoorDyn::BreakLine(moordyn::Point* point, moordyn::Line* line) +{ + // Detach the line from the point + moordyn::EndPoints end_point; + auto pt_attachments = point->getLines(); + for (auto attachment : pt_attachments) { + if (attachment.line == line) { + end_point = attachment.end_point; + } + } + point->removeLine(line); + // Create the new point + moordyn::Point *pt = new Point(point, PointList.size()); + PointList.push_back(pt); + waves->addPoint(pt); + _t_integrator->AddPoint(pt); + // Set the state of the point + auto state = _t_integrator->r(0); + pt->initialize(state->get(pt)); + for (unsigned int i = 1; i < _t_integrator->GetNState(); i++) { + _t_integrator->r(i)->get(pt) = state->get(pt); + } + for (unsigned int i = 0; i < _t_integrator->GetNDeriv(); i++) { + _t_integrator->rd(i)->get(pt).row(0)(Eigen::seqN(0, 3)) = + state->get(pt).row(0)(Eigen::seqN(3, 3)); + // NOTE: Although when cloning free points we can actually get the + // acceleration, I (Jose Luis Cercos-Pita) do not think is worthy, so + // I am simply setting no acceleration + _t_integrator->rd(i)->get(pt).row(0)(Eigen::seqN(3, 3)) = vec::Zero(); + } + // Attach the line to the point + pt->addLine(line, end_point); +} + std::vector MoorDyn::Serialize(void) { @@ -1155,8 +1190,7 @@ moordyn::MoorDyn::ReadInFile() env->WtrDpth = -r0[2]; LOGWRN << "\t Water depth set to point " << PointList.size() + 1 << " z position because point was specified below the " - "seabed" - << endl; + "seabed" << endl; } // Check point ID is sequential starting from 1 @@ -2896,6 +2930,28 @@ MoorDyn_GetFASTtens(MoorDyn system, return MOORDYN_SUCCESS; } +int DECLDIR +MoorDyn_BreakLine(MoorDyn system, + MoorDynPoint point, + MoorDynLine line) +{ + CHECK_SYSTEM(system); + + moordyn::error_id err = MOORDYN_SUCCESS; + string err_msg; + try { + ((moordyn::MoorDyn*)system)->BreakLine((moordyn::Point*)point, + (moordyn::Line*)line); + } + MOORDYN_CATCHER(err, err_msg); + if (err != MOORDYN_SUCCESS) { + cerr << "Error (" << err << ") at " << __FUNC_NAME__ << "():" << endl + << err_msg << endl; + return err; + } + return MOORDYN_SUCCESS; +} + int DECLDIR MoorDyn_GetDt(MoorDyn system, double* dt) { diff --git a/source/MoorDyn2.h b/source/MoorDyn2.h index 8d1f20e7..4677c29c 100644 --- a/source/MoorDyn2.h +++ b/source/MoorDyn2.h @@ -435,6 +435,21 @@ MoorDyn_GetFASTtens(MoorDyn system, float AnchHTen[], float AnchVTen[]); +/** @brief Break a line on a specific point + * + * This method is dettaching a line from the point, and in exchange is + * creating a point duplicate of type moordyn::Point::types::FREE + * @param system The Moordyn system + * @param point The discconection point + * @param line The line that is dettaching from the point + * @throw MOORDYN_SUCESS If the line is correctly dettached, an error code + * otherwise (see @ref moordyn_errors) + */ +int DECLDIR +MoorDyn_BreakLine(MoorDyn system, + MoorDynPoint point, + MoorDynLine line); + /** @brief Get the current model time step * @param system The Moordyn system * @param dt The output time step diff --git a/source/MoorDyn2.hpp b/source/MoorDyn2.hpp index d1cc22cc..7aea6396 100644 --- a/source/MoorDyn2.hpp +++ b/source/MoorDyn2.hpp @@ -268,6 +268,17 @@ class MoorDyn final : public io::IO waves->setWaveKinematics(U, Ud); } + /** @brief Break a line on a specific point + * + * This method is dettaching a line from the point, and in exchange is + * creating a point duplicate of type moordyn::Point::types::FREE + * @param point The discconection point + * @param line The line that is dettaching from the point + * @throw moordyn::invalid_value_error If the line is not attached to the + * point. + */ + void BreakLine(moordyn::Point* point, moordyn::Line* line); + /** @brief Produce the packed data to be saved * * The produced data can be used afterwards to restore the saved information diff --git a/source/Point.cpp b/source/Point.cpp index 06c3db10..1819db2d 100644 --- a/source/Point.cpp +++ b/source/Point.cpp @@ -38,12 +38,36 @@ namespace moordyn { Point::Point(moordyn::Log* log, size_t id) : Instance(log) + , waves(nullptr) , seafloor(nullptr) , pointId(id) { vtk.set_binary(); } +Point::Point(Point* visitor, size_t id) + : Instance(visitor->GetLogger()) + , env(visitor->env) + , waves(visitor->waves) + , seafloor(visitor->seafloor) + , pointM(visitor->pointM) + , pointV(visitor->pointV) + , pointF(visitor->pointF) + , pointCdA(visitor->pointCdA) + , pointCa(visitor->pointCa) + , r(visitor->r) + , rd(visitor->rd) + , Fnet(visitor->Fnet) + , t(visitor->t) + , M(visitor->M) + , acc(visitor->acc) + , pointId(id) + , number(id + 1) + , type(types::FREE) +{ + vtk.set_binary(); +} + Point::~Point() {} void @@ -150,7 +174,9 @@ Point::initialize() seafloor ? seafloor->getDepthAt(r[0], r[1]) : -env->WtrDpth; if (waterDepth > r[2]) { LOGERR << "Error: water depth is shallower than Point " << number - << "." << endl; + << "." << endl + << "\tseabed z = " << waterDepth << endl + << "\tpoint = " << r << endl; throw moordyn::invalid_value_error("Invalid water depth"); } } diff --git a/source/Point.hpp b/source/Point.hpp index d1304e26..d984ecb6 100644 --- a/source/Point.hpp +++ b/source/Point.hpp @@ -74,6 +74,15 @@ class DECLDIR Point final */ Point(moordyn::Log* log, size_t id); + /** @brief Point duplicator + * + * The duplicated point will have the same dynamics of the original point. + * It will be a ::types::FREE kind of point though + * @param visitor Point to duplicate + * @param id Unique identifier of this instance + */ + Point(Point* visitor, size_t id); + /** @brief Destructor */ ~Point(); diff --git a/source/Time.hpp b/source/Time.hpp index 9ef9a27b..764c5165 100644 --- a/source/Time.hpp +++ b/source/Time.hpp @@ -256,11 +256,26 @@ class Scheme : public io::IO */ virtual void Step(real& dt) { t_local += dt; }; + /** @brief Get the number of state variables + * @return The number of state variables + */ + virtual const unsigned int GetNState() const = 0; + + /** @brief Get the number of state derivative variables + * @return The number of state derivative variables + */ + virtual const unsigned int GetNDeriv() const = 0; + /** @brief Get the state variable * @param i The index of the state variable to take * @return The state variable + * @{ */ virtual moordyn::state::State GetState(unsigned int i = 0) = 0; + virtual moordyn::state::State* r(unsigned int i = 0) = 0; + /** + * @} + */ /** @brief Resume the simulation from the stationary solution * @param state The stationary solution @@ -288,6 +303,17 @@ class Scheme : public io::IO { } + /** @brief Get the state derivative variable + * @param i The index of the state derivative variable to take + * @return The state derivative variable + * @{ + */ + virtual moordyn::state::State GetDeriv(unsigned int i = 0) = 0; + virtual moordyn::state::State* rd(unsigned int i = 0) = 0; + /** + * @} + */ + protected: /** @brief Constructor * @param log Logging handler @@ -623,6 +649,16 @@ class SchemeBase : public Scheme */ virtual void Step(real& dt) { Scheme::Step(dt); }; + /** @brief Get the number of state variables + * @return The number of state variables + */ + inline const unsigned int GetNState() const { return NSTATE; } + + /** @brief Get the number of state derivative variables + * @return The number of state derivative variables + */ + inline const unsigned int GetNDeriv() const { return NDERIV; } + /** @brief Get the state * @param i The index of the state variable to take * @return The state variable @@ -720,6 +756,7 @@ class SchemeBase : public Scheme * @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) { @@ -732,6 +769,11 @@ class SchemeBase : public Scheme return _rd[i]; } + inline state::State GetDeriv(unsigned int i = 0) { return *rd(i); } + /** + * @} + */ + /** @brief Produce the packed data to be saved * * The produced data can be used afterwards to restore the saved information diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 108f526e..6f169699 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -55,6 +55,7 @@ set(CATCH2_TESTS midpoint aca wilson + line_break ) function(make_executable test_name, extension) diff --git a/tests/line_break.cpp b/tests/line_break.cpp new file mode 100644 index 00000000..50856e29 --- /dev/null +++ b/tests/line_break.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2022 Jose Luis Cercos-Pita + * + * 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 minimal.cpp + * Minimal tests that only checks the library is correctly initialized, + * running and closing + */ + +#include "MoorDyn2.h" +#include +#include +#include + +// #define SAVE_VTK + +using namespace std; + +TEST_CASE("Breaker") +{ + MoorDyn system = MoorDyn_Create("Mooring/lines.txt"); + REQUIRE(system); + + unsigned int n_dof; + REQUIRE(MoorDyn_NCoupledDOF(system, &n_dof) == MOORDYN_SUCCESS); + REQUIRE(n_dof == 9); + + int err; + double x[9], dx[9]; + // Get the initial positions from the config file + for (unsigned int i = 0; i < 3; i++) { + // 4 = first fairlead id + auto point = MoorDyn_GetPoint(system, i + 4); + REQUIRE(point); + REQUIRE(MoorDyn_GetPointPos(point, x + 3 * i) == MOORDYN_SUCCESS); + } + + auto point = MoorDyn_GetPoint(system, 4); + REQUIRE(point); + auto line = MoorDyn_GetLine(system, 1); + REQUIRE(line); + + std::fill(dx, dx + 9, 0.0); + REQUIRE(MoorDyn_Init(system, x, dx) == MOORDYN_SUCCESS); +#ifdef SAVE_VTK + REQUIRE(MoorDyn_SaveVTK(system, "line_break.000.vtm") == MOORDYN_SUCCESS); +#endif + // Let's move the system at a 10.0m/s speed during 0.5 seconds + double f[9]; + dx[0] = 10.0; + double t = 0.0, dt = 0.5; + REQUIRE(MoorDyn_Step(system, x, dx, f, &t, &dt) == MOORDYN_SUCCESS); +#ifdef SAVE_VTK + REQUIRE(MoorDyn_SaveVTK(system, "line_break.001.vtm") == MOORDYN_SUCCESS); +#endif + x[0] += dx[0] * dt; + + // Break a line and repeat 3 times + REQUIRE(MoorDyn_BreakLine(system, point, line) == MOORDYN_SUCCESS); + REQUIRE(MoorDyn_Step(system, x, dx, f, &t, &dt) == MOORDYN_SUCCESS); +#ifdef SAVE_VTK + REQUIRE(MoorDyn_SaveVTK(system, "line_break.002.vtm") == MOORDYN_SUCCESS); +#endif + x[0] += dx[0] * dt; + REQUIRE(MoorDyn_Step(system, x, dx, f, &t, &dt) == MOORDYN_SUCCESS); +#ifdef SAVE_VTK + REQUIRE(MoorDyn_SaveVTK(system, "line_break.003.vtm") == MOORDYN_SUCCESS); +#endif + x[0] += dx[0] * dt; + REQUIRE(MoorDyn_Step(system, x, dx, f, &t, &dt) == MOORDYN_SUCCESS); +#ifdef SAVE_VTK + REQUIRE(MoorDyn_SaveVTK(system, "line_break.004.vtm") == MOORDYN_SUCCESS); +#endif + x[0] += dx[0] * dt; + + REQUIRE(MoorDyn_Close(system) == MOORDYN_SUCCESS); +} diff --git a/wrappers/fortran/MoorDyn.f90 b/wrappers/fortran/MoorDyn.f90 index 2f825f04..2358e4ad 100644 --- a/wrappers/fortran/MoorDyn.f90 +++ b/wrappers/fortran/MoorDyn.f90 @@ -56,7 +56,7 @@ module moordyn MD_ExternalWaveKinSet, MD_GetNumberBodies, MD_GetBody, & MD_GetNumberRods, MD_GetRod, MD_GetNumberPoints, MD_GetPoint, & MD_GetNumberLines, MD_GetLine, MD_GetFASTtens, & - MD_GetDt, MD_SetDt, MD_GetCFL, MD_SetCFL, & + MD_BreakLine, MD_GetDt, MD_SetDt, MD_GetCFL, MD_SetCFL, & MD_GetTimeScheme, MD_SetTimeScheme, & MD_SaveState, MD_LoadState, MD_Serialize, MD_Deserialize, & MD_Save, MD_Load, MD_SaveVTK, & @@ -256,6 +256,13 @@ function MoorDyn_GetFASTtens(instance, n, fht, fvt, aht, avt) bind(c, name='Moor integer(c_int) :: rc end function MoorDyn_GetFASTtens + integer(c_int) function MD_BreakLine(instance, point, line) bind(c, name='MoorDyn_BreakLine') + import :: c_ptr, c_int + type(c_ptr), value, intent(in) :: instance + type(c_ptr), value, intent(in) :: point + type(c_ptr), value, intent(in) :: line + end function MD_BreakLine + function MoorDyn_GetDt(instance, dt) bind(c, name='MoorDyn_GetDt') result(rc) import :: c_ptr, c_double, c_int type(c_ptr), value, intent(in) :: instance diff --git a/wrappers/matlab/MoorDynM_BreakLine.cpp b/wrappers/matlab/MoorDynM_BreakLine.cpp new file mode 100644 index 00000000..4e708fe5 --- /dev/null +++ b/wrappers/matlab/MoorDynM_BreakLine.cpp @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#include "mex.hpp" +#include "mexAdapter.hpp" + +#include "MoorDyn2.h" +#include "moordyn_matlab.h" + +using namespace matlab::data; +using matlab::mex::ArgumentList; + +MOORDYNM_MEX_FUNCTION_BEGIN(MoorDyn, 3, 0) +{ + const uint64_t _point_id = inputs[1][0]; + MoorDynPoint point = (MoorDynPoint)decode_ptr(_point_id); + const uint64_t _line_id = inputs[2][0]; + MoorDynLine line = (MoorDynLine)decode_ptr(_line_id); + + const int err = MoorDyn_BreakLine(instance, point, line); + MOORDYNM_CHECK_ERROR(err); +} +MOORDYNM_MEX_FUNCTION_END diff --git a/wrappers/python/cmoordyn.cpp b/wrappers/python/cmoordyn.cpp index 5f585ab6..7adc4ce1 100644 --- a/wrappers/python/cmoordyn.cpp +++ b/wrappers/python/cmoordyn.cpp @@ -862,6 +862,41 @@ get_fast_tens(PyObject*, PyObject* args) return lst; } +/** @brief Wrapper to MoorDyn_BreakLine() function + * @param args Python passed arguments + * @return None + */ +static PyObject* +break_line(PyObject*, PyObject* args) +{ + PyObject *capsule, *pt_capcule, *ln_capsule; + int num_lines; + + if (!PyArg_ParseTuple(args, "OOO", &capsule, &pt_capcule, &ln_capsule)) + return NULL; + + MoorDyn system = + (MoorDyn)PyCapsule_GetPointer(capsule, moordyn_capsule_name); + if (!system) + return NULL; + MoorDynPoint point = + (MoorDynPoint)PyCapsule_GetPointer(pt_capcule, point_capsule_name); + if (!point) + return NULL; + MoorDynLine line = + (MoorDynLine)PyCapsule_GetPointer(ln_capsule, line_capsule_name); + if (!line) + return NULL; + + const int err = MoorDyn_BreakLine(system, point, line); + if (err != 0) { + PyErr_SetString(PyExc_RuntimeError, "MoorDyn reported an error"); + return NULL; + } + + Py_RETURN_NONE; +} + /** @brief Wrapper to MoorDyn_GetDt() function * @param args Python passed arguments * @return The time step @@ -3008,6 +3043,10 @@ static PyMethodDef moordyn_methods[] = { get_fast_tens, METH_VARARGS, "Get vertical and horizontal forces in the mooring lines" }, + { "break_line", + break_line, + METH_VARARGS, + "Break a line on a certain point" }, { "get_dt", get_dt, METH_VARARGS, "Get the inner time step" }, { "set_dt", set_dt, METH_VARARGS, "Set the inner time step" }, { "get_cfl", get_cfl, METH_VARARGS, "Get the CFL factor" }, diff --git a/wrappers/python/moordyn/moordyn.py b/wrappers/python/moordyn/moordyn.py index 1af43490..bed89479 100644 --- a/wrappers/python/moordyn/moordyn.py +++ b/wrappers/python/moordyn/moordyn.py @@ -527,6 +527,20 @@ def GetFASTtens(instance, n_lines): return data[0], data[1], data[2], data[3] +def BreakLine(instance, point, line): + """Break a line on a specific point + This method is dettaching a line from the point, and in exchange is + creating a point duplicate of type FREE + + Parameters: + instance (cmoordyn.MoorDyn): The MoorDyn instance + point (cmoordyn.MoorDynPoint): The point instance + line (cmoordyn.MoorDynLine): The line instance + """ + import cmoordyn + cmoordyn.break_line(instance, point, line) + + def GetDt(instance): """Get the current model time step