From 26e3316e7d717bfe2d39421971c146cfd776b34b Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Tue, 17 Oct 2017 21:00:22 -0700 Subject: [PATCH 01/11] CableUnitLoadCalculator renamed. - Solver is used with a lot of other classes. Mostly done for consistency. --- ...d_calculator.h => cable_unit_load_solver.h} | 12 ++++++------ src/sagtension/line_cable_reloader.cc | 10 +++++----- ...calculator.cc => cable_unit_load_solver.cc} | 18 +++++++++--------- src/transmissionline/catenary_solver.cc | 10 +++++----- ..._test.cc => cable_unit_load_solver_test.cc} | 14 +++++++------- 5 files changed, 32 insertions(+), 32 deletions(-) rename include/models/transmissionline/{cable_unit_load_calculator.h => cable_unit_load_solver.h} (89%) rename src/transmissionline/{cable_unit_load_calculator.cc => cable_unit_load_solver.cc} (80%) rename test/transmissionline/{cable_unit_load_calculator_test.cc => cable_unit_load_solver_test.cc} (84%) diff --git a/include/models/transmissionline/cable_unit_load_calculator.h b/include/models/transmissionline/cable_unit_load_solver.h similarity index 89% rename from include/models/transmissionline/cable_unit_load_calculator.h rename to include/models/transmissionline/cable_unit_load_solver.h index c3098ea..424d5e6 100644 --- a/include/models/transmissionline/cable_unit_load_calculator.h +++ b/include/models/transmissionline/cable_unit_load_solver.h @@ -1,8 +1,8 @@ // This is free and unencumbered software released into the public domain. // For more information, please refer to -#ifndef OTLS_MODELS_TRANSMISSIONLINE_CABLEUNITLOADCALCULATOR_H_ -#define OTLS_MODELS_TRANSMISSIONLINE_CABLEUNITLOADCALCULATOR_H_ +#ifndef OTLS_MODELS_TRANSMISSIONLINE_CABLEUNITLOADSOLVER_H_ +#define OTLS_MODELS_TRANSMISSIONLINE_CABLEUNITLOADSOLVER_H_ #include @@ -22,13 +22,13 @@ /// /// - uniform loading (constat wind speed, equal ice accumulation) /// - drag coefficient is 1.0 (no aerodynamic effects) -class CableUnitLoadCalculator { +class CableUnitLoadSolver { public: /// \brief Default constructor. - CableUnitLoadCalculator(); + CableUnitLoadSolver(); /// \brief Destructor. - ~CableUnitLoadCalculator(); + ~CableUnitLoadSolver(); /// \brief Gets the load per unit length of cable. /// \param[in] case_load_weather @@ -77,4 +77,4 @@ class CableUnitLoadCalculator { const double* weight_unit_cable_; }; -#endif // OTLS_MODELS_TRANSMISSIONLINE_CABLEUNITLOADCALCULATOR_H_ +#endif // OTLS_MODELS_TRANSMISSIONLINE_CABLEUNITLOADSOLVER_H_ diff --git a/src/sagtension/line_cable_reloader.cc b/src/sagtension/line_cable_reloader.cc index feacb8f..6292bdc 100644 --- a/src/sagtension/line_cable_reloader.cc +++ b/src/sagtension/line_cable_reloader.cc @@ -8,7 +8,7 @@ #include "models/sagtension/catenary_cable_reloader.h" #include "models/sagtension/catenary_cable_unloader.h" #include "models/transmissionline/catenary_solver.h" -#include "models/transmissionline/cable_unit_load_calculator.h" +#include "models/transmissionline/cable_unit_load_solver.h" LineCableReloader::LineCableReloader() { condition_reloaded_ = CableConditionType::kNull; @@ -348,11 +348,11 @@ bool LineCableReloader::IsUpdated() const { Vector3d LineCableReloader::UnitLoad( const WeatherLoadCase& weathercase) const { - CableUnitLoadCalculator calculator; - calculator.set_diameter_cable(&line_cable_->cable()->diameter); - calculator.set_weight_unit_cable(&line_cable_->cable()->weight_unit); + CableUnitLoadSolver solver; + solver.set_diameter_cable(&line_cable_->cable()->diameter); + solver.set_weight_unit_cable(&line_cable_->cable()->weight_unit); - return calculator.UnitCableLoad(weathercase); + return solver.UnitCableLoad(weathercase); } bool LineCableReloader::Update() const { diff --git a/src/transmissionline/cable_unit_load_calculator.cc b/src/transmissionline/cable_unit_load_solver.cc similarity index 80% rename from src/transmissionline/cable_unit_load_calculator.cc rename to src/transmissionline/cable_unit_load_solver.cc index 53eefda..ddf3883 100644 --- a/src/transmissionline/cable_unit_load_calculator.cc +++ b/src/transmissionline/cable_unit_load_solver.cc @@ -1,19 +1,19 @@ // This is free and unencumbered software released into the public domain. // For more information, please refer to -#include "models/transmissionline/cable_unit_load_calculator.h" +#include "models/transmissionline/cable_unit_load_solver.h" #include "models/base/geometric_shapes.h" -CableUnitLoadCalculator::CableUnitLoadCalculator() { +CableUnitLoadSolver::CableUnitLoadSolver() { diameter_cable_ = nullptr; weight_unit_cable_ = nullptr; } -CableUnitLoadCalculator::~CableUnitLoadCalculator() { +CableUnitLoadSolver::~CableUnitLoadSolver() { } -bool CableUnitLoadCalculator::Validate( +bool CableUnitLoadSolver::Validate( const bool& is_included_warnings, std::list* messages) const { // initializes @@ -47,7 +47,7 @@ bool CableUnitLoadCalculator::Validate( /// /// The transverse load on the cable is solved using the following formula: /// \f[ T = AP \f] -Vector3d CableUnitLoadCalculator::UnitCableLoad( +Vector3d CableUnitLoadSolver::UnitCableLoad( const WeatherLoadCase& case_load_weather) const { // creates bare and iced cylinders Cylinder cylinder_bare; @@ -73,19 +73,19 @@ Vector3d CableUnitLoadCalculator::UnitCableLoad( return load_unit_cable; } -const double* CableUnitLoadCalculator::diameter_cable() const { +const double* CableUnitLoadSolver::diameter_cable() const { return diameter_cable_; } -const double* CableUnitLoadCalculator::weight_unit_cable() const { +const double* CableUnitLoadSolver::weight_unit_cable() const { return weight_unit_cable_; } -void CableUnitLoadCalculator::set_diameter_cable(const double* diameter_cable) { +void CableUnitLoadSolver::set_diameter_cable(const double* diameter_cable) { diameter_cable_ = diameter_cable; } -void CableUnitLoadCalculator::set_weight_unit_cable( +void CableUnitLoadSolver::set_weight_unit_cable( const double* weight_unit_cable) { weight_unit_cable_ = weight_unit_cable; } diff --git a/src/transmissionline/catenary_solver.cc b/src/transmissionline/catenary_solver.cc index b0a4645..5db40e1 100644 --- a/src/transmissionline/catenary_solver.cc +++ b/src/transmissionline/catenary_solver.cc @@ -5,7 +5,7 @@ #include -#include "models/transmissionline/cable_unit_load_calculator.h" +#include "models/transmissionline/cable_unit_load_solver.h" CatenarySolver::CatenarySolver() { cable_ = nullptr; @@ -415,12 +415,12 @@ bool CatenarySolver::SolveHorizontalTensionFromSupportTension() const { bool CatenarySolver::SolveWeightUnit() const { // creates a calculator based on the line cable - CableUnitLoadCalculator calculator; - calculator.set_diameter_cable(&cable_->diameter); - calculator.set_weight_unit_cable(&cable_->weight_unit); + CableUnitLoadSolver solver; + solver.set_diameter_cable(&cable_->diameter); + solver.set_weight_unit_cable(&cable_->weight_unit); // calculates the unit load and updates catenary - Vector3d load_unit = calculator.UnitCableLoad(*constraint_->case_weather); + Vector3d load_unit = solver.UnitCableLoad(*constraint_->case_weather); catenary_.set_weight_unit(load_unit); return true; diff --git a/test/transmissionline/cable_unit_load_calculator_test.cc b/test/transmissionline/cable_unit_load_solver_test.cc similarity index 84% rename from test/transmissionline/cable_unit_load_calculator_test.cc rename to test/transmissionline/cable_unit_load_solver_test.cc index 56ea86e..7b28f1f 100644 --- a/test/transmissionline/cable_unit_load_calculator_test.cc +++ b/test/transmissionline/cable_unit_load_solver_test.cc @@ -1,7 +1,7 @@ // This is free and unencumbered software released into the public domain. // For more information, please refer to -#include "models/transmissionline/cable_unit_load_calculator.h" +#include "models/transmissionline/cable_unit_load_solver.h" #include "gtest/gtest.h" @@ -9,16 +9,16 @@ #include "models/base/helper.h" #include "models/base/units.h" -class CableUnitLoadCalculatorTest : public ::testing::Test { +class CableUnitLoadSolverTest : public ::testing::Test { protected: - CableUnitLoadCalculatorTest() { + CableUnitLoadSolverTest() { cable_ = factory::BuildCable(); c_.set_diameter_cable(&cable_->diameter); c_.set_weight_unit_cable(&cable_->weight_unit); } - ~CableUnitLoadCalculatorTest() { + ~CableUnitLoadSolverTest() { delete cable_; } @@ -26,10 +26,10 @@ class CableUnitLoadCalculatorTest : public ::testing::Test { const Cable* cable_; // test object - CableUnitLoadCalculator c_; + CableUnitLoadSolver c_; }; -TEST_F(CableUnitLoadCalculatorTest, UnitCableLoad) { +TEST_F(CableUnitLoadSolverTest, UnitCableLoad) { Vector3d load_unit; // ice only @@ -71,6 +71,6 @@ TEST_F(CableUnitLoadCalculatorTest, UnitCableLoad) { EXPECT_EQ(2.099, helper::Round(load_unit.z(), 3)); } -TEST_F(CableUnitLoadCalculatorTest, Validate) { +TEST_F(CableUnitLoadSolverTest, Validate) { EXPECT_TRUE(c_.Validate(true, nullptr)); } From 1cd8c20a814a68e41e9f7e1a294db7721c64554f Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Tue, 17 Oct 2017 21:19:22 -0700 Subject: [PATCH 02/11] Methods redirected to C++ library functions. - Without using std namespace, not sure if the method was using double precision parameters. --- src/transmissionline/catenary.cc | 42 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/transmissionline/catenary.cc b/src/transmissionline/catenary.cc index d33ee1d..e089693 100644 --- a/src/transmissionline/catenary.cc +++ b/src/transmissionline/catenary.cc @@ -231,11 +231,12 @@ double Catenary2d::TangentAngle(const double& position_fraction, const Point2d coordinate = Coordinate(position_fraction); // calculates slope at position - const double slope = sinh(coordinate.x / (tension_horizontal_/weight_unit_)); + const double slope = std::sinh(coordinate.x + / (tension_horizontal_/weight_unit_)); // converts to degrees double tangent_angle = units::ConvertAngle( - atan(slope), + std::atan(slope), units::AngleConversionType::kRadiansToDegrees); // adjusts if direction is negative @@ -268,11 +269,11 @@ Vector2d Catenary2d::TangentVector(const double& position_fraction, angle_tangent, units::AngleConversionType::kDegreesToRadians); if (direction == AxisDirectionType::kNegative) { - tangent_vector.set_x( -(1 * cos(angle_radians)) ); - tangent_vector.set_y( sin(angle_radians) ); + tangent_vector.set_x( -(1 * std::cos(angle_radians)) ); + tangent_vector.set_y( std::sin(angle_radians) ); } else if (direction == AxisDirectionType::kPositive) { - tangent_vector.set_x( cos(angle_radians) ); - tangent_vector.set_y( sin(angle_radians) ); + tangent_vector.set_x( std::cos(angle_radians) ); + tangent_vector.set_y( std::sin(angle_radians) ); } return tangent_vector; @@ -289,7 +290,7 @@ double Catenary2d::Tension(const double& position_fraction) const { Point2d coordinate = Coordinate(position_fraction); return tension_horizontal_ - * cosh( coordinate.x / (tension_horizontal_/weight_unit_)); + * std::cosh( coordinate.x / (tension_horizontal_/weight_unit_)); } /// A tangent unit vector is calculated and then multiplied by the tension magnitude. @@ -330,15 +331,14 @@ double Catenary2d::TensionAverage(const int& num_points) const { const double w = weight_unit_; const double l = Length(); - const double term_1 = (pow(h, 2) / (2 * w * l)); - const double term_2 = sinh(point_end_right_.x / (h / w)) - * cosh(point_end_right_.x / (h / w)); - const double term_3 = sinh(point_end_left_.x / (h / w)) - * cosh(point_end_left_.x / (h / w)); - const double term_4 = (point_end_right_.x - point_end_left_.x) - / (h / w); + const double k1 = (std::pow(h, 2) / (2 * w * l)); + const double k2 = std::sinh(point_end_right_.x / (h / w)) + * std::cosh(point_end_right_.x / (h / w)); + const double k3 = std::sinh(point_end_left_.x / (h / w)) + * std::cosh(point_end_left_.x / (h / w)); + const double k4 = (point_end_right_.x - point_end_left_.x) / (h / w); - tension_average = term_1 * (term_2 - term_3 + term_4); + tension_average = k1 * (k2 - k3 + k4); } else if (0 < num_points) { @@ -478,11 +478,11 @@ double Catenary2d::CoordinateX( // BOL from origin - negative x coordinate if (direction_origin_to_position == AxisDirectionType::kNegative) { - coordinate_x = -(h/w) * (asinh(l / (h/w))); + coordinate_x = -(h/w) * (std::asinh(l / (h/w))); } // AOL from origin - positive x coordinate else if (direction_origin_to_position == AxisDirectionType::kPositive) { - coordinate_x = (h/w) * (asinh(l / (h/w))); + coordinate_x = (h/w) * (std::asinh(l / (h/w))); } return coordinate_x; @@ -497,7 +497,7 @@ double Catenary2d::CoordinateY( const double h = tension_horizontal_; const double w = weight_unit_; - return (h/w) * (cosh(x / (h/w)) - 1); + return (h/w) * (std::cosh(x / (h/w)) - 1); } bool Catenary2d::IsUpdated() const { @@ -515,7 +515,7 @@ double Catenary2d::LengthFromOrigin(const Point2d& coordinate) const { const double h = tension_horizontal_; const double w = weight_unit_; - return std::abs((h/w) * sinh(x / (h/w))); + return std::abs((h/w) * std::sinh(x / (h/w))); } double Catenary2d::PositionFraction(const double& tangent_angle) const { @@ -586,12 +586,12 @@ bool Catenary2d::UpdateEndPoints() const { const double z = (a/2) / (h/w); // solves for left endpoint coordinate - point_end_left_.x = (h/w) * (asinh((b * z) / (a * sinh(z))) - z); + point_end_left_.x = (h/w) * (std::asinh((b * z) / (a * std::sinh(z))) - z); point_end_left_.y = CoordinateY(LengthFromOrigin(point_end_left_), AxisDirectionType::kNegative); // solves for right endpoint coordinate - point_end_right_.x = (h/w) * (asinh((b * z) / (a * sinh(z))) + z); + point_end_right_.x = (h/w) * (std::asinh((b * z) / (a * std::sinh(z))) + z); point_end_right_.y = CoordinateY(LengthFromOrigin(point_end_right_), AxisDirectionType::kPositive); From 2ca6489429aed60fd05c9a16b71c19b119228d4b Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Tue, 17 Oct 2017 22:29:47 -0700 Subject: [PATCH 03/11] Catenary sag override method added to accept position fraction. - Sag can be calculated at any point along the curve. - Tests have been added for Sag methods. --- include/models/transmissionline/catenary.h | 16 ++++++++++++ src/transmissionline/catenary.cc | 25 +++++++++++++----- test/transmissionline/catenary_test.cc | 30 ++++++++++++++++++++++ 3 files changed, 65 insertions(+), 6 deletions(-) diff --git a/include/models/transmissionline/catenary.h b/include/models/transmissionline/catenary.h index f0cdeda..d19c727 100644 --- a/include/models/transmissionline/catenary.h +++ b/include/models/transmissionline/catenary.h @@ -98,6 +98,14 @@ class Catenary2d { /// the sag point. double Sag() const; + /// \brief Gets the sag at a specific point. + /// \param[in] position_fraction + /// A decimal ranging from 0 and 1 that describes position on the curve by + /// the fraction of curve length from the left end point. + /// \return The vertical distance between the straight line and the catenary + /// the position. + double Sag(const double& position_fraction) const; + /// \brief Gets a tangent angle from the horizontal axis to the curve tangent /// line. /// \param[in] position_fraction @@ -347,6 +355,14 @@ class Catenary3d { /// the sag point. double Sag() const; + /// \brief Gets the sag at a specific point. + /// \param[in] position_fraction + /// A decimal ranging from 0 and 1 that describes position on the curve by + /// the fraction of curve length from the left end point. + /// \return The distance between the straight line and the catenary the + /// position. + double Sag(const double& position_fraction) const; + /// \brief Gets the swing angle of the catenary due to transverse loading. /// \return The swing angle, which is referenced from the vertical plane. double SwingAngle() const; diff --git a/src/transmissionline/catenary.cc b/src/transmissionline/catenary.cc index e089693..e672201 100644 --- a/src/transmissionline/catenary.cc +++ b/src/transmissionline/catenary.cc @@ -207,13 +207,16 @@ double Catenary2d::Sag() const { // gets sag position const double position_fraction_sagpoint = PositionFractionSagPoint(); - // gets catenary coordinate at sag position - const Point2d coordinate_catenary = - Coordinate(position_fraction_sagpoint); + // calculates and returns + return Sag(position_fraction_sagpoint); +} + +double Catenary2d::Sag(const double& position_fraction) const { + // gets catenary coordinate at the position + const Point2d coordinate_catenary = Coordinate(position_fraction); - // gets a chord coordinate at sag position - const Point2d coordinate_chord = - CoordinateChord(position_fraction_sagpoint); + // gets a chord coordinate at the position + const Point2d coordinate_chord = CoordinateChord(position_fraction); // gets height difference between chord and catenary return coordinate_chord.y - coordinate_catenary.y; @@ -786,6 +789,16 @@ double Catenary3d::Sag() const { return catenary_2d_.Sag(); } +double Catenary3d::Sag(const double& position_fraction) const { + if (IsUpdated() == false) { + if (Update() == false) { + return -999999; + } + } + + return catenary_2d_.Sag(position_fraction); +} + double Catenary3d::SwingAngle() const { double angle_swing = -999999; diff --git a/test/transmissionline/catenary_test.cc b/test/transmissionline/catenary_test.cc index 411ac09..14cfc3d 100644 --- a/test/transmissionline/catenary_test.cc +++ b/test/transmissionline/catenary_test.cc @@ -114,6 +114,21 @@ TEST_F(Catenary2dTest, LengthSlack) { EXPECT_EQ(9.36, helper::Round(c_.LengthSlack(), 2)); } +TEST_F(Catenary2dTest, Sag) { + double value = -999999; + + // checks max sag + value = c_.Sag(); + EXPECT_EQ(62.83, helper::Round(value, 2)); + + // checks sag at various position fractions + value = c_.Sag(0.25); + EXPECT_EQ(46.94, helper::Round(value, 2)); + + value = c_.Sag(0.75); + EXPECT_EQ(46.94, helper::Round(value, 2)); +} + TEST_F(Catenary2dTest, Tension) { EXPECT_EQ(1031.41, helper::Round(c_.Tension(0), 2)); EXPECT_EQ(1000.00, helper::Round(c_.Tension(0.5), 2)); @@ -256,6 +271,21 @@ TEST_F(Catenary3dTest, CoordinateChord) { EXPECT_EQ(100, helper::Round(coord.z, 1)); } +TEST_F(Catenary3dTest, Sag) { + double value = -999999; + + // checks max sag + value = c_.Sag(); + EXPECT_EQ(62.83, helper::Round(value, 2)); + + // checks sag at various position fractions + value = c_.Sag(0.25); + EXPECT_EQ(46.94, helper::Round(value, 2)); + + value = c_.Sag(0.75); + EXPECT_EQ(46.94, helper::Round(value, 2)); +} + TEST_F(Catenary3dTest, SwingAngle) { double value; From 21389e7088b4da4eac13ad075ef1655c3519a453 Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Tue, 17 Oct 2017 22:57:55 -0700 Subject: [PATCH 04/11] CatenarySolver reworked to be independent of LineCable. - Now accepts native catenary parameters (unit weight, end point spacing, etc) as well as a target value to determine the horizontal tension. - LineCable has a new method to convert its parameters (constraint, ruling span spacing) to catenary parameters. Uses CatenarySolver to get the ruling span catenary. - LineCableReloader updated to reference the new class interface. --- .../models/transmissionline/catenary_solver.h | 150 ++++++---- include/models/transmissionline/line_cable.h | 5 + src/sagtension/line_cable_reloader.cc | 19 +- src/transmissionline/catenary_solver.cc | 280 ++++++++++-------- src/transmissionline/line_cable.cc | 60 +++- test/transmissionline/catenary_solver_test.cc | 116 +++----- test/transmissionline/line_cable_test.cc | 21 ++ 7 files changed, 376 insertions(+), 275 deletions(-) diff --git a/include/models/transmissionline/catenary_solver.h b/include/models/transmissionline/catenary_solver.h index c5b2039..0b24241 100644 --- a/include/models/transmissionline/catenary_solver.h +++ b/include/models/transmissionline/catenary_solver.h @@ -14,8 +14,26 @@ /// \par OVERVIEW /// /// This class solves for a catenary. +/// +/// \par HORIZONTAL TENSION +/// +/// This is the typical value that is solved for. The remaining catenary parameters +/// must be known, as well as a specific target type. +/// +/// The horizontal tension is often solved numerically. class CatenarySolver { public: + /// \par OVERVIEW + /// + /// This enum contains types of CatenarySolver targets. + enum class TargetType { + kConstant, + kLength, + kNull, + kSag, + kTension + }; + /// \brief Default constructor. CatenarySolver(); @@ -23,8 +41,13 @@ class CatenarySolver { ~CatenarySolver(); /// \brief Gets the catenary. + /// \return The catenary. Catenary3d Catenary() const; + /// \brief Gets the catenary horizontal tension. + /// \return The catenary horizontal tension. + double TensionHorizontal() const; + /// \brief Validates member variables. /// \param[in] is_included_warnings /// A flag that tightens the acceptable value range. @@ -35,42 +58,57 @@ class CatenarySolver { bool Validate(const bool& is_included_warnings = true, std::list* messages = nullptr) const; - /// \brief Gets the cable. - /// \return The cable. - const Cable* cable() const; + /// \brief Gets the target position. + /// \return The target position. + double position_target() const; + + /// \brief Sets the target position. + /// \param[in] position + /// The catenary position. If the target type applies to the entire curve, + /// and not just a specific point, leave this to the default value. + void set_position_target(const double& position = -1); - /// \brief Gets the constraint. - /// \return The constraint. - const CableConstraint* constraint() const; + /// \brief Sets the end point spacing. + /// \param[in] spacing_endpoints + /// The endpoint spacing. + void set_spacing_endpoints(const Vector3d& spacing_endpoints); - /// \brief Sets the cable. - /// \param[in] cable - /// The cable. - void set_cable(const Cable* cable); + /// \brief Sets the target type. + /// \param[in] type + /// The target type. + void set_type_target(const TargetType& type); - /// \brief Gets the constraint. - /// \param[in] constraint - /// The constraint. - void set_constraint(const CableConstraint* constraint); + /// \brief Sets the target value. + /// \param[in] value + /// The target value. + void set_value_target(const double& value); - /// \brief Sets the attachment spacing. - /// \param[in] spacing_attachments - void set_spacing_attachments(const Vector3d* spacing_attachments); + /// \brief Sets the unit weight. + /// \param[in] weight_unit + /// The unit weight. + void set_weight_unit(const Vector3d& weight_unit); - /// \brief Gets the attachment spacing. - /// \return The attachment spacing. - const Vector3d* spacing_attachments() const; + /// \brief Gets the end point spacing. + /// \return The end point spacing. + const Vector3d spacing_endpoints() const; + + /// \brief Gets the target type. + /// \return The target type. + TargetType type_target() const; + + /// \brief Gets the target value. + /// \return The target value. + double value_target() const; + + /// \brief Gets the unit weight. + /// \return The unit weight. + const Vector3d weight_unit() const; private: /// \brief Determines if class is updated. /// \return A boolean indicating if class is updated. bool IsUpdated() const; - /// \brief Updates cached member variables and modifies control variables if - /// update is required. - /// \return A boolean indicating if class updates completed successfully. - bool Update() const; - /// \brief Solves and updates catenary horizontal tension by converting from /// the catenary constant. /// \return A boolean indicating the success status of the update. @@ -82,18 +120,19 @@ class CatenarySolver { bool SolveHorizontalTensionFromLength() const; /// \brief Solves and updates catenary horizontal tension by converting from - /// sag. + /// sag at a specific position on the catenary. /// \return A boolean indicating the success status of the update. bool SolveHorizontalTensionFromSag() const; /// \brief Solves and updates catenary horizontal tension by converting from - /// support tension. + /// a tension at a specific position on the catenary. /// \return A boolean indicating the success status of the update. - bool SolveHorizontalTensionFromSupportTension() const; + bool SolveHorizontalTensionFromTension() const; - /// \brief Solves and updates the catenary unit weight. - /// \return A boolean indicating the success status of the update. - bool SolveWeightUnit() const; + /// \brief Updates cached member variables and modifies control variables if + /// update is required. + /// \return A boolean indicating if class updates completed successfully. + bool Update() const; /// \brief Updates catenary horizontal tension and calculates new length. /// \param[in] tension_horizontal @@ -102,38 +141,47 @@ class CatenarySolver { /// catenary. double UpdateCatenaryLength(const double& tension_horizontal) const; - /// \brief Updates catenary tension and calculates new support tension. + /// \brief Updates catenary horizontal tension and calculates new sag at the + /// specified position. /// \param[in] tension_horizontal /// The new horizontal tension for the catenary. - /// \return The support tension correlating to the new horizontal tension of - /// the catenary. - double UpdateCatenaryMaxTension(const double& tension_horizontal) const; + /// \param[in] position_fraction + /// The position fraction to calculate the catenary tension at. + /// \return The sag correlating to the new horizontal tension of the catenary. + double UpdateCatenarySag(const double& tension_horizontal, + const double& position_fraction) const; - /// \brief Updates catenary horizontal tension and calculates new sag. + /// \brief Updates catenary horizontal tension and calculates new tension at + /// the specified position. /// \param[in] tension_horizontal /// The new horizontal tension for the catenary. - /// \return The sag correlating to the new horizontal tension of the catenary. - double UpdateCatenarySag(const double& tension_horizontal) const; - - /// \var cable_ - /// The cable. - const Cable* cable_; + /// \param[in] position_fraction + /// The position fraction to calculate the catenary tension at. + /// \return The tension correlating to the new horizontal tension of the + /// catenary. + double UpdateCatenaryTension(const double& tension_horizontal, + const double& position_fraction) const; /// \var catenary_ /// The catenary being solved for. mutable Catenary3d catenary_; - /// \var constraint_ - /// The constraint. - const CableConstraint* constraint_; + /// \var is_updated_ + /// An indicator that tells if the class is updated. + mutable bool is_updated_; + + /// \var position_target_ + /// The target position fraction along the catenary curve. If the position + /// isn't needed, set to -1. + double position_target_; - /// \var is_updated_catenary_ - /// An indicator that tells if the catenary is updated. - mutable bool is_updated_catenary_; + /// \var type_target_ + /// The target type. + TargetType type_target_; - /// \var spacing_attachments_ - /// The attachment spacing for the catenary. - const Vector3d* spacing_attachments_; + /// \var value_target_ + /// The target value. + double value_target_; }; #endif // OTLS_MODELS_TRANSMISSIONLINE_CATENARYSOLVER_H_ diff --git a/include/models/transmissionline/line_cable.h b/include/models/transmissionline/line_cable.h index 0f72c6d..af9e308 100644 --- a/include/models/transmissionline/line_cable.h +++ b/include/models/transmissionline/line_cable.h @@ -10,6 +10,7 @@ #include "models/base/vector.h" #include "models/transmissionline/cable.h" #include "models/transmissionline/cable_constraint.h" +#include "models/transmissionline/catenary.h" class LineStructure; @@ -54,6 +55,10 @@ class LineCable { /// connection to the line cable fails, -1 will be returned. int AddConnection(const LineCableConnection& connection); + /// \brief Gets a catenary based on the constraint and ruling span geometry. + /// \return A catenary based on the constraint and ruling span geometry. + Catenary3d CatenaryRulingSpan() const; + /// \brief Clears all connections. void ClearConnections(); diff --git a/src/sagtension/line_cable_reloader.cc b/src/sagtension/line_cable_reloader.cc index 6292bdc..f64cdb4 100644 --- a/src/sagtension/line_cable_reloader.cc +++ b/src/sagtension/line_cable_reloader.cc @@ -7,7 +7,6 @@ #include "models/sagtension/catenary_cable_reloader.h" #include "models/sagtension/catenary_cable_unloader.h" -#include "models/transmissionline/catenary_solver.h" #include "models/transmissionline/cable_unit_load_solver.h" LineCableReloader::LineCableReloader() { @@ -530,20 +529,12 @@ bool LineCableReloader::UpdateConstraintCableModel() const { } bool LineCableReloader::UpdateConstraintCatenary() const { - CableConstraint constraint = line_cable_->constraint(); - Vector3d spacing = line_cable_->spacing_attachments_ruling_span(); - - // builds a catenary solver - CatenarySolver solver; - solver.set_cable(cable_sagtension_.cable_base()); - solver.set_constraint(&constraint); - solver.set_spacing_attachments(&spacing); - - if (solver.Validate() == false) { - return false; - } else { - catenary_constraint_ = solver.Catenary(); + Catenary3d catenary = line_cable_->CatenaryRulingSpan(); + if (catenary.Validate() == true) { + catenary_constraint_ = catenary; return true; + } else { + return false; } } diff --git a/src/transmissionline/catenary_solver.cc b/src/transmissionline/catenary_solver.cc index 5db40e1..33a724c 100644 --- a/src/transmissionline/catenary_solver.cc +++ b/src/transmissionline/catenary_solver.cc @@ -5,14 +5,13 @@ #include -#include "models/transmissionline/cable_unit_load_solver.h" - CatenarySolver::CatenarySolver() { - cable_ = nullptr; - constraint_ = nullptr; - spacing_attachments_ = nullptr; + catenary_ = Catenary3d(); + position_target_ = -1; + type_target_ = TargetType::kNull; + value_target_ = -999999; - is_updated_catenary_ = false; + is_updated_ = false; } CatenarySolver::~CatenarySolver() { @@ -29,6 +28,17 @@ Catenary3d CatenarySolver::Catenary() const { return catenary_; } +double CatenarySolver::TensionHorizontal() const { + // updates class if necessary + if (IsUpdated() == false) { + if (Update() == false) { + return -999999; + } + } + + return catenary_.tension_horizontal(); +} + bool CatenarySolver::Validate( const bool& is_included_warnings, std::list* messages) const { @@ -37,53 +47,84 @@ bool CatenarySolver::Validate( ErrorMessage message; message.title = "CATENARY SOLVER"; - // validates cable - if (cable_ == nullptr) { + // validates position-target + if ((position_target_ < 0) || (1 < position_target_)) { is_valid = false; if (messages != nullptr) { - message.description = "Invalid cable"; + message.description = "Invalid target position"; messages->push_back(message); } - } else { - if (cable_->Validate(is_included_warnings, messages) == false) { - is_valid = false; + } + + // validates spacing-endpoints + Vector3d spacing_endpoints = catenary_.spacing_endpoints(); + if (spacing_endpoints.x() <= 0) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid horizontal end point spacing"; + messages->push_back(message); } } - // validates constraint - if (constraint_ == nullptr) { + if (spacing_endpoints.y() != 0) { is_valid = false; if (messages != nullptr) { - message.description = "Invalid constraint"; + message.description = "Invalid end point spacing"; messages->push_back(message); } - } else { - if (constraint_->Validate(is_included_warnings, messages) == false) { - is_valid = false; + } + + if (2000 < std::abs(spacing_endpoints.z())) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid vertical end point spacing"; + messages->push_back(message); + } + } + + // validates type-target + if (type_target_ == CatenarySolver::TargetType::kNull) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid target type"; + messages->push_back(message); } } - // validates spacing-attachments - if (spacing_attachments_->x() <= 0) { + // validates value-target + if (value_target_ <= 0) { is_valid = false; if (messages != nullptr) { - message.description = "Invalid horizontal attachment spacing"; + message.description = "Invalid target value"; messages->push_back(message); } } - if (spacing_attachments_->y() != 0) { + // validates weight-unit-horizontal + Vector3d weight_unit = catenary_.weight_unit(); + if (weight_unit.x() != 0) { is_valid = false; if (messages != nullptr) { - message.description = "Invalid attachment spacing"; + message.description = "Invalid horizontal unit weight. It must " + "equal zero"; messages->push_back(message); } } - if (2000 < std::abs(spacing_attachments_->z())) { + // validates weight-unit-transverse + if (weight_unit.y() < 0) { is_valid = false; if (messages != nullptr) { - message.description = "Invalid vertical attachment spacing"; + message.description = "Invalid transverse unit weight"; + messages->push_back(message); + } + } + + // validates weight-unit-vertical + if (weight_unit.z() <= 0) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid vertical unit weight"; messages->push_back(message); } } @@ -107,36 +148,53 @@ bool CatenarySolver::Validate( return is_valid; } -const Cable* CatenarySolver::cable() const { - return cable_; +double CatenarySolver::position_target() const { + return position_target_; +} + +void CatenarySolver::set_position_target(const double& position) { + position_target_ = position; + is_updated_ = false; +} + +void CatenarySolver::set_spacing_endpoints(const Vector3d& spacing_endpoints) { + catenary_.set_spacing_endpoints(spacing_endpoints); + is_updated_ = false; +} + +void CatenarySolver::set_type_target(const TargetType& type) { + type_target_ = type; + is_updated_ = false; } -const CableConstraint* CatenarySolver::constraint() const { - return constraint_; +void CatenarySolver::set_value_target(const double& value) { + value_target_ = value; + is_updated_ = false; } -void CatenarySolver::set_cable(const Cable* cable) { - cable_ = cable; - is_updated_catenary_ = false; +void CatenarySolver::set_weight_unit(const Vector3d& weight_unit) { + catenary_.set_weight_unit(weight_unit); + is_updated_ = false; } -void CatenarySolver::set_constraint(const CableConstraint* constraint) { - constraint_ = constraint; - is_updated_catenary_ = false; +const Vector3d CatenarySolver::spacing_endpoints() const { + return catenary_.spacing_endpoints(); } -void CatenarySolver::set_spacing_attachments( - const Vector3d* spacing_attachments) { - spacing_attachments_ = spacing_attachments; - is_updated_catenary_ = false; +CatenarySolver::TargetType CatenarySolver::type_target() const { + return type_target_; } -const Vector3d* CatenarySolver::spacing_attachments() const { - return spacing_attachments_; +double CatenarySolver::value_target() const { + return value_target_; +} + +const Vector3d CatenarySolver::weight_unit() const { + return catenary_.weight_unit(); } bool CatenarySolver::IsUpdated() const { - if (is_updated_catenary_ == true) { + if (is_updated_ == true) { return true; } else { return false; @@ -146,8 +204,8 @@ bool CatenarySolver::IsUpdated() const { bool CatenarySolver::SolveHorizontalTensionFromConstant() const { // the catenary unit weight should already be updated, so this multiplies // catenary constant by w to get horizontal tension - catenary_.set_tension_horizontal(constraint_->limit - * catenary_.weight_unit().Magnitude()); + catenary_.set_tension_horizontal( + value_target_ * catenary_.weight_unit().Magnitude()); return true; } @@ -159,7 +217,7 @@ bool CatenarySolver::SolveHorizontalTensionFromLength() const { // y = length // initializes target - double target_solution = constraint_->limit; + double target_solution = value_target_; // declares and initializes left point // lowest acceptable value for catenary @@ -170,7 +228,7 @@ bool CatenarySolver::SolveHorizontalTensionFromLength() const { point_left.y = UpdateCatenaryLength(point_left.x); // checks if target length is less than straight line distance - if (target_solution <= spacing_attachments_->Magnitude()) { + if (target_solution <= catenary_.spacing_endpoints().Magnitude()) { return false; } @@ -249,7 +307,7 @@ bool CatenarySolver::SolveHorizontalTensionFromSag() const { // y = sag // initializes target - double target_solution = constraint_->limit; + double target_solution = value_target_; // declares and initializes left point // lowest acceptable value for catenary @@ -257,7 +315,7 @@ bool CatenarySolver::SolveHorizontalTensionFromSag() const { point_left.x = Catenary2d::ConstantMinimum( catenary_.spacing_endpoints().Magnitude()) * catenary_.weight_unit().Magnitude(); - point_left.y = UpdateCatenarySag(point_left.x); + point_left.y = UpdateCatenarySag(point_left.x, position_target_); // target sag is greater than lowest acceptable catenary value if (point_left.y < target_solution) { @@ -267,7 +325,7 @@ bool CatenarySolver::SolveHorizontalTensionFromSag() const { // declares and initializes right point to 10,000 H/w Point2d point_right; point_right.x = point_left.x * 1.10; - point_right.y = UpdateCatenarySag(point_right.x); + point_right.y = UpdateCatenarySag(point_right.x, position_target_); // declares and initializes current point Point2d point_current; @@ -286,7 +344,7 @@ bool CatenarySolver::SolveHorizontalTensionFromSag() const { + ((target_solution - point_left.y) / slope_line); // gets current point y value - point_current.y = UpdateCatenarySag(point_current.x); + point_current.y = UpdateCatenarySag(point_current.x, position_target_); // updates either left or right point based on current point if (point_current.x < point_left.x) { @@ -329,12 +387,12 @@ bool CatenarySolver::SolveHorizontalTensionFromSag() const { /// This is done iteratively by adjusting the horizontal tension until the /// support tension is matched. -bool CatenarySolver::SolveHorizontalTensionFromSupportTension() const { +bool CatenarySolver::SolveHorizontalTensionFromTension() const { // x = tension-horizontal // y = tension-support // initializes target - double target_solution = constraint_->limit; + double target_solution = value_target_; // declares and initializes left point // lowest acceptable value for catenary @@ -342,7 +400,7 @@ bool CatenarySolver::SolveHorizontalTensionFromSupportTension() const { point_left.x = Catenary2d::ConstantMinimum( catenary_.spacing_endpoints().Magnitude()) * catenary_.weight_unit().Magnitude(); - point_left.y = UpdateCatenaryMaxTension(point_left.x); + point_left.y = UpdateCatenaryTension(point_left.x, position_target_); // target is less than lowest acceptable catenary value if (target_solution < point_left.y) { @@ -352,8 +410,8 @@ bool CatenarySolver::SolveHorizontalTensionFromSupportTension() const { // declares and initializes right point // highest value (horizontal tension cannot exceed support tension) Point2d point_right; - point_right.x = constraint_->limit; - point_right.y = UpdateCatenaryMaxTension(point_right.x); + point_right.x = value_target_; + point_right.y = UpdateCatenaryTension(point_right.x, position_target_); // declares and initializes current point Point2d point_current; @@ -372,7 +430,7 @@ bool CatenarySolver::SolveHorizontalTensionFromSupportTension() const { + ((target_solution - point_left.y) / slope_line); // gets current point y value - point_current.y = UpdateCatenaryMaxTension(point_current.x); + point_current.y = UpdateCatenaryTension(point_current.x, position_target_); // updates either left or right point based on current point if (point_current.x < point_left.x) { @@ -413,77 +471,39 @@ bool CatenarySolver::SolveHorizontalTensionFromSupportTension() const { } } -bool CatenarySolver::SolveWeightUnit() const { - // creates a calculator based on the line cable - CableUnitLoadSolver solver; - solver.set_diameter_cable(&cable_->diameter); - solver.set_weight_unit_cable(&cable_->weight_unit); - - // calculates the unit load and updates catenary - Vector3d load_unit = solver.UnitCableLoad(*constraint_->case_weather); - catenary_.set_weight_unit(load_unit); - - return true; -} - bool CatenarySolver::Update() const { // updates catenary - if (is_updated_catenary_ == false) { - - // updates spacing - catenary_.set_spacing_endpoints(*spacing_attachments_); - - // solves for the unit weight - is_updated_catenary_ = SolveWeightUnit(); - if (is_updated_catenary_ == false) { + if (type_target_ == CatenarySolver::TargetType::kConstant) { + // solves for horizontal tension based on constant + is_updated_ = SolveHorizontalTensionFromConstant(); + if (is_updated_ == false) { return false; } - - // solves for the horizontal tension - if (constraint_->type_limit - == CableConstraint::LimitType::kCatenaryConstant) { - - is_updated_catenary_ = SolveHorizontalTensionFromConstant(); - if (is_updated_catenary_ == false) { - return false; - } - - } else if (constraint_->type_limit - == CableConstraint::LimitType::kHorizontalTension) { - - catenary_.set_tension_horizontal(constraint_->limit); - - } else if (constraint_->type_limit - == CableConstraint::LimitType::kLength) { - - is_updated_catenary_ = SolveHorizontalTensionFromLength(); - if (is_updated_catenary_ == false) { - return false; - } - - } else if (constraint_->type_limit == CableConstraint::LimitType::kSag) { - - is_updated_catenary_ = SolveHorizontalTensionFromSag(); - if (is_updated_catenary_ == false) { - return false; - } - - } else if (constraint_->type_limit - == CableConstraint::LimitType::kSupportTension) { - - is_updated_catenary_ = SolveHorizontalTensionFromSupportTension(); - if (is_updated_catenary_ == false) { - return false; - } - - } else { + } else if (type_target_ == CatenarySolver::TargetType::kLength) { + // solves for horizontal tension based on length + is_updated_ = SolveHorizontalTensionFromLength(); + if (is_updated_ == false) { return false; } - - // validates catenary to make sure that H/w is valid - if (catenary_.Validate(false, nullptr) == false) { + } else if (type_target_ == CatenarySolver::TargetType::kSag) { + // solves for horizontal tension based on sag + is_updated_ = SolveHorizontalTensionFromSag(); + if (is_updated_ == false) { + return false; + } + } else if (type_target_ == CatenarySolver::TargetType::kTension) { + // solves for horizontal tension based on tension + is_updated_ = SolveHorizontalTensionFromTension(); + if (is_updated_ == false) { return false; } + } else { + return false; + } + + // validates catenary to make sure that H/w is valid + if (catenary_.Validate(false, nullptr) == false) { + return false; } // if it reaches this point, update was successful @@ -499,20 +519,26 @@ double CatenarySolver::UpdateCatenaryLength( return catenary_.Length(); } -double CatenarySolver::UpdateCatenaryMaxTension( - const double& tension_horizontal) const { +double CatenarySolver::UpdateCatenarySag( + const double& tension_horizontal, + const double& position_fraction) const { // updates catenary catenary_.set_tension_horizontal(tension_horizontal); - // returns support tension - return catenary_.TensionMax(); + // returns sag at specified position + if (position_target_ == -1) { + return catenary_.Sag(); + } else { + return catenary_.Sag(position_fraction); + } } -double CatenarySolver::UpdateCatenarySag( - const double& tension_horizontal) const { +double CatenarySolver::UpdateCatenaryTension( + const double& tension_horizontal, + const double& position_fraction) const { // updates catenary catenary_.set_tension_horizontal(tension_horizontal); - // returns sag - return catenary_.Sag(); + // returns tension at specified position + return catenary_.Tension(position_fraction); } diff --git a/src/transmissionline/line_cable.cc b/src/transmissionline/line_cable.cc index d99f30b..6209977 100644 --- a/src/transmissionline/line_cable.cc +++ b/src/transmissionline/line_cable.cc @@ -5,6 +5,7 @@ #include +#include "models/transmissionline/cable_unit_load_solver.h" #include "models/transmissionline/catenary_solver.h" #include "models/transmissionline/line_structure.h" @@ -52,6 +53,56 @@ int LineCable::AddConnection(const LineCableConnection& connection) { return std::distance(connections_.begin(), iter) - 1; } +Catenary3d LineCable::CatenaryRulingSpan() const { + // initializes + Catenary3d catenary; + catenary.set_spacing_endpoints(spacing_attachments_ruling_span_); + + // solves for the unit loads + CableUnitLoadSolver solver_unit_loads; + solver_unit_loads.set_diameter_cable(&cable_->diameter); + solver_unit_loads.set_weight_unit_cable(&cable_->weight_unit); + + Vector3d load_unit = solver_unit_loads.UnitCableLoad( + *constraint_.case_weather); + catenary.set_weight_unit(load_unit); + + // solves for the horizontal tension + CatenarySolver solver_tension; + solver_tension.set_spacing_endpoints(catenary.spacing_endpoints()); + solver_tension.set_value_target(constraint_.limit); + solver_tension.set_weight_unit(load_unit); + + if (constraint_.type_limit == CableConstraint::LimitType::kCatenaryConstant) { + // sets up solver for an analysis target type + solver_tension.set_type_target(CatenarySolver::TargetType::kConstant); + } else if (constraint_.type_limit + == CableConstraint::LimitType::kHorizontalTension) { + // horizontal tension is already known + catenary.set_tension_horizontal(constraint_.limit); + return catenary; + } else if (constraint_.type_limit == CableConstraint::LimitType::kLength) { + // sets up the solver for a length target type + solver_tension.set_type_target(CatenarySolver::TargetType::kLength); + } else if (constraint_.type_limit == CableConstraint::LimitType::kSag) { + // sets up the solver for a sag target type + solver_tension.set_type_target(CatenarySolver::TargetType::kSag); + } else if (constraint_.type_limit + == CableConstraint::LimitType::kSupportTension) { + // sets up the solver for a tension target type + solver_tension.set_type_target(CatenarySolver::TargetType::kTension); + + if (spacing_attachments_ruling_span_.z() <= 0) { + solver_tension.set_position_target(0); + } else { + solver_tension.set_position_target(1); + } + } + + catenary.set_tension_horizontal(solver_tension.TensionHorizontal()); + return catenary; +} + void LineCable::ClearConnections() { connections_.clear(); } @@ -176,13 +227,10 @@ bool LineCable::Validate(const bool& is_included_warnings, return is_valid; } - // validates if catenary can be solved for with contraint and ruling + // validates if catenary can be solved for with constraint and ruling // span geometry - CatenarySolver solver; - solver.set_cable(cable_); - solver.set_constraint(&constraint_); - solver.set_spacing_attachments(&spacing_attachments_ruling_span_); - if (solver.Validate(is_included_warnings, messages) == false) { + Catenary3d catenary = CatenaryRulingSpan(); + if (catenary.Validate(is_included_warnings, messages) == false) { is_valid = false; } diff --git a/test/transmissionline/catenary_solver_test.cc b/test/transmissionline/catenary_solver_test.cc index d2ab6ce..9fde715 100644 --- a/test/transmissionline/catenary_solver_test.cc +++ b/test/transmissionline/catenary_solver_test.cc @@ -5,101 +5,63 @@ #include "gtest/gtest.h" -#include "factory.h" #include "models/base/helper.h" -#include "models/base/units.h" class CatenarySolverTest : public ::testing::Test { protected: CatenarySolverTest() { - // builds dependency object - line cable - cable_ = factory::BuildCable(); - - weathercase_ = new WeatherLoadCase(); - weathercase_->description = "0.5-8-0"; - weathercase_->thickness_ice = - units::ConvertLength(0.5, units::LengthConversionType::kInchesToFeet); - weathercase_->density_ice = 57.3; - weathercase_->pressure_wind = 8; - weathercase_->temperature_cable = 0; - - constraint_ = new CableConstraint(); - constraint_->case_weather = weathercase_; - constraint_->condition = CableConditionType::kInitial; - constraint_->limit = 4000; - constraint_->type_limit = CableConstraint::LimitType::kCatenaryConstant; - - spacing_attachments_ = new Vector3d(1200, 0, 0); - // builds fixture object - c_.set_cable(cable_); - c_.set_constraint(constraint_); - c_.set_spacing_attachments(spacing_attachments_); + c_.set_position_target(0); + c_.set_spacing_endpoints(Vector3d(1200, 0, 0)); + c_.set_type_target(CatenarySolver::TargetType::kConstant); + c_.set_value_target(4000); + c_.set_weight_unit(Vector3d(0, 0, 1)); } ~CatenarySolverTest() { - delete cable_; - delete constraint_; - delete spacing_attachments_; - delete weathercase_; } - // allocated dependency objects - const Cable* cable_; - CableConstraint* constraint_; - Vector3d* spacing_attachments_; - WeatherLoadCase* weathercase_; - // test object CatenarySolver c_; }; TEST_F(CatenarySolverTest, Catenary) { - // gets catenary and saves values for comparison + // gets catenary and checks horizontal tension Catenary3d catenary = c_.Catenary(); + double value = catenary.tension_horizontal(); + EXPECT_EQ(4000.0, helper::Round(value, 1)); +} - // weight - Vector3d weight_unit = catenary.weight_unit(); - EXPECT_EQ(0, helper::Round(weight_unit.x(), 3)); - EXPECT_EQ(1.405, helper::Round(weight_unit.y(), 3)); - EXPECT_EQ(2.099, helper::Round(weight_unit.z(), 3)); - - // spacing - Vector3d spacing = catenary.spacing_endpoints(); - EXPECT_EQ(1200, helper::Round(spacing.x(), 0)); - EXPECT_EQ(0, helper::Round(spacing.y(), 0)); - EXPECT_EQ(0, helper::Round(spacing.z(), 0)); - - // horizontal tension - from catenary constant type constraint - EXPECT_EQ(10104.3, helper::Round(catenary.tension_horizontal(), 1)); - - // horizontal tension - from horizontal tension type constraint - constraint_->limit = 10104.3; - constraint_->type_limit = CableConstraint::LimitType::kHorizontalTension; - c_.set_constraint(constraint_); - catenary = c_.Catenary(); - EXPECT_EQ(10104.3, helper::Round(catenary.tension_horizontal(), 1)); - - // horizontal tension - from length type constraint - constraint_->limit = 1204.5051; - constraint_->type_limit = CableConstraint::LimitType::kLength; - c_.set_constraint(constraint_); - catenary = c_.Catenary(); - EXPECT_EQ(10104.3, helper::Round(catenary.tension_horizontal(), 1)); - - // horizontal tension - from sag type constraint - constraint_->limit = 45.0844; - constraint_->type_limit = CableConstraint::LimitType::kSag; - c_.set_constraint(constraint_); - catenary = c_.Catenary(); - EXPECT_EQ(10104.3, helper::Round(catenary.tension_horizontal(), 1)); - - // horizontal tension - from support tension type constraint - constraint_->limit = 10218.2; - constraint_->type_limit = CableConstraint::LimitType::kSupportTension; - c_.set_constraint(constraint_); - catenary = c_.Catenary(); - EXPECT_EQ(10104.3, helper::Round(catenary.tension_horizontal(), 1)); +TEST_F(CatenarySolverTest, TensionHorizontal) { + double value = -999999; + + // checks catenary constant type + c_.set_position_target(-1); + c_.set_type_target(CatenarySolver::TargetType::kConstant); + c_.set_value_target(4000); + value = c_.TensionHorizontal(); + EXPECT_EQ(4000.0, helper::Round(value, 1)); + + // checks length type + c_.set_position_target(-1); + c_.set_type_target(CatenarySolver::TargetType::kLength); + c_.set_value_target(1204.505065); + value = c_.TensionHorizontal(); + EXPECT_EQ(4000.0, helper::Round(value, 1)); + + // checks sag type + c_.set_position_target(-1); + c_.set_type_target(CatenarySolver::TargetType::kSag); + c_.set_value_target(45.08438); + value = c_.TensionHorizontal(); + EXPECT_EQ(4000.0, helper::Round(value, 1)); + + // checks tension type + c_.set_position_target(0); + c_.set_type_target(CatenarySolver::TargetType::kTension); + c_.set_value_target(4045.084438); + value = c_.TensionHorizontal(); + EXPECT_EQ(4000.0, helper::Round(value, 1)); } TEST_F(CatenarySolverTest, Validate) { diff --git a/test/transmissionline/line_cable_test.cc b/test/transmissionline/line_cable_test.cc index c527e73..cc12839 100644 --- a/test/transmissionline/line_cable_test.cc +++ b/test/transmissionline/line_cable_test.cc @@ -6,6 +6,7 @@ #include "gtest/gtest.h" #include "factory.h" +#include "models/base/helper.h" class LineCableTest : public ::testing::Test { protected: @@ -52,6 +53,26 @@ TEST_F(LineCableTest, AddConnection) { EXPECT_EQ(1, index); } +TEST_F(LineCableTest, CatenaryRulingSpan) { + Catenary3d catenary = l_->CatenaryRulingSpan(); + + // checks tension + const double value = catenary.tension_horizontal(); + EXPECT_EQ(6000.0, helper::Round(value, 1)); + + // checks unit weight + Vector3d weight_unit = catenary.weight_unit(); + EXPECT_EQ(0.000, helper::Round(weight_unit.x(), 3)); + EXPECT_EQ(0.000, helper::Round(weight_unit.y(), 3)); + EXPECT_EQ(1.094, helper::Round(weight_unit.z(), 3)); + + // checks spacing + Vector3d spacing = catenary.spacing_endpoints(); + EXPECT_EQ(1200, helper::Round(spacing.x(), 0)); + EXPECT_EQ(0, helper::Round(spacing.y(), 0)); + EXPECT_EQ(0, helper::Round(spacing.z(), 0)); +} + TEST_F(LineCableTest, DeleteConnection) { // deletes a valid connection EXPECT_TRUE(l_->DeleteConnection(1)); From 9c697079c47e8b0d87437d91fb17d2c682774357 Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Thu, 19 Oct 2017 19:10:32 -0700 Subject: [PATCH 05/11] Catenary bug fixes and formatting changes. - Coordinate bug fixed when both end points were left of origin. - 3D -> 2D distance projection calculations weren't referencing std namespace functions. - Space padding added for operators (divide/multiply,etc). - Tests updated to calculate coordinates for a varying origin position. --- src/transmissionline/catenary.cc | 70 ++++++++++++-------------- test/transmissionline/catenary_test.cc | 50 +++++++++++++++++- 2 files changed, 81 insertions(+), 39 deletions(-) diff --git a/src/transmissionline/catenary.cc b/src/transmissionline/catenary.cc index e672201..4a4b009 100644 --- a/src/transmissionline/catenary.cc +++ b/src/transmissionline/catenary.cc @@ -51,35 +51,30 @@ Point2d Catenary2d::Coordinate(const double& position_fraction, // select how to calculate length and position from origin based on // endpoint coordinates - // both endpoints are left from origin if (point_end_left_.x < 0 && point_end_right_.x < 0) { + // both endpoints are left from origin length_origin_to_position = length_origin_to_left - length_left_to_position; - direction_origin_to_position = AxisDirectionType::kPositive; - } - // one endpoint left from origin, one endpoint right from origin - else if (point_end_left_.x < 0 && 0 < point_end_right_.x) { - - // left from origin + direction_origin_to_position = AxisDirectionType::kNegative; + } else if (point_end_left_.x < 0 && 0 < point_end_right_.x) { + // one endpoint left from origin, one endpoint right from origin if (length_left_to_position < length_origin_to_left) { + // left from origin length_origin_to_position = length_origin_to_left - length_left_to_position; direction_origin_to_position = AxisDirectionType::kNegative; - } - // at origin - else if (length_left_to_position == length_origin_to_left) { + } else if (length_left_to_position == length_origin_to_left) { + // at origin length_origin_to_position = 0; direction_origin_to_position = AxisDirectionType::kPositive; - } - // right from origin - else if (length_origin_to_left < length_left_to_position) { + } else if (length_origin_to_left < length_left_to_position) { + // right from origin length_origin_to_position = length_left_to_position - length_origin_to_left; direction_origin_to_position = AxisDirectionType::kPositive; } - } - // both endpoints are AOL of origin - else if (0 < point_end_left_.x && 0 < point_end_right_.x) { + } else if (0 < point_end_left_.x && 0 < point_end_right_.x) { + // both endpoints are AOL of origin length_origin_to_position = length_origin_to_left + length_left_to_position; direction_origin_to_position = AxisDirectionType::kPositive; @@ -272,11 +267,11 @@ Vector2d Catenary2d::TangentVector(const double& position_fraction, angle_tangent, units::AngleConversionType::kDegreesToRadians); if (direction == AxisDirectionType::kNegative) { - tangent_vector.set_x( -(1 * std::cos(angle_radians)) ); - tangent_vector.set_y( std::sin(angle_radians) ); + tangent_vector.set_x(-(1 * std::cos(angle_radians))); + tangent_vector.set_y(std::sin(angle_radians)); } else if (direction == AxisDirectionType::kPositive) { - tangent_vector.set_x( std::cos(angle_radians) ); - tangent_vector.set_y( std::sin(angle_radians) ); + tangent_vector.set_x(std::cos(angle_radians)); + tangent_vector.set_y(std::sin(angle_radians)); } return tangent_vector; @@ -293,7 +288,7 @@ double Catenary2d::Tension(const double& position_fraction) const { Point2d coordinate = Coordinate(position_fraction); return tension_horizontal_ - * std::cosh( coordinate.x / (tension_horizontal_/weight_unit_)); + * std::cosh(coordinate.x / (tension_horizontal_ / weight_unit_)); } /// A tangent unit vector is calculated and then multiplied by the tension magnitude. @@ -481,11 +476,11 @@ double Catenary2d::CoordinateX( // BOL from origin - negative x coordinate if (direction_origin_to_position == AxisDirectionType::kNegative) { - coordinate_x = -(h/w) * (std::asinh(l / (h/w))); + coordinate_x = -(h / w) * (std::asinh(l / (h / w))); } // AOL from origin - positive x coordinate else if (direction_origin_to_position == AxisDirectionType::kPositive) { - coordinate_x = (h/w) * (std::asinh(l / (h/w))); + coordinate_x = (h / w) * (std::asinh(l / (h / w))); } return coordinate_x; @@ -500,7 +495,7 @@ double Catenary2d::CoordinateY( const double h = tension_horizontal_; const double w = weight_unit_; - return (h/w) * (std::cosh(x / (h/w)) - 1); + return (h / w) * (std::cosh(x / (h / w)) - 1); } bool Catenary2d::IsUpdated() const { @@ -518,7 +513,7 @@ double Catenary2d::LengthFromOrigin(const Point2d& coordinate) const { const double h = tension_horizontal_; const double w = weight_unit_; - return std::abs((h/w) * std::sinh(x / (h/w))); + return std::abs((h / w) * std::sinh(x / (h / w))); } double Catenary2d::PositionFraction(const double& tangent_angle) const { @@ -578,9 +573,6 @@ bool Catenary2d::Update() const { return true; } -/// Hyperbolic identity equations are used to solve for the endpoint coordinates: -/// \f[ xBOL = \frac{A}{2} - \frac{H}{w} sinh^{-1} \left( \frac{\frac{B}{2}}{ \frac{H}{w} sinh \left( \frac{\frac{A}{2}}{\frac{H}{w}} \right)} \right) \f] -/// \f[ xAOL = \frac{A}{2} + \frac{H}{w} sinh^{-1} \left( \frac{\frac{B}{2}}{ \frac{H}{w} sinh \left( \frac{\frac{A}{2}}{\frac{H}{w}} \right)} \right) \f] bool Catenary2d::UpdateEndPoints() const { const double h = tension_horizontal_; const double w = weight_unit_; @@ -588,13 +580,13 @@ bool Catenary2d::UpdateEndPoints() const { const double b = spacing_endpoints_.y(); const double z = (a/2) / (h/w); - // solves for left endpoint coordinate - point_end_left_.x = (h/w) * (std::asinh((b * z) / (a * std::sinh(z))) - z); + // solves for left endpoint coordinate (Ehrenburg) + point_end_left_.x = (h / w) * (std::asinh((b * z) / (a * std::sinh(z))) - z); point_end_left_.y = CoordinateY(LengthFromOrigin(point_end_left_), AxisDirectionType::kNegative); - // solves for right endpoint coordinate - point_end_right_.x = (h/w) * (std::asinh((b * z) / (a * std::sinh(z))) + z); + // solves for right endpoint coordinate (Ehrenburg) + point_end_right_.x = (h / w) * (std::asinh((b * z) / (a * std::sinh(z))) + z); point_end_right_.y = CoordinateY(LengthFromOrigin(point_end_right_), AxisDirectionType::kPositive); @@ -827,8 +819,8 @@ double Catenary3d::TangentAngleTransverse( Vector3d tangent_vector = TangentVector(position_fraction, direction); // adjusts components to use only positive values in unit circle - tangent_vector.set_y( std::abs(tangent_vector.y()) ); - tangent_vector.set_z( std::abs(tangent_vector.z()) ); + tangent_vector.set_y(std::abs(tangent_vector.y())); + tangent_vector.set_z(std::abs(tangent_vector.z())); // returns transverse angle return tangent_vector.Angle(Plane2dType::kZy, true); @@ -847,7 +839,7 @@ double Catenary3d::TangentAngleVertical( Vector3d tangent_vector = TangentVector(position_fraction, direction); // adjusts x component to use only positive values in unit circle - tangent_vector.set_x( std::abs(tangent_vector.x()) ); + tangent_vector.set_x(std::abs(tangent_vector.x())); // returns vertical angle return tangent_vector.Angle(Plane2dType::kXz, true); @@ -879,7 +871,8 @@ Vector3d Catenary3d::TangentVector(const double& position_fraction, Vector2d spacing_endpoints_2d = catenary_2d_.spacing_endpoints(); double angle_endpoints_2d = spacing_endpoints_2d.Angle(true); - double angle_endpoints_3d = spacing_endpoints_.Angle(Plane2dType::kXz, true); + double angle_endpoints_3d = spacing_endpoints_.Angle(Plane2dType::kXz, + true); tangent_vector.Rotate(Plane2dType::kXz, angle_endpoints_3d - angle_endpoints_2d); @@ -1102,8 +1095,9 @@ bool Catenary3d::UpdateCatenary2d() const { // solves for 2D catenary endpoint spacing Vector2d spacing_endpoints_2d; - spacing_endpoints_2d.set_y(b * (v/w)); - spacing_endpoints_2d.set_x(sqrt( pow(c,2)-pow(spacing_endpoints_2d.y(), 2))); + spacing_endpoints_2d.set_y(b * (v / w)); + spacing_endpoints_2d.set_x( + std::sqrt(std::pow(c,2) - std::pow(spacing_endpoints_2d.y(), 2))); // sets catenary spacing and unit weight catenary_2d_.set_spacing_endpoints(spacing_endpoints_2d); diff --git a/test/transmissionline/catenary_test.cc b/test/transmissionline/catenary_test.cc index 14cfc3d..10a5720 100644 --- a/test/transmissionline/catenary_test.cc +++ b/test/transmissionline/catenary_test.cc @@ -51,7 +51,7 @@ TEST_F(Catenary2dTest, Coordinate) { EXPECT_EQ(0, helper::Round(coord.x, 1)); EXPECT_EQ(0, helper::Round(coord.y, 1)); - // inclined geometry + // inclined geometry - origin between end points Vector2d spacing(1000, 100); c_.set_spacing_endpoints(spacing); @@ -66,6 +66,54 @@ TEST_F(Catenary2dTest, Coordinate) { coord = c_.Coordinate(1, true); EXPECT_EQ(1000, helper::Round(coord.x, 1)); EXPECT_EQ(100, helper::Round(coord.y, 1)); + + // inclined geometry - origin beyond end points + spacing = Vector2d(1000, 300); + c_.set_spacing_endpoints(spacing); + + coord = c_.Coordinate(0, true); + EXPECT_EQ(0, helper::Round(coord.x, 1)); + EXPECT_EQ(0, helper::Round(coord.y, 1)); + + coord = c_.Coordinate(0.5, true); + EXPECT_EQ(517.9, helper::Round(coord.x, 1)); + EXPECT_EQ(89.8, helper::Round(coord.y, 1)); + + coord = c_.Coordinate(1, true); + EXPECT_EQ(1000, helper::Round(coord.x, 1)); + EXPECT_EQ(300, helper::Round(coord.y, 1)); + + // declined geometry - origin between end points + spacing = Vector2d(1000, -100); + c_.set_spacing_endpoints(spacing); + + coord = c_.Coordinate(0, true); + EXPECT_EQ(0, helper::Round(coord.x, 1)); + EXPECT_EQ(0, helper::Round(coord.y, 1)); + + coord = c_.Coordinate(0.5, true); + EXPECT_EQ(493.8, helper::Round(coord.x, 1)); + EXPECT_EQ(-112.5, helper::Round(coord.y, 1)); + + coord = c_.Coordinate(1, true); + EXPECT_EQ(1000, helper::Round(coord.x, 1)); + EXPECT_EQ(-100, helper::Round(coord.y, 1)); + + // declined geometry - origin beyond end points + spacing = Vector2d(1000, -300); + c_.set_spacing_endpoints(spacing); + + coord = c_.Coordinate(0, true); + EXPECT_EQ(0, helper::Round(coord.x, 1)); + EXPECT_EQ(0, helper::Round(coord.y, 1)); + + coord = c_.Coordinate(0.5, true); + EXPECT_EQ(482.1, helper::Round(coord.x, 1)); + EXPECT_EQ(-210.2, helper::Round(coord.y, 1)); + + coord = c_.Coordinate(1, true); + EXPECT_EQ(1000, helper::Round(coord.x, 1)); + EXPECT_EQ(-300, helper::Round(coord.y, 1)); } TEST_F(Catenary2dTest, CoordinateChord) { From 3605b01464fc2e9a5a18ed60e8375442e4674ad8 Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Fri, 20 Oct 2017 19:48:25 -0700 Subject: [PATCH 06/11] Methods redirected to C++ library functions. - Without using std namespace, not sure if the method was using double precision parameters. --- src/base/geometric_shapes.cc | 2 +- src/base/helper.cc | 2 +- src/base/polynomial.cc | 4 ++-- src/base/units.cc | 2 +- src/base/vector.cc | 16 ++++++++-------- src/sagtension/cable_elongation_model.cc | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/base/geometric_shapes.cc b/src/base/geometric_shapes.cc index 8d2c687..d16474e 100644 --- a/src/base/geometric_shapes.cc +++ b/src/base/geometric_shapes.cc @@ -15,7 +15,7 @@ Cylinder::Cylinder() { Cylinder::~Cylinder() {} double Cylinder::AreaCrossSection() const { - return (units::kPi / 4) * pow(diameter_, 2); + return (units::kPi / 4) * std::pow(diameter_, 2); } bool Cylinder::Validate(const bool& is_included_warnings, diff --git a/src/base/helper.cc b/src/base/helper.cc index c292567..b04a461 100644 --- a/src/base/helper.cc +++ b/src/base/helper.cc @@ -68,7 +68,7 @@ std::list Parse(const std::string& str, } double Round(const double& number, const int& precision) { - return std::round(number * pow(10, precision)) / pow(10, precision); + return std::round(number * std::pow(10, precision)) / std::pow(10, precision); } } // namespace helper diff --git a/src/base/polynomial.cc b/src/base/polynomial.cc index 4d5b400..2ddb9c3 100644 --- a/src/base/polynomial.cc +++ b/src/base/polynomial.cc @@ -94,7 +94,7 @@ double Polynomial::X(const double& y, const int& decimal_precision_y, } // converts to decimal precision - const double precision_y = 1 / pow(10, decimal_precision_y); + const double precision_y = 1 / std::pow(10, decimal_precision_y); // gets a shifted polynomial so that y value is on x-axis std::vector coefficients = *coefficients_; @@ -135,7 +135,7 @@ double Polynomial::Y(const double& x) const { for (int order = 0; order <= order_max; order++) { const double& coefficient = coefficients_->at(order); - y = y + (coefficient * pow(x, order)); + y = y + (coefficient * std::pow(x, order)); } return y; diff --git a/src/base/units.cc b/src/base/units.cc index ded928d..177b458 100644 --- a/src/base/units.cc +++ b/src/base/units.cc @@ -59,7 +59,7 @@ double Convert(const double& value, const double& exponent, const bool& is_numerator) { // adjusts the factor based on the exponent - double factor_adj = pow(factor, exponent); + double factor_adj = std::pow(factor, exponent); // inverts factor if unit value is not in the numerator if (is_numerator == false) { diff --git a/src/base/vector.cc b/src/base/vector.cc index d722ab7..7f31e5d 100644 --- a/src/base/vector.cc +++ b/src/base/vector.cc @@ -60,21 +60,21 @@ double Vector2d::Angle(const bool& is_enabled_negative_angles) const { if (0 <= x_) { // quadrant I angle = units::ConvertAngle( - atan(y_ / x_), + std::atan(y_ / x_), units::AngleConversionType::kRadiansToDegrees); } else { // quadrant II angle = 180 - units::ConvertAngle( - std::abs(atan(y_ / x_)), + std::abs(std::atan(y_ / x_)), units::AngleConversionType::kRadiansToDegrees); } } else { if (x_ <= 0) { // quadrant III angle = 180 + units::ConvertAngle( - std::abs(atan(y_ / x_)), + std::abs(std::atan(y_ / x_)), units::AngleConversionType::kRadiansToDegrees); } else { // quadrant IV angle = 360 - units::ConvertAngle( - std::abs(atan(y_ / x_)), + std::abs(std::atan(y_ / x_)), units::AngleConversionType::kRadiansToDegrees); } } @@ -89,7 +89,7 @@ double Vector2d::Angle(const bool& is_enabled_negative_angles) const { } double Vector2d::Magnitude() const { - return sqrt( pow(x_, 2) + pow(y_, 2) ); + return sqrt(std::pow(x_, 2) + std::pow(y_, 2)); } /// Converts the vector into the radial coordinate system (magnitude and angle) @@ -111,8 +111,8 @@ void Vector2d::Rotate(const double& angle_rotation) { const double angle_radians = units::ConvertAngle(angle_new, units::AngleConversionType::kDegreesToRadians); - x_ = magnitude * cos(angle_radians); - y_ = magnitude * sin(angle_radians); + x_ = magnitude * std::cos(angle_radians); + y_ = magnitude * std::sin(angle_radians); } void Vector2d::Scale(const double& factor_scale) { @@ -185,7 +185,7 @@ double Vector3d::Angle(const Plane2dType& plane, } double Vector3d::Magnitude() const { - return sqrt( pow(x_, 2) + pow(y_, 2) + pow(z_, 2) ); + return sqrt(std::pow(x_, 2) + std::pow(y_, 2) + std::pow(z_, 2)); } /// The planar vector components are assigned to a 2D vector. The rotation is diff --git a/src/sagtension/cable_elongation_model.cc b/src/sagtension/cable_elongation_model.cc index c35b6a3..e41c724 100644 --- a/src/sagtension/cable_elongation_model.cc +++ b/src/sagtension/cable_elongation_model.cc @@ -351,7 +351,7 @@ double CableElongationModel::StrainCombined( // declares iteration variables // iterates until current point load matches target load int iter = 1; - double precision = 1 / pow(10, precision_decimal_load); + double precision = 1 / std::pow(10, precision_decimal_load); double slope_line =-999999; while ((precision < std::abs(point_current.y - load)) && (iter < 100)) { From 075430eaed5c18b4cb3bde0e19fe2ef1d7646bc8 Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Fri, 20 Oct 2017 19:49:35 -0700 Subject: [PATCH 07/11] Formatting fix. - Alphabetization --- .../cable_component_elongation_model.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/models/sagtension/cable_component_elongation_model.h b/include/models/sagtension/cable_component_elongation_model.h index 8d3a78c..197fc4a 100644 --- a/include/models/sagtension/cable_component_elongation_model.h +++ b/include/models/sagtension/cable_component_elongation_model.h @@ -427,6 +427,14 @@ class CableComponentElongationModel { /// The polynomial that describes the cable component elongation. Polynomial polynomial_loadstrain_; + /// \var state_ + /// The state of the component. + const CableState* state_; + + /// \var state_stretch_ + /// The state used to stretch the component. + const CableStretchState* state_stretch_; + /// \var strain_thermal_state_ /// The amount of strain due to shifting from reference temperature to the /// state temperature. @@ -437,14 +445,6 @@ class CableComponentElongationModel { /// stretch temperature. mutable double strain_thermal_stretch_; - /// \var state_ - /// The state of the component. - const CableState* state_; - - /// \var state_stretch_ - /// The state used to stretch the component. - const CableStretchState* state_stretch_; - /// \var temperature_reference_ /// The base reference temperature for the cable component. The polynomial /// is defined at this temperature. From bf0c0899591286265f49b50a4697880fbcf8d905 Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Fri, 20 Oct 2017 20:03:32 -0700 Subject: [PATCH 08/11] LineCableReloader refactored to use a base class. - Base class name is LineCableLoaderBase. Main purpose is to set up the LineCable for a sag-tension analysis. - With a base class in place, a LineCableUnloader can be developed. Many of the setup methods are going to be reused. --- .../sagtension/line_cable_loader_base.h | 149 ++++++ .../models/sagtension/line_cable_reloader.h | 129 +---- src/sagtension/line_cable_loader_base.cc | 418 +++++++++++++++++ src/sagtension/line_cable_reloader.cc | 440 ++---------------- .../sagtension/line_cable_loader_base_test.cc | 130 ++++++ test/sagtension/line_cable_reloader_test.cc | 111 +---- 6 files changed, 741 insertions(+), 636 deletions(-) create mode 100644 include/models/sagtension/line_cable_loader_base.h create mode 100644 src/sagtension/line_cable_loader_base.cc create mode 100644 test/sagtension/line_cable_loader_base_test.cc diff --git a/include/models/sagtension/line_cable_loader_base.h b/include/models/sagtension/line_cable_loader_base.h new file mode 100644 index 0000000..f34f076 --- /dev/null +++ b/include/models/sagtension/line_cable_loader_base.h @@ -0,0 +1,149 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to + +#ifndef OTLS_MODELS_SAGTENSION_LINECABLELOADERBASE_H_ +#define OTLS_MODELS_SAGTENSION_LINECABLELOADERBASE_H_ + +#include + +#include "models/base/error_message.h" +#include "models/sagtension/cable_elongation_model.h" +#include "models/sagtension/sag_tension_cable.h" +#include "models/transmissionline/catenary.h" +#include "models/transmissionline/line_cable.h" + +/// \par OVERVIEW +/// +/// This class sets up a LineCable for a sag-tension analysis. The process is +/// as follows: +/// - solve for a catenary based on the line cable constraint +/// - solve cable model for the constraint +/// - solve cable models for the stretch conditions +/// - determine the stretch load for each condition +/// +/// \par STRETCH +/// +/// This class supports multiple forms of stretch, including: +/// - short term, heavy load stretch +/// - long term, creep stretch +/// +/// \par LINE CABLE GEOMETRY +/// +/// This class uses the ruling span geometry defined in the line cable. +class LineCableLoaderBase { + public: + /// \brief Default constructor. + LineCableLoaderBase(); + + /// \brief Destructor. + ~LineCableLoaderBase(); + + /// \brief Gets the stretch state for the creep condition. + /// \return The stretch state for the creep condition. + CableStretchState StretchStateCreep() const; + + /// \brief Gets the stretch state for the load condition. + /// \return The stretch state for the load condition. + CableStretchState StretchStateLoad() const; + + /// \brief Validates member variables. + /// \param[in] is_included_warnings + /// A flag that tightens the acceptable value range. + /// \param[in,out] messages + /// A list of detailed error messages. If this is provided, any validation + /// errors will be appended to the list. + /// \return A boolean value indicating status of member variables. + bool Validate(const bool& is_included_warnings = true, + std::list* messages = nullptr) const; + + /// \brief Gets the line cable. + /// \return The line cable. + const LineCable* line_cable() const; + + /// \brief Sets the line cable. + /// \param[in] line_cable + /// The line cable. + void set_line_cable(const LineCable* line_cable); + + protected: + /// \brief Initializes the models defined in the line cable. + /// \return The success status of the update. + /// This function initializes the cable models with the correct temperature + /// and polynomial, but does not solve for any stretch. + bool InitializeLineCableModels() const; + + /// \brief Determines if class is updated. + /// \return A boolean indicating if class is updated. + virtual bool IsUpdated() const; + + /// \brief Calculates the unit load of the catenary. + /// \param[in] weathercase + /// The weather case. + /// \return The unit loading of the cable. + Vector3d UnitLoad(const WeatherLoadCase& weathercase) const; + + /// \brief Updates cached member variables and modifies control variables if + /// update is required. + /// \return A boolean indicating if class updates completed successfully. + virtual bool Update() const; + + /// \brief Updates the constraint cable model. + /// \return The success status of the update. + /// This function further updates the constraint cable model to include any + /// stretch. + bool UpdateConstraintCableModel() const; + + /// \brief Updates the constraint catenary. + /// \return The success status of the update. + bool UpdateConstraintCatenary() const; + + /// \brief Updates the stretch due to load and creep. + /// \return The success status of the update. + bool UpdateLoadStretch() const; + + /// \var cable_sagtension_ + /// The cable, which is referenced for sag-tension methods. + SagTensionCable cable_sagtension_; + + /// \var catenary_constraint_ + /// The catenary for the line cable constraint. + mutable Catenary3d catenary_constraint_; + + /// \var is_updated_catenary_constraint_ + /// An indicator that tells if the constraint catenary cable is updated. + mutable bool is_updated_catenary_constraint_; + + /// \var is_updated_stretch_ + /// An indicator that tells if the stretch load is updated. + mutable bool is_updated_stretch_; + + /// \var line_cable_ + /// The line cable that is being reloaded. + const LineCable* line_cable_; + + /// \var model_constraint_ + /// The cable model for the constraint weathercase. This model may contain + /// stretch, depending on the constraint condition. + mutable CableElongationModel model_constraint_; + + /// \var model_creep_ + /// The cable model used for solving creep stretch. This model will always + /// contain zero stretch to ensure that only the polynomial is referenced. + mutable CableElongationModel model_creep_; + + /// \var model_load_ + /// The cable model used for solving heavy load stretch. This model will + /// always contain zero stretch to ensure that only the polynomial is + /// referenced. + mutable CableElongationModel model_load_; + + /// \var state_stretch_creep_ + /// The stretch state for the creep condition. + mutable CableStretchState state_stretch_creep_; + + /// \var state_stretch_load_; + /// The stretch state for the load condition. + mutable CableStretchState state_stretch_load_; +}; + +#endif // OTLS_MODELS_SAGTENSION_LINECABLELOADERBASE_H_ diff --git a/include/models/sagtension/line_cable_reloader.h b/include/models/sagtension/line_cable_reloader.h index d48e248..deb25b9 100644 --- a/include/models/sagtension/line_cable_reloader.h +++ b/include/models/sagtension/line_cable_reloader.h @@ -7,32 +7,18 @@ #include #include "models/base/error_message.h" -#include "models/sagtension/cable_elongation_model.h" -#include "models/sagtension/sag_tension_cable.h" +#include "models/sagtension/line_cable_loader_base.h" #include "models/transmissionline/catenary.h" -#include "models/transmissionline/line_cable.h" /// \par OVERVIEW /// /// This class reloads a line cable to a specified weather case and condition. /// The process is as follows: -/// - solve for a catenary based on the line cable constraint -/// - build cable models for the constraint, creep-stretch, load-stretch, and -/// reloaded weathercases -/// - determine the stretch load for creep and load conditions -/// - reload the catenary (based on the line cable constraint) at the reloaded -/// weathercase -/// -/// \par STRETCH -/// -/// This class supports multiple forms of stretch, including: -/// - short term, heavy load stretch -/// - long term, creep stretch -/// -/// \par LINE CABLE GEOMETRY -/// -/// This class uses the ruling span geometry defined in the line cable. -class LineCableReloader { +/// - set up the line cable for sag-tension analysis (see LineCableLoaderBase) +/// - build a cable model for the reloaded state +/// - reload the constraint catenary to the reloaded state +/// (see CatenaryCableReloader) +class LineCableReloader : public LineCableLoaderBase { public: /// \brief Default constructor. LineCableReloader(); @@ -44,26 +30,10 @@ class LineCableReloader { /// \return A catenary at the reloaded load case. Catenary3d CatenaryReloaded() const; - /// \brief Gets the unloaded length of the constraint catenary. - /// \return The unloaded length of the constraint catenary. - double LengthUnloadedConstraint() const; - - /// \brief Gets the unloaded length of the reloaded catenary. - /// \return The unloaded length of the reloaded catenary. - double LengthUnloadedReloaded() const; - /// \brief Gets the reloaded cable state. /// \return The reloaded cable state. CableState StateReloaded() const; - /// \brief Gets the stretch state for the creep condition. - /// \return The stretch state for the creep condition. - CableStretchState StretchStateCreep() const; - - /// \brief Gets the stretch state for the load condition. - /// \return The stretch state for the load condition. - CableStretchState StretchStateLoad() const; - /// \brief Gets the average load of the cable component. /// \param[in] type_component /// The model component type. @@ -96,20 +66,11 @@ class LineCableReloader { /// \return The reloaded cable condition. CableConditionType condition_reloaded() const; - /// \brief Gets the line cable. - /// \return The line cable. - const LineCable* line_cable() const; - /// \brief Sets the reloaded cable condition. /// \param[in] condition_reloaded /// The reloaded cable condition. void set_condition_reloaded(const CableConditionType& condition_reloaded); - /// \brief Sets the line cable. - /// \param[in] line_cable - /// The line cable. - void set_line_cable(const LineCable* line_cable); - /// \brief Sets the reloaded load case. /// \param[in] weathercase_reloaded /// The reloaded weathercase. @@ -120,40 +81,14 @@ class LineCableReloader { const WeatherLoadCase* weathercase_reloaded() const; private: - /// \brief Initializes the models defined in the line cable. - /// \return The success status of the update. - /// This function initializes the cable models with the correct temperature - /// and polynomial, but does not solve for any stretch. - bool InitializeLineCableModels() const; - /// \brief Determines if class is updated. /// \return A boolean indicating if class is updated. - bool IsUpdated() const; - - /// \brief Calculates the unit load of the catenary. - /// \param[in] weathercase - /// The weather case. - /// \return The unit loading of the cable. - Vector3d UnitLoad(const WeatherLoadCase& weathercase) const; + virtual bool IsUpdated() const; /// \brief Updates cached member variables and modifies control variables if /// update is required. /// \return A boolean indicating if class updates completed successfully. - bool Update() const; - - /// \brief Updates the constraint cable model. - /// \return The success status of the update. - /// This function further updates the constraint cable model to include any - /// stretch. - bool UpdateConstraintCableModel() const; - - /// \brief Updates the constraint catenary. - /// \return The success status of the update. - bool UpdateConstraintCatenary() const; - - /// \brief Updates the stretch due to load and creep. - /// \return The success status of the update. - bool UpdateLoadStretch() const; + virtual bool Update() const; /// \brief Updates the reloaded cable model. /// \return The success status of the update. @@ -163,14 +98,6 @@ class LineCableReloader { /// \return The success status of the update. bool UpdateReloadedCatenary() const; - /// \var cable_sagtension_ - /// The cable, which is referenced for sag-tension methods. - SagTensionCable cable_sagtension_; - - /// \var catenary_constraint_ - /// The catenary for the line cable constraint. - mutable Catenary3d catenary_constraint_; - /// \var catenary_reloaded_ /// The catenary that is reloaded. mutable Catenary3d catenary_reloaded_; @@ -179,51 +106,19 @@ class LineCableReloader { /// The condition of the cable when loaded at the query load case. CableConditionType condition_reloaded_; - /// \var is_updated_catenary_constraint_ - /// An indicator that tells if the constraint catenary cable is updated. - mutable bool is_updated_catenary_constraint_; - /// \var is_updated_catenary_reloaded_ - /// An indicator that tells if the query catenary cable is updated. + /// An indicator that tells if the reloaded catenary is updated. mutable bool is_updated_catenary_reloaded_; - /// \var is_updated_stretch_ - /// An indicator that tells if the stretch load is updated. - mutable bool is_updated_stretch_; - - /// \var line_cable_ - /// The line cable that is being reloaded. - const LineCable* line_cable_; - - /// \var model_constraint_ - /// The cable model for the constraint weathercase. This model may contain - /// stretch, depending on the constraint condition. - mutable CableElongationModel model_constraint_; - - /// \var model_creep_ - /// The cable model used for solving creep stretch. This model will always - /// contain zero stretch to ensure that only the polynomial is referenced. - mutable CableElongationModel model_creep_; - - /// \var model_load_ - /// The cable model used for solving heavy load stretch. This model will - /// always contain zero stretch to ensure that only the polynomial is - /// referenced. - mutable CableElongationModel model_load_; + /// \var is_updated_model_reloaded_ + /// An indicator that tells if the reloaded cable model is updated. + mutable bool is_updated_model_reloaded_; /// \var model_reloaded_ /// The cable model for the reloaded weathercase. This model may contain /// stretch, depending on the reloaded condition. mutable CableElongationModel model_reloaded_; - /// \var state_stretch_creep_ - /// The stretch state for the creep condition. - mutable CableStretchState state_stretch_creep_; - - /// \var state_stretch_load_; - /// The stretch state for the load condition. - mutable CableStretchState state_stretch_load_; - /// \var weathercase_reloaded_ /// The load case that the cable is being reloaded to. const WeatherLoadCase* weathercase_reloaded_; diff --git a/src/sagtension/line_cable_loader_base.cc b/src/sagtension/line_cable_loader_base.cc new file mode 100644 index 0000000..788c734 --- /dev/null +++ b/src/sagtension/line_cable_loader_base.cc @@ -0,0 +1,418 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to + +#include "models/sagtension/line_cable_loader_base.h" + +#include + +#include "models/sagtension/catenary_cable_reloader.h" +#include "models/transmissionline/cable_unit_load_solver.h" + +LineCableLoaderBase::LineCableLoaderBase() { + line_cable_ = nullptr; + + is_updated_catenary_constraint_ = false; + is_updated_stretch_ = false; +} + +LineCableLoaderBase::~LineCableLoaderBase() { +} + +CableStretchState LineCableLoaderBase::StretchStateCreep() const { + // updates class if necessary + if (IsUpdated() == false) { + if (Update() == false) { + return CableStretchState(); + } + } + + return state_stretch_creep_; +} + +CableStretchState LineCableLoaderBase::StretchStateLoad() const { + // updates class if necessary + if (IsUpdated() == false) { + if (Update() == false) { + return CableStretchState(); + } + } + + return state_stretch_load_; +} + +bool LineCableLoaderBase::Validate( + const bool& is_included_warnings, + std::list* messages) const { + // initializes + bool is_valid = true; + ErrorMessage message; + message.title = "LINE CABLE LOADER BASE"; + + // validates cable-sagtension + if (cable_sagtension_.Validate(is_included_warnings, messages) == false) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid sag-tension cable"; + messages->push_back(message); + } + } + + // validates line cable + if (line_cable_ == nullptr) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid line cable"; + messages->push_back(message); + } + } else { + if (line_cable_->Validate(is_included_warnings, messages) == false) { + is_valid = false; + } + } + + // returns if errors are present + if (is_valid == false) { + return is_valid; + } + + // validates update process + if (Update() == false) { + is_valid = false; + if (messages != nullptr) { + message.description = ""; + if (is_updated_catenary_constraint_ == false) { + message.description = "Error updating class. Could not solve for " + "constraint catenary."; + } else if (is_updated_stretch_ == false) { + message.description = "Error updating class. Could not solve for " + "creep or load based stretch."; + } + messages->push_back(message); + } + } + + return is_valid; +} + +const LineCable* LineCableLoaderBase::line_cable() const { + return line_cable_; +} + +void LineCableLoaderBase::set_line_cable(const LineCable* line_cable) { + line_cable_ = line_cable; + + // updates sag-tension cable + if (line_cable == nullptr) { + cable_sagtension_.set_cable_base(nullptr); + } else { + cable_sagtension_.set_cable_base(line_cable_->cable()); + } + + is_updated_catenary_constraint_ = false; + is_updated_stretch_ = false; +} + +bool LineCableLoaderBase::InitializeLineCableModels() const { + // declares working variable + // all models are initialized with zero stretch + CableState state; + + CableStretchState state_stretch; + state_stretch.load = 0; + + // initializes the constraint cable model + CableConstraint constraint = line_cable_->constraint(); + state.temperature = constraint.case_weather->temperature_cable; + state.type_polynomial = SagTensionCableComponent::PolynomialType::kLoadStrain; + + if (constraint.condition == CableConditionType::kInitial) { + state_stretch.temperature = 0; // doesn't matter, model is never stretched + state_stretch.type_polynomial = + SagTensionCableComponent::PolynomialType::kLoadStrain; + } else if (constraint.condition == CableConditionType::kCreep) { + state_stretch.temperature = + line_cable_->weathercase_stretch_creep()->temperature_cable; + state_stretch.type_polynomial = + SagTensionCableComponent::PolynomialType::kCreep; + } else if (constraint.condition == CableConditionType::kLoad) { + state_stretch.temperature = + line_cable_->weathercase_stretch_load()->temperature_cable; + state_stretch.type_polynomial = + SagTensionCableComponent::PolynomialType::kLoadStrain; + } else { + return false; + } + + model_constraint_.set_cable(&cable_sagtension_); + model_constraint_.set_state(state); + model_constraint_.set_state_stretch(state_stretch); + + // updates the creep stretch model + state.temperature = + line_cable_->weathercase_stretch_creep()->temperature_cable; + state.type_polynomial = SagTensionCableComponent::PolynomialType::kCreep; + + state_stretch.temperature = state.temperature; + state_stretch.type_polynomial = + SagTensionCableComponent::PolynomialType::kCreep; + + model_creep_.set_cable(&cable_sagtension_); + model_creep_.set_state(state); + model_creep_.set_state_stretch(state_stretch); + + // updates the load stretch model + state.temperature = + line_cable_->weathercase_stretch_load()->temperature_cable; + state.type_polynomial = SagTensionCableComponent::PolynomialType::kLoadStrain; + + state_stretch.temperature = state.temperature; + state_stretch.type_polynomial = + SagTensionCableComponent::PolynomialType::kLoadStrain; + + model_load_.set_cable(&cable_sagtension_); + model_load_.set_state(state); + model_load_.set_state_stretch(state_stretch); + + return true; +} + +bool LineCableLoaderBase::IsUpdated() const { + if ((is_updated_catenary_constraint_ == true) + && (is_updated_stretch_ == true)) { + return true; + } else { + return false; + } +} + +Vector3d LineCableLoaderBase::UnitLoad( + const WeatherLoadCase& weathercase) const { + CableUnitLoadSolver solver; + solver.set_diameter_cable(&line_cable_->cable()->diameter); + solver.set_weight_unit_cable(&line_cable_->cable()->weight_unit); + + return solver.UnitCableLoad(weathercase); +} + +bool LineCableLoaderBase::Update() const { + // updates constraint catenary + is_updated_catenary_constraint_ = UpdateConstraintCatenary(); + if (is_updated_catenary_constraint_ == false) { + return false; + } + + InitializeLineCableModels(); + + is_updated_catenary_constraint_ = UpdateConstraintCableModel(); + if (is_updated_catenary_constraint_ == false) { + return false; + } + + // updates load-stretch + is_updated_stretch_ = UpdateLoadStretch(); + if (is_updated_stretch_ == false) { + return false; + } + + // if it reaches this point, update was successful + return true; +} + +/// This function checks the condition of the line cable constraint, and +/// determines if stretch is necessary to solve for. Solving for the stretch +/// requires an iterative approach. The line cable is reloaded from the +/// constraint cable model to the stretch cable model and loading. The solution +/// is found when the stretch load in the constraint cable model matches the +/// reloaded catenary average tension (which is reloaded at the stretch cable +/// model). +bool LineCableLoaderBase::UpdateConstraintCableModel() const { + // checks if no stretch is defined (initial modulus) + // constraint cable model was already initialized with zero stretch + if (line_cable_->constraint().condition == CableConditionType::kInitial) { + return true; + } + + // x = stretch load + // y = stretch load difference + // i.e.(reloaded catenary avg tension - constraint model stretch load) + + // iterative routine to determine solution + // solution reached when y = 0 + const double target_solution = 0; + + // initializes the stretch state + CableStretchState state_stretch = model_constraint_.state_stretch(); + + // initializes the reloader + Vector3d weight_unit_reloaded; + CatenaryCableReloader reloader; + reloader.set_catenary(&catenary_constraint_); + reloader.set_model_reference(&model_constraint_); + + if (line_cable_->constraint().condition == CableConditionType::kCreep) { + weight_unit_reloaded = UnitLoad(*line_cable_->weathercase_stretch_creep()); + + reloader.set_model_reloaded(&model_creep_); + reloader.set_weight_unit_reloaded(&weight_unit_reloaded); + } else if (line_cable_->constraint().condition == CableConditionType::kLoad) { + weight_unit_reloaded = UnitLoad(*line_cable_->weathercase_stretch_load()); + + reloader.set_model_reloaded(&model_load_); + reloader.set_weight_unit_reloaded(&weight_unit_reloaded); + } else { + // condition isn't recognized + return false; + } + + // initializes left point + Point2d point_left; + point_left.x = 0; + point_left.y = reloader.CatenaryReloaded().TensionAverage() + - state_stretch.load; + + // initializes right point + Point2d point_right; + point_right.x = *cable_sagtension_.strength_rated(); + + state_stretch.load = point_right.x; + model_constraint_.set_state_stretch(state_stretch); + reloader.set_model_reference(&model_constraint_); + + point_right.y = reloader.CatenaryReloaded().TensionAverage() + - state_stretch.load; + + // initializes current point + Point2d point_current; + + // iterates until target solution is reached + unsigned int iter = 1; + const int iter_max = 100; + const double precision = 0.01; + double slope_line = -999999; + + while ((precision < std::abs(point_current.y - target_solution)) + && (iter < iter_max)) { + + // solves for new stretch value is calculated for current point + slope_line = (point_right.y - point_left.y) + / (point_right.x - point_left.x); + point_current.x = point_left.x + + ((target_solution - point_left.y) / slope_line); + + // updates constraint model and reloader + state_stretch.load = point_current.x; + model_constraint_.set_state_stretch(state_stretch); + reloader.set_model_reference(&model_constraint_); + + // solves for new horizontal tension value for current point + point_current.y = reloader.CatenaryReloaded().TensionAverage() + - state_stretch.load; + + // current point is left of left/right points + if (point_current.x < point_left.x) { + + point_right.x = point_left.x; + point_right.y = point_left.y; + point_left.x = point_current.x; + point_left.y = point_current.y; + + // current point is between left/right points + } else if ((point_left.x <= point_current.x) + && (point_current.x <= point_right.x)) { + + if (point_current.y < target_solution) { + point_right.x = point_current.x; + point_right.y = point_current.y; + } else if (target_solution < point_current.y) { + point_left.x = point_current.x; + point_left.y = point_current.y; + } + + // current point is right of left/right points + } else if (point_right.x < point_current.x) { + + point_left.x = point_right.x; + point_left.y = point_right.y; + point_right.x = point_current.x; + point_right.y = point_current.y; + } + + iter++; + } + + // returns success status + if (iter < iter_max) { + return true; + } else { + // re-initializes constraint model stretch + state_stretch.load = 0; + model_constraint_.set_state_stretch(state_stretch); + return false; + } +} + +bool LineCableLoaderBase::UpdateConstraintCatenary() const { + Catenary3d catenary = line_cable_->CatenaryRulingSpan(); + if (catenary.Validate() == true) { + catenary_constraint_ = catenary; + return true; + } else { + return false; + } +} + +bool LineCableLoaderBase::UpdateLoadStretch() const { + // initializes working variables + CatenaryCableReloader reloader; + reloader.set_catenary(&catenary_constraint_); + reloader.set_model_reference(&model_constraint_); + + Vector3d weight_unit_stretch; + const WeatherLoadCase* weathercase_stretch = nullptr; + + // solves the stretch due to creep + if (line_cable_->constraint().condition == CableConditionType::kCreep) { + // stretch has already been solved for during constraint model update + state_stretch_creep_ = model_constraint_.state_stretch(); + } else { + // solves for the unit load at the creep stretch case + weathercase_stretch = line_cable_->weathercase_stretch_creep(); + weight_unit_stretch = UnitLoad(*weathercase_stretch); + + // reloads the constraint catenary to the creep stretch case + reloader.set_model_reloaded(&model_creep_); + reloader.set_weight_unit_reloaded(&weight_unit_stretch); + + if (reloader.Validate(false, nullptr) == false) { + return false; + } else { + state_stretch_creep_.load = reloader.CatenaryReloaded().TensionAverage(); + state_stretch_creep_.temperature = model_creep_.state().temperature; + state_stretch_creep_.type_polynomial = model_creep_.state().type_polynomial; + } + } + + // solves the stretch due to load + if (line_cable_->constraint().condition == CableConditionType::kLoad) { + // stretch has already been solved for during constraint model update + state_stretch_load_ = model_constraint_.state_stretch(); + } else { + // solves for the unit load at the load stretch case + weathercase_stretch = line_cable_->weathercase_stretch_load(); + weight_unit_stretch = UnitLoad(*weathercase_stretch); + + // reloads the constraint catenary to the load stretch case + reloader.set_model_reloaded(&model_load_); + reloader.set_weight_unit_reloaded(&weight_unit_stretch); + + if (reloader.Validate(false, nullptr) == false) { + return false; + } else { + state_stretch_load_.load = reloader.CatenaryReloaded().TensionAverage(); + state_stretch_load_.temperature = model_load_.state().temperature; + state_stretch_load_.type_polynomial = model_load_.state().type_polynomial; + } + } + + return true; +} diff --git a/src/sagtension/line_cable_reloader.cc b/src/sagtension/line_cable_reloader.cc index f64cdb4..54e14c9 100644 --- a/src/sagtension/line_cable_reloader.cc +++ b/src/sagtension/line_cable_reloader.cc @@ -3,20 +3,14 @@ #include "models/sagtension/line_cable_reloader.h" -#include - #include "models/sagtension/catenary_cable_reloader.h" -#include "models/sagtension/catenary_cable_unloader.h" -#include "models/transmissionline/cable_unit_load_solver.h" LineCableReloader::LineCableReloader() { condition_reloaded_ = CableConditionType::kNull; - line_cable_ = nullptr; weathercase_reloaded_ = nullptr; - is_updated_catenary_constraint_ = false; - is_updated_stretch_ = false; is_updated_catenary_reloaded_ = false; + is_updated_model_reloaded_ = false; } LineCableReloader::~LineCableReloader() { @@ -33,40 +27,6 @@ Catenary3d LineCableReloader::CatenaryReloaded() const { return catenary_reloaded_; } -double LineCableReloader::LengthUnloadedConstraint() const { - // updates class if necessary - if (IsUpdated() == false) { - if (Update() == false) { - return -999999; - } - } - - // creates a catenary cable unloader to solve for length - CatenaryCableUnloader unloader; - unloader.set_catenary(&catenary_constraint_); - unloader.set_model_reference(&model_constraint_); - unloader.set_model_unloaded(&model_constraint_); - - return unloader.LengthUnloaded(); -} - -double LineCableReloader::LengthUnloadedReloaded() const { - // updates class if necessary - if (IsUpdated() == false) { - if (Update() == false) { - return -999999; - } - } - - // creates a catenary cable unloader to solve for length - CatenaryCableUnloader unloader; - unloader.set_catenary(&catenary_reloaded_); - unloader.set_model_reference(&model_reloaded_); - unloader.set_model_unloaded(&model_reloaded_); - - return unloader.LengthUnloaded(); -} - CableState LineCableReloader::StateReloaded() const { // updates class if necessary if (IsUpdated() == false) { @@ -78,28 +38,6 @@ CableState LineCableReloader::StateReloaded() const { return model_reloaded_.state(); } -CableStretchState LineCableReloader::StretchStateCreep() const { - // updates class if necessary - if (IsUpdated() == false) { - if (Update() == false) { - return CableStretchState(); - } - } - - return state_stretch_creep_; -} - -CableStretchState LineCableReloader::StretchStateLoad() const { - // updates class if necessary - if (IsUpdated() == false) { - if (Update() == false) { - return CableStretchState(); - } - } - - return state_stretch_load_; -} - double LineCableReloader::TensionAverageComponent( const CableElongationModel::ComponentType& type_component) const { // updates class if necessary @@ -158,13 +96,9 @@ bool LineCableReloader::Validate( ErrorMessage message; message.title = "LINE CABLE RELOADER"; - // validates cable-sagtension - if (cable_sagtension_.Validate(is_included_warnings, messages) == false) { + // validates base class + if (LineCableLoaderBase::Validate(is_included_warnings, messages) == false) { is_valid = false; - if (messages != nullptr) { - message.description = "Invalid sag-tension cable"; - messages->push_back(message); - } } // validates condition-reloaded @@ -176,19 +110,6 @@ bool LineCableReloader::Validate( } } - // validates line cable - if (line_cable_ == nullptr) { - is_valid = false; - if (messages != nullptr) { - message.description = "Invalid line cable"; - messages->push_back(message); - } - } else { - if (line_cable_->Validate(is_included_warnings, messages) == false) { - is_valid = false; - } - } - // validates weathercase-reloaded if (weathercase_reloaded_ == nullptr) { is_valid = false; @@ -213,15 +134,12 @@ bool LineCableReloader::Validate( is_valid = false; if (messages != nullptr) { message.description = ""; - if (is_updated_catenary_constraint_ == false) { - message.description = "Error updating class. Could not solve for " - "constraint catenary."; - } else if (is_updated_stretch_ == false) { - message.description = "Error updating class. Could not solve for " - "creep or load based stretch."; - } else if (is_updated_catenary_reloaded_ == false) { + if (is_updated_catenary_reloaded_ == false) { message.description = "Error updating class. Could not solve for " "reloaded catenary."; + } else if (is_updated_model_reloaded_ == false) { + message.description = "Error updating class. Could not solve for " + "reloaded cable model."; } messages->push_back(message); } @@ -234,36 +152,19 @@ CableConditionType LineCableReloader::condition_reloaded() const { return condition_reloaded_; } -const LineCable* LineCableReloader::line_cable() const { - return line_cable_; -} - -void LineCableReloader::set_weathercase_reloaded( - const WeatherLoadCase* case_reloaded) { - weathercase_reloaded_ = case_reloaded; - - is_updated_catenary_reloaded_ = false; -} - void LineCableReloader::set_condition_reloaded( const CableConditionType& condition_reloaded) { condition_reloaded_ = condition_reloaded; + is_updated_model_reloaded_ = false; is_updated_catenary_reloaded_ = false; } -void LineCableReloader::set_line_cable(const LineCable* line_cable) { - line_cable_ = line_cable; - - // updates sag-tension cable - if (line_cable == nullptr) { - cable_sagtension_.set_cable_base(nullptr); - } else { - cable_sagtension_.set_cable_base(line_cable_->cable()); - } +void LineCableReloader::set_weathercase_reloaded( + const WeatherLoadCase* case_reloaded) { + weathercase_reloaded_ = case_reloaded; - is_updated_catenary_constraint_ = false; - is_updated_stretch_ = false; + is_updated_model_reloaded_ = false; is_updated_catenary_reloaded_ = false; } @@ -271,73 +172,9 @@ const WeatherLoadCase* LineCableReloader::weathercase_reloaded() const { return weathercase_reloaded_; } -bool LineCableReloader::InitializeLineCableModels() const { - // declare working variable - // all models are initialized with zero stretch - CableState state; - - CableStretchState state_stretch; - state_stretch.load = 0; - - // initializes the constraint cable model - CableConstraint constraint = line_cable_->constraint(); - state.temperature = constraint.case_weather->temperature_cable; - state.type_polynomial = SagTensionCableComponent::PolynomialType::kLoadStrain; - - if (constraint.condition == CableConditionType::kInitial) { - state_stretch.temperature = 0; // doesn't matter, model is never stretched - state_stretch.type_polynomial = - SagTensionCableComponent::PolynomialType::kLoadStrain; - } else if (constraint.condition == CableConditionType::kCreep) { - state_stretch.temperature = - line_cable_->weathercase_stretch_creep()->temperature_cable; - state_stretch.type_polynomial = - SagTensionCableComponent::PolynomialType::kCreep; - } else if (constraint.condition == CableConditionType::kLoad) { - state_stretch.temperature = - line_cable_->weathercase_stretch_load()->temperature_cable; - state_stretch.type_polynomial = - SagTensionCableComponent::PolynomialType::kLoadStrain; - } else { - return false; - } - - model_constraint_.set_cable(&cable_sagtension_); - model_constraint_.set_state(state); - model_constraint_.set_state_stretch(state_stretch); - - // updates the creep stretch model - state.temperature = - line_cable_->weathercase_stretch_creep()->temperature_cable; - state.type_polynomial = SagTensionCableComponent::PolynomialType::kCreep; - - state_stretch.temperature = state.temperature; - state_stretch.type_polynomial = - SagTensionCableComponent::PolynomialType::kCreep; - - model_creep_.set_cable(&cable_sagtension_); - model_creep_.set_state(state); - model_creep_.set_state_stretch(state_stretch); - - // updates the load stretch model - state.temperature = - line_cable_->weathercase_stretch_load()->temperature_cable; - state.type_polynomial = SagTensionCableComponent::PolynomialType::kLoadStrain; - - state_stretch.temperature = state.temperature; - state_stretch.type_polynomial = - SagTensionCableComponent::PolynomialType::kLoadStrain; - - model_load_.set_cable(&cable_sagtension_); - model_load_.set_state(state); - model_load_.set_state_stretch(state_stretch); - - return true; -} - bool LineCableReloader::IsUpdated() const { - if ((is_updated_catenary_constraint_ == true) - && (is_updated_stretch_ == true) + if ((LineCableLoaderBase::IsUpdated() == true) + && (is_updated_model_reloaded_ == true) && (is_updated_catenary_reloaded_ == true)) { return true; } else { @@ -345,46 +182,28 @@ bool LineCableReloader::IsUpdated() const { } } -Vector3d LineCableReloader::UnitLoad( - const WeatherLoadCase& weathercase) const { - CableUnitLoadSolver solver; - solver.set_diameter_cable(&line_cable_->cable()->diameter); - solver.set_weight_unit_cable(&line_cable_->cable()->weight_unit); - - return solver.UnitCableLoad(weathercase); -} - bool LineCableReloader::Update() const { - // updates constraint catenary - if (is_updated_catenary_constraint_ == false) { - is_updated_catenary_constraint_ = UpdateConstraintCatenary(); - if (is_updated_catenary_constraint_ == false) { - return false; - } - - InitializeLineCableModels(); + // updates base class + if (LineCableLoaderBase::IsUpdated() == false) { + // resets downstream update indicators + is_updated_model_reloaded_ = false; + is_updated_catenary_reloaded_ = false; - is_updated_catenary_constraint_ = UpdateConstraintCableModel(); - if (is_updated_catenary_constraint_ == false) { + if (LineCableLoaderBase::Update() == false) { return false; } } - // updates load-stretch - if (is_updated_stretch_ == false) { - is_updated_stretch_ = UpdateLoadStretch(); - if (is_updated_stretch_ == false) { + // updates reloaded cable model + if (is_updated_model_reloaded_ == false) { + is_updated_model_reloaded_ = UpdateReloadedCableModel(); + if (is_updated_model_reloaded_ == false) { return false; } } // updates reloaded catenary if (is_updated_catenary_reloaded_ == false) { - is_updated_catenary_reloaded_ = UpdateReloadedCableModel(); - if (is_updated_catenary_reloaded_ == false) { - return false; - } - is_updated_catenary_reloaded_ = UpdateReloadedCatenary(); if (is_updated_catenary_reloaded_ == false) { return false; @@ -395,207 +214,8 @@ bool LineCableReloader::Update() const { return true; } -/// This function checks the condition of the line cable constraint, and -/// determines if stretch is necessary to solve for. Solving for the stretch -/// requires an iterative approach. The line cable is reloaded from the -/// constraint cable model to the stretch cable model and loading. The solution -/// is found when the stretch load in the constraint cable model matches the -/// reloaded catenary average tension (which is reloaded at the stretch cable -/// model). -bool LineCableReloader::UpdateConstraintCableModel() const { - // checks if no stretch is defined (initial modulus) - // constraint cable model was already initialized with zero stretch - if (line_cable_->constraint().condition == CableConditionType::kInitial) { - return true; - } - - // x = stretch load - // y = stretch load difference - // i.e.(reloaded catenary avg tension - constraint model stretch load) - - // iterative routine to determine solution - // solution reached when y = 0 - const double target_solution = 0; - - // initializes the stretch state - CableStretchState state_stretch = model_constraint_.state_stretch(); - - // initializes the reloader - Vector3d weight_unit_reloaded; - CatenaryCableReloader reloader; - reloader.set_catenary(&catenary_constraint_); - reloader.set_model_reference(&model_constraint_); - - if (line_cable_->constraint().condition == CableConditionType::kCreep) { - weight_unit_reloaded = UnitLoad(*line_cable_->weathercase_stretch_creep()); - - reloader.set_model_reloaded(&model_creep_); - reloader.set_weight_unit_reloaded(&weight_unit_reloaded); - } else if (line_cable_->constraint().condition == CableConditionType::kLoad) { - weight_unit_reloaded = UnitLoad(*line_cable_->weathercase_stretch_load()); - - reloader.set_model_reloaded(&model_load_); - reloader.set_weight_unit_reloaded(&weight_unit_reloaded); - } else { - // condition isn't recognized - return false; - } - - // initializes left point - Point2d point_left; - point_left.x = 0; - point_left.y = reloader.CatenaryReloaded().TensionAverage() - - state_stretch.load; - - // initializes right point - Point2d point_right; - point_right.x = *cable_sagtension_.strength_rated(); - - state_stretch.load = point_right.x; - model_constraint_.set_state_stretch(state_stretch); - reloader.set_model_reference(&model_constraint_); - - point_right.y = reloader.CatenaryReloaded().TensionAverage() - - state_stretch.load; - - // initializes current point - Point2d point_current; - - // iterates until target solution is reached - unsigned int iter = 1; - const int iter_max = 100; - const double precision = 0.01; - double slope_line = -999999; - - while ((precision < std::abs(point_current.y - target_solution)) - && (iter < iter_max)) { - - // solves for new stretch value is calculated for current point - slope_line = (point_right.y - point_left.y) - / (point_right.x - point_left.x); - point_current.x = point_left.x - + ((target_solution - point_left.y) / slope_line); - - // updates constraint model and reloader - state_stretch.load = point_current.x; - model_constraint_.set_state_stretch(state_stretch); - reloader.set_model_reference(&model_constraint_); - - // solves for new horizontal tension value for current point - point_current.y = reloader.CatenaryReloaded().TensionAverage() - - state_stretch.load; - - // current point is left of left/right points - if (point_current.x < point_left.x) { - - point_right.x = point_left.x; - point_right.y = point_left.y; - point_left.x = point_current.x; - point_left.y = point_current.y; - - // current point is between left/right points - } else if ((point_left.x <= point_current.x) - && (point_current.x <= point_right.x)) { - - if (point_current.y < target_solution) { - point_right.x = point_current.x; - point_right.y = point_current.y; - } else if (target_solution < point_current.y) { - point_left.x = point_current.x; - point_left.y = point_current.y; - } - - // current point is right of left/right points - } else if (point_right.x < point_current.x) { - - point_left.x = point_right.x; - point_left.y = point_right.y; - point_right.x = point_current.x; - point_right.y = point_current.y; - } - - iter++; - } - - // returns success status - if (iter < iter_max) { - return true; - } else { - // re-initializes constraint model stretch - state_stretch.load = 0; - model_constraint_.set_state_stretch(state_stretch); - return false; - } -} - -bool LineCableReloader::UpdateConstraintCatenary() const { - Catenary3d catenary = line_cable_->CatenaryRulingSpan(); - if (catenary.Validate() == true) { - catenary_constraint_ = catenary; - return true; - } else { - return false; - } -} - -bool LineCableReloader::UpdateLoadStretch() const { - // initializes working variables - CatenaryCableReloader reloader; - reloader.set_catenary(&catenary_constraint_); - reloader.set_model_reference(&model_constraint_); - - Vector3d weight_unit_stretch; - const WeatherLoadCase* weathercase_stretch = nullptr; - - // solves the stretch due to creep - if (line_cable_->constraint().condition == CableConditionType::kCreep) { - // stretch has already been solved for during constraint model update - state_stretch_creep_ = model_constraint_.state_stretch(); - } else { - // solves for the unit load at the creep stretch case - weathercase_stretch = line_cable_->weathercase_stretch_creep(); - weight_unit_stretch = UnitLoad(*weathercase_stretch); - - // reloads the constraint catenary to the creep stretch case - reloader.set_model_reloaded(&model_creep_); - reloader.set_weight_unit_reloaded(&weight_unit_stretch); - - if (reloader.Validate(false, nullptr) == false) { - return false; - } else { - state_stretch_creep_.load = reloader.CatenaryReloaded().TensionAverage(); - state_stretch_creep_.temperature = model_creep_.state().temperature; - state_stretch_creep_.type_polynomial = model_creep_.state().type_polynomial; - } - } - - // solves the stretch due to load - if (line_cable_->constraint().condition == CableConditionType::kLoad) { - // stretch has already been solved for during constraint model update - state_stretch_load_ = model_constraint_.state_stretch(); - } else { - // solves for the unit load at the load stretch case - weathercase_stretch = line_cable_->weathercase_stretch_load(); - weight_unit_stretch = UnitLoad(*weathercase_stretch); - - // reloads the constraint catenary to the load stretch case - reloader.set_model_reloaded(&model_load_); - reloader.set_weight_unit_reloaded(&weight_unit_stretch); - - if (reloader.Validate(false, nullptr) == false) { - return false; - } else { - state_stretch_load_.load = reloader.CatenaryReloaded().TensionAverage(); - state_stretch_load_.temperature = model_load_.state().temperature; - state_stretch_load_.type_polynomial = model_load_.state().type_polynomial; - } - } - - return true; -} - bool LineCableReloader::UpdateReloadedCableModel() const { - // builds cable state based on reloaded weathercase and stretch + // builds cable states based on reloaded weathercase and stretch CableState state; state.temperature = weathercase_reloaded_->temperature_cable; state.type_polynomial = SagTensionCableComponent::PolynomialType::kLoadStrain; @@ -611,6 +231,8 @@ bool LineCableReloader::UpdateReloadedCableModel() const { state_stretch = state_stretch_creep_; } else if (condition_reloaded_ == CableConditionType::kLoad) { state_stretch = state_stretch_load_; + } else { + return false; } // updates model @@ -625,17 +247,17 @@ bool LineCableReloader::UpdateReloadedCatenary() const { // calculates reloaded unit weight Vector3d weight_unit_reloaded = UnitLoad(*weathercase_reloaded_); - // builds reloader and gets the calculates the reloaded catenary + // builds reloader and gets the reloaded catenary CatenaryCableReloader reloader; reloader.set_catenary(&catenary_constraint_); reloader.set_model_reference(&model_constraint_); reloader.set_model_reloaded(&model_reloaded_); reloader.set_weight_unit_reloaded(&weight_unit_reloaded); - if (reloader.Validate(false, nullptr) == false) { - return false; - } else { + if (reloader.Validate(false, nullptr) == true) { catenary_reloaded_ = reloader.CatenaryReloaded(); return true; + } else { + return false; } } diff --git a/test/sagtension/line_cable_loader_base_test.cc b/test/sagtension/line_cable_loader_base_test.cc new file mode 100644 index 0000000..2945d5b --- /dev/null +++ b/test/sagtension/line_cable_loader_base_test.cc @@ -0,0 +1,130 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to + +#include "models/sagtension/line_cable_loader_base.h" + +#include "gtest/gtest.h" + +#include "factory.h" +#include "models/base/helper.h" + +class LineCableLoaderBaseTest : public ::testing::Test { + protected: + LineCableLoaderBaseTest() { + // gets line cable from factory + linecable_ = factory::BuildLineCable(); + + // builds fixture object + l_.set_line_cable(linecable_); + } + + ~LineCableLoaderBaseTest() { + factory::DestroyLineCable(linecable_); + } + + // allocated dependency objects + LineCable* linecable_; + + // test object + LineCableLoaderBase l_; +}; + +TEST_F(LineCableLoaderBaseTest, StateStretchCreep) { + double value = -999999; + CableConstraint constraint = linecable_->constraint(); + CableStretchState state; + + // checks state with constraint type and limit set to initial condition + constraint.condition = CableConditionType::kInitial; + constraint.limit = 6000; + linecable_->set_constraint(constraint); + l_.set_line_cable(linecable_); + + state = l_.StretchStateCreep(); + value = state.temperature; + EXPECT_EQ(60, helper::Round(value, 0)); + EXPECT_EQ(SagTensionCableComponent::PolynomialType::kCreep, + state.type_polynomial); + value = state.load; + EXPECT_EQ(5595, helper::Round(value, 0)); + + // checks state with constraint type and limit set to load condition + constraint.condition = CableConditionType::kLoad; + constraint.limit = 5561.5; + linecable_->set_constraint(constraint); + l_.set_line_cable(linecable_); + + state = l_.StretchStateCreep(); + value = state.temperature; + EXPECT_EQ(60, helper::Round(value, 0)); + EXPECT_EQ(SagTensionCableComponent::PolynomialType::kCreep, + state.type_polynomial); + value = state.load; + EXPECT_EQ(5595, helper::Round(value, 0)); + + // checks state with constraint type and limit set to creep condition + constraint.condition = CableConditionType::kCreep; + constraint.limit = 5582.25; + linecable_->set_constraint(constraint); + l_.set_line_cable(linecable_); + + state = l_.StretchStateCreep(); + value = state.temperature; + EXPECT_EQ(60, helper::Round(value, 0)); + EXPECT_EQ(SagTensionCableComponent::PolynomialType::kCreep, + state.type_polynomial); + value = state.load; + EXPECT_EQ(5595, helper::Round(value, 0)); +} + +TEST_F(LineCableLoaderBaseTest, StateStretchLoad) { + double value = -999999; + CableConstraint constraint = linecable_->constraint(); + CableStretchState state; + + // checks state with constraint type and limit set to initial condition + constraint.condition = CableConditionType::kInitial; + constraint.limit = 6000; + linecable_->set_constraint(constraint); + l_.set_line_cable(linecable_); + + state = l_.StretchStateLoad(); + value = state.temperature; + EXPECT_EQ(0, helper::Round(value, 0)); + EXPECT_EQ(SagTensionCableComponent::PolynomialType::kLoadStrain, + state.type_polynomial); + value = state.load; + EXPECT_EQ(12179, helper::Round(value, 0)); + + // checks state with constraint type and limit set to load condition + constraint.condition = CableConditionType::kLoad; + constraint.limit = 5561.5; + linecable_->set_constraint(constraint); + l_.set_line_cable(linecable_); + + state = l_.StretchStateLoad(); + value = state.temperature; + EXPECT_EQ(0, helper::Round(value, 0)); + EXPECT_EQ(SagTensionCableComponent::PolynomialType::kLoadStrain, + state.type_polynomial); + value = state.load; + EXPECT_EQ(12179, helper::Round(value, 0)); + + // checks state with constraint type and limit set to creep condition + constraint.condition = CableConditionType::kCreep; + constraint.limit = 5582.25; + linecable_->set_constraint(constraint); + l_.set_line_cable(linecable_); + + state = l_.StretchStateLoad(); + value = state.temperature; + EXPECT_EQ(0, helper::Round(value, 0)); + EXPECT_EQ(SagTensionCableComponent::PolynomialType::kLoadStrain, + state.type_polynomial); + value = state.load; + EXPECT_EQ(12179, helper::Round(value, 0)); +} + +TEST_F(LineCableLoaderBaseTest, Validate) { + EXPECT_TRUE(l_.Validate(true, nullptr)); +} diff --git a/test/sagtension/line_cable_reloader_test.cc b/test/sagtension/line_cable_reloader_test.cc index e9136b4..449478a 100644 --- a/test/sagtension/line_cable_reloader_test.cc +++ b/test/sagtension/line_cable_reloader_test.cc @@ -49,26 +49,6 @@ TEST_F(LineCableReloaderTest, CatenaryReloaded) { // more reloading tests are done in the horizontal tension test } -TEST_F(LineCableReloaderTest, LengthUnloadedConstraint) { - const double value = l_.LengthUnloadedConstraint(); - EXPECT_EQ(1201.04, helper::Round(value, 2)); -} - -TEST_F(LineCableReloaderTest, LengthUnloadedReloaded) { - // updates reloader weathercase and condition - weathercase_reloaded_->description = "0-0-212"; - weathercase_reloaded_->thickness_ice = 0; - weathercase_reloaded_->density_ice = 0; - weathercase_reloaded_->pressure_wind = 0; - weathercase_reloaded_->temperature_cable = 212; - - l_.set_weathercase_reloaded(weathercase_reloaded_); - l_.set_condition_reloaded(CableConditionType::kLoad); - - const double value = l_.LengthUnloadedReloaded(); - EXPECT_EQ(1202.26, helper::Round(value, 2)); -} - TEST_F(LineCableReloaderTest, StateReloaded) { CableState state = l_.StateReloaded(); @@ -79,36 +59,6 @@ TEST_F(LineCableReloaderTest, StateReloaded) { state.type_polynomial); } -TEST_F(LineCableReloaderTest, StateStretchCreep) { - double value = -999999; - - CableStretchState state = l_.StretchStateCreep(); - - value = state.temperature; - EXPECT_EQ(60, helper::Round(value, 0)); - - EXPECT_EQ(SagTensionCableComponent::PolynomialType::kCreep, - state.type_polynomial); - - value = state.load; - EXPECT_EQ(5595, helper::Round(value, 0)); -} - -TEST_F(LineCableReloaderTest, StateStretchLoad) { - double value = -999999; - - CableStretchState state = l_.StretchStateLoad(); - - value = state.temperature; - EXPECT_EQ(0, helper::Round(value, 0)); - - EXPECT_EQ(SagTensionCableComponent::PolynomialType::kLoadStrain, - state.type_polynomial); - - value = state.load; - EXPECT_EQ(12179, helper::Round(value, 0)); -} - TEST_F(LineCableReloaderTest, TensionAverageComponent) { double value = -999999; @@ -123,14 +73,8 @@ TEST_F(LineCableReloaderTest, TensionAverageComponent) { TEST_F(LineCableReloaderTest, TensionHorizontal) { double value = -999999; - CableConstraint constraint = linecable_->constraint(); - - // changes the constraint type and limit to initial condition - constraint.condition = CableConditionType::kInitial; - constraint.limit = 6000; - linecable_->set_constraint(constraint); - l_.set_line_cable(linecable_); + // reloads at same case as constraint, checks all conditions weathercase_reloaded_->description = "0-0-60"; weathercase_reloaded_->thickness_ice = 0; weathercase_reloaded_->density_ice = 0; @@ -138,7 +82,6 @@ TEST_F(LineCableReloaderTest, TensionHorizontal) { weathercase_reloaded_->temperature_cable = 60; l_.set_weathercase_reloaded(weathercase_reloaded_); - // reloads at same case as constraint, checks all conditions l_.set_condition_reloaded(CableConditionType::kInitial); value = l_.TensionHorizontal(); EXPECT_EQ(6000, helper::Round(value, 0)); @@ -191,58 +134,6 @@ TEST_F(LineCableReloaderTest, TensionHorizontal) { l_.set_condition_reloaded(CableConditionType::kCreep); value = l_.TensionHorizontal(); EXPECT_EQ(17123, helper::Round(value, 0)); - - // changes the constraint type and limit to stretched load condition - constraint.condition = CableConditionType::kLoad; - constraint.limit = 5561.5; - linecable_->set_constraint(constraint); - l_.set_line_cable(linecable_); - - // reloads at same case as constraint, checks all conditions - weathercase_reloaded_->description = "0-0-60"; - weathercase_reloaded_->thickness_ice = 0; - weathercase_reloaded_->density_ice = 0; - weathercase_reloaded_->pressure_wind = 0; - weathercase_reloaded_->temperature_cable = 60; - l_.set_weathercase_reloaded(weathercase_reloaded_); - - l_.set_condition_reloaded(CableConditionType::kInitial); - value = l_.TensionHorizontal(); - EXPECT_EQ(6000, helper::Round(value, 0)); - - l_.set_condition_reloaded(CableConditionType::kLoad); - value = l_.TensionHorizontal(); - EXPECT_EQ(5561, helper::Round(value, 0)); - - l_.set_condition_reloaded(CableConditionType::kCreep); - value = l_.TensionHorizontal(); - EXPECT_EQ(5582, helper::Round(value, 0)); - - // changes the constraint type and limit to stretched creep condition - constraint.condition = CableConditionType::kCreep; - constraint.limit = 5582.25; - linecable_->set_constraint(constraint); - l_.set_line_cable(linecable_); - - // reloads at same case as constraint, checks all conditions - weathercase_reloaded_->description = "0-0-60"; - weathercase_reloaded_->thickness_ice = 0; - weathercase_reloaded_->density_ice = 0; - weathercase_reloaded_->pressure_wind = 0; - weathercase_reloaded_->temperature_cable = 60; - l_.set_weathercase_reloaded(weathercase_reloaded_); - - l_.set_condition_reloaded(CableConditionType::kInitial); - value = l_.TensionHorizontal(); - EXPECT_EQ(6000, helper::Round(value, 0)); - - l_.set_condition_reloaded(CableConditionType::kLoad); - value = l_.TensionHorizontal(); - EXPECT_EQ(5562, helper::Round(value, 0)); - - l_.set_condition_reloaded(CableConditionType::kCreep); - value = l_.TensionHorizontal(); - EXPECT_EQ(5582, helper::Round(value, 0)); } TEST_F(LineCableReloaderTest, TensionHorizontalComponent) { From 804c1139c56540c93804b487f6d54b7ffc0e0ab4 Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Fri, 20 Oct 2017 20:13:42 -0700 Subject: [PATCH 09/11] LineCableUnloader class added. --- .../models/sagtension/line_cable_unloader.h | 118 +++++++++ src/sagtension/line_cable_unloader.cc | 238 ++++++++++++++++++ test/sagtension/line_cable_unloader_test.cc | 69 +++++ 3 files changed, 425 insertions(+) create mode 100644 include/models/sagtension/line_cable_unloader.h create mode 100644 src/sagtension/line_cable_unloader.cc create mode 100644 test/sagtension/line_cable_unloader_test.cc diff --git a/include/models/sagtension/line_cable_unloader.h b/include/models/sagtension/line_cable_unloader.h new file mode 100644 index 0000000..767c6fb --- /dev/null +++ b/include/models/sagtension/line_cable_unloader.h @@ -0,0 +1,118 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to + +#ifndef OTLS_MODELS_SAGTENSION_LINECABLEUNLOADER_H_ +#define OTLS_MODELS_SAGTENSION_LINECABLEUNLOADER_H_ + +#include + +#include "models/base/error_message.h" +#include "models/sagtension/line_cable_loader_base.h" + +/// \par OVERVIEW +/// +/// This class unloads a line cable at a specific condition and temperature. +/// The process is as follows: +/// - set up the line cable for sag-tension analysis (see LineCableLoaderBase) +/// - build a cable model for the unloaded state +/// - unload the constraint catenary to the unloaded state +/// (see CatenaryCableUnloader) +class LineCableUnloader : public LineCableLoaderBase { + public: + /// \brief Default constructor. + LineCableUnloader(); + + /// \brief Destructor. + ~LineCableUnloader(); + + /// \brief Gets the unloaded length. + /// \return The unloaded length. + double LengthUnloaded() const; + + /// \brief Validates member variables. + /// \param[in] is_included_warnings + /// A flag that tightens the acceptable value range. + /// \param[in,out] messages + /// A list of detailed error messages. If this is provided, any validation + /// errors will be appended to the list. + /// \return A boolean value indicating status of member variables. + bool Validate(const bool& is_included_warnings = true, + std::list* messages = nullptr) const; + + /// \brief Gets the unloaded cable condition. + /// \return The unloaded cable condition. + CableConditionType condition_unloaded() const; + + /// \brief Sets the unloaded cable condition. + /// \param[in] condition_unloaded + /// The unloaded cable condition. + void set_condition_unloaded(const CableConditionType& condition_unloaded); + + /// \brief Sets the attachment spacing. + /// \param[in] spacing_attachments + /// The attachment spacing + void set_spacing_attachments(const Vector3d& spacing_attachments); + + /// \brief Sets the unloaded temperature. + /// \param[in] temperature_unloaded + /// The unloaded temperature. + void set_temperature_unloaded(const double& temperature_unloaded); + + /// \brief Gets the attachment spacing. + /// \return The attachment spacing. + Vector3d spacing_attachments() const; + + /// \brief Gets the unloaded temperature. + /// \return The unloaded temperature. + double temperature_unloaded() const; + + private: + /// \brief Determines if class is updated. + /// \return A boolean indicating if class is updated. + virtual bool IsUpdated() const; + + /// \brief Updates cached member variables and modifies control variables if + /// update is required. + /// \return A boolean indicating if class updates completed successfully. + virtual bool Update() const; + + /// \brief Updates the unloaded cable model. + /// \return The success status of the update. + bool UpdateUnloadedCableModel() const; + + /// \brief Updates the unloaded length. + /// \return The success status of the update. + bool UpdateUnloadedLength() const; + + /// \var condition_unloaded_ + /// The condition of the cable when unloaded. + CableConditionType condition_unloaded_; + + /// \var is_updated_length_unloaded_ + /// An indicator that tells if the unloaded cable length is updated. + mutable bool is_updated_length_unloaded_; + + /// \var is_updated_model_unloaded_ + /// An indicator that tells if the unloaded cable model is updated. + mutable bool is_updated_model_unloaded_; + + /// \var length_unloaded_ + /// The unloaded cable length. + mutable double length_unloaded_; + + /// \var model_unloaded_ + /// The cable model for the unloaded state. This model may contain stretch, + /// depending on the unloaded condition. + mutable CableElongationModel model_unloaded_; + + /// \var spacing_attachments_ + /// The spacing of the span attachments. The coordinates follows the + /// Catenary3d coordinate system. + Vector3d spacing_attachments_; + + /// \var temperature_unloaded_ + /// The temperature that the cable is being unloaded to. + double temperature_unloaded_; +}; + +#endif // OTLS_MODELS_SAGTENSION_LINECABLEUNLOADER_H_ diff --git a/src/sagtension/line_cable_unloader.cc b/src/sagtension/line_cable_unloader.cc new file mode 100644 index 0000000..264826d --- /dev/null +++ b/src/sagtension/line_cable_unloader.cc @@ -0,0 +1,238 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to + +#include "models/sagtension/line_cable_unloader.h" + +#include + +#include "models/sagtension/catenary_cable_unloader.h" + +LineCableUnloader::LineCableUnloader() { + condition_unloaded_ = CableConditionType::kNull; + spacing_attachments_ = Vector3d(); + temperature_unloaded_ = -999999; + + length_unloaded_ = -999999; + + is_updated_length_unloaded_ = false; + is_updated_model_unloaded_ = false; +} + +LineCableUnloader::~LineCableUnloader() { +} + +double LineCableUnloader::LengthUnloaded() const { + // updates class if necessary + if (IsUpdated() == false) { + if (Update() == false) { + return -999999; + } + } + + return length_unloaded_; +} + +bool LineCableUnloader::Validate( + const bool& is_included_warnings, + std::list* messages) const { + // initializes + bool is_valid = true; + ErrorMessage message; + message.title = "LINE CABLE UNLOADER"; + + // validates base class + if (LineCableLoaderBase::Validate(is_included_warnings, messages) == false) { + is_valid = false; + } + + // validates condition-unloaded + if (condition_unloaded_ == CableConditionType::kNull) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid unloaded condition"; + messages->push_back(message); + } + } + + // validates spacing-attachments + if (spacing_attachments_.x() <= 0) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid horizontal attachment spacing"; + messages->push_back(message); + } + } + + if (spacing_attachments_.y() != 0) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid transverse attachment spacing"; + messages->push_back(message); + } + } + + if (2000 < std::abs(spacing_attachments_.z())) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid vertical attachment spacing"; + messages->push_back(message); + } + } + + // validates temperature-unloaded + if (temperature_unloaded_ < -50) { + is_valid = false; + if (messages != nullptr) { + message.description = "Invalid unloaded temperature"; + messages->push_back(message); + } + } + + // returns if errors are present + if (is_valid == false) { + return is_valid; + } + + // validates update process + if (Update() == false) { + is_valid = false; + if (messages != nullptr) { + message.description = ""; + if (is_updated_length_unloaded_ == false) { + message.description = "Error updating class. Could not solve for " + "unloaded cable length."; + } else if (is_updated_model_unloaded_ == false) { + message.description = "Error updating class. Could not solve for " + "unloaded cable model."; + } + messages->push_back(message); + } + } + + return is_valid; +} + +CableConditionType LineCableUnloader::condition_unloaded() const { + return condition_unloaded_; +} + +void LineCableUnloader::set_condition_unloaded( + const CableConditionType& condition_unloaded) { + condition_unloaded_ = condition_unloaded; + + is_updated_model_unloaded_ = false; + is_updated_length_unloaded_ = false; +} + +void LineCableUnloader::set_spacing_attachments( + const Vector3d& spacing_attachments) { + spacing_attachments_ = spacing_attachments; + + is_updated_length_unloaded_ = false; +} + +void LineCableUnloader::set_temperature_unloaded( + const double& temperature_unloaded) { + temperature_unloaded_ = temperature_unloaded; + + is_updated_model_unloaded_ = false; + is_updated_length_unloaded_ = false; +} + +Vector3d LineCableUnloader::spacing_attachments() const { + return spacing_attachments_; +} + +double LineCableUnloader::temperature_unloaded() const { + return temperature_unloaded_; +} + +bool LineCableUnloader::IsUpdated() const { + if ((LineCableLoaderBase::IsUpdated() == true) + && (is_updated_model_unloaded_ == true) + && (is_updated_length_unloaded_ == true)) { + return true; + } else { + return false; + } +} + +bool LineCableUnloader::Update() const { + // updates base class + if (LineCableLoaderBase::IsUpdated() == false) { + // resets downstream update indicators + is_updated_model_unloaded_ = false; + is_updated_length_unloaded_ = false; + + if (LineCableLoaderBase::Update() == false) { + return false; + } + } + + // updates unloaded cable model + if (is_updated_model_unloaded_ == false) { + is_updated_model_unloaded_ = UpdateUnloadedCableModel(); + if (is_updated_model_unloaded_ == false) { + return false; + } + } + + // updates unloaded length + if (is_updated_length_unloaded_ == false) { + is_updated_length_unloaded_ = UpdateUnloadedLength(); + if (is_updated_length_unloaded_ == false) { + return false; + } + } + + // if it reaches this point, update was successful + return true; +} + +bool LineCableUnloader::UpdateUnloadedCableModel() const { + // builds cable states based on unloaded condition and temperature + CableState state; + state.temperature = temperature_unloaded_; + state.type_polynomial = SagTensionCableComponent::PolynomialType::kLoadStrain; + + CableStretchState state_stretch; + if (condition_unloaded_ == CableConditionType::kInitial) { + // stretch parameters don't matter, model isn't stretched + state_stretch.load = 0; + state_stretch.temperature = 0; + state_stretch.type_polynomial = + SagTensionCableComponent::PolynomialType::kLoadStrain; + } else if (condition_unloaded_ == CableConditionType::kCreep) { + state_stretch = state_stretch_creep_; + } else if (condition_unloaded_ == CableConditionType::kLoad) { + state_stretch = state_stretch_load_; + } else { + return false; + } + + // updates model + model_unloaded_.set_cable(&cable_sagtension_); + model_unloaded_.set_state(state); + model_unloaded_.set_state_stretch(state_stretch); + + return true; +} + +bool LineCableUnloader::UpdateUnloadedLength() const { + // builds catenary to unload + Catenary3d catenary = catenary_constraint_; + catenary.set_spacing_endpoints(spacing_attachments_); + + // builds unloader and gets the unloaded length + CatenaryCableUnloader unloader; + unloader.set_catenary(&catenary); + unloader.set_model_reference(&model_constraint_); + unloader.set_model_unloaded(&model_unloaded_); + + if (unloader.Validate(false, nullptr) == true) { + length_unloaded_ = unloader.LengthUnloaded(); + return true; + } else { + return false; + } +} diff --git a/test/sagtension/line_cable_unloader_test.cc b/test/sagtension/line_cable_unloader_test.cc new file mode 100644 index 0000000..6cb61bc --- /dev/null +++ b/test/sagtension/line_cable_unloader_test.cc @@ -0,0 +1,69 @@ +// This is free and unencumbered software released into the public domain. +// For more information, please refer to + +#include "models/sagtension/line_cable_unloader.h" + +#include "gtest/gtest.h" + +#include "factory.h" +#include "models/base/helper.h" + +class LineCableUnloaderTest : public ::testing::Test { + protected: + LineCableUnloaderTest() { + // gets a sag-tension cable from factory and removes compression + cable_ = factory::BuildCable(); + cable_->component_core.modulus_compression_elastic_area = 0; + cable_->component_shell.modulus_compression_elastic_area = 0; + + // gets line cable from factory + // replaces with custom cable + linecable_ = factory::BuildLineCable(); + linecable_->set_cable(cable_); + + // builds fixture object + l_.set_line_cable(linecable_); + l_.set_condition_unloaded(CableConditionType::kInitial); + l_.set_spacing_attachments(Vector3d(1500, 0, 0)); + l_.set_temperature_unloaded(60); + } + + ~LineCableUnloaderTest() { + factory::DestroyLineCable(linecable_); + } + + // allocated dependency objects + Cable* cable_; + LineCable* linecable_; + + // test object + LineCableUnloader l_; +}; + +TEST_F(LineCableUnloaderTest, LengthUnloaded) { + double value = -999999; + + // the following results aren't intuitive, but still provide a benchmark + // + // for this fixture temperature, the cable core defines the unloaded strain + // the cable core slope is above and below the tension modulus at varying + // strain points, making the unloaded lengths unpredictable between the + // different conditions + + // checks all conditions + l_.set_condition_unloaded(CableConditionType::kInitial); + value = l_.LengthUnloaded(); + EXPECT_EQ(1502.973, helper::Round(value, 3)); + + l_.set_condition_unloaded(CableConditionType::kCreep); + value = l_.LengthUnloaded(); + EXPECT_EQ(1502.951, helper::Round(value, 3)); + + l_.set_condition_unloaded(CableConditionType::kLoad); + value = l_.LengthUnloaded(); + EXPECT_EQ(1502.916, helper::Round(value, 3)); +} + +TEST_F(LineCableUnloaderTest, Validate) { + EXPECT_TRUE(l_.Validate(true, nullptr)); +} From 4ed2fea330f2a17eec866936f1a1c3ce2fee4733 Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Fri, 20 Oct 2017 20:21:54 -0700 Subject: [PATCH 10/11] Documentation fixes. - ThermalRating library files added to doxygen. - Fixed any mistakes that showed up. --- docs/doxygen_config | 2 ++ .../models/thermalrating/cable_heat_transfer_state.h | 2 +- .../models/thermalrating/solar_radiation_solver.h | 2 +- .../transient_cable_temperature_solver.h | 12 ++++++------ 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/doxygen_config b/docs/doxygen_config index e057230..9e29adf 100644 --- a/docs/doxygen_config +++ b/docs/doxygen_config @@ -745,9 +745,11 @@ WARN_LOGFILE = "./doxygen.log" INPUT = ../include/models/base \ ../include/models/sagtension \ + ../include/models/thermalrating \ ../include/models/transmissionline \ ../src/base \ ../src/sagtension \ + ../src/thermalrating \ ../src/transmissionline # This tag can be used to specify the character encoding of the source files diff --git a/include/models/thermalrating/cable_heat_transfer_state.h b/include/models/thermalrating/cable_heat_transfer_state.h index 6f4e4b3..9b7939a 100644 --- a/include/models/thermalrating/cable_heat_transfer_state.h +++ b/include/models/thermalrating/cable_heat_transfer_state.h @@ -51,7 +51,7 @@ struct CableHeatTransferState { /// The heat transfer due to solar. This should always be positive. double heat_solar; - /// \var heat_stored + /// \var heat_storage /// The heat transfer rate to/from the cable material (thermal storage). /// This can either be positive or negative. double heat_storage; diff --git a/include/models/thermalrating/solar_radiation_solver.h b/include/models/thermalrating/solar_radiation_solver.h index 4490622..a401c6c 100644 --- a/include/models/thermalrating/solar_radiation_solver.h +++ b/include/models/thermalrating/solar_radiation_solver.h @@ -217,7 +217,7 @@ class SolarRadiationSolver { /// x = solar altitude, y = solar radiation mutable std::vector coefficients_atmosphere_; - /// \var coefficients_altitude_ + /// \var coefficients_elevation_ /// The polynomial coefficients used to correct radiation based on /// elevation. /// x = elevation, y = solar radiation correction factor diff --git a/include/models/thermalrating/transient_cable_temperature_solver.h b/include/models/thermalrating/transient_cable_temperature_solver.h index 575b818..162ae5a 100644 --- a/include/models/thermalrating/transient_cable_temperature_solver.h +++ b/include/models/thermalrating/transient_cable_temperature_solver.h @@ -102,13 +102,13 @@ class TransientCableTemperatureSolver { void set_cable(const ThermalRatingCable* cable); /// \brief Sets the steady-state current. - /// \param[in] current - /// The electrical current. + /// \param[in] current_steady + /// The steady-state electrical current. void set_current_steady(const double& current_steady); /// \brief Sets the step current. - /// \param[in] current - /// The electrical current. + /// \param[in] current_step + /// The electrical current to step to. void set_current_step(const double& current_step); /// \brief Sets the time duration. @@ -187,7 +187,7 @@ class TransientCableTemperatureSolver { /// The current to step to. double current_step_; - /// \var duration + /// \var duration_ /// The time duration. int duration_; @@ -199,7 +199,7 @@ class TransientCableTemperatureSolver { /// The calculated temperature points. mutable std::list points_temperature_; - /// \var solver_heat_ + /// \var solver_ /// The heat transfer solver. mutable CableHeatTransferSolver solver_; From 15f4f6d01e7de3a091db218daa1e594a2043cc11 Mon Sep 17 00:00:00 2001 From: bretzel12 Date: Sat, 21 Oct 2017 09:28:49 -0700 Subject: [PATCH 11/11] Build files updated. --- build/codeblocks/otlsmodels_sagtension.cbp | 12 ++++++++++++ build/codeblocks/otlsmodels_transmissionline.cbp | 4 ++-- build/msvc/otlsmodels_sagtension.vcxproj | 4 ++++ build/msvc/otlsmodels_sagtension.vcxproj.filters | 12 ++++++++++++ build/msvc/otlsmodels_transmissionline.vcxproj | 4 ++-- .../msvc/otlsmodels_transmissionline.vcxproj.filters | 12 ++++++------ test/test.cbp | 8 +++++++- test/test.vcxproj | 4 +++- test/test.vcxproj.filters | 12 +++++++++--- 9 files changed, 57 insertions(+), 15 deletions(-) diff --git a/build/codeblocks/otlsmodels_sagtension.cbp b/build/codeblocks/otlsmodels_sagtension.cbp index f66b338..1d40fc6 100644 --- a/build/codeblocks/otlsmodels_sagtension.cbp +++ b/build/codeblocks/otlsmodels_sagtension.cbp @@ -65,12 +65,18 @@ + + + + @@ -92,12 +98,18 @@ + + + + diff --git a/build/codeblocks/otlsmodels_transmissionline.cbp b/build/codeblocks/otlsmodels_transmissionline.cbp index b7ea8d6..25f08a5 100644 --- a/build/codeblocks/otlsmodels_transmissionline.cbp +++ b/build/codeblocks/otlsmodels_transmissionline.cbp @@ -60,7 +60,7 @@ - + @@ -102,7 +102,7 @@ - + diff --git a/build/msvc/otlsmodels_sagtension.vcxproj b/build/msvc/otlsmodels_sagtension.vcxproj index 27c8784..d4aebdc 100644 --- a/build/msvc/otlsmodels_sagtension.vcxproj +++ b/build/msvc/otlsmodels_sagtension.vcxproj @@ -61,8 +61,10 @@ + + @@ -72,8 +74,10 @@ + + diff --git a/build/msvc/otlsmodels_sagtension.vcxproj.filters b/build/msvc/otlsmodels_sagtension.vcxproj.filters index f6b6cf6..455f0cd 100644 --- a/build/msvc/otlsmodels_sagtension.vcxproj.filters +++ b/build/msvc/otlsmodels_sagtension.vcxproj.filters @@ -38,6 +38,12 @@ Header Files + + Header Files + + + Header Files + @@ -67,5 +73,11 @@ Source Files + + Source Files + + + Source Files + \ No newline at end of file diff --git a/build/msvc/otlsmodels_transmissionline.vcxproj b/build/msvc/otlsmodels_transmissionline.vcxproj index 5b6c100..aa8436b 100644 --- a/build/msvc/otlsmodels_transmissionline.vcxproj +++ b/build/msvc/otlsmodels_transmissionline.vcxproj @@ -60,7 +60,7 @@ - + @@ -76,7 +76,7 @@ - + diff --git a/build/msvc/otlsmodels_transmissionline.vcxproj.filters b/build/msvc/otlsmodels_transmissionline.vcxproj.filters index 7fa2cf4..5a4504e 100644 --- a/build/msvc/otlsmodels_transmissionline.vcxproj.filters +++ b/build/msvc/otlsmodels_transmissionline.vcxproj.filters @@ -17,9 +17,6 @@ Header Files - - Header Files - Header Files @@ -53,6 +50,9 @@ Header Files + + Header Files + @@ -61,9 +61,6 @@ Source Files - - Source Files - Source Files @@ -97,5 +94,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/test/test.cbp b/test/test.cbp index f3207f2..2566acf 100644 --- a/test/test.cbp +++ b/test/test.cbp @@ -93,12 +93,18 @@ + + + + @@ -144,7 +150,7 @@ - + diff --git a/test/test.vcxproj b/test/test.vcxproj index 3d2e24d..a4e53d4 100644 --- a/test/test.vcxproj +++ b/test/test.vcxproj @@ -109,8 +109,10 @@ + + @@ -126,7 +128,7 @@ - + diff --git a/test/test.vcxproj.filters b/test/test.vcxproj.filters index 1c76a3d..3abf62e 100644 --- a/test/test.vcxproj.filters +++ b/test/test.vcxproj.filters @@ -75,9 +75,6 @@ Tests TransmissionLine - - Tests TransmissionLine - Tests TransmissionLine @@ -141,5 +138,14 @@ Tests ThermalRating + + Tests TransmissionLine + + + Tests SagTension + + + Tests SagTension + \ No newline at end of file