diff --git a/empennage_design/CMakeLists.txt b/empennage_design/CMakeLists.txt index f67040a50ab30dc9e78b2fb15077cb559786db26..9215e4f251a184713e8392d3f022f41332961efc 100644 --- a/empennage_design/CMakeLists.txt +++ b/empennage_design/CMakeLists.txt @@ -13,9 +13,11 @@ set(MODULE_NAME empennage_design) # Conventional tail files set(MODULE_SOURCES_BWB_VERTICAL_TAILS - src/bwb/vertical_tails/low/LowVerticalTailsBwb.cpp - src/bwb/vertical_tails/bwbVerticalTailsData.cpp - src/bwb/vertical_tails/bwbVerticalTailsConfig.cpp +src/bwb/vertical_tails/low/lowBwbVerticalTailsDesign.cpp +src/bwb/vertical_tails/low/lowBwbVerticalTailsDesignPlot.cpp +src/bwb/vertical_tails/low/lowBwbVerticalTailsDesignReport.cpp +src/bwb/vertical_tails/bwbVerticalTailsDesignData.cpp +src/bwb/vertical_tails/bwbVerticalTailsDesignConfig.cpp ) set(MODULE_SOURCES_TAW_CONVENTIONAL @@ -59,7 +61,7 @@ find_package(Eigen3 3.3 REQUIRED NO_MODULE) # Link the runtime libraries target_link_libraries(${MODULE_NAME} - PUBLIC + PUBLIC Eigen3::Eigen PRIVATE UnicadoLibs::moduleBasics @@ -94,13 +96,13 @@ endif() install (TARGETS ${MODULE_NAME} DESTINATION ${MODULE_NAME}) if(WIN32) install(FILES - ${MODULE_NAME}_conf.xml + ${MODULE_NAME}_conf.xml gmp-10.dll mpfr-6.dll DESTINATION ${MODULE_NAME}) else() install(FILES - ${MODULE_NAME}_conf.xml + ${MODULE_NAME}_conf.xml DESTINATION ${MODULE_NAME}) endif() @@ -108,4 +110,4 @@ endif() if(BUILD_SHARED_LIBS) # Install the runtime dependencies install(RUNTIME_DEPENDENCY_SET ${MODULE_NAME}_dependencies) -endif() \ No newline at end of file +endif() diff --git a/empennage_design/empennage_design_conf.xml b/empennage_design/empennage_design_conf.xml index 3c46af7b57a203d6c568048f40f125104b247768..c30f24549a2c00f9e5707902fd82fdf848eb1dc5 100644 --- a/empennage_design/empennage_design_conf.xml +++ b/empennage_design/empennage_design_conf.xml @@ -1021,9 +1021,6 @@ <fidelity_selection description="selection of fidelity level"> <value>low</value> </fidelity_selection> - <tailtype_selection description="selection of tailtype"> - <value>vertical_tails</value> - </tailtype_selection> <low_fidelity description="Low fidelity methods"> <vertical_tails description="vertical tails method - creates symmetric tails"> <tail_element description="tail element" ID="0"> @@ -1090,6 +1087,9 @@ </profiles> <spars description="spars"> <spar description="front spar" ID="0"> + <name description="name of spar"> + <value>front_spar</value> + </name> <position description="chord relative position of control device"> <inner_position description="relative inner position"> <spanwise description="relative spanwise position"> @@ -1100,13 +1100,13 @@ </spanwise> <chord description="control device chord position"> <from description="relative chord position"> - <value>0.7</value> + <value>0.2</value> <unit>1</unit> <lower_boundary>0.0</lower_boundary> <upper_boundary>1.0</upper_boundary> </from> <to description="relative chord position"> - <value>1.0</value> + <value>0.2</value> <unit>1</unit> <lower_boundary>0.0</lower_boundary> <upper_boundary>1.0</upper_boundary> @@ -1115,20 +1115,20 @@ </inner_position> <outer_position description="relative outer position"> <spanwise description="relative spanwise position"> - <value>0.9</value> + <value>1.0</value> <unit>1</unit> <lower_boundary>0</lower_boundary> <upper_boundary>1.0</upper_boundary> </spanwise> <chord description="control device chord position"> <from description="relative chord position"> - <value>0.7</value> + <value>0.2</value> <unit>1</unit> <lower_boundary>0.0</lower_boundary> <upper_boundary>1.0</upper_boundary> </from> <to description="relative chord position"> - <value>1.0</value> + <value>0.2</value> <unit>1</unit> <lower_boundary>0.0</lower_boundary> <upper_boundary>1.0</upper_boundary> @@ -1138,6 +1138,9 @@ </position> </spar> <spar description="rear spar" ID="1"> + <name description="name of spar"> + <value>rear_spar</value> + </name> <position description="chord relative position of control device"> <inner_position description="relative inner position"> <spanwise description="relative spanwise position"> @@ -1163,7 +1166,7 @@ </inner_position> <outer_position description="relative outer position"> <spanwise description="relative spanwise position"> - <value>0.2</value> + <value>1.0</value> <unit>1</unit> <lower_boundary>0</lower_boundary> <upper_boundary>1.0</upper_boundary> @@ -1208,7 +1211,7 @@ <position description="chord relative position of control device"> <inner_position description="relative inner position"> <spanwise description="relative spanwise position"> - <value>0.2</value> + <value>0.1</value> <unit>1</unit> <lower_boundary>0</lower_boundary> <upper_boundary>1.0</upper_boundary> @@ -1230,7 +1233,7 @@ </inner_position> <outer_position description="relative outer position"> <spanwise description="relative spanwise position"> - <value>0.2</value> + <value>0.95</value> <unit>1</unit> <lower_boundary>0</lower_boundary> <upper_boundary>1.0</upper_boundary> diff --git a/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsConfig.cpp b/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignConfig.cpp similarity index 88% rename from empennage_design/src/bwb/vertical_tails/bwbVerticalTailsConfig.cpp rename to empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignConfig.cpp index 0e9b882ac0ce14f9f6ebce3606aa7ac90f6ba4b4..bd940ed974d49a52ef7cea32750196917899d2a6 100644 --- a/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsConfig.cpp +++ b/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignConfig.cpp @@ -20,7 +20,7 @@ * This file is part of UNICADO. */ -#include "bwbVerticalTailsConfig.h" +#include "bwbVerticalTailsDesignConfig.h" #include <aircraftGeometry2/airfoil_surface.h> @@ -29,12 +29,14 @@ namespace low { VerticalTailsConfig::VerticalTailsConfig() : design_mode(EndnodeReadOnly<std::string>("module_configuration_file/program_settings/modes/design_mode")), mass_mode(EndnodeReadOnly<std::string>("module_configuration_file/program_settings/modes/mass_mode/method")), - mass_mode_flops_technology_factor(EndnodeReadOnly<double>("module_configuration_file/program_settings/modes/mass_mode/parameters/mode_0/technology_factor")) {} + mass_mode_flops_technology_factor(EndnodeReadOnly<double>("module_configuration_file/program_settings/modes/mass_mode/parameters/mode_0/technology_factor")), + common_airfoil_data_path(EndnodeReadOnly<std::string>("module_configuration_file/program_settings/common_airfoil_data_path")) {} void VerticalTailsConfig::read(const node& xml) { design_mode.read(xml); mass_mode.read(xml); mass_mode_flops_technology_factor.read(xml); + common_airfoil_data_path.read(xml); /* List all tail elements */ std::string path_to_tail_elements = diff --git a/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsConfig.h b/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignConfig.h similarity index 96% rename from empennage_design/src/bwb/vertical_tails/bwbVerticalTailsConfig.h rename to empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignConfig.h index 07bdaa82835017642a431580fc011684f9e233e4..0a3c44fe405e5dfb7438d63739b75a467b133bb3 100644 --- a/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsConfig.h +++ b/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignConfig.h @@ -39,6 +39,7 @@ class VerticalTailsConfig { void read(const node& xml); + EndnodeReadOnly<std::string> common_airfoil_data_path; EndnodeReadOnly<std::string> design_mode; EndnodeReadOnly<std::string> mass_mode; EndnodeReadOnly<double> mass_mode_flops_technology_factor; diff --git a/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsData.cpp b/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignData.cpp similarity index 61% rename from empennage_design/src/bwb/vertical_tails/bwbVerticalTailsData.cpp rename to empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignData.cpp index 1542fd075f96a49928d2f2de14f34a588eb8dc66..0c2d00dde76732007cbfca96fc7f3cdda3b5c6fd 100644 --- a/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsData.cpp +++ b/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignData.cpp @@ -24,7 +24,7 @@ #include <aircraftGeometry2/geometry/surface.h> #include <aixml/node.h> -#include "bwbVerticalTailsData.h" +#include "bwbVerticalTailsDesignData.h" namespace bwb { namespace low { @@ -47,7 +47,7 @@ void VerticalTailsData::update(node& xml) { update_vertical_tails(xml); empennage_mass.update(xml); empennage_position.update(xml); - + } void VerticalTailsData::update_vertical_tails(node& xml) { for (size_t id = 0; id < tails.size(); ++id) { @@ -56,6 +56,36 @@ void VerticalTailsData::update_vertical_tails(node& xml) { {"aerodynamic_surface", std::to_string(id), tails[id].name}), io); tails_mass[id].update(xml); + int i = 0; + for (auto &spar : spars) { + geom2::io::SurfaceType io_spar = geom2::io::Spar(spar); + const std::string path_to_spars = + "component_design/empennage/specific/geometry/aerodynamic_surface@" + std::to_string(i) + "/parameters/spars"; + std::visit(geom2::io::AixmlConverter(xml[path_to_spars], {"spar", std::to_string(i++), spar.name}), io_spar); + } + i = 0; + for (auto control_device : controls) { + geom2::io::SurfaceType io_control_device = geom2::io::ControlDevice(control_device); + std::string path_to_device = + "component_design/empennage/specific/geometry/aerodynamic_surface@0/parameters/control_devices"; + std::visit( + geom2::io::AixmlConverter(xml[path_to_device], {"control_device", std::to_string(i), control_device.name}), + io_control_device); + Endnode<double> min_deflection( + path_to_device + "/control_device@" + std::to_string(i) + "/deflection/full_negative_deflection", + "full negative deflection"); + Endnode<double> max_deflection( + path_to_device + "/control_device@" + std::to_string(i) + "/deflection/full_positive_deflection", + "full positive deflection"); + const auto [min, max] = control_devices_deflections[i]; + min_deflection.set_unit("rad"); + min_deflection.set_value(min); + min_deflection.update(xml); + max_deflection.set_unit("rad"); + max_deflection.set_value(max); + max_deflection.update(xml); + i++; + } } } } // namespace low diff --git a/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsData.h b/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignData.h similarity index 96% rename from empennage_design/src/bwb/vertical_tails/bwbVerticalTailsData.h rename to empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignData.h index bd8b6a52d0d18a62a1b598368593da40eb2d6dd2..5de0e7ad664a52a96a0bb4e9c94d53a50a495178 100644 --- a/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsData.h +++ b/empennage_design/src/bwb/vertical_tails/bwbVerticalTailsDesignData.h @@ -45,6 +45,7 @@ class VerticalTailsData { std::vector<geom2::MultisectionSurface<geom2::AirfoilSection>> tails; std::vector<geom2::MultisectionSurface<geom2::PolygonSection>> controls; std::vector<geom2::MultisectionSurface<geom2::PolygonSection>> spars; + std::vector<std::tuple<double,double>> control_devices_deflections; std::vector<MassPropertiesIO> tails_mass; MassPropertiesIO empennage_mass; PositionIO empennage_position; diff --git a/empennage_design/src/bwb/vertical_tails/low/LowVerticalTailsBwb.h b/empennage_design/src/bwb/vertical_tails/low/LowVerticalTailsBwb.h deleted file mode 100644 index 0909dd6bc9d8620ee5d3f996b5a8dd528a165f37..0000000000000000000000000000000000000000 --- a/empennage_design/src/bwb/vertical_tails/low/LowVerticalTailsBwb.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * UNICADO - UNIversity Conceptual Aircraft Design and Optimization - * - * Copyright (C) 2025 UNICADO consortium - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - * - * Description: - * This file is part of UNICADO. - */ - -#ifndef LOW_VERTICAL_TAILS_BWB_H_ -#define LOW_VERTICAL_TAILS_BWB_H_ - -#include <moduleBasics/module.h> -#include <moduleBasics/strategySelector.h> - -#include <functional> -#include <map> - -#include "bwb/vertical_tails/bwbVerticalTailsConfig.h" -#include "bwb/vertical_tails/bwbVerticalTailsData.h" - -namespace bwb { -namespace low { - -class VerticalTails : public Strategy { - public: - VerticalTails(const std::shared_ptr<RuntimeIO>& rtIO); - ~VerticalTails() = default; - void initialize(); - void run(); - void update(); - void report(); - void save(); - - void design(); - void redesign(); - - void flops_mass(); - - const std::shared_ptr<RuntimeIO>& rtIO; - const std::shared_ptr<bwb::low::VerticalTailsConfig> config; - const std::shared_ptr<bwb::low::VerticalTailsData> data; - std::map<std::string, std::function<void(void)>> design_mode; - std::map<std::string, std::function<void(void)>> mass_mode; -}; - -} // namespace low -} // namespace bwb - -#endif // LOW_VERTICAL_TAILS_H_ \ No newline at end of file diff --git a/empennage_design/src/bwb/vertical_tails/low/LowVerticalTailsBwb.cpp b/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesign.cpp similarity index 64% rename from empennage_design/src/bwb/vertical_tails/low/LowVerticalTailsBwb.cpp rename to empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesign.cpp index c0b18e13e0af93096b59544ea39f981d1d918b56..51125e7f64caa152c5a768ce30036112240c00af 100644 --- a/empennage_design/src/bwb/vertical_tails/low/LowVerticalTailsBwb.cpp +++ b/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesign.cpp @@ -20,7 +20,7 @@ * This file is part of UNICADO. */ -#include "LowVerticalTailsBwb.h" +#include "lowBwbVerticalTailsDesign.h" #include <aircraftGeometry2/processing/measure.h> #include <cmath> @@ -36,15 +36,28 @@ VerticalTails::VerticalTails(const std::shared_ptr<RuntimeIO>& rtIO) config(std::make_shared<bwb::low::VerticalTailsConfig>()), data(std::make_shared<bwb::low::VerticalTailsData>()), design_mode({{"mode_0", [this]() { design(); }}, {"mode_1", [this]() { redesign(); }}}), - mass_mode({{"mode_0", [this]() { flops_mass(); }}}) {} + mass_mode({{"mode_0", [this]() { flops_mass(); }}}), + reporter(rtIO) {} void VerticalTails::initialize() { // Setup methods for mode myRuntimeInfo->out << "Initializing empennage" << std::endl; myRuntimeInfo->out << "Empennage type -> vertical tails (multiple vertical tails - symmetric)" << std::endl; + + /* Read configuration data */ config->read(rtIO->moduleConfig); + + /* Create airfoils directory in project path (only if non-existing -> e.g. if standalone usage w. given wing and fuselage)*/ + + /* Read acxml data - non aircraftgeometry 2 */ data->read(rtIO->acxml); + + /* Read acxml data - aircraftgeometry2 */ data->read_wing(rtIO->acxmlAccess, rtIO->getAirfoilDataDir()); + + /* Create airfoils library */ + airfoils_library = std::make_shared<Airfoils>(static_cast<std::filesystem::path>(config->common_airfoil_data_path.value())); + airfoils_library->add_directory_airfoils(rtIO->getAirfoilDataDir()); } void VerticalTails::run() { @@ -64,6 +77,12 @@ void VerticalTails::update() { void VerticalTails::report() { // report sections myRuntimeInfo->out << "Reporting empennage -> vertical tails" << std::endl; + + /* Set html report data and plots */ + set_html_body(); + + /* Generate reports */ + reporter.generateReports(); for (auto& tail : data->tails) { geom2::Mesh mesh = geom2::transform::to_mesh(tail); if (tail.origin.y() < 0) { @@ -110,15 +129,22 @@ void VerticalTails::design() { geom2::measure::chord(data->wing, -vertical_tail.centerline_y_offset.value()) + data->wing_reference_position.x.value(); + std::string inner_profile_name = vertical_tail.inner_profile.value(); + std::string outer_profile_name = vertical_tail.outer_profile.value(); + volume_coefficient_method.compute_geometry(number_of_tails, volume_coefficient, sweep, taper_ratio, aspect_ratio, wing_reference_area, wing_span, wing_length, wing_neutral_position, offset_position, orientation, false); /* Read */ - geom2::AirfoilSection profile_root( - geom2::io::read_dat_file(rtIO->getAirfoilDataDir() + "/" + vertical_tail.inner_profile.value() + ".dat")); - geom2::AirfoilSection profile_tip( - geom2::io::read_dat_file(rtIO->getAirfoilDataDir() + "/" + vertical_tail.outer_profile.value() + ".dat")); + geom2::AirfoilSection profile_root(airfoils_library->get_airfoil(inner_profile_name)); + airfoils_library->copy_available_airfoil(inner_profile_name, rtIO->getAirfoilDataDir()); + profile_root.name = inner_profile_name; + + /* Read outer profile */ + geom2::AirfoilSection profile_tip(airfoils_library->get_airfoil(inner_profile_name)); + airfoils_library->copy_available_airfoil(inner_profile_name, rtIO->getAirfoilDataDir()); + profile_tip.name = outer_profile_name; /* Vertical tail */ data->tails.push_back(volume_coefficient_method.get_design(profile_root, profile_tip, 0.0)); @@ -127,13 +153,35 @@ void VerticalTails::design() { geom2::Point_3(0, data->tails.front().origin.y(), data->tails.front().origin.z()); data->tails.back().name = vertical_tail.name.value() + "_positive"; + /* Vertical tail - controls */ + for (auto device : vertical_tail.control_devices) { + data->controls.push_back(control_device(device.type.value(), device.position.inner_position_chord_from.value(), + device.position.inner_position_chord_to.value(), device.position.inner_position_spanwise.value(), + device.position.outer_position_chord_from.value(), device.position.outer_position_chord_to.value(), + device.position.outer_position_spanwise.value())); + data->control_devices_deflections.push_back( + {device.deflection_full_negative.value(), device.deflection_full_positive.value()}); + } + /* Vertical tail - spars */ + for (auto spar_element : vertical_tail.spars) { + data->spars.push_back(spar( + spar_element.name.value(), spar_element.position.inner_position_chord_from.value(), + spar_element.position.inner_position_chord_to.value(), spar_element.position.inner_position_spanwise.value(), + spar_element.position.outer_position_chord_from.value(), spar_element.position.outer_position_chord_to.value(), + spar_element.position.outer_position_spanwise.value())); + } + + + /* Vertical tail (mirrored) */ data->tails.push_back(data->tails.back()); data->tails.back().origin = geom2::Point_3(0, -data->tails.front().origin.y(), data->tails.front().origin.z()); data->tails.back().name = vertical_tail.name.value() + "_negative"; - // set empennage position + + + // set empennage position data->empennage_position.y = 0; data->empennage_position.z = 0; } @@ -166,6 +214,54 @@ void VerticalTails::redesign() { // } } +// == Spars and Controls ============================================================================================== + +geom2::MultisectionSurface<geom2::PolygonSection> VerticalTails::control_device( + const std::string &name, double inner_chord_rel_pos_from, double inner_chord_rel_pos_to, + double inner_spanwise_rel_pos, double outer_chord_rel_pos_from, double outer_chord_rel_pos_to, + double outer_spanwise_rel_pos) { +geom2::MultisectionSurface<geom2::PolygonSection> control_surface; +geom2::Polygon_2 inner_chord; +inner_chord.push_back(geom2::Point_2(inner_chord_rel_pos_from, 0.0)); +inner_chord.push_back(geom2::Point_2(inner_chord_rel_pos_to, 0.0)); + +geom2::Polygon_2 outer_chord; +outer_chord.push_back(geom2::Point_2(outer_chord_rel_pos_from, 0.0)); +outer_chord.push_back(geom2::Point_2(outer_chord_rel_pos_to, 0.0)); + +control_surface.sections.emplace_back(inner_chord); +control_surface.sections.emplace_back(outer_chord); + +control_surface.sections.front().origin = geom2::Point_3(0.0, 0.0, inner_spanwise_rel_pos); +control_surface.sections.back().origin = geom2::Point_3(0.0, 0.0, outer_spanwise_rel_pos); + +control_surface.name = name; +return control_surface; +} + +geom2::MultisectionSurface<geom2::PolygonSection> VerticalTails::spar( + const std::string &name, double inner_chord_rel_pos_from, double inner_chord_rel_pos_to, + double inner_spanwise_rel_pos, double outer_chord_rel_pos_from, double outer_chord_rel_pos_to, + double outer_spanwise_rel_pos) { +geom2::MultisectionSurface<geom2::PolygonSection> spar; +geom2::Polygon_2 inner_chord; +inner_chord.push_back(geom2::Point_2(inner_chord_rel_pos_from, 0.0)); +inner_chord.push_back(geom2::Point_2(inner_chord_rel_pos_to, 0.0)); + +geom2::Polygon_2 outer_chord; +outer_chord.push_back(geom2::Point_2(outer_chord_rel_pos_from, 0.0)); +outer_chord.push_back(geom2::Point_2(outer_chord_rel_pos_to, 0.0)); + +spar.sections.emplace_back(inner_chord); +spar.sections.emplace_back(outer_chord); + +spar.sections.front().origin = geom2::Point_3(0.0, 0.0, inner_spanwise_rel_pos); +spar.sections.back().origin = geom2::Point_3(0.0, 0.0, outer_spanwise_rel_pos); +spar.name = name; + +return spar; +} + // == Mass ============================================================================================================ void VerticalTails::flops_mass() { myRuntimeInfo->out << "Mode active -> Mass computation - FLOPS" << std::endl; @@ -193,7 +289,6 @@ void VerticalTails::flops_mass() { data->tails_mass.back().data["y"] = 0.0; } overall_empennage_mass += data->tails_mass.back().data["mass"].value(); - data->tails_mass.back().print(); } data->empennage_mass.data["mass"] = overall_empennage_mass; double tmp_x(0), tmp_y(0), tmp_z(0); @@ -206,7 +301,11 @@ void VerticalTails::flops_mass() { data->empennage_mass.data["y"] = tmp_y / data->empennage_mass.data["mass"].value(); data->empennage_mass.data["z"] = tmp_z / data->empennage_mass.data["mass"].value(); - data->empennage_mass.print(); + myRuntimeInfo->out << "Mass and CG Position for vertical tails" << std::endl; + myRuntimeInfo->out << std::format("Mass : {:.3f}", data->empennage_mass.data["mass"].value()) << std::endl; + myRuntimeInfo->out << std::format("x-cg : {:.3f}", data->empennage_mass.data["x"].value()) << std::endl; + myRuntimeInfo->out << std::format("y-cg : {:.3f}", data->empennage_mass.data["y"].value()) << std::endl; + myRuntimeInfo->out << std::format("z-cg : {:.3f}", data->empennage_mass.data["z"].value()) << std::endl; } } // namespace low diff --git a/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesign.h b/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesign.h new file mode 100644 index 0000000000000000000000000000000000000000..b83f55b65858e888589c175d71b05839c27ad1d6 --- /dev/null +++ b/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesign.h @@ -0,0 +1,176 @@ +/* + * UNICADO - UNIversity Conceptual Aircraft Design and Optimization + * + * Copyright (C) 2025 UNICADO consortium + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Description: + * This file is part of UNICADO. + */ + +#ifndef LOW_VERTICAL_TAILS_BWB_H_ +#define LOW_VERTICAL_TAILS_BWB_H_ + +#include <airfoils/airfoils.h> +#include <moduleBasics/module.h> +#include <moduleBasics/report.h> +#include <moduleBasics/strategySelector.h> + +#include <functional> +#include <map> + +#include "bwb/vertical_tails/bwbVerticalTailsDesignConfig.h" +#include "bwb/vertical_tails/bwbVerticalTailsDesignData.h" + +namespace bwb { +namespace low { + +class VerticalTails : public Strategy { + public: + VerticalTails(const std::shared_ptr<RuntimeIO>& rtIO); + ~VerticalTails() = default; + + /** + * \brief Initialize method + * + */ + void initialize(); + + /** + * \brief Run method + * + */ + void run(); + + + void update(); + + /** + * \brief Report generation + * + */ + void report(); + + /** + * \brief Save results + * + */ + void save(); + + /** + * \brief Design method + * + */ + void design(); + + /** + * \brief Redesign method for empennage + * + */ + void redesign(); + + /** + * \brief Method to create shape of a control surface + * + * \param name name of the control surface + * \param inner_chord_rel_pos_from + * \param inner_chord_rel_pos_to + * \param inner_spanwise_rel_pos + * \param outer_chord_rel_pos_from + * \param outer_chord_rel_pos_to + * \param outer_spanwise_rel_pos + * \return geom2::MultisectionSurface<geom2::PolygonSection> + */ + geom2::MultisectionSurface<geom2::PolygonSection> control_device( + const std::string& name, double inner_chord_rel_pos_from, double inner_chord_rel_pos_to, + double inner_spanwise_rel_pos, double outer_chord_rel_pos_from, double outer_chord_rel_pos_to, + double outer_spanwise_rel_pos); + + /** + * \brief Method to create shape of a predefined spar + * + * \param name Name of the spar + * \param inner_chord_rel_pos_from + * \param inner_chord_rel_pos_to + * \param inner_spanwise_rel_pos + * \param outer_chord_rel_pos_from + * \param outer_chord_rel_pos_to + * \param outer_spanwise_rel_pos + * \return geom2::MultisectionSurface<geom2::PolygonSection> spar element as a polygonsection + */ + geom2::MultisectionSurface<geom2::PolygonSection> spar(const std::string& name, double inner_chord_rel_pos_from, + double inner_chord_rel_pos_to, + double inner_spanwise_rel_pos, + double outer_chord_rel_pos_from, + double outer_chord_rel_pos_to, + double outer_spanwise_rel_pos); + + /** + * \brief Calculate empennage mass (vertical tails) and associated center of gravity by + * Flops method + * + */ + void flops_mass(); + + /** + * \brief Set the html body report + * + */ + void set_html_body(); + + /** + * \brief Generates plots for stabilizer planform and returns relative path to plot (svg) + * + * \param surface stabilizer + * \param spars spars vector + * \param devices control devices vector + * \return fs::path relative path between report and plot + */ + fs::path plot_stabilizer_planform(const geom2::MultisectionSurface<geom2::AirfoilSection>& surface, + const std::vector<geom2::MultisectionSurface<geom2::PolygonSection>>& spars, + const std::vector<geom2::MultisectionSurface<geom2::PolygonSection>>& devices); + + /** + * \brief Generate unique plots of airfoils + * + * \param surface stabilizer + * \return std::vector<fs::path> vector of relative paths between report and plots + */ + std::vector<fs::path> plot_airfoils(const geom2::MultisectionSurface<geom2::AirfoilSection>& surface); + + /** + * \brief Generate plot of thickness and twist distribution + * + * \param surface stabilizer + * \return fs::path relative path between report and plot + */ + fs::path plot_thickness_and_twist_distribution(const geom2::MultisectionSurface<geom2::AirfoilSection>& surface); + + public: + std::map<std::string, std::function<void(void)>> design_mode; + std::map<std::string, std::function<void(void)>> mass_mode; + std::shared_ptr<Airfoils> airfoils_library; + + const std::shared_ptr<bwb::low::VerticalTailsConfig> config; + const std::shared_ptr<bwb::low::VerticalTailsData> data; + const std::shared_ptr<RuntimeIO>& rtIO; + Report reporter; + +}; + +} // namespace low +} // namespace bwb + +#endif // LOW_VERTICAL_TAILS_H_ diff --git a/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesignPlot.cpp b/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesignPlot.cpp new file mode 100644 index 0000000000000000000000000000000000000000..21123ff06b4b02b53418ec31a5ff538164e787c7 --- /dev/null +++ b/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesignPlot.cpp @@ -0,0 +1,231 @@ +/* + * UNICADO - UNIversity Conceptual Aircraft Design and Optimization + * + * Copyright (C) 2025 UNICADO consortium + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Description: + * This file is part of UNICADO. + */ + +#include <aircraftGeometry2/processing/measure.h> +#include <aircraftGeometry2/processing/transform.h> +#include <matplot/matplot.h> + +#include <array> +#include <cstdlib> +#include <deque> +#include <map> +#include <utility> +#include <algorithm> + +#include "lowBwbVerticalTailsDesign.h" +#include "lib/plot_methods/plot_utility.h" +namespace bwb { +namespace low { + +fs::path VerticalTails::plot_stabilizer_planform( + const geom2::MultisectionSurface<geom2::AirfoilSection>& surface, + const std::vector<geom2::MultisectionSurface<geom2::PolygonSection>>& spars, + const std::vector<geom2::MultisectionSurface<geom2::PolygonSection>>& devices) { + /* Plot active */ + if (!rtIO->plotOn) { + return ""; + } + auto coordinates_xy = geom2::transform::outline_2d(surface, geom2::Direction_3(0, 0, 1)); + std::deque<double> spanwise; + std::deque<double> chordwise; + auto span = geom2::measure::span(surface); + auto half_span = surface.is_symmetric ? span * 0.5 : span; + auto sections = surface.sections; + + /* Check if control surface is oriented vertical */ + bool is_vertical = std::fabs(surface.normal.dz()) > 0.95; + + /* Loop coordinates from out to inside for LE and TE */ + for (auto it = sections.rbegin(); it != sections.rend(); ++it) { + spanwise.push_front(-it->origin.z()); + spanwise.push_back(-it->origin.z()); + chordwise.push_front(it->origin.x()); + chordwise.push_back(it->origin.x() + it->get_chord_length()); + } + + /* Generate plot */ + std::string name = surface.name; + std::replace(name.begin(), name.end(), '_', ' '); + std::vector<std::string> legend_strings; + auto f = matplot::figure(true); + f->title(std::format("{} planform", name)); + auto ax = f->add_axes(false); + if (is_vertical) { + ax->plot(std::vector<double>(chordwise.begin(), chordwise.end()), + std::vector<double>(spanwise.begin(), spanwise.end())) + ->color("black") + .line_width(1.5); + } else { + ax->plot(std::vector<double>(spanwise.begin(), spanwise.end()), + std::vector<double>(chordwise.begin(), chordwise.end())) + ->color("black") + .line_width(1.5); + } + + legend_strings.push_back("contour"); + matplot::hold(true); + + /* Plot spars */ + for (auto& spar : spars) { + const auto [x, y, _] = gen_polygon(surface, spar); + (void)_; + if (is_vertical) { + ax->plot(x, y)->color(get_spar_colors()).line_width(1).line_style("--"); + } else { + ax->plot(y, x)->color(get_spar_colors()).line_width(1).line_style("--"); + } + legend_strings.push_back("spar"); + } + for (auto& device : devices) { + const auto [x, y, name] = gen_polygon(surface, device); + if (is_vertical) { + ax->plot(x, y)->color(get_control_device_colors(name)).line_width(1.5); + } else { + ax->plot(y, x)->color(get_control_device_colors(name)).line_width(1.5); + } + legend_strings.push_back(device.name); + } + + /* Select if vertical or horizontal element */ + if (std::fabs(surface.normal.dz()) > 0.7) { + ax->xlabel("x [m]"); + ax->ylabel("z [m]"); + } else { + ax->xlabel("y [m]"); + ax->ylabel("x [m]"); + } + + ax->limits_mode(matplot::manual); + ax->axis(matplot::equal); + if (is_vertical) { + ax->ylim({0, *std::max_element(spanwise.begin(), spanwise.end()) * 1.1}); + } else { + ax->xlim({0, *std::max_element(spanwise.begin(), spanwise.end()) * 1.1}); + } + ax->legend(legend_strings); + ax->legend()->location(matplot::legend::general_alignment::bottomright); + ax->legend()->inside(true); + ax->legend()->box(true); + ax->legend()->font_size(8); + + fs::path plot_path = rtIO->getPlotDir() + "/empennage_design_" + surface.name + "_planform.svg"; + matplot::save(f, plot_path.string(), "svg"); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + return fs::relative(plot_path, fs::path(rtIO->getHtmlDir())); +} + +fs::path VerticalTails::plot_thickness_and_twist_distribution( + const geom2::MultisectionSurface<geom2::AirfoilSection>& surface) { + /* Plot active */ + if (!rtIO->plotOn) { + return ""; + } + std::vector<double> eta; + std::vector<double> thickness_to_chord_ratio; + std::vector<double> twist; + double half_span = surface.is_symmetric ? geom2::measure::span(surface) * 0.5 : geom2::measure::span(surface); + for (auto section : surface.sections) { + eta.push_back(-section.origin.z() / half_span); + thickness_to_chord_ratio.push_back(geom2::measure::thickness_max(section) / section.get_chord_length()); + twist.push_back(section.get_twist_angle()); + } + const auto [t_to_c_min,t_to_c_max] = std::ranges::minmax_element(thickness_to_chord_ratio); + const auto [twist_min,twist_max] = std::ranges::minmax_element(twist); + + auto f = matplot::figure(true); + + auto ax1 = f->add_subplot(2, 1, 0); + ax1->plot(eta, thickness_to_chord_ratio, "g-")->color("black").line_width(1.5); + // ax1->xlabel("\u03B7"); + ax1->x_axis().visible(true); + ax1->grid(true); + ax1->ylabel("t / c [1]"); + ax1->ylim({*t_to_c_min - 0.01,*t_to_c_max + 0.01}); + + auto ax2 = f->add_subplot(2, 1, 1); + ax2->plot(eta, twist, "b-")->color("black").line_style("--").line_width(1.5); + ax2->ylabel("\u03F5 [\u00b0]"); + ax2->xlabel("\u03B7 [1]"); + ax2->grid(true); + ax2->ylim({*twist_min - 0.01, *twist_max + 0.01}); + + fs::path plot_path = rtIO->getPlotDir() + "/empennage_design_" + surface.name + "_thickness_and_twist_distribution.svg"; + matplot::save(f, plot_path.string(), "svg"); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + return fs::relative(plot_path, fs::path(rtIO->getHtmlDir())); +} + +/** + * \brief Plot airfoils + * + * \return fs::path + */ +std::vector<fs::path> VerticalTails::plot_airfoils(const geom2::MultisectionSurface<geom2::AirfoilSection>& surface) { + /* Plot active */ + if (!rtIO->plotOn) { + return {""}; + } + std::map<std::string, std::vector<geom2::Point_2>> airfoils_data{}; + std::vector<fs::path> airfoil_plots_path; + + for (auto section : surface.sections) { + if (airfoils_data.find(section.name) != airfoils_data.end()) { + continue; + } else { + airfoils_data.insert({section.name, section.get_contour().vertices()}); + } + } + /* loop over airfoils */ + + for (const auto& airfoil_data : airfoils_data) { + std::vector<double> x{}, y{}; + for (auto coord : airfoil_data.second) { + x.push_back(coord.x()); + y.push_back(coord.y()); + } + + { + auto f = matplot::figure(true); + auto h = f->current_axes()->plot(x, y); + h->line_width(1.5).color("black"); + + auto ax = f->current_axes(); + ax->title(std::format("Airfoil - {}", airfoil_data.first)); + matplot::xlabel("\u03B7"); + ax->ylabel("z/c"); + ax->xlabel("\u03B7 [1]"); + ax->grid(true); + ax->ylim({-0.2, 0.2}); + ax->xlim({0, 1}); + ax->axis(matplot::equal); + + fs::path plot_path = rtIO->getPlotDir() + "/empennage_design_" + surface.name + "_" + airfoil_data.first + "_airfoil.svg"; + matplot::save(f, plot_path.string(), "svg"); + airfoil_plots_path.push_back(fs::relative(plot_path, fs::path(rtIO->getHtmlDir()))); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + } + return airfoil_plots_path; +} + +} // namespace low +} // namespace bwb diff --git a/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesignReport.cpp b/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesignReport.cpp new file mode 100644 index 0000000000000000000000000000000000000000..81f606c4dc48ed8ae0b12f5536a6d64aae0460f5 --- /dev/null +++ b/empennage_design/src/bwb/vertical_tails/low/lowBwbVerticalTailsDesignReport.cpp @@ -0,0 +1,328 @@ +/* + * UNICADO - UNIversity Conceptual Aircraft Design and Optimization + * + * Copyright (C) 2025 UNICADO consortium + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * Description: + * This file is part of UNICADO. + */ + +#include <aircraftGeometry2/processing/measure.h> +#include <aircraftGeometry2/processing/transform.h> +#include <moduleBasics/html.h> + +#include "lowBwbVerticalTailsDesign.h" + +namespace bwb { +namespace low { +void VerticalTails::set_html_body() { + /* Add box data */ + reporter.htmlReportStream() << "<div class=\"box data\">\n"; + /* Add headline Data*/ + reporter.htmlReportStream() << "<h2>Data</h2>\n"; + /* Table 1 - General parameters */ + reporter.htmlReportStream() + << "<table class=\"content-table\">\n" + << "<caption>Configuration</caption>\n" + << "<thead>\n<th>Type</th><th>Vertical Tails</th></thead>" + << "<tbody>\n" + << "</tbody>\n" + << "</table>" + << "<table class=\"content-table\">\n" + << "<caption>Data for single vertical tail (symmetric)</caption>\n" + << "<thead>\n" + << "<tr>\n" + << "<th>parameter</th><th>symbol</th><th>unit</th><th>value</th>\n" + << "</tr>\n" + << "</thead>\n" + << "<tbody>\n" + << "<tr>\n" + << std::format("<td>Reference area</td><td>S<sub>ref</sub></td><td>m<sup>2</sup></td><td>{:.2f}</td>\n", + geom2::measure::reference_area(data->tails.front())) + << "</tr>\n" + << "<tr>\n" + << std::format("<td>Mean aero. chord</td><td>MAC</td><td>m</td><td>{:.2f}</td>\n", + geom2::measure::mean_aerodynamic_chord(data->tails.front())) + << "</tr>\n" + << "<tr>\n" + << std::format("<td>Span</td><td>b</td><td>m</td><td>{:.2f}</td>\n", + geom2::measure::span(data->tails.front())) + << "</tr>\n" + << "<tr>\n" + << std::format("<td>Aspect ratio</td><td>AR</td><td>1</td><td>{:.2f}</td>\n", + geom2::measure::aspect_ratio(data->tails.front())) + << "</tr>\n" + << "<tr>\n" + << std::format("<td>Taper ratio</td><td>λ</td><td>1</td><td>{:.2f}</td>\n", + geom2::measure::taper_ratio(data->tails.front())) + << "</tr>\n" + << std::format("<td>Ref. position</td><td>x<sub>ref</sub></td><td>m</td><td>{:.2f}</td>\n", + data->empennage_position.x.value() + data->tails.front().origin.x()) + << "</tr>\n" + << std::format("<td></td><td>y<sub>ref</sub></td><td>m</td><td>{:.2f}</td>\n", + data->empennage_position.y.value() + data->tails.front().origin.y()) + << "</tr>\n" + << std::format("<td></td><td>z<sub>ref</sub></td><td>m</td><td>{:.2f}</td>\n", + data->empennage_position.z.value() + -data->tails.front().origin.z()) + << "</tr>\n" + << "</tbody>\n" + << "</table>\n"; + + /* Table 2 - Section parameters*/ + reporter.htmlReportStream() << "<table class=\"content-table\">\n" + << "<caption>Section Parameter - Vertical tail</caption>\n" + << "<thead>\n" + << "<tr>\n" + << "<th>parameter</th><th>symbol</th><th>unit</th>"; + for (int i = 0; i < data->tails.front().sections.size(); ++i) { + reporter.htmlReportStream() << std::format("<th>S<sub>{:02d}</sub></th>", i); + } + reporter.htmlReportStream() << "\n</tr>\n" + << "</thead>\n" + << "<tbody>\n" + /* Dimensionless half span - eta*/ + << "<tr>\n" + << "<td>Dimensionless half span</td><td>η</td><td>1</td>"; + for (auto section : data->tails.front().sections) { + reporter.htmlReportStream() << std::format( + "<td>{:.2f}</td>", std::fabs(section.origin.z() / geom2::measure::span(data->tails.front()))); + } + /* Spanwise coordinate - y*/ + reporter.htmlReportStream() << "\n</tr>\n" + << "<td>Spanwise coordinate</td><td>y</td><td>m</td>"; + for (auto section : data->tails.front().sections) { + reporter.htmlReportStream() << std::format("<td>{:.2f}</td>", std::fabs(section.origin.z())); + } + reporter.htmlReportStream() << "\n</tr>\n" + /* Chord - c*/ + << "<tr>\n" + << "<td>Chord</td><td>c</td><td>m</td>"; + for (auto section : data->tails.front().sections) { + reporter.htmlReportStream() << std::format("<td>{:.2f}</td>", section.get_chord_length()); + } + reporter.htmlReportStream() << "\n</tr>\n" + /* Sweep - Leading edge - phi_LE*/ + << "<tr>\n" + << "<td>Sweep leading edge</td><td>φ<sub>LE</sub></td><td>°</td>"; + for (auto section : data->tails.front().sections) { + reporter.htmlReportStream() << std::format( + "<td>{:.2f}</td>", + -geom2::detail::to_degrees * geom2::measure::sweep(data->tails.front(), section.origin.z(), 0.0)); + } + reporter.htmlReportStream() << "\n</tr>\n" + /* Sweep - 25 percent - phi_25%*/ + << "<tr>\n" + << "<td>Sweep quarter chord</td><td>φ<sub>25</sub></td><td>°</td>"; + for (auto section : data->tails.front().sections) { + reporter.htmlReportStream() << std::format( + "<td>{:.2f}</td>", + -geom2::detail::to_degrees * geom2::measure::sweep(data->tails.front(), section.origin.z(), 0.25)); + } + reporter.htmlReportStream() << "\n</tr>\n" + /* Sweep - Trailing edge percent - phi_TE*/ + << "<tr>\n" + << "<td>Sweep Trailing edge</td><td>φ<sub>TE</sub></td><td>°</td>"; + for (auto section : data->tails.front().sections) { + reporter.htmlReportStream() << std::format( + "<td>{:.2f}</td>", + -geom2::detail::to_degrees * geom2::measure::sweep(data->tails.front(), section.origin.z(), 1.0)); + } + reporter.htmlReportStream() << "\n</tr>\n" + /* Dihedral - nu*/ + << "<tr>\n" + << "<td>Dihedral</td><td>ν</td><td>°</td>"; + for (auto section : data->tails.front().sections) { + reporter.htmlReportStream() << std::format( + "<td>{:.2f}</td>", + geom2::detail::to_degrees * geom2::measure::dihedral(data->tails.front(), section.origin.z())); + } + reporter.htmlReportStream() << "\n</tr>\n" + /* Twist - epsilon*/ + << "<tr>\n" + << "<td>Twist angle</td><td>ε</td><td>°</td>"; + for (auto section : data->tails.front().sections) { + reporter.htmlReportStream() << std::format("<td>{:.2f}</td>", geom2::detail::to_degrees * section.rotation_z); + } + reporter.htmlReportStream() << "\n</tr>\n" + /* Thickness to chord - epsilon*/ + << "<tr>\n" + << "<td>Thickness ratio</td><td>t/c</td><td>%</td>"; + for (auto section : data->tails.front().sections) { + /* Remove when thickness max is working */ + reporter.htmlReportStream() << std::format( + "<td>{:.2f}</td>", 100 * geom2::measure::thickness_max(section) / section.get_chord_length()); + } + reporter.htmlReportStream() << "\n</tr>\n" + /* Airfoil - none */ + << "<tr>\n" + << "<td>Airfoil</td><td>-</td><td>-</td>"; + for (auto section : data->tails.front().sections) { + reporter.htmlReportStream() << std::format("<td>{}</td>", section.name); + } + reporter.htmlReportStream() << "\n</tr>\n" + << "</tbody>\n" + << "</table>\n"; + + /* Table 3 - Spar parameters*/ + std::vector<geom2::MultisectionSurface<geom2::PolygonSection>> vertical_tails_spars = + data->spars; + for (size_t spar_id = 0; spar_id < vertical_tails_spars.size(); ++spar_id) { + reporter.htmlReportStream() << "<table class=\"content-table\">\n"; + if (spar_id == 0) { + reporter.htmlReportStream() << "<caption>Spar parameters</caption>\n"; + } + reporter.htmlReportStream() << "<thead>\n" + << "<tr>\n" + << std::format("<th>{:<10}</th><th>η [1]<th>x/c [%]</th>", + vertical_tails_spars.at(spar_id).name) + << "\n</tr>\n" + << "</thead>\n" + << "<tbody>\n"; + /* Spar name - eta, x/c*/ + for (size_t i = 0; i < vertical_tails_spars.at(spar_id).sections.size(); ++i) { + reporter.htmlReportStream() << "<tr>\n"; + std::string tag = ""; + if (i == 0) { + tag = "from"; + } else if (i == vertical_tails_spars.at(spar_id).sections.size() - 1) { + tag = "to"; + } + reporter.htmlReportStream() << std::format( + "<td>{}</td><td>{:.2f}</td><td>{:.2f}</td>\n", tag, + vertical_tails_spars.at(spar_id).sections.at(i).origin.z(), + vertical_tails_spars.at(spar_id).sections.at(i).get_contour().vertex(0).x()); + reporter.htmlReportStream() << "\n</tr>\n"; + } + /* Spanwise coordinate - y*/ + reporter.htmlReportStream() << "</tbody>\n" + << "</table>\n"; + } + + /* Sort devices to leading, trailing and other devices*/ + /* device data - name, eta_from, rel_chord_from_start, rel_chord_from_end, eta_to, rel_chord_to_start, + * rel_chord_to_end */ + using device_data = std::tuple<std::string, double, double, double, double, double, double>; + std::vector<device_data> vertical_tails_devices; + std::vector<device_data> other_devices; + + std::vector<geom2::MultisectionSurface<geom2::PolygonSection>> rudders = data->controls; + + for (auto device : rudders) { + /* Device has only two sections (from + to) spanwise */ + double eta_from = device.sections.at(0).origin.z(); + double rel_chord_from_start = device.sections.at(0).get_contour().vertex(0).x(), + rel_chord_from_end = device.sections.at(0).get_contour().vertex(1).x(); + double eta_to = device.sections.at(1).origin.z(); + double rel_chord_to_start = device.sections.at(1).get_contour().vertex(0).x(), + rel_chord_to_end = device.sections.at(1).get_contour().vertex(1).x(); + vertical_tails_devices.push_back({device.name, eta_from, rel_chord_from_start, rel_chord_from_end, eta_to, + rel_chord_to_start, rel_chord_to_end}); + } + + reporter.htmlReportStream() + << "<table class=\"content-table\">\n" + << "<caption>Vertical tail control devices (single)</caption>\n" + << "<thead>\n" + << "<tr>\n" + << "<th>Device</th><th>η<sub>from</sub></th><th>x/c<sub>from,start</sub></th><th>x/c<sub>from,end</sub></" + "th><th>η<sub>to</sub></th><th>x/c<sub>to,start</sub></th><th>x/c<sub>to,end</sub></th>\n" + << "</tr>\n" + << "</thead>\n" + << "<tbody>\n"; + for (auto device : vertical_tails_devices) { + const auto [name, eta_from, rel_chord_from_start, rel_chord_from_end, eta_to, rel_chord_to_start, + rel_chord_to_end] = device; + reporter.htmlReportStream() << "<tr>\n"; + reporter.htmlReportStream() << std::format( + "<td>{}</td><td>{:.2f}</td><td>{:.2f}</td><td>{:.2f}</td><td>{:.2f}</td><td>{:.2f}</td><td>{:.2f}</td>\n", name, + eta_from, rel_chord_from_start, rel_chord_from_end, eta_to, rel_chord_to_start, rel_chord_to_end); + reporter.htmlReportStream() << "\n</tr>\n"; + } + reporter.htmlReportStream() << "</tbody>\n" + << "</table>\n"; + + reporter.htmlReportStream() + << "<table class=\"content-table\">\n" + << "<caption>Mass and CoG - Vertical tail (single)</caption>\n" + << "<thead>\n" + << "<tr>\n" + << "<th>Mass</th><th>CoG<sub>x</sub></th><th>CoG<sub>y</sub></th><th>CoG<sub>z</sub></th>\n" + << "</tr>\n" + << "</thead>\n" + << "<tbody>\n" + << "<tr>\n" + << std::format("<td>{:.2f}kg</td><td>{:.2f}m</td><td>{:.2f}m</td><td>{:.2f}m</td>", + data->tails_mass.front().data["mass"].value(), + data->tails_mass.front().data["x"].value(), data->tails_mass.front().data["y"].value(), + data->tails_mass.front().data["z"].value()) + << "\n<tr>\n" + << "</tbody>\n" + << "</table>\n"; + + + reporter.htmlReportStream() + << "<table class=\"content-table\">\n" + << "<caption>Mass and CoG - Empennage</caption>\n" + << "<thead>\n" + << "<tr>\n" + << "<th>Mass</th><th>CoG<sub>x</sub></th><th>CoG<sub>y</sub></th><th>CoG<sub>z</sub></th>\n" + << "</tr>\n" + << "</thead>\n" + << "<tbody>\n" + << "<tr>\n" + << std::format("<td>{:.2f}kg</td><td>{:.2f}m</td><td>{:.2f}m</td><td>{:.2f}m</td>", + data->empennage_mass.data["mass"].value(), data->empennage_mass.data["x"].value(), + data->empennage_mass.data["y"].value(), data->empennage_mass.data["z"].value()) + << "\n<tr>\n" + << "</tbody>\n" + << "</table>\n"; + + + /* close box data div */ + reporter.htmlReportStream() << "</div>\n"; + + if (rtIO->plotOn) { + /* open box plot div */ + reporter.htmlReportStream() << "<div class=\"box plot\">\n"; + /* Add headline Data*/ + reporter.htmlReportStream() << "<h2>Plots</h2>\n"; + + /* Add Geometry Headline */ + reporter.htmlReportStream() << "<h3>Geometry</h3>\n"; + + + /* Vertical stabilizer planform plot */ + auto planform_vertical_tail_plot_path = plot_stabilizer_planform( + data->tails.front(), data->spars, data->controls); + reporter.htmlReportStream() << std::format("<img class=\"image-plot\" src=\"{}\"/>\n", + planform_vertical_tail_plot_path.string()); + /* Vertical stabilizer airfoil */ + auto vertical_tail_airfoil_plot_paths = plot_airfoils(data->tails.front()); + reporter.htmlReportStream() << "<h3>Vertical Stabilizer Airfoils</h3>\n"; + for( auto airfoil_path : vertical_tail_airfoil_plot_paths) { + reporter.htmlReportStream() << std::format("<img class=\"image-plot\" src=\"{}\"/>\n", airfoil_path.string()); + } + reporter.htmlReportStream() << "<h3>Vertical Tail Thickness & Geometric Twist</h3>\n"; + auto vertical_tail_thickness_and_twist_plot_path = plot_thickness_and_twist_distribution(data->tails.front()); + reporter.htmlReportStream() << std::format("<img class=\"image-plot\" src=\"{}\"/>\n", + vertical_tail_thickness_and_twist_plot_path.string()); + + // /* close box plot */ + reporter.htmlReportStream() << "</div>\n"; + } +} +} // namespace low +} // namespace bwb diff --git a/empennage_design/src/empennageDesign.cpp b/empennage_design/src/empennageDesign.cpp index 295c936bd96d4ed30e78756b7156e9b28d145f37..18d7feb2cef72082b34532a1fb7b20065dd9ba41 100644 --- a/empennage_design/src/empennageDesign.cpp +++ b/empennage_design/src/empennageDesign.cpp @@ -29,7 +29,7 @@ #include <vector> #include <format> -#include "bwb/vertical_tails/low/LowVerticalTailsBwb.h" +#include "bwb/vertical_tails/low/lowBwbVerticalTailsDesign.h" #include "taw/conventional/low/lowConventionalEmpennageDesign.h" #include "taw/t_tail/low/low_t_tail_empennage_design.h" @@ -38,8 +38,7 @@ EmpennageDesign::EmpennageDesign(const int argc, char *argv[], const std::string : Module(argc, argv, toolName, toolVersion) { std::string configuration = rtIO_->aircraft_configuration_type(); std::string usedtype = EndnodeReadOnly<std::string>("requirements_and_specifications/design_specification/configuration/empennage_definition/empennage_type").read(rtIO_->acxml).value(); - std::string fidelity = EndnodeReadOnly<std::string>("program_settings/fidelity_selection").read(rtIO_->moduleConfig).value(); - + std::string fidelity = EndnodeReadOnly<std::string>("program_settings/" + configuration + "/fidelity_selection").read(rtIO_->moduleConfig).value(); /* Register existing strategies */ /* tube and wing strategies */ strategy.registerStrategy<taw::low::Conventional>({"tube_and_wing", "conventional", "low"});