diff --git a/gui/source/post/CurvaturePlot.cpp b/gui/source/post/CurvaturePlot.cpp index cce3efc8..136a2be0 100644 --- a/gui/source/post/CurvaturePlot.cpp +++ b/gui/source/post/CurvaturePlot.cpp @@ -1,8 +1,8 @@ #include "CurvaturePlot.hpp" #include "pre/viewmodel/units/UnitSystem.hpp" -CurvaturePlot::CurvaturePlot(const LimbSetup& limb, const States& states) - : limb(limb), +CurvaturePlot::CurvaturePlot(const Common& common, const States& states) + : common(common), states(states), index(0), quantity_length(Quantities::length), @@ -31,9 +31,9 @@ void CurvaturePlot::updatePlot() { void CurvaturePlot::updateCurvature() { this->graph(0)->data()->clear(); - for(size_t i = 0; i < limb.length.size(); ++i) { + for(size_t i = 0; i < common.limb.length.size(); ++i) { this->graph(0)->addData( - quantity_length.getUnit().fromBase(limb.length[i]), + quantity_length.getUnit().fromBase(common.limb.length[i]), quantity_curvature.getUnit().fromBase(states.limb_strain[index][i][0]) ); } @@ -44,8 +44,8 @@ void CurvaturePlot::updateAxes() { this->yAxis->setLabel("Curvature " + quantity_curvature.getUnit().getLabel()); QCPRange x_range( - quantity_length.getUnit().fromBase(limb.length.front()), - quantity_length.getUnit().fromBase(limb.length.back()) + quantity_length.getUnit().fromBase(common.limb.length.front()), + quantity_length.getUnit().fromBase(common.limb.length.back()) ); QCPRange y_range( diff --git a/gui/source/post/CurvaturePlot.hpp b/gui/source/post/CurvaturePlot.hpp index 77ec9444..33a52411 100644 --- a/gui/source/post/CurvaturePlot.hpp +++ b/gui/source/post/CurvaturePlot.hpp @@ -5,11 +5,11 @@ class CurvaturePlot: public PlotWidget { public: - CurvaturePlot(const LimbSetup& limb, const States& states); + CurvaturePlot(const Common& common, const States& states); void setStateIndex(int i); private: - const LimbSetup& limb; + const Common& common; const States& states; int index; diff --git a/gui/source/post/OutputWidget.cpp b/gui/source/post/OutputWidget.cpp index 4e002eca..5467e631 100644 --- a/gui/source/post/OutputWidget.cpp +++ b/gui/source/post/OutputWidget.cpp @@ -85,21 +85,21 @@ StaticOutputWidget::StaticOutputWidget(const OutputData& data) numbers->addColumn(); numbers->addGroup("Performance"); numbers->addValue("Final draw force", data.statics->final_draw_force, Quantities::force); - numbers->addValue("Drawing work", data.statics->drawing_work, Quantities::energy); + numbers->addValue("Drawing work", data.statics->final_drawing_work, Quantities::energy); numbers->addValue("Energy storage factor", data.statics->storage_factor, Quantities::ratio); numbers->addGroup("Properties"); - numbers->addValue("Limb mass", data.setup.limb_mass, Quantities::mass); - numbers->addValue("String mass", data.setup.string_mass, Quantities::mass); - numbers->addValue("String length", data.setup.string_length, Quantities::length); + numbers->addValue("Limb mass", data.common.limb_mass, Quantities::mass); + numbers->addValue("String mass", data.common.string_mass, Quantities::mass); + numbers->addValue("String length", data.common.string_length, Quantities::length); numbers->addColumn(); numbers->addGroup("Minimum stress by layer"); - for(size_t i = 0; i < data.statics->min_layer_stresses.size(); ++i) { - numbers->addValue(QString::fromStdString("TODO"), std::get<0>(data.statics->min_layer_stresses[i]), Quantities::stress); + for(size_t i = 0; i < data.common.layers.size(); ++i) { + numbers->addValue(QString::fromStdString(data.common.layers[i].name), std::get<0>(data.statics->min_layer_stresses.at(i)), Quantities::stress); } numbers->addGroup("Maximum stress by layer"); - for(size_t i = 0; i < data.statics->max_layer_stresses.size(); ++i) { - numbers->addValue(QString::fromStdString("TODO"), std::get<0>(data.statics->max_layer_stresses[i]), Quantities::stress); + for(size_t i = 0; i < data.common.layers.size(); ++i) { + numbers->addValue(QString::fromStdString(data.common.layers[i].name), std::get<0>(data.statics->max_layer_stresses.at(i)), Quantities::stress); } numbers->addColumn(); @@ -107,11 +107,11 @@ StaticOutputWidget::StaticOutputWidget(const OutputData& data) numbers->addValue("Draw force", std::get<0>(data.statics->max_draw_force), Quantities::force); numbers->addValue("Grip force", std::get<0>(data.statics->max_grip_force), Quantities::force); numbers->addValue("String force (total)", std::get<0>(data.statics->max_string_force), Quantities::force); - numbers->addValue("String force (strand)", 0.0, Quantities::force); + numbers->addValue("String force (strand)", std::get<0>(data.statics->max_strand_force), Quantities::force); - auto plot_shapes = new ShapePlot(data.setup.limb, data.statics->states, 4); - auto plot_stress = new StressPlot(data.setup.limb, data.statics->states); - auto plot_curvature = new CurvaturePlot(data.setup.limb, data.statics->states); + auto plot_shapes = new ShapePlot(data.common, data.statics->states, 4); + auto plot_stress = new StressPlot(data.common, data.statics->states); + auto plot_curvature = new CurvaturePlot(data.common, data.statics->states); auto plot_energy = new EnergyPlot(data.statics->states, data.statics->states.draw_length, "Draw length", Quantities::length, Quantities::energy); auto plot_combo = new ComboPlot(); plot_combo->addData("Draw length", data.statics->states.draw_length, Quantities::length); @@ -163,50 +163,49 @@ StaticOutputWidget::~StaticOutputWidget() { DynamicOutputWidget::DynamicOutputWidget(const OutputData& data) : tabs(new QTabWidget()) { - /* auto numbers = new NumberGrid(); numbers->addColumn(); numbers->addGroup("Performance"); - numbers->addValue("Final arrow velocity", data.dynamics.final_vel_arrow, Quantities::velocity); - numbers->addValue("Degree of efficiency", data.dynamics.efficiency, Quantities::ratio); + numbers->addValue("Final arrow velocity", data.dynamics->final_arrow_vel, Quantities::velocity); + numbers->addValue("Degree of efficiency", data.dynamics->energy_efficiency, Quantities::ratio); numbers->addGroup("Energy at arrow departure"); - numbers->addValue("Kinetic energy arrow", data.dynamics.final_e_kin_arrow, Quantities::energy); - numbers->addValue("Kinetic energy limbs", data.dynamics.final_e_kin_limbs, Quantities::energy); - numbers->addValue("Kinetic energy string", data.dynamics.final_e_kin_string, Quantities::energy); + numbers->addValue("Kinetic energy arrow", data.dynamics->final_e_kin_arrow, Quantities::energy); + numbers->addValue("Kinetic energy limbs", data.dynamics->final_e_kin_limbs, Quantities::energy); + numbers->addValue("Kinetic energy string", data.dynamics->final_e_kin_string, Quantities::energy); numbers->addColumn(); numbers->addGroup("Minimum stress by layer"); - for(size_t i = 0; i < data.statics.min_stress_index.size(); ++i) { - numbers->addValue(QString::fromStdString(data.setup.limb_properties.layers[i].name), data.dynamics.min_stress_value[i], Quantities::stress); + for(size_t i = 0; i < data.common.layers.size(); ++i) { + numbers->addValue(QString::fromStdString(data.common.layers[i].name), std::get<0>(data.dynamics->min_layer_stresses.at(i)), Quantities::stress); } numbers->addGroup("Maximum stress by layer"); - for(size_t i = 0; i < data.statics.max_stress_index.size(); ++i) { - numbers->addValue(QString::fromStdString(data.setup.limb_properties.layers[i].name), data.dynamics.max_stress_value[i], Quantities::stress); + for(size_t i = 0; i < data.common.layers.size(); ++i) { + numbers->addValue(QString::fromStdString(data.common.layers[i].name), std::get<0>(data.dynamics->max_layer_stresses.at(i)), Quantities::stress); } numbers->addColumn(); numbers->addGroup("Maximum absolute forces"); - numbers->addValue("String force (total)", data.dynamics.states.string_force[data.dynamics.max_string_force_index], Quantities::force); - numbers->addValue("String force (strand)", data.dynamics.states.strand_force[data.dynamics.max_string_force_index], Quantities::force); - numbers->addValue("Grip force", data.dynamics.states.grip_force[data.dynamics.max_grip_force_index], Quantities::force); - - auto plot_shapes = new ShapePlot(data.setup.limb_properties, data.dynamics.states, 0); - auto plot_stress = new StressPlot(data.setup.limb_properties, data.dynamics.states); - auto plot_curvature = new CurvaturePlot(data.setup.limb_properties, data.dynamics.states); - auto plot_energy = new EnergyPlot(data.dynamics.states, data.dynamics.states.time, "Time", Quantities::time, Quantities::energy); + numbers->addValue("Grip force", std::get<0>(data.dynamics->max_grip_force), Quantities::force); + numbers->addValue("String force (total)", std::get<0>(data.dynamics->max_string_force), Quantities::force); + numbers->addValue("String force (strand)", std::get<0>(data.dynamics->max_strand_force), Quantities::force); + + auto plot_shapes = new ShapePlot(data.common, data.dynamics->states, 0); + auto plot_stress = new StressPlot(data.common, data.dynamics->states); + auto plot_curvature = new CurvaturePlot(data.common, data.dynamics->states); + auto plot_energy = new EnergyPlot(data.dynamics->states, data.dynamics->states.time, "Time", Quantities::time, Quantities::energy); auto plot_combo = new ComboPlot(); - plot_combo->addData("Time", data.dynamics.states.time, Quantities::time); - plot_combo->addData("Arrow position", data.dynamics.states.pos_arrow, Quantities::position); - plot_combo->addData("Arrow velocity", data.dynamics.states.vel_arrow, Quantities::velocity); - plot_combo->addData("Arrow acceleration", data.dynamics.states.acc_arrow, Quantities::acceleration); - plot_combo->addData("String force (total)", data.dynamics.states.string_force, Quantities::force); - plot_combo->addData("String force (strand)", data.dynamics.states.strand_force, Quantities::force); - plot_combo->addData("Grip force", data.dynamics.states.grip_force, Quantities::force); - plot_combo->addData("Pot. energy limbs", data.dynamics.states.e_pot_limbs, Quantities::energy); - plot_combo->addData("Kin. energy limbs", data.dynamics.states.e_kin_limbs, Quantities::energy); - plot_combo->addData("Pot. energy string", data.dynamics.states.e_pot_string, Quantities::energy); - plot_combo->addData("Kin. energy string", data.dynamics.states.e_kin_string, Quantities::energy); - plot_combo->addData("Kin. energy arrow", data.dynamics.states.e_kin_arrow, Quantities::energy); + plot_combo->addData("Time", data.dynamics->states.time, Quantities::time); + plot_combo->addData("Arrow position", data.dynamics->states.arrow_pos, Quantities::position); + plot_combo->addData("Arrow velocity", data.dynamics->states.arrow_vel, Quantities::velocity); + plot_combo->addData("Arrow acceleration", data.dynamics->states.arrow_acc, Quantities::acceleration); + plot_combo->addData("String force (total)", data.dynamics->states.string_force, Quantities::force); + plot_combo->addData("String force (strand)", data.dynamics->states.strand_force, Quantities::force); + plot_combo->addData("Grip force", data.dynamics->states.grip_force, Quantities::force); + plot_combo->addData("Pot. energy limbs", data.dynamics->states.e_pot_limbs, Quantities::energy); + plot_combo->addData("Kin. energy limbs", data.dynamics->states.e_kin_limbs, Quantities::energy); + plot_combo->addData("Pot. energy string", data.dynamics->states.e_pot_string, Quantities::energy); + plot_combo->addData("Kin. energy string", data.dynamics->states.e_kin_string, Quantities::energy); + plot_combo->addData("Kin. energy arrow", data.dynamics->states.e_kin_arrow, Quantities::energy); plot_combo->setCombination(0, 1); tabs->addTab(scrollArea(numbers), "Characteristics"); @@ -216,13 +215,13 @@ DynamicOutputWidget::DynamicOutputWidget(const OutputData& data) tabs->addTab(plot_energy, "Energy"); tabs->addTab(plot_combo, "Other Plots"); - auto slider = new Slider(data.dynamics.states.time, "Time", Quantities::time); - slider->addJumpAction("Arrow departure", data.dynamics.arrow_departure_index); - slider->addJumpAction("Max. grip force", data.dynamics.max_grip_force_index); - slider->addJumpAction("Max. string force", data.dynamics.max_string_force_index); - for (size_t i = 0; i < data.dynamics.max_stress_index.size(); ++i) { - slider->addJumpAction(QString::fromStdString("Max. stress for layer: " + data.setup.limb_properties.layers[i].name), data.dynamics.max_stress_index[i].first); - } + auto slider = new Slider(data.dynamics->states.time, "Time", Quantities::time); + //slider->addJumpAction("Arrow departure", data.dynamics.arrow_departure_index); + //slider->addJumpAction("Max. grip force", data.dynamics.max_grip_force_index); + //slider->addJumpAction("Max. string force", data.dynamics.max_string_force_index); + //for (size_t i = 0; i < data.dynamics.max_stress_index.size(); ++i) { + // slider->addJumpAction(QString::fromStdString("Max. stress for layer: " + data.setup.limb_properties.layers[i].name), data.dynamics.max_stress_index[i].first); + //} QObject::connect(slider, &Slider::indexChanged, plot_shapes, &ShapePlot::setStateIndex); QObject::connect(slider, &Slider::indexChanged, plot_stress, &StressPlot::setStateIndex); @@ -238,7 +237,6 @@ DynamicOutputWidget::DynamicOutputWidget(const OutputData& data) UserSettings settings; tabs->setCurrentIndex(settings.value("DynamicOutputWidget/selectedTab", tabs->currentIndex()).toInt()); - */ } DynamicOutputWidget::~DynamicOutputWidget() { diff --git a/gui/source/post/ShapePlot.cpp b/gui/source/post/ShapePlot.cpp index 0fbe8d34..2aad2ecf 100755 --- a/gui/source/post/ShapePlot.cpp +++ b/gui/source/post/ShapePlot.cpp @@ -1,8 +1,8 @@ #include "ShapePlot.hpp" #include "pre/viewmodel/units/UnitSystem.hpp" -ShapePlot::ShapePlot(const LimbSetup& limb, const States& states, int background_states) - : limb(limb), +ShapePlot::ShapePlot(const Common& common, const States& states, int background_states) + : common(common), states(states), quantity(Quantities::length), background_states(background_states), @@ -87,7 +87,7 @@ void ShapePlot::updateBackgroundStates() { if(intermediate_states >= 0) { // Unbraced state - plotLimbOutline(limb_left[intermediate_states], limb_right[intermediate_states], limb.position); + plotLimbOutline(limb_left[intermediate_states], limb_right[intermediate_states], common.limb.position); } } @@ -120,7 +120,7 @@ void ShapePlot::updateAxes() { } }; - expand3(limb.position); + expand3(common.limb.position); for(size_t i = 0; i < states.time.size(); ++i) { // Add 0.5*height as an estimated upper bound //expand(states.x_pos_limb[i] + 0.5*limb.height, states.y_pos_limb[i] + 0.5*limb.height); @@ -153,8 +153,8 @@ void ShapePlot::plotLimbOutline(QCPCurve* left, QCPCurve* right, const std::vect // Iterate backward and plot belly for(int i = position.size() - 1; i >= 0; --i) { - double xi = position[i][0] + limb.height[i]*sin(position[i][2]); - double yi = position[i][1] - limb.height[i]*cos(position[i][2]); + double xi = position[i][0] + common.limb.height[i]*sin(position[i][2]); + double yi = position[i][1] - common.limb.height[i]*cos(position[i][2]); left->addData( quantity.getUnit().fromBase(-xi), diff --git a/gui/source/post/ShapePlot.hpp b/gui/source/post/ShapePlot.hpp index 51367f9d..18ed0a07 100755 --- a/gui/source/post/ShapePlot.hpp +++ b/gui/source/post/ShapePlot.hpp @@ -5,11 +5,11 @@ class ShapePlot: public PlotWidget { public: - ShapePlot(const LimbSetup& limb, const States& states, int background_states); + ShapePlot(const Common& common, const States& states, int background_states); void setStateIndex(int i); private: - const LimbSetup& limb; + const Common& common; const States& states; const Quantity& quantity; diff --git a/gui/source/post/StressPlot.cpp b/gui/source/post/StressPlot.cpp index a6df74cc..5fba2c9e 100755 --- a/gui/source/post/StressPlot.cpp +++ b/gui/source/post/StressPlot.cpp @@ -16,8 +16,8 @@ const QList COLOR_PALETTE = { QColor("#17becf") }; -StressPlot::StressPlot(const LimbSetup& limb, const States& states) - : limb(limb), +StressPlot::StressPlot(const Common& common, const States& states) + : common(common), states(states), index(0), quantity_length(Quantities::length), @@ -25,9 +25,9 @@ StressPlot::StressPlot(const LimbSetup& limb, const States& states) { this->setupTopLegend(); - for(size_t iLayer = 0; iLayer < limb.layers.size(); ++iLayer) { - QString name = QString::fromStdString("TODO" /*limb.layers[i].name*/); - QColor color = COLOR_PALETTE[iLayer % COLOR_PALETTE.size()]; // Wrap around when colors are exhausted + for(size_t iLayer = 0; iLayer < common.layers.size(); ++iLayer) { + QString name = QString::fromStdString(common.layers[iLayer].name); + QColor color = COLOR_PALETTE[iLayer % COLOR_PALETTE.size()]; // Wrap around when all colors have been used this->addGraph(); this->graph(2*iLayer)->setName(name + " (back)"); @@ -56,17 +56,17 @@ void StressPlot::updatePlot() { } void StressPlot::updateStresses() { - for(size_t iLayer = 0; iLayer < limb.layers.size(); ++iLayer) { + for(size_t iLayer = 0; iLayer < common.layers.size(); ++iLayer) { this->graph(2*iLayer)->data()->clear(); this->graph(2*iLayer+1)->data()->clear(); - for(size_t iLength = 0; iLength < limb.layers[iLayer].length.size(); ++iLength) { + for(size_t iLength = 0; iLength < common.layers[iLayer].length.size(); ++iLength) { this->graph(2*iLayer)->addData( - quantity_length.getUnit().fromBase(limb.layers[iLayer].length[iLength]), + quantity_length.getUnit().fromBase(common.layers[iLayer].length[iLength]), quantity_stress.getUnit().fromBase(std::get<0>(states.layer_stress[index][iLayer][iLength])) ); this->graph(2*iLayer+1)->addData( - quantity_length.getUnit().fromBase(limb.layers[iLayer].length[iLength]), + quantity_length.getUnit().fromBase(common.layers[iLayer].length[iLength]), quantity_stress.getUnit().fromBase(std::get<1>(states.layer_stress[index][iLayer][iLength])) ); } @@ -78,8 +78,8 @@ void StressPlot::updateAxes() { this->yAxis->setLabel("Stress " + quantity_stress.getUnit().getLabel()); QCPRange x_range( - quantity_length.getUnit().fromBase(limb.length.front()), - quantity_length.getUnit().fromBase(limb.length.back()) + quantity_length.getUnit().fromBase(common.limb.length.front()), + quantity_length.getUnit().fromBase(common.limb.length.back()) ); QCPRange y_range( 0.0, diff --git a/gui/source/post/StressPlot.hpp b/gui/source/post/StressPlot.hpp index aa203b48..8eb4ec18 100755 --- a/gui/source/post/StressPlot.hpp +++ b/gui/source/post/StressPlot.hpp @@ -5,11 +5,11 @@ class StressPlot: public PlotWidget { public: - StressPlot(const LimbSetup& limb, const States& states); + StressPlot(const Common& common, const States& states); void setStateIndex(int i); private: - const LimbSetup& limb; + const Common& common; const States& states; int index; diff --git a/gui/source/solver/model/output/OutputData.hpp b/gui/source/solver/model/output/OutputData.hpp index 19fc320a..93aecbe8 100644 --- a/gui/source/solver/model/output/OutputData.hpp +++ b/gui/source/solver/model/output/OutputData.hpp @@ -15,7 +15,7 @@ struct nlohmann::adl_serializer> { } } static void to_json(json & json, std::optional t) { - if (t) { + if(t) { json = *t; } else { json = nullptr; @@ -23,24 +23,31 @@ struct nlohmann::adl_serializer> { } }; -struct LayerSetup { +struct LayerInfo { + std::string name; std::vector length; }; -struct LimbSetup { - // Layer properties - std::vector layers; - - // Geometry at eval points +struct LimbInfo { std::vector length; std::vector> position; std::vector width; std::vector height; }; +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LayerInfo, name, length) +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LimbInfo, length, position, width, height) + +struct Common { + LimbInfo limb; + std::vector layers; + + double limb_mass; + double string_mass; + double string_length; +}; -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LayerSetup, length) -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LimbSetup, length, position, width, height, layers) +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Common, limb, layers, limb_mass, string_mass, string_length) struct States { std::vector time; @@ -104,23 +111,15 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( strand_force ) -struct Setup { - LimbSetup limb; - double limb_mass; - double string_mass; - double string_length; -}; - -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Setup, limb, limb_mass, string_mass, string_length) - struct Statics { States states; double final_draw_force; - double drawing_work; + double final_drawing_work; double storage_factor; std::tuple max_string_force; + std::tuple max_strand_force; std::tuple max_grip_force; std::tuple max_draw_force; @@ -132,9 +131,10 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( Statics, states, final_draw_force, - drawing_work, + final_drawing_work, storage_factor, max_string_force, + max_strand_force, max_grip_force, max_draw_force, min_layer_stresses, @@ -156,6 +156,7 @@ struct Dynamics { double energy_efficiency; std::tuple max_string_force; + std::tuple max_strand_force; std::tuple max_grip_force; std::tuple max_draw_force; @@ -175,6 +176,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( final_e_kin_string, energy_efficiency, max_string_force, + max_strand_force, max_grip_force, max_draw_force, min_layer_stresses, @@ -182,7 +184,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE( ) struct OutputData { - Setup setup; + Common common; std::optional statics; std::optional dynamics; @@ -192,4 +194,4 @@ struct OutputData { void save(const std::string& path) const; }; -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(OutputData, setup, statics, dynamics) +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(OutputData, common, statics, dynamics) diff --git a/solver/bows/tests/invalid_msgpack_2.json b/solver/bows/tests/invalid_msgpack_2.json deleted file mode 100644 index d97c604b..00000000 --- a/solver/bows/tests/invalid_msgpack_2.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": 3, - "setup": { - "limb": { - "layers": [], - "length": [], - "position": [], - "width": [], - "height": [], - "density": [], - "stiffness": [] - } - }, - "statics": "yes please", - "dynamics": true -} diff --git a/solver/bows/tests/invalid_version.json b/solver/bows/tests/invalid_version.json deleted file mode 100644 index fd17baa5..00000000 --- a/solver/bows/tests/invalid_version.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": "abc", - "setup": { - "limb": { - "layers": [], - "length": [], - "position": [], - "width": [], - "height": [], - "density": [], - "stiffness": [] - } - }, - "statics": null, - "dynamics": null -} diff --git a/solver/bows/tests/saved.json b/solver/bows/tests/saved.json deleted file mode 100644 index 7e7a1fbd..00000000 --- a/solver/bows/tests/saved.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": 3, - "setup": { - "limb": { - "layers": [], - "length": [], - "position": [], - "width": [], - "height": [], - "density": [], - "stiffness": [] - } - }, - "statics": null, - "dynamics": null -} \ No newline at end of file diff --git a/solver/bows/tests/saved.res b/solver/bows/tests/saved.res index 1a8ba1d0..262a2bc1 100644 Binary files a/solver/bows/tests/saved.res and b/solver/bows/tests/saved.res differ diff --git a/solver/bows/tests/valid.res b/solver/bows/tests/valid.res index 1a8ba1d0..262a2bc1 100644 Binary files a/solver/bows/tests/valid.res and b/solver/bows/tests/valid.res differ diff --git a/solver/src/bin.rs b/solver/src/bin.rs index a2671d85..5268558c 100644 --- a/solver/src/bin.rs +++ b/solver/src/bin.rs @@ -1,6 +1,6 @@ use virtualbow::bow::simulation::{Simulation, SimulationMode}; use virtualbow::bow::errors::ModelError; -use virtualbow::bow::model::BowModel; +use virtualbow::bow::input::BowInput; use clap::Parser; @@ -35,7 +35,7 @@ impl Args { // Performs the desired simulation according to the command line arguments // and save the results to the specified output path fn execute(&self) -> Result<(), ModelError> { - let model = BowModel::load(&self.input)?; + let model = BowInput::load(&self.input)?; let output = Simulation::simulate(&model, self.mode, |stage, progress| { if self.progress { diff --git a/solver/src/bow/compatibility.rs b/solver/src/bow/compatibility.rs index fb4c93df..5ac05de1 100644 --- a/solver/src/bow/compatibility.rs +++ b/solver/src/bow/compatibility.rs @@ -4,7 +4,7 @@ use std::hash::{Hash, Hasher}; use itertools::Itertools; use serde_json::{json, Value}; use crate::bow::errors::ModelError; -use crate::bow::model::BowModel; +use crate::bow::input::BowInput; // Converts the json definition of a bow file to the currently required format, if necessary and possible. // Each time the input format changes, the version counter must be increased and a new conversion function must be added. @@ -12,17 +12,17 @@ use crate::bow::model::BowModel; pub fn convert_to_current_format(value: &mut Value) -> Result<(), ModelError> { let version = get_file_version(value)?; - if version > BowModel::FILE_VERSION { + if version > BowInput::FILE_VERSION { return Err(ModelError::InputVersionTooNew(version)); } - for current in version..BowModel::FILE_VERSION { + for current in version..BowInput::FILE_VERSION { match current { 0 => convert_v0_to_v1(value)?, 1 => convert_v1_to_v2(value)?, 2 => convert_v2_to_v3(value)?, - BowModel::FILE_VERSION => (), - BowModel::FILE_VERSION.. => unreachable!(), + BowInput::FILE_VERSION => (), + BowInput::FILE_VERSION.. => unreachable!(), } } @@ -64,8 +64,8 @@ fn get_application_version<'a>(version: u64) -> Option<&'a str> { 0 => Some("0.7.0"), 1 => Some("0.8.0"), 2 => Some("0.9.0"), - BowModel::FILE_VERSION => Some(env!("CARGO_PKG_VERSION")), - BowModel::FILE_VERSION.. => unreachable!(), + BowInput::FILE_VERSION => Some(env!("CARGO_PKG_VERSION")), + BowInput::FILE_VERSION.. => unreachable!(), } } @@ -255,7 +255,7 @@ fn convert_v0_to_v1(value: &mut Value) -> Result<(), ModelError> { mod tests { use std::io::Error; use itertools::Itertools; - use crate::bow::model::BowModel; + use crate::bow::input::BowInput; #[test] fn test_model_conversion() -> Result<(), Error> { @@ -276,9 +276,9 @@ mod tests { .filter(|path| path.extension().map(|s| s == "bow").unwrap()) .map(|file| { println!("\t- Load {:?}", file.file_name().unwrap()); - BowModel::load(file).expect("Failed to load model") + BowInput::load(file).expect("Failed to load model") }) - .collect::>(); + .collect::>(); // Compare loaded models for equality models.iter().tuple_windows().for_each(|(a, b)| { @@ -287,13 +287,13 @@ mod tests { // Save model file in the latest version let model = models.last().unwrap(); - let file = &path.join(format!("v{}.bow", BowModel::FILE_VERSION)); + let file = &path.join(format!("v{}.bow", BowInput::FILE_VERSION)); println!("\t- Save {:?}", file.file_name().unwrap()); model.save(file).unwrap(); // Load it again and check for equality println!("\t- Load {:?}", file.file_name().unwrap()); - let loaded = BowModel::load(file).expect("Failed to load model"); + let loaded = BowInput::load(file).expect("Failed to load model"); assert_eq!(loaded, *model, "Model data must be equal"); } } diff --git a/solver/src/bow/model.rs b/solver/src/bow/input.rs similarity index 95% rename from solver/src/bow/model.rs rename to solver/src/bow/input.rs index d02948ab..bb1249f3 100644 --- a/solver/src/bow/model.rs +++ b/solver/src/bow/input.rs @@ -12,7 +12,7 @@ use crate::bow::profile::segments::clothoid::LineInput; use crate::bow::versioning::{VersionedWrapper, VersionedWrapperRef}; #[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] -pub struct BowModel { +pub struct BowInput { pub comment: String, pub settings: Settings, pub dimensions: Dimensions, @@ -25,12 +25,12 @@ pub struct BowModel { pub damping: Damping, } -impl BowModel { +impl BowInput { // Current version of the bow model format, increase when incompatible changes are made. pub const FILE_VERSION: u64 = 3; // Loads a bow bow from a json file, including necessary conversions from older file formats for compatibility. - pub fn load>(path: P) -> Result { + pub fn load>(path: P) -> Result { let file = File::open(&path).map_err(|e| ModelError::InputLoadFileError(path.as_ref().to_owned(), e))?; let mut reader = BufReader::new(file); @@ -75,7 +75,7 @@ impl BowModel { } } -impl Default for BowModel { +impl Default for BowInput { // Valid default values for a new bow bow fn default() -> Self { Self { @@ -441,45 +441,45 @@ impl Damping { #[cfg(test)] mod tests { - use crate::bow::model::BowModel; + use crate::bow::input::BowInput; use crate::bow::errors::ModelError; #[test] fn test_load_model() { // IO error when loading from an invalid path - assert_matches!(BowModel::load("bows/tests/nonexistent.bow"), Err(ModelError::InputLoadFileError(_, _))); + assert_matches!(BowInput::load("bows/tests/nonexistent.bow"), Err(ModelError::InputLoadFileError(_, _))); // Deserialization error due to invalid file contents (no valid json) - assert_matches!(BowModel::load("bows/tests/invalid_json_1.bow"), Err(ModelError::InputDeserializeJsonError(_))); + assert_matches!(BowInput::load("bows/tests/invalid_json_1.bow"), Err(ModelError::InputDeserializeJsonError(_))); // Deserialization error due to invalid file contents (valid json but invalid structure) - assert_matches!(BowModel::load("bows/tests/invalid_json_2.bow"), Err(ModelError::InputInterpretJsonError(_))); + assert_matches!(BowInput::load("bows/tests/invalid_json_2.bow"), Err(ModelError::InputInterpretJsonError(_))); // Error when loading a bow file without version entry - assert_matches!(BowModel::load("bows/tests/no_version.bow"), Err(ModelError::InputVersionNotFound)); + assert_matches!(BowInput::load("bows/tests/no_version.bow"), Err(ModelError::InputVersionNotFound)); // Error when loading a bow files with an invalid version entry (wrong type) - assert_matches!(BowModel::load("bows/tests/invalid_version_1.bow"), Err(ModelError::InputVersionInvalid(_))); + assert_matches!(BowInput::load("bows/tests/invalid_version_1.bow"), Err(ModelError::InputVersionInvalid(_))); // Error when loading a bow files with an invalid version entry (version does not exist) - assert_matches!(BowModel::load("bows/tests/invalid_version_2.bow"), Err(ModelError::InputVersionInvalid(_))); + assert_matches!(BowInput::load("bows/tests/invalid_version_2.bow"), Err(ModelError::InputVersionInvalid(_))); // Error when loading a bow file with a version that is too old - assert_matches!(BowModel::load("bows/tests/old_version.bow"), Err(ModelError::InputVersionTooOld(_))); + assert_matches!(BowInput::load("bows/tests/old_version.bow"), Err(ModelError::InputVersionTooOld(_))); // Error when loading a bow file with a version that is too new - assert_matches!(BowModel::load("bows/tests/new_version.bow"), Err(ModelError::InputVersionTooNew(_))); + assert_matches!(BowInput::load("bows/tests/new_version.bow"), Err(ModelError::InputVersionTooNew(_))); // Error when loading a bow file that can not be converted to the current format - assert_matches!(BowModel::load("bows/tests/inconvertible.bow"), Err(ModelError::InputConversionError(_, _, _))); + assert_matches!(BowInput::load("bows/tests/inconvertible.bow"), Err(ModelError::InputConversionError(_, _, _))); // No error when loading a valid bow model - assert_matches!(BowModel::load("bows/tests/valid.bow"), Ok(_)); + assert_matches!(BowInput::load("bows/tests/valid.bow"), Ok(_)); } #[test] fn test_save_model() { - let model = BowModel::default(); + let model = BowInput::default(); // IO error from saving to an invalid path assert_matches!(model.save("bows/tests/nonexistent/valid.bow"), Err(ModelError::InputSaveFileError(_, _))); diff --git a/solver/src/bow/mod.rs b/solver/src/bow/mod.rs index cc3beda6..e77f7744 100644 --- a/solver/src/bow/mod.rs +++ b/solver/src/bow/mod.rs @@ -1,4 +1,4 @@ -pub mod model; +pub mod input; pub mod output; pub mod simulation; pub mod sections; diff --git a/solver/src/bow/output.rs b/solver/src/bow/output.rs index 31903500..c1aba906 100644 --- a/solver/src/bow/output.rs +++ b/solver/src/bow/output.rs @@ -8,19 +8,19 @@ use crate::bow::errors::ModelError; use crate::bow::versioning::{VersionedWrapper, VersionedWrapperRef}; #[derive(Serialize, Deserialize, PartialEq, Debug)] -pub struct Output { - pub setup: Setup, +pub struct BowOutput { + pub common: Common, pub statics: Option, pub dynamics: Option, } -impl Output { +impl BowOutput { // Current version of the output format, increase when incompatible changes are made. pub const FILE_VERSION: u64 = 3; // Loads output from a msgpack file, including a version check. // Since output files make no attempt at backwards compatibility, the version is simply checked for equality and rejected on mismatch. - pub fn load>(path: P) -> Result { + pub fn load>(path: P) -> Result { let file = File::open(&path).map_err(|e| ModelError::OutputLoadFileError(path.as_ref().to_owned(), e))?; let mut reader = BufReader::new(file); @@ -65,10 +65,10 @@ impl Output { } } -impl Default for Output { +impl Default for BowOutput { fn default() -> Self { Self { - setup: Default::default(), + common: Default::default(), statics: None, dynamics: None, } @@ -76,8 +76,9 @@ impl Default for Output { } #[derive(Serialize, Deserialize, Default, PartialEq, Debug)] -pub struct Setup { - pub limb: LimbSetup, +pub struct Common { + pub limb: LimbInfo, + pub layers: Vec, pub string_length: f64, pub string_mass: f64, @@ -89,10 +90,11 @@ pub struct Statics { pub states: StateVec, pub final_draw_force: f64, - pub drawing_work: f64, + pub final_drawing_work: f64, pub storage_factor: f64, pub max_string_force: (f64, usize), // (value, state) + pub max_strand_force: (f64, usize), // (value, state) pub max_grip_force: (f64, usize), // (value, state) pub max_draw_force: (f64, usize), // (value, state) @@ -159,11 +161,7 @@ pub struct State { } #[derive(Serialize, Deserialize, Default, PartialEq, Debug)] -pub struct LimbSetup { - // Layer properties - pub layers: Vec, - - // Geometry at eval points +pub struct LimbInfo { pub length: Vec, pub position: Vec<[f64; 3]>, // x, y, φ pub width: Vec, @@ -171,42 +169,43 @@ pub struct LimbSetup { } #[derive(Serialize, Deserialize, PartialEq, Debug)] -pub struct LayerSetup { +pub struct LayerInfo { + pub name: String, pub length: Vec, // Arc lengths at which the layer is evaluated } #[cfg(test)] mod tests { - use crate::bow::output::Output; + use crate::bow::output::BowOutput; use crate::bow::errors::ModelError; #[test] fn test_load_output() { // IO error when loading from an invalid path - assert_matches!(Output::load("bows/tests/nonexistent.res"), Err(ModelError::OutputLoadFileError(_, _))); + assert_matches!(BowOutput::load("bows/tests/nonexistent.res"), Err(ModelError::OutputLoadFileError(_, _))); // Decoding error due to invalid file contents (invalid messagepack) - assert_matches!(Output::load("bows/tests/invalid_msgpack_1.res"), Err(ModelError::OutputDecodeMsgPackError(_))); + assert_matches!(BowOutput::load("bows/tests/invalid_msgpack_1.res"), Err(ModelError::OutputDecodeMsgPackError(_))); // Decoding error due to invalid file contents (valid messagepack but invalid structure) - assert_matches!(Output::load("bows/tests/invalid_msgpack_2.res"), Err(ModelError::OutputInterpretMsgPackError(_))); + assert_matches!(BowOutput::load("bows/tests/invalid_msgpack_2.res"), Err(ModelError::OutputInterpretMsgPackError(_))); // Error when loading results without version entry - assert_matches!(Output::load("bows/tests/no_version.res"), Err(ModelError::OutputVersionNotFound)); + assert_matches!(BowOutput::load("bows/tests/no_version.res"), Err(ModelError::OutputVersionNotFound)); // Error when loading results with an invalid version entry (wrong type) - assert_matches!(Output::load("bows/tests/invalid_version.res"), Err(ModelError::OutputVersionInvalid(_))); + assert_matches!(BowOutput::load("bows/tests/invalid_version.res"), Err(ModelError::OutputVersionInvalid(_))); // Error when loading results with a different version - assert_matches!(Output::load("bows/tests/new_version.res"), Err(ModelError::OutputVersionMismatch(_))); + assert_matches!(BowOutput::load("bows/tests/new_version.res"), Err(ModelError::OutputVersionMismatch(_))); // No error when loading a valid output file - assert_matches!(Output::load("bows/tests/valid.res"), Ok(_)); + assert_matches!(BowOutput::load("bows/tests/valid.res"), Ok(_)); } #[test] fn test_save_output() { - let output = Output::default(); + let output = BowOutput::default(); // IO error from saving to an invalid path assert_matches!(output.save("bows/tests/nonexistent/output.res"), Err(ModelError::OutputSaveFileError(_, _))); diff --git a/solver/src/bow/sections/section.rs b/solver/src/bow/sections/section.rs index f2571c6a..8324fab6 100644 --- a/solver/src/bow/sections/section.rs +++ b/solver/src/bow/sections/section.rs @@ -2,7 +2,7 @@ use nalgebra::{DVector, matrix, SMatrix, SVector, vector}; use serde::{Deserialize, Serialize}; use crate::fem::elements::beam::CrossSection; use crate::bow::errors::ModelError; -use crate::bow::model::{Layer, Material, Width}; +use crate::bow::input::{Layer, Material, Width}; use crate::numerics::cubic_spline::{BoundaryCondition, CubicSpline, Extrapolation}; use crate::numerics::intervals::{Bound, Interval}; diff --git a/solver/src/bow/simulation.rs b/solver/src/bow/simulation.rs index 07da8513..29e565c8 100644 --- a/solver/src/bow/simulation.rs +++ b/solver/src/bow/simulation.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use nalgebra::{SVector, vector}; use crate::fem::elements::bar::BarElement; use crate::fem::elements::beam::{BeamElementCoRot, CrossSection, PlanarCurve}; -use crate::fem::solvers::eigen::{ModeInfo, natural_frequencies}; +use crate::fem::solvers::eigen::{Mode, natural_frequencies}; use crate::fem::solvers::statics::{Settings, StaticSolver}; use crate::fem::system::element::Element; use crate::fem::system::nodes::{Constraints, GebfNode, PointNode}; @@ -13,8 +13,8 @@ use crate::fem::system::system::{StaticEval, System}; use crate::bow::sections::section::LayeredCrossSection; use crate::bow::errors::ModelError; use crate::bow::profile::profile::{CurvePoint, ProfileCurve}; -use crate::bow::model::BowModel; -use crate::bow::output::{Dynamics, LayerSetup, LimbSetup, Output, Setup, State, StateVec, Statics}; +use crate::bow::input::BowInput; +use crate::bow::output::{Dynamics, LayerInfo, LimbInfo, BowOutput, Common, State, StateVec, Statics}; use crate::numerics::root_finding::regula_falsi; #[derive(ValueEnum, Debug, Copy, Clone)] @@ -24,8 +24,8 @@ pub enum SimulationMode { } pub struct Simulation<'a> { - model: &'a BowModel, - setup: Setup, + model: &'a BowInput, + info: Common, limb_nodes: Vec, limb_elements: Vec, @@ -43,7 +43,7 @@ impl<'a> Simulation<'a> { const BRACING_TARGET_ITER: usize = 5; // Desired number of iterations for the static solver // Set up the simulation either with or without string, depending on simulation mode - fn new(model: &'a BowModel, string: bool) -> Result<(Simulation, System), ModelError> { + fn new(model: &'a BowInput, string: bool) -> Result<(Simulation, System), ModelError> { // Check basic validity of the model data and propagate any errors model.validate()?; @@ -71,13 +71,14 @@ impl<'a> Simulation<'a> { // Layer setup data let layers = model.layers.iter().map(|layer| { - let h0 = layer.height.first().unwrap()[0]; - let h1 = layer.height.last().unwrap()[1]; - LayerSetup { - length: lin_space(h0..=h1, model.settings.n_layer_eval_points).collect(), + let l0 = profile.s_start() + profile.length()*layer.height.first().unwrap()[0]; // Start arc length of the layer + let l1 = profile.s_start() + profile.length()*layer.height.last().unwrap()[0]; // End arc length of the layer + LayerInfo { + name: layer.name.clone(), + length: lin_space(l0..=l1, model.settings.n_layer_eval_points).collect(), } - }).collect(); - + }).collect_vec(); + // Additional setup data let limb_position = s_eval.iter().map(|&s| { let position = profile.position(s); @@ -109,14 +110,14 @@ impl<'a> Simulation<'a> { (None, None) }; - let setup = Setup { - limb: LimbSetup { - layers: layers, + let setup = Common { + limb: LimbInfo { length: s_eval, position: limb_position, width: limb_width, height: limb_height, }, + layers, string_length: 0.0, string_mass: 0.0, limb_mass: 0.0, @@ -124,7 +125,7 @@ impl<'a> Simulation<'a> { let simulation = Self { model, - setup, + info: setup, limb_nodes, limb_elements, string_node, @@ -135,7 +136,7 @@ impl<'a> Simulation<'a> { } // Callback: (phase, progress) -> continue - pub fn simulate(model: &'a BowModel, mode: SimulationMode, mut callback: F) -> Result + pub fn simulate(model: &'a BowInput, mode: SimulationMode, mut callback: F) -> Result where F: FnMut(&str, f64) -> bool { let (simulation, mut system) = Self::new(&model, true)?; @@ -222,10 +223,11 @@ impl<'a> Simulation<'a> { let e_pot_back = states.e_pot_limbs.last().unwrap() + states.e_pot_string.last().unwrap(); let final_draw_force = draw_force_back; - let drawing_work = e_pot_back - e_pot_front; + let final_drawing_work = e_pot_back - e_pot_front; let storage_factor = (e_pot_back - e_pot_front)/(0.5*(draw_length_back - draw_length_front)*draw_force_back); let max_string_force = states.string_force.iter().enumerate().map(|(a, b)| {(*b, a)}).max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()).unwrap(); // TODO: Write function for this + let max_strand_force = (max_string_force.0/(model.string.n_strands as f64), max_string_force.1); let max_grip_force = states.grip_force.iter().enumerate().map(|(a, b)| {(*b, a)}).max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()).unwrap(); // TODO: Write function for this let max_draw_force = states.draw_force.iter().enumerate().map(|(a, b)| {(*b, a)}).max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap()).unwrap(); // TODO: Write function for this @@ -233,13 +235,14 @@ impl<'a> Simulation<'a> { let statics = Some(Statics { states, final_draw_force, - drawing_work, + final_drawing_work, storage_factor, max_string_force, + max_strand_force, max_grip_force, max_draw_force, - min_layer_stresses: vec![], - max_layer_stresses: vec![], + min_layer_stresses: vec![(0.0, 0, 0); model.layers.len()], + max_layer_stresses: vec![(0.0, 0, 0); model.layers.len()], }); // TODO: Implement dynamic simulation @@ -248,30 +251,30 @@ impl<'a> Simulation<'a> { SimulationMode::Dynamic => Some(Dynamics::default()) }; - Ok(Output { - setup: simulation.setup, + Ok(BowOutput { + common: simulation.info, statics, dynamics, }) } - pub fn simulate_statics(model: &'a BowModel) -> Result { + pub fn simulate_statics(model: &'a BowInput) -> Result { Self::simulate(model, SimulationMode::Static, |_, _| true) } - pub fn simulate_dynamics(model: &'a BowModel) -> Result { + pub fn simulate_dynamics(model: &'a BowInput) -> Result { Self::simulate(model, SimulationMode::Dynamic, |_, _| true) } - pub fn simulate_natural_frequencies(model: &'a BowModel) -> Result<(Setup, Vec), ModelError> { + pub fn simulate_natural_frequencies(model: &'a BowInput) -> Result<(Common, Vec), ModelError> { let (simulation, mut system) = Self::new(&model, false)?; let results = natural_frequencies(&mut system); - Ok((simulation.setup, results)) + Ok((simulation.info, results)) } // Simulates a static load (two forces, one moment) applied to the limb tip like a cantilever. // This is only used for testing the bow bow against other simulations/results. - pub fn simulate_static_load(model: &'a BowModel, Fx: f64, Fy: f64, Mz: f64) -> Result<(Setup, State), ModelError> { + pub fn simulate_static_load(model: &'a BowInput, Fx: f64, Fy: f64, Mz: f64) -> Result<(Common, State), ModelError> { let (simulation, mut system) = Self::new(&model, false)?; if let Some(node) = simulation.limb_nodes.last() { @@ -288,7 +291,7 @@ impl<'a> Simulation<'a> { let statics = solver.statics.clone(); // Only for the borrow checker let state = simulation.get_bow_state(&system, &statics); - Ok((simulation.setup, state)) + Ok((simulation.info, state)) } // TODO: Lett mutation, write functions for intermediate results diff --git a/solver/src/fem/elements/beam.rs b/solver/src/fem/elements/beam.rs index 31e8fc05..e69dd26c 100644 --- a/solver/src/fem/elements/beam.rs +++ b/solver/src/fem/elements/beam.rs @@ -503,7 +503,7 @@ mod tests { use nalgebra::{DMatrix, matrix, SMatrix, stack, vector}; use crate::fem::elements::beam::{CrossSection, LinearBeamSegment, PlanarCurve}; use crate::bow::sections::section::{LayerAlignment, LayeredCrossSection}; - use crate::bow::model::{Layer, Material, Width}; + use crate::bow::input::{Layer, Material, Width}; use crate::bow::profile::profile::CurvePoint; use crate::bow::profile::segments::clothoid::{ArcInput, ClothoidSegment, LineInput}; diff --git a/solver/src/fem/solvers/eigen.rs b/solver/src/fem/solvers/eigen.rs index fb5e36f8..6682f8c3 100644 --- a/solver/src/fem/solvers/eigen.rs +++ b/solver/src/fem/solvers/eigen.rs @@ -3,13 +3,13 @@ use itertools::Itertools; use crate::fem::system::system::System; #[derive(Copy, Clone, Debug)] -pub struct ModeInfo { +pub struct Mode { pub omega0: f64, // Undamped frequency pub omega: f64, // Damped frequency pub zeta: f64 // Damping ratio } -impl ModeInfo { +impl Mode { pub fn new(lambda: Complex) -> Self { Self { omega0: lambda.abs(), @@ -20,7 +20,7 @@ impl ModeInfo { } // Finds the natural frequencies of the system -pub fn natural_frequencies(system: &mut System) -> Vec { +pub fn natural_frequencies(system: &mut System) -> Vec { let mut eval = system.default_eigen_eval(); system.eval_eigen(&mut eval); @@ -31,7 +31,7 @@ pub fn natural_frequencies(system: &mut System) -> Vec { ); } -pub fn natural_frequencies_from_matrices(M: &DVector, D: &DMatrix, K: &DMatrix) -> Vec { +pub fn natural_frequencies_from_matrices(M: &DVector, D: &DMatrix, K: &DMatrix) -> Vec { let A = stack![ 0, K; K, D; @@ -47,10 +47,10 @@ pub fn natural_frequencies_from_matrices(M: &DVector, D: &DMatrix, K: let lambda = (B_inv*A).complex_eigenvalues(); // Create natural frequency result for each pair of complex conjugated eigenvalues - let mut results: Vec = lambda.iter() + let mut results: Vec = lambda.iter() .tuple_windows() .filter(|(l1, l2)| l1.conj().eq(l2)) // TODO: Should there be a small tolerance here? - .map(|(l1, _)| ModeInfo::new(*l1)) + .map(|(l1, _)| Mode::new(*l1)) .collect(); // Sort results by undamped natural frequency diff --git a/solver/src/tests/test_00_command_line_interface.rs b/solver/src/tests/test_00_command_line_interface.rs index 55f16cf7..bd50c5c8 100644 --- a/solver/src/tests/test_00_command_line_interface.rs +++ b/solver/src/tests/test_00_command_line_interface.rs @@ -1,4 +1,4 @@ -use crate::bow::output::Output; +use crate::bow::output::BowOutput; use assert_cmd::assert::OutputAssertExt; use assert_cmd::cargo::CommandCargoExt; use predicates::prelude::predicate; diff --git a/solver/src/tests/test_01_element_properties.rs b/solver/src/tests/test_01_element_properties.rs index 1ebae284..05ec0a9a 100644 --- a/solver/src/tests/test_01_element_properties.rs +++ b/solver/src/tests/test_01_element_properties.rs @@ -4,7 +4,7 @@ use crate::fem::elements::mass::MassElement; use crate::fem::system::nodes::Constraints; use crate::fem::system::system::System; use crate::bow::sections::section::{LayerAlignment, LayeredCrossSection}; -use crate::bow::model::{Layer, Material, Width}; +use crate::bow::input::{Layer, Material, Width}; use crate::bow::profile::profile::CurvePoint; use crate::bow::profile::segments::clothoid::{ArcInput, ClothoidSegment}; use crate::tests::utils; diff --git a/solver/src/tests/test_03_static_beams.rs b/solver/src/tests/test_03_static_beams.rs index 3a5f40d2..e180c5cd 100644 --- a/solver/src/tests/test_03_static_beams.rs +++ b/solver/src/tests/test_03_static_beams.rs @@ -1,7 +1,7 @@ use std::f64::consts::{FRAC_PI_4, TAU}; use nalgebra::DVector; use crate::bow::sections::section::LayerAlignment; -use crate::bow::model::{BowModel, Layer, Material, Profile, Width}; +use crate::bow::input::{BowInput, Layer, Material, Profile, Width}; use crate::bow::profile::input::SegmentInput; use crate::bow::profile::segments::clothoid::{ArcInput, LineInput}; use crate::bow::simulation::Simulation; @@ -35,7 +35,7 @@ fn linear_straight_uniform_elongation() { let rho = 7850.0; // Beam bow - let mut model = BowModel::default(); + let mut model = BowInput::default(); model.settings.n_limb_elements = 25; model.settings.n_limb_eval_points = 100; model.settings.n_draw_steps = 5; @@ -103,7 +103,7 @@ fn linear_straight_uniform_cantilever() { let rho = 7850.0; // Beam bow - let mut model = BowModel::default(); + let mut model = BowInput::default(); model.settings.n_limb_elements = 25; model.settings.n_limb_eval_points = 100; model.settings.n_draw_steps = 5; @@ -171,7 +171,7 @@ fn linear_straight_uniform_coilup() { let rho = 7850.0; // Beam bow - let mut model = BowModel::default(); + let mut model = BowInput::default(); model.settings.n_limb_elements = 50; model.settings.n_limb_eval_points = 100; model.settings.n_draw_steps = 9; @@ -246,7 +246,7 @@ fn nonlinear_straight_uniform_cantilever() { let Fy = 200.0; // Beam bow - let mut model = BowModel::default(); + let mut model = BowInput::default(); model.settings.n_limb_elements = 25; model.settings.n_limb_eval_points = u_ref.len(); model.settings.n_draw_steps = 5; @@ -331,7 +331,7 @@ fn nonlinear_straight_tapered_cantilever() { let Fy = 100.0; // Beam bow - let mut model = BowModel::default(); + let mut model = BowInput::default(); model.settings.n_limb_elements = 25; model.settings.n_limb_eval_points = u_ref.len(); model.settings.n_draw_steps = 5; @@ -417,7 +417,7 @@ fn nonlinear_curved_uniform_cantilever() { let Fy = 200.0; // Beam bow - let mut model = BowModel::default(); + let mut model = BowInput::default(); model.settings.n_limb_elements = 25; model.settings.n_limb_eval_points = u_ref.len(); model.settings.n_draw_steps = 5; @@ -503,7 +503,7 @@ fn nonlinear_curved_tapered_cantilever() { let Fy = 100.0; // Beam bow - let mut model = BowModel::default(); + let mut model = BowInput::default(); model.settings.n_limb_elements = 25; model.settings.n_limb_eval_points = u_ref.len(); model.settings.n_draw_steps = 5; @@ -584,7 +584,7 @@ fn nonlinear_straight_uniform_cantilever_offsets() { let solve_for_alignment = |alignment: LayerAlignment| { // Beam bow - let mut model = BowModel::default(); + let mut model = BowInput::default(); model.settings.n_limb_elements = 25; model.settings.n_limb_eval_points = 100; model.settings.n_draw_steps = 5; @@ -680,7 +680,7 @@ fn nonlinear_curved_uniform_cantilever_offsets() { let ld = rd/r*l; // Beam bow - let mut model = BowModel::default(); + let mut model = BowInput::default(); model.settings.n_limb_elements = 20; model.settings.n_limb_eval_points = 100; model.settings.n_draw_steps = 5; diff --git a/solver/src/tests/test_05_example_bows.rs b/solver/src/tests/test_05_example_bows.rs index 50f346cd..e5678531 100644 --- a/solver/src/tests/test_05_example_bows.rs +++ b/solver/src/tests/test_05_example_bows.rs @@ -1,5 +1,5 @@ use itertools::Itertools; -use crate::bow::model::BowModel; +use crate::bow::input::BowInput; use crate::bow::simulation::Simulation; use crate::tests::utils::plotter::Plotter; @@ -135,7 +135,7 @@ fn bow_v3u2t11b() { */ fn perform_bow_test(file: &str) { - let model = BowModel::load(file).expect("Failed to load bow file"); + let model = BowInput::load(file).expect("Failed to load bow file"); let output = Simulation::simulate_statics(&model).unwrap(); //output.save_json(outfile).expect("Failed to save simulation output"); @@ -152,14 +152,18 @@ fn perform_bow_test(file: &str) { } // Check basic dimensions of the setup data - assert_eq!(output.setup.limb.layers.len(), model.layers.len()); - for layer in &output.setup.limb.layers { + assert_eq!(output.common.layers.len(), model.layers.len()); + for layer in &output.common.layers { assert_eq!(layer.length.len(), model.settings.n_layer_eval_points); } - assert_eq!(output.setup.limb.length.len(), model.settings.n_limb_eval_points); - assert_eq!(output.setup.limb.width.len(), model.settings.n_limb_eval_points); - assert_eq!(output.setup.limb.height.len(), model.settings.n_limb_eval_points); - assert_eq!(output.setup.limb.position.len(), model.settings.n_limb_eval_points); + assert_eq!(output.common.limb.length.len(), model.settings.n_limb_eval_points); + assert_eq!(output.common.limb.width.len(), model.settings.n_limb_eval_points); + assert_eq!(output.common.limb.height.len(), model.settings.n_limb_eval_points); + assert_eq!(output.common.limb.position.len(), model.settings.n_limb_eval_points); + + // Basic dimension of the fixed static data + assert_eq!(statics.min_layer_stresses.len(), model.layers.len()); + assert_eq!(statics.max_layer_stresses.len(), model.layers.len()); // Check if number of states matches settings assert_eq!(states.len(), model.settings.n_draw_steps + 1); @@ -177,12 +181,8 @@ fn perform_bow_test(file: &str) { assert_eq!(state.limb_force.len(), model.settings.n_limb_eval_points); assert_eq!(state.layer_strain.len(), model.layers.len()); assert_eq!(state.layer_stress.len(), model.layers.len()); - for layer in state.layer_strain { - assert_eq!(layer.len(), model.settings.n_layer_eval_points); - } - for layer in state.layer_stress { - assert_eq!(layer.len(), model.settings.n_layer_eval_points); - } + assert!(state.layer_strain.iter().all(|layer| layer.len() == model.settings.n_layer_eval_points)); + assert!(state.layer_stress.iter().all(|layer| layer.len() == model.settings.n_layer_eval_points)); // Check limb starting point (positions and angle) assert_abs_diff_eq!(state.limb_pos[0][0], 0.5*model.dimensions.handle_length, epsilon=1e-12); @@ -222,7 +222,7 @@ fn perform_bow_test(file: &str) { let Fx = -state.string_force*f64::cos(alpha); let Fy = -state.string_force*f64::sin(alpha); - for (j, &s) in output.setup.limb.length.iter().enumerate() { + for (j, &s) in output.common.limb.length.iter().enumerate() { let x = state.limb_pos[j][0]; let y = state.limb_pos[j][1]; let φ = state.limb_pos[j][2];