From f1f8bdb05b4649d4ecfbe1efa6ad5059a12b6116 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 17 Sep 2024 18:00:40 -0600 Subject: [PATCH 01/87] Begin creating wrapper class for the aerodyn-inflow c-binding library --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 78 +++++++++++++++++++ .../regression/test_aerodyn_inflow.cpp | 45 +++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/utilities/aerodynamics/aerodyn_inflow.hpp create mode 100644 tests/unit_tests/regression/test_aerodyn_inflow.cpp diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp new file mode 100644 index 00000000..7e3d938c --- /dev/null +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include + +namespace openturbine::util { + +// Forward declare the C functions residing in the Aerodyn Inflow shared library to be used in the +// C++ wrapper class +extern "C" { +void ADI_C_PreInit(int*, int*, int*, int*, char*); +void ADI_C_SetupRotor(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, int*, float*, double*, int*, int*, char*); +void ADI_C_Init(int*, char**, int*, int*, char**, int*, char*, float*, float*, float*, float*, float*, float*, float*, int*, double*, double*, int*, int*, float*, float*, int*, double*, int*, char*, char*, int*, char*); +void ADI_C_SetRotorMotion(int*, float*, double*, float*, float*, float*, double*, float*, float*, float*, double*, float*, float*, int*, float*, double*, float*, float*); +void ADI_C_GetRotorLoads(int*, int*, float*, int*, char*); +void ADI_C_CalcOutput(double*, float*, int*, char*); +void ADI_C_UpdateStates(double*, double*, int*, char*); +void ADI_C_End(int*, char*); +} + +/// @brief Wrapper class for the AeroDyn Inflow shared library +class AeroDynInflowLib { +public: + // Error levels + enum class ErrorLevel { + kNone = 0, + kInfo = 1, + kWarning = 2, + kSevereError = 3, + kFatalError = 4 + }; + + // Define some constants + static constexpr int kErrorMessagesLength{ + 1025 // Error message length in Fortran + }; + static constexpr int kDefaultStringLength{ + 1025 // Length of the name used for any output file written by the HD Fortran code + }; + + /// @brief Constructor + AeroDynInflowLib(const std::string& library_path) : library_path_(library_path), ended_(false) { + InitializeRoutines(); + InitializeData(); + } + + /// Wrapper for the ADI_C_PreInit routine + void ADI_PreInit() { + int n_turbines{1}; // input: Number of turbines + int transpose_DCM{1}; // input: Transpose the direction cosine matrix? + int debug_level{0}; // input: Debug level + int error_status{0}; // output: Error status + char error_message[kErrorMessagesLength]{'\0'}; // output: Error message buffer + + ADI_C_PreInit(&n_turbines, &transpose_DCM, &debug_level, &error_status, error_message); + + if (error_status != 0) { + throw std::runtime_error(std::string("PreInit error: ") + error_message); + } + } + +private: + std::string library_path_; // Path to the shared library + [[maybe_unused]] bool ended_; // For error handling at end + [[maybe_unused]] int n_blades_ = 3; // Default number of blades + + void InitializeRoutines() { + // Add other routine initializations if needed + } + + void InitializeData() { + // Initialize buffers for the class data + // Similar to the Python __init__ but adapted for C++ + } +}; + +} // namespace openturbine::util diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp new file mode 100644 index 00000000..1b304256 --- /dev/null +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -0,0 +1,45 @@ +#include + +#include "test_utilities.hpp" + +#include "src/vendor/dylib/dylib.hpp" + +namespace openturbine::tests { + +#ifdef OpenTurbine_BUILD_OPENFAST_ADI +TEST(AerodynInflowTest, ADI_C_PreInit) { + // Use dylib to load the dynamic library and get access to the aerodyn inflow c binding functions + const std::filesystem::path project_root = FindProjectRoot(); + const std::filesystem::path full_path = + project_root / "build/tests/unit_tests/libaerodyn_inflow_c_binding."; + std::string path = full_path.string(); +#ifdef __APPLE__ + path += "dylib"; +#elif __linux__ + path += "so"; +#else // Windows + path += "dll"; +#endif + const util::dylib lib(path, util::dylib::no_filename_decorations); + auto ADI_C_PreInit = lib.get_function("ADI_C_PreInit"); + + // Call ADI_C_PreInit routine and expect the following outputs + int numTurbines{0}; // input: Number of turbines + int transposeDCM{1}; // input: Transpose the direction cosine matrix + int debuglevel{0}; // input: Debug level + int error_status_c{0}; // output: error status + char error_message_c[] = {'\0'}; // output: error message + ADI_C_PreInit( + &numTurbines, &transposeDCM, &debuglevel, &error_status_c, + static_cast(error_message_c) + ); + + EXPECT_EQ(numTurbines, 0); + EXPECT_EQ(transposeDCM, 1); + EXPECT_EQ(debuglevel, 0); + EXPECT_EQ(error_status_c, 0); + EXPECT_STREQ(static_cast(error_message_c), ""); +} +#endif + +} // namespace openturbine::tests \ No newline at end of file From 2b6fe4ed9a3d42bf0dfb19923f03f4e1d7df4884 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 19 Sep 2024 00:12:50 -0600 Subject: [PATCH 02/87] Add bunch of data attributes to the AeroDynInflowLibrary struct --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 140 ++++++++++-------- 1 file changed, 82 insertions(+), 58 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 7e3d938c..439587b9 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -6,23 +6,9 @@ namespace openturbine::util { -// Forward declare the C functions residing in the Aerodyn Inflow shared library to be used in the -// C++ wrapper class -extern "C" { -void ADI_C_PreInit(int*, int*, int*, int*, char*); -void ADI_C_SetupRotor(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, int*, float*, double*, int*, int*, char*); -void ADI_C_Init(int*, char**, int*, int*, char**, int*, char*, float*, float*, float*, float*, float*, float*, float*, int*, double*, double*, int*, int*, float*, float*, int*, double*, int*, char*, char*, int*, char*); -void ADI_C_SetRotorMotion(int*, float*, double*, float*, float*, float*, double*, float*, float*, float*, double*, float*, float*, int*, float*, double*, float*, float*); -void ADI_C_GetRotorLoads(int*, int*, float*, int*, char*); -void ADI_C_CalcOutput(double*, float*, int*, char*); -void ADI_C_UpdateStates(double*, double*, int*, char*); -void ADI_C_End(int*, char*); -} - -/// @brief Wrapper class for the AeroDyn Inflow shared library -class AeroDynInflowLib { -public: - // Error levels +/// Struct for error handling settings +struct ErrorHandling { + /// Error levels used in the ADI library enum class ErrorLevel { kNone = 0, kInfo = 1, @@ -31,48 +17,86 @@ class AeroDynInflowLib { kFatalError = 4 }; - // Define some constants - static constexpr int kErrorMessagesLength{ - 1025 // Error message length in Fortran - }; - static constexpr int kDefaultStringLength{ - 1025 // Length of the name used for any output file written by the HD Fortran code - }; + static constexpr size_t kErrorMessagesLength{1025U}; // Max error message length in Fortran + int abort_error_level{4}; // Error level at which to abort + int error_status_c{0}; // Error status + std::array error_message_c{}; // Error message buffer +}; + +/// Struct to hold the environmental conditions related to the working fluid +struct EnvironmentalConditions { + double gravity{9.80665}; // Gravitational acceleration (m/s^2) + double air_density{1.225}; // Air density (kg/m^3) + double kinematic_viscosity{1.464E-05}; // Kinematic viscosity (m^2/s) + double sound_speed{335.}; // Speed of sound in working fluid (m/s) + double atmospheric_pressure{103500.}; // Atmospheric pressure (Pa) + double vapor_pressure{1700.}; // Vapour pressure of working fluid (Pa) + double water_depth{0.}; // Water depth (m) + double mean_sea_level_to_still_water_level{0.}; // Offset (m) +}; + +/// Struct to hold the settings for the turbine +struct TurbineSettings { + int n_turbines{1}; // Number of turbines + int n_blades{3}; // Number of blades + std::array initial_hub_position{0, 0, 0}; // Initial hub position + std::array initial_hub_orientation{0}; // Initial hub orientation + std::array initial_nacelle_position{0}; // Initial nacelle position + std::array initial_nacelle_orientation{0}; // Initial nacelle orientation + std::array initial_root_position{0}; // Initial root position + std::array initial_root_orientation{0}; // Initial root orientation +}; + +/// Struct to hold the settings for the simulation controls +struct SimulationControls { + static constexpr size_t kDefaultStringLength{ + 1025}; // Max length of the name used for any output file written by the HD Fortran code + + // Input file handling + bool aerodyn_input_passed{true}; // Assume passing of input file as a string + bool inflowwind_input_passed{true}; // Assume passing of input file as a string + + // Interpolation order (must be 1: linear, or 2: quadratic) + int interpolation_order{1}; // Interpolation order - linear by default + + // Initial time related variables + float dt{0.1f}; // Timestep (s) + float tmax{600.f}; // Maximum time (s) + float total_time{0.f}; // Total elapsed time (s) + int n_time_steps{0}; // Number of time steps + + // Flags + bool store_HH_wind_speed{1}; // Store hub-height wind speed? + bool transpose_DCM{1}; // Transpose the direction cosine matrix? + int debug_level{0}; // Debug level (0-4) + + // Outputs + int output_format{0}; // File format for writing outputs + float output_timestep{0.}; // Timestep for outputs to file + char output_root_name[kDefaultStringLength]{"output"}; // Root name for output files + int n_channels{0}; // Number of channels returned +}; + +/// Struct to hold the settings for writing VTK output +struct VTKSettings { + bool write_vtk{false}; // Write VTK output? + int vtk_type{1}; // VTK output type (1: surface meshes) + std::array vtk_nacelle_dimensions{ + -2.5f, -2.5f, 0.f, + 10.f, 5.f, 5.f}; // Nacelle dimensions for VTK surface rendering [x0,y0,z0,Lx,Ly,Lz] (m) + float VTKHubRad{1.5f}; // Hub radius for VTK surface rendering +}; + +/// @brief Wrapper class for the AeroDynInflow (ADI) shared library +struct AeroDynInflowLibrary { + std::string library_path; //< Path to the shared library + ErrorHandling error_handling; //< Error handling settings + EnvironmentalConditions env_conditions; //< Environmental conditions + TurbineSettings turbine_settings; //< Turbine settings + SimulationControls sim_controls; //< Simulation controls + VTKSettings vtk_settings; //< VTK settings - /// @brief Constructor - AeroDynInflowLib(const std::string& library_path) : library_path_(library_path), ended_(false) { - InitializeRoutines(); - InitializeData(); - } - - /// Wrapper for the ADI_C_PreInit routine - void ADI_PreInit() { - int n_turbines{1}; // input: Number of turbines - int transpose_DCM{1}; // input: Transpose the direction cosine matrix? - int debug_level{0}; // input: Debug level - int error_status{0}; // output: Error status - char error_message[kErrorMessagesLength]{'\0'}; // output: Error message buffer - - ADI_C_PreInit(&n_turbines, &transpose_DCM, &debug_level, &error_status, error_message); - - if (error_status != 0) { - throw std::runtime_error(std::string("PreInit error: ") + error_message); - } - } - -private: - std::string library_path_; // Path to the shared library - [[maybe_unused]] bool ended_; // For error handling at end - [[maybe_unused]] int n_blades_ = 3; // Default number of blades - - void InitializeRoutines() { - // Add other routine initializations if needed - } - - void InitializeData() { - // Initialize buffers for the class data - // Similar to the Python __init__ but adapted for C++ - } + AeroDynInflowLibrary(const std::string& path) : library_path(path) {} }; } // namespace openturbine::util From a18f1a0384637d8c6ca1cf09d778218b9c9a2fcb Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 19 Sep 2024 12:11:05 -0600 Subject: [PATCH 03/87] Add StructuralMesh attribute to AeroDynInflowLibrary struct + some refactoring of previous attrs --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 65 +++++++++++-------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 439587b9..16d2388d 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -1,14 +1,14 @@ #pragma once -#include #include -#include + +#include "src/vendor/dylib/dylib.hpp" namespace openturbine::util { /// Struct for error handling settings struct ErrorHandling { - /// Error levels used in the ADI library + /// Error levels used in InflowWind enum class ErrorLevel { kNone = 0, kInfo = 1, @@ -17,28 +17,28 @@ struct ErrorHandling { kFatalError = 4 }; - static constexpr size_t kErrorMessagesLength{1025U}; // Max error message length in Fortran - int abort_error_level{4}; // Error level at which to abort - int error_status_c{0}; // Error status - std::array error_message_c{}; // Error message buffer + static constexpr size_t kErrorMessagesLength{1025U}; // Max error message length in Fortran + int abort_error_level{4}; // Error level at which to abort + int error_status{0}; // Error status + std::array error_message{}; // Error message buffer }; -/// Struct to hold the environmental conditions related to the working fluid +/// Struct to hold the environmental conditions related to the working fluid i.e. air struct EnvironmentalConditions { - double gravity{9.80665}; // Gravitational acceleration (m/s^2) - double air_density{1.225}; // Air density (kg/m^3) - double kinematic_viscosity{1.464E-05}; // Kinematic viscosity (m^2/s) - double sound_speed{335.}; // Speed of sound in working fluid (m/s) - double atmospheric_pressure{103500.}; // Atmospheric pressure (Pa) - double vapor_pressure{1700.}; // Vapour pressure of working fluid (Pa) - double water_depth{0.}; // Water depth (m) - double mean_sea_level_to_still_water_level{0.}; // Offset (m) + double gravity{9.80665}; // Gravitational acceleration (m/s^2) + double density{1.225}; // Air density (kg/m^3) + double kinematic_viscosity{1.464E-05}; // Kinematic viscosity (m^2/s) + double sound_speed{335.}; // Speed of sound in working fluid (m/s) + double atm_pressure{103500.}; // Atmospheric pressure (Pa) + double vapor_pressure{1700.}; // Vapour pressure of working fluid (Pa) + double water_depth{0.}; // Water depth (m) + double mean_sea_level_2_still_water_level{0.}; // Offset (m) }; -/// Struct to hold the settings for the turbine +/// Struct to hold the settings for the turbine (assuming a single turbine) struct TurbineSettings { - int n_turbines{1}; // Number of turbines - int n_blades{3}; // Number of blades + int n_turbines{1}; // Number of turbines - 1 by default + int n_blades{3}; // Number of blades - 3 by default std::array initial_hub_position{0, 0, 0}; // Initial hub position std::array initial_hub_orientation{0}; // Initial hub orientation std::array initial_nacelle_position{0}; // Initial nacelle position @@ -47,6 +47,15 @@ struct TurbineSettings { std::array initial_root_orientation{0}; // Initial root orientation }; +/// Struct to hold the structural mesh data +struct StructuralMesh { + int n_mesh_points{1}; // Number of mesh points + std::vector> initial_mesh_position{}; // N x 3 array [x, y, z] + std::vector> + initial_mesh_orientation{}; // N x 9 array [r11, r12, ..., r33] + std::vector mesh_point_to_blade_num{}; // N x 1 array [blade number] +}; + /// Struct to hold the settings for the simulation controls struct SimulationControls { static constexpr size_t kDefaultStringLength{ @@ -66,15 +75,19 @@ struct SimulationControls { int n_time_steps{0}; // Number of time steps // Flags - bool store_HH_wind_speed{1}; // Store hub-height wind speed? - bool transpose_DCM{1}; // Transpose the direction cosine matrix? - int debug_level{0}; // Debug level (0-4) + int store_HH_wind_speed{1}; // Store hub-height wind speed? + int transpose_DCM{1}; // Transpose the direction cosine matrix? + int debug_level{0}; // Debug level (0-4) // Outputs - int output_format{0}; // File format for writing outputs - float output_timestep{0.}; // Timestep for outputs to file - char output_root_name[kDefaultStringLength]{"output"}; // Root name for output files - int n_channels{0}; // Number of channels returned + int output_format{0}; // File format for writing outputs + float output_timestep{0.}; // Timestep for outputs to file + std::array output_root_name{ + "Output_ADIlib_default" // Root name for output files + }; + int n_channels{0}; // Number of channels returned + std::array channel_names_c{}; // Output channel names + std::array channel_units_c{}; // Output channel units }; /// Struct to hold the settings for writing VTK output From 98a8c8546a945bd84d3f49c77ddcb2f73709b3d4 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 19 Sep 2024 12:12:31 -0600 Subject: [PATCH 04/87] Add a dylib attribute to AeroDynInflowLibrary struct to contain the shared ADI lib handle --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 16d2388d..242af16a 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -100,16 +100,36 @@ struct VTKSettings { float VTKHubRad{1.5f}; // Hub radius for VTK surface rendering }; -/// @brief Wrapper class for the AeroDynInflow (ADI) shared library +/// @brief Wrapper class for the AeroDynInflow (ADI) shared library containing the following C +/// interfaces +/// - ADI_C_PreInit(int*, int*, int*, int*, char*) +/// - ADI_C_SetupRotor(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, +/// int*, float*, double*, int*, int*, char*) +/// - ADI_C_Init(int*, char**, int*, int*, char**, int*, char*, float*, float*, float*, float*, +/// float*, float*, float*, int*, double*, double*, int*, int*, float*, float*, int*, double*, +/// int*, char*, char*, int*, char*) +/// - ADI_C_SetRotorMotion(int*, float*, double*, float*, float*, float*, double*, float*, float*, +/// float*, double*, float*, float*, int*, float*, double*, float*, float*) +/// - ADI_C_GetRotorLoads(int*, int*, float*, int*, char*) +/// - ADI_C_CalcOutput(double*, float*, int*, char*) +/// - ADI_C_UpdateStates(double*, double*, int*, char*) +/// - ADI_C_End(int*, char*) struct AeroDynInflowLibrary { - std::string library_path; //< Path to the shared library - ErrorHandling error_handling; //< Error handling settings - EnvironmentalConditions env_conditions; //< Environmental conditions - TurbineSettings turbine_settings; //< Turbine settings - SimulationControls sim_controls; //< Simulation controls - VTKSettings vtk_settings; //< VTK settings - - AeroDynInflowLibrary(const std::string& path) : library_path(path) {} + util::dylib lib{ + "libaerodyn_inflow_c_binding.dylib", + util::dylib::no_filename_decorations}; //< Dynamic library object for AeroDyn Inflow + ErrorHandling error_handling; //< Error handling settings + EnvironmentalConditions env_conditions; //< Environmental conditions + TurbineSettings turbine_settings; //< Turbine settings + StructuralMesh structural_mesh; //< Structural mesh data + SimulationControls sim_controls; //< Simulation controls + VTKSettings vtk_settings; //< VTK settings + + AeroDynInflowLibrary(std::string shared_lib_path = "") { + if (shared_lib_path != "") { + lib = util::dylib(shared_lib_path, util::dylib::no_filename_decorations); + } + } }; } // namespace openturbine::util From 51b75fcbe264dd98c80bca2ae53c1ea85db203d8 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 19 Sep 2024 12:19:56 -0600 Subject: [PATCH 05/87] Add a prototype wrapper for the ADI_PreInit() routine --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 242af16a..3050f89f 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -100,8 +100,10 @@ struct VTKSettings { float VTKHubRad{1.5f}; // Hub radius for VTK surface rendering }; -/// @brief Wrapper class for the AeroDynInflow (ADI) shared library containing the following C -/// interfaces +/// @brief Wrapper class for the AeroDynInflow (ADI) shared library +/// @details The AeroDynInflow (ADI) shared library is a Fortran library that provides C bindings +/// for interfacing with the AeroDyn Inflow module. The following functions are available in the +/// shared library /// - ADI_C_PreInit(int*, int*, int*, int*, char*) /// - ADI_C_SetupRotor(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, /// int*, float*, double*, int*, int*, char*) @@ -130,6 +132,25 @@ struct AeroDynInflowLibrary { lib = util::dylib(shared_lib_path, util::dylib::no_filename_decorations); } } + + /// Wrapper for ADI_C_PreInit routine to initialize AeroDyn Inflow library + void ADI_PreInit() { + auto ADI_C_PreInit = + this->lib.get_function("ADI_C_PreInit"); + ADI_C_PreInit( + &turbine_settings.n_turbines, // input: Number of turbines + &sim_controls.transpose_DCM, // input: Transpose the direction cosine matrix? + &sim_controls.debug_level, // input: Debug level + &error_handling.error_status, // output: Error status + error_handling.error_message.data() // output: Error message buffer + ); + + if (error_handling.error_status != 0) { + throw std::runtime_error( + std::string("PreInit error: ") + error_handling.error_message.data() + ); + } + } }; } // namespace openturbine::util From 94e279864159a499878f319a4c198335b8f5611a Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 19 Sep 2024 16:44:14 -0600 Subject: [PATCH 06/87] Added wrapper method for ADI_SetupRotor routine of ADI --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 77 ++++++++++++++++--- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 3050f89f..85bf2c55 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -23,7 +23,7 @@ struct ErrorHandling { std::array error_message{}; // Error message buffer }; -/// Struct to hold the environmental conditions related to the working fluid i.e. air +/// Struct to hold the environmental conditions related to the working fluid struct EnvironmentalConditions { double gravity{9.80665}; // Gravitational acceleration (m/s^2) double density{1.225}; // Air density (kg/m^3) @@ -37,14 +37,14 @@ struct EnvironmentalConditions { /// Struct to hold the settings for the turbine (assuming a single turbine) struct TurbineSettings { - int n_turbines{1}; // Number of turbines - 1 by default - int n_blades{3}; // Number of blades - 3 by default - std::array initial_hub_position{0, 0, 0}; // Initial hub position - std::array initial_hub_orientation{0}; // Initial hub orientation - std::array initial_nacelle_position{0}; // Initial nacelle position - std::array initial_nacelle_orientation{0}; // Initial nacelle orientation - std::array initial_root_position{0}; // Initial root position - std::array initial_root_orientation{0}; // Initial root orientation + int n_turbines{1}; // Number of turbines - 1 by default + int n_blades{3}; // Number of blades - 3 by default + std::array initial_hub_position{0, 0, 0}; // Initial hub position + std::array initial_hub_orientation{0}; // Initial hub orientation + std::array initial_nacelle_position{0}; // Initial nacelle position + std::array initial_nacelle_orientation{0}; // Initial nacelle orientation + std::array initial_root_position{0}; // Initial root position + std::array initial_root_orientation{0}; // Initial root orientation }; /// Struct to hold the structural mesh data @@ -151,6 +151,65 @@ struct AeroDynInflowLibrary { ); } } + + /// Wrapper for ADI_C_SetupRotor routine to set up the rotor + void ADI_SetupRotor( + int turbine_number, int is_horizontal_axis, std::vector turbine_ref_pos + ) { + auto ADI_C_SetupRotor = this->lib.get_function< + void(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, int*, float*, double*, int*, int*, char*)>( + "ADI_C_SetupRotor" + ); + + // Flatten mesh arrays + std::vector init_mesh_pos_flat = + this->FlattenArray(structural_mesh.initial_mesh_position); + std::vector init_mesh_orient_flat = + this->FlattenArray(structural_mesh.initial_mesh_orientation); + + ADI_C_SetupRotor( + &turbine_number, // input: current turbine number + &is_horizontal_axis, // input: 1: is HAWT, 0: VAWT or cross-flow + const_cast(turbine_ref_pos.data()), // input: initial hub position + const_cast(turbine_settings.initial_hub_position.data() + ), // input: initial hub position + const_cast(turbine_settings.initial_hub_orientation.data() + ), // input: initial hub orientation + const_cast(turbine_settings.initial_nacelle_position.data() + ), // input: initial nacelle position + const_cast(turbine_settings.initial_nacelle_orientation.data() + ), // input: initial nacelle orientation + &turbine_settings.n_blades, // input: number of blades + const_cast(turbine_settings.initial_root_position.data() + ), // input: initial blade root positions + const_cast(turbine_settings.initial_root_orientation.data() + ), // input: initial blade root orientation + &structural_mesh.n_mesh_points, // input: number of mesh points + const_cast(init_mesh_pos_flat.data()), // input: initial node positions + const_cast(init_mesh_orient_flat.data()), // input: initial node orientation + const_cast(structural_mesh.mesh_point_to_blade_num.data() + ), // input: initial mesh point to blade number mapping + &error_handling.error_status, // output: Error status + error_handling.error_message.data() // output: Error message buffer + ); + + if (error_handling.error_status != 0) { + throw std::runtime_error( + std::string("SetupRotor error: ") + error_handling.error_message.data() + ); + } + } + +private: + /// Method to flatten a 2D array into a 1D array for Fortran compatibility + template + std::vector FlattenArray(const std::vector>& input) { + std::vector output; + for (const auto& arr : input) { + output.insert(output.end(), arr.begin(), arr.end()); + } + return output; + } }; } // namespace openturbine::util From da765e0ec7f64f35bac273326ea09ea625e1daa3 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 19 Sep 2024 17:33:28 -0600 Subject: [PATCH 07/87] Quick refactor --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 176 ++++++++---------- 1 file changed, 81 insertions(+), 95 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 85bf2c55..f852dc07 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -1,6 +1,9 @@ #pragma once +#include #include +#include +#include #include "src/vendor/dylib/dylib.hpp" @@ -17,34 +20,44 @@ struct ErrorHandling { kFatalError = 4 }; - static constexpr size_t kErrorMessagesLength{1025U}; // Max error message length in Fortran - int abort_error_level{4}; // Error level at which to abort - int error_status{0}; // Error status - std::array error_message{}; // Error message buffer + static constexpr size_t kErrorMessagesLength = 1025U; //< Max error message length in Fortran + int abort_error_level{ + static_cast(ErrorLevel::kFatalError)}; //< Error level at which to abort + int error_status{0}; //< Error status + std::array error_message{}; //< Error message buffer + + /// Check for errors and throw an exception if found + bool CheckError() const { + return error_status == 0 ? true : throw std::runtime_error(error_message.data()); + } +}; + +/// Struct to hold the properties of the working fluid (air) +struct FluidProperties { + double density{1.225}; // Air density (kg/m^3) + double kinematic_viscosity{1.464E-5}; // Kinematic viscosity (m^2/s) + double sound_speed{335.}; // Speed of sound in the working fluid (m/s) + double vapor_pressure{1700.}; // Vapor pressure of the working fluid (Pa) }; -/// Struct to hold the environmental conditions related to the working fluid +/// Struct to hold the environmental conditions struct EnvironmentalConditions { - double gravity{9.80665}; // Gravitational acceleration (m/s^2) - double density{1.225}; // Air density (kg/m^3) - double kinematic_viscosity{1.464E-05}; // Kinematic viscosity (m^2/s) - double sound_speed{335.}; // Speed of sound in working fluid (m/s) - double atm_pressure{103500.}; // Atmospheric pressure (Pa) - double vapor_pressure{1700.}; // Vapour pressure of working fluid (Pa) - double water_depth{0.}; // Water depth (m) - double mean_sea_level_2_still_water_level{0.}; // Offset (m) + double gravity{9.80665}; // Gravitational acceleration (m/s^2) + double atm_pressure{103500.}; // Atmospheric pressure (Pa) + double water_depth{0.}; // Water depth (m) + double msl_offset{0.}; // Mean sea level to still water level offset (m) }; /// Struct to hold the settings for the turbine (assuming a single turbine) struct TurbineSettings { - int n_turbines{1}; // Number of turbines - 1 by default - int n_blades{3}; // Number of blades - 3 by default - std::array initial_hub_position{0, 0, 0}; // Initial hub position - std::array initial_hub_orientation{0}; // Initial hub orientation - std::array initial_nacelle_position{0}; // Initial nacelle position - std::array initial_nacelle_orientation{0}; // Initial nacelle orientation - std::array initial_root_position{0}; // Initial root position - std::array initial_root_orientation{0}; // Initial root orientation + int n_turbines{1}; // Number of turbines - 1 by default + int n_blades{3}; // Number of blades - 3 by default + std::array initial_hub_position{0.}; // Initial hub position + std::array initial_hub_orientation{0.}; // Initial hub orientation + std::array initial_nacelle_position{0.}; // Initial nacelle position + std::array initial_nacelle_orientation{0.}; // Initial nacelle orientation + std::array initial_root_position{0.}; // Initial root position + std::array initial_root_orientation{0.}; // Initial root orientation }; /// Struct to hold the structural mesh data @@ -52,27 +65,27 @@ struct StructuralMesh { int n_mesh_points{1}; // Number of mesh points std::vector> initial_mesh_position{}; // N x 3 array [x, y, z] std::vector> - initial_mesh_orientation{}; // N x 9 array [r11, r12, ..., r33] - std::vector mesh_point_to_blade_num{}; // N x 1 array [blade number] + initial_mesh_orientation{}; // N x 9 array [r11, r12, ..., r33] + std::vector + mesh_point_to_blade_num{}; // N x 1 array for mapping a mesh point to blade number }; /// Struct to hold the settings for the simulation controls struct SimulationControls { - static constexpr size_t kDefaultStringLength{ - 1025}; // Max length of the name used for any output file written by the HD Fortran code + static constexpr size_t kDefaultStringLength{1025}; // Max length for output filenames // Input file handling - bool aerodyn_input_passed{true}; // Assume passing of input file as a string - bool inflowwind_input_passed{true}; // Assume passing of input file as a string + bool aerodyn_input_passed{true}; // Input file passed for AeroDyn + bool inflowwind_input_passed{true}; // Input file passed for InflowWind - // Interpolation order (must be 1: linear, or 2: quadratic) + // Interpolation order (must be either 1: linear, or 2: quadratic) int interpolation_order{1}; // Interpolation order - linear by default // Initial time related variables - float dt{0.1f}; // Timestep (s) - float tmax{600.f}; // Maximum time (s) - float total_time{0.f}; // Total elapsed time (s) - int n_time_steps{0}; // Number of time steps + float time_step{0.1f}; // Simulation timestep (s) + float max_time{600.f}; // Maximum simulation time (s) + float total_elapsed_time{0.f}; // Total elapsed time (s) + int num_time_steps{0}; // Number of time steps // Flags int store_HH_wind_speed{1}; // Store hub-height wind speed? @@ -80,8 +93,8 @@ struct SimulationControls { int debug_level{0}; // Debug level (0-4) // Outputs - int output_format{0}; // File format for writing outputs - float output_timestep{0.}; // Timestep for outputs to file + int output_format{0}; // File format for writing outputs + float output_time_step{0.}; // Timestep for outputs to file std::array output_root_name{ "Output_ADIlib_default" // Root name for output files }; @@ -90,45 +103,32 @@ struct SimulationControls { std::array channel_units_c{}; // Output channel units }; -/// Struct to hold the settings for writing VTK output +/// Struct to hold the settings for VTK output struct VTKSettings { - bool write_vtk{false}; // Write VTK output? - int vtk_type{1}; // VTK output type (1: surface meshes) - std::array vtk_nacelle_dimensions{ - -2.5f, -2.5f, 0.f, - 10.f, 5.f, 5.f}; // Nacelle dimensions for VTK surface rendering [x0,y0,z0,Lx,Ly,Lz] (m) - float VTKHubRad{1.5f}; // Hub radius for VTK surface rendering + bool write_vtk{false}; // Flag to write VTK output + int vtk_type{1}; // Type of VTK output (1: surface meshes) + std::array vtk_nacelle_dimensions{// Nacelle dimensions for VTK rendering + -2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}; + float vtk_hub_radius{1.5f}; // Hub radius for VTK rendering }; -/// @brief Wrapper class for the AeroDynInflow (ADI) shared library +/// Wrapper class for the AeroDynInflow (ADI) shared library /// @details The AeroDynInflow (ADI) shared library is a Fortran library that provides C bindings -/// for interfacing with the AeroDyn Inflow module. The following functions are available in the -/// shared library -/// - ADI_C_PreInit(int*, int*, int*, int*, char*) -/// - ADI_C_SetupRotor(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, -/// int*, float*, double*, int*, int*, char*) -/// - ADI_C_Init(int*, char**, int*, int*, char**, int*, char*, float*, float*, float*, float*, -/// float*, float*, float*, int*, double*, double*, int*, int*, float*, float*, int*, double*, -/// int*, char*, char*, int*, char*) -/// - ADI_C_SetRotorMotion(int*, float*, double*, float*, float*, float*, double*, float*, float*, -/// float*, double*, float*, float*, int*, float*, double*, float*, float*) -/// - ADI_C_GetRotorLoads(int*, int*, float*, int*, char*) -/// - ADI_C_CalcOutput(double*, float*, int*, char*) -/// - ADI_C_UpdateStates(double*, double*, int*, char*) -/// - ADI_C_End(int*, char*) +/// for interfacing with the AeroDyn Inflow module struct AeroDynInflowLibrary { util::dylib lib{ "libaerodyn_inflow_c_binding.dylib", util::dylib::no_filename_decorations}; //< Dynamic library object for AeroDyn Inflow ErrorHandling error_handling; //< Error handling settings + FluidProperties air; //< Properties of the working fluid (air) EnvironmentalConditions env_conditions; //< Environmental conditions TurbineSettings turbine_settings; //< Turbine settings StructuralMesh structural_mesh; //< Structural mesh data - SimulationControls sim_controls; //< Simulation controls - VTKSettings vtk_settings; //< VTK settings + SimulationControls sim_controls; //< Simulation control settings + VTKSettings vtk_settings; //< VTK output settings AeroDynInflowLibrary(std::string shared_lib_path = "") { - if (shared_lib_path != "") { + if (!shared_lib_path.empty()) { lib = util::dylib(shared_lib_path, util::dylib::no_filename_decorations); } } @@ -139,17 +139,13 @@ struct AeroDynInflowLibrary { this->lib.get_function("ADI_C_PreInit"); ADI_C_PreInit( &turbine_settings.n_turbines, // input: Number of turbines - &sim_controls.transpose_DCM, // input: Transpose the direction cosine matrix? + &sim_controls.transpose_DCM, // input: Transpose DCM? &sim_controls.debug_level, // input: Debug level &error_handling.error_status, // output: Error status - error_handling.error_message.data() // output: Error message buffer + error_handling.error_message.data() // output: Error message ); - if (error_handling.error_status != 0) { - throw std::runtime_error( - std::string("PreInit error: ") + error_handling.error_message.data() - ); - } + error_handling.CheckError(); } /// Wrapper for ADI_C_SetupRotor routine to set up the rotor @@ -162,42 +158,32 @@ struct AeroDynInflowLibrary { ); // Flatten mesh arrays - std::vector init_mesh_pos_flat = - this->FlattenArray(structural_mesh.initial_mesh_position); - std::vector init_mesh_orient_flat = - this->FlattenArray(structural_mesh.initial_mesh_orientation); + auto init_mesh_pos_flat = FlattenArray(structural_mesh.initial_mesh_position); + auto init_mesh_orient_flat = FlattenArray(structural_mesh.initial_mesh_orientation); ADI_C_SetupRotor( - &turbine_number, // input: current turbine number - &is_horizontal_axis, // input: 1: is HAWT, 0: VAWT or cross-flow - const_cast(turbine_ref_pos.data()), // input: initial hub position - const_cast(turbine_settings.initial_hub_position.data() - ), // input: initial hub position - const_cast(turbine_settings.initial_hub_orientation.data() - ), // input: initial hub orientation - const_cast(turbine_settings.initial_nacelle_position.data() - ), // input: initial nacelle position - const_cast(turbine_settings.initial_nacelle_orientation.data() - ), // input: initial nacelle orientation - &turbine_settings.n_blades, // input: number of blades - const_cast(turbine_settings.initial_root_position.data() - ), // input: initial blade root positions - const_cast(turbine_settings.initial_root_orientation.data() - ), // input: initial blade root orientation - &structural_mesh.n_mesh_points, // input: number of mesh points - const_cast(init_mesh_pos_flat.data()), // input: initial node positions - const_cast(init_mesh_orient_flat.data()), // input: initial node orientation - const_cast(structural_mesh.mesh_point_to_blade_num.data() + &turbine_number, // input: current turbine number + &is_horizontal_axis, // input: 1: HAWT, 0: VAWT or cross-flow + turbine_ref_pos.data(), // input: turbine reference position + turbine_settings.initial_hub_position.data(), // input: initial hub position + turbine_settings.initial_hub_orientation.data(), // input: initial hub orientation + turbine_settings.initial_nacelle_position.data(), // input: initial nacelle position + turbine_settings.initial_nacelle_orientation.data( + ), // input: initial nacelle orientation + &turbine_settings.n_blades, // input: number of blades + turbine_settings.initial_root_position.data(), // input: initial blade root positions + turbine_settings.initial_root_orientation.data( + ), // input: initial blade root orientation + &structural_mesh.n_mesh_points, // input: number of mesh points + init_mesh_pos_flat.data(), // input: initial node positions + init_mesh_orient_flat.data(), // input: initial node orientation + structural_mesh.mesh_point_to_blade_num.data( ), // input: initial mesh point to blade number mapping &error_handling.error_status, // output: Error status error_handling.error_message.data() // output: Error message buffer ); - if (error_handling.error_status != 0) { - throw std::runtime_error( - std::string("SetupRotor error: ") + error_handling.error_message.data() - ); - } + error_handling.CheckError(); } private: From 00c8f425230c87bacae61854c012ce8d497b5641 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 20 Sep 2024 16:43:42 -0600 Subject: [PATCH 08/87] Add wrapper function for ADI_Init routine --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 109 +++++++++++++++--- 1 file changed, 95 insertions(+), 14 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index f852dc07..16238461 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -9,6 +9,13 @@ namespace openturbine::util { +// Notes from Derek: +// +// Premitive data types should match the Fortran data types in the shared library (SingPrec.f90), so: +// int should int_32_t etc. +// +// How are we providing the structural mesh data? + /// Struct for error handling settings struct ErrorHandling { /// Error levels used in InflowWind @@ -34,18 +41,18 @@ struct ErrorHandling { /// Struct to hold the properties of the working fluid (air) struct FluidProperties { - double density{1.225}; // Air density (kg/m^3) - double kinematic_viscosity{1.464E-5}; // Kinematic viscosity (m^2/s) - double sound_speed{335.}; // Speed of sound in the working fluid (m/s) - double vapor_pressure{1700.}; // Vapor pressure of the working fluid (Pa) + float density{1.225f}; // Air density (kg/m^3) + float kinematic_viscosity{1.464E-5f}; // Kinematic viscosity (m^2/s) + float sound_speed{335.f}; // Speed of sound in the working fluid (m/s) + float vapor_pressure{1700.f}; // Vapor pressure of the working fluid (Pa) }; /// Struct to hold the environmental conditions struct EnvironmentalConditions { - double gravity{9.80665}; // Gravitational acceleration (m/s^2) - double atm_pressure{103500.}; // Atmospheric pressure (Pa) - double water_depth{0.}; // Water depth (m) - double msl_offset{0.}; // Mean sea level to still water level offset (m) + float gravity{9.80665f}; // Gravitational acceleration (m/s^2) + float atm_pressure{103500.f}; // Atmospheric pressure (Pa) + float water_depth{0.f}; // Water depth (m) + float msl_offset{0.f}; // Mean sea level to still water level offset (m) }; /// Struct to hold the settings for the turbine (assuming a single turbine) @@ -75,16 +82,16 @@ struct SimulationControls { static constexpr size_t kDefaultStringLength{1025}; // Max length for output filenames // Input file handling - bool aerodyn_input_passed{true}; // Input file passed for AeroDyn - bool inflowwind_input_passed{true}; // Input file passed for InflowWind + int aerodyn_input_passed{true}; // Input file passed for AeroDyn + int inflowwind_input_passed{true}; // Input file passed for InflowWind // Interpolation order (must be either 1: linear, or 2: quadratic) int interpolation_order{1}; // Interpolation order - linear by default // Initial time related variables - float time_step{0.1f}; // Simulation timestep (s) - float max_time{600.f}; // Maximum simulation time (s) - float total_elapsed_time{0.f}; // Total elapsed time (s) + double time_step{0.1}; // Simulation timestep (s) + double max_time{600.}; // Maximum simulation time (s) + double total_elapsed_time{0.}; // Total elapsed time (s) int num_time_steps{0}; // Number of time steps // Flags @@ -105,7 +112,7 @@ struct SimulationControls { /// Struct to hold the settings for VTK output struct VTKSettings { - bool write_vtk{false}; // Flag to write VTK output + int write_vtk{false}; // Flag to write VTK output int vtk_type{1}; // Type of VTK output (1: surface meshes) std::array vtk_nacelle_dimensions{// Nacelle dimensions for VTK rendering -2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}; @@ -186,6 +193,71 @@ struct AeroDynInflowLibrary { error_handling.CheckError(); } + /// Wrapper for ADI_Init routine to initialize the AeroDyn Inflow library + void ADI_Init( + std::vector aerodyn_input_string_array, + std::vector inflowwind_input_string_array + ) { + auto ADI_C_Init = + this->lib + .get_function< + void(int*, const char*, int*, int*, const char*, int*, char*, float*, float*, float*, float*, float*, float*, float*, float*, int*, double*, double*, int*, int*, int*, float*, float*, int*, float*, int*, char*, char*, int*, char*)>( + "ADI_C_Init" + ); + + // Flatten arrays to pass + auto vtk_nacelle_dim_flat = std::array{}; + std::copy( + vtk_settings.vtk_nacelle_dimensions.begin(), vtk_settings.vtk_nacelle_dimensions.end(), + vtk_nacelle_dim_flat.begin() + ); + + // Primary input files will be passed as a single string joined by C_NULL_CHAR i.e. '\0' + std::string aerodyn_input_string = this->JoinStringArray(aerodyn_input_string_array, '\0'); + aerodyn_input_string = aerodyn_input_string + '\0'; + int aerodyn_input_string_length = static_cast(aerodyn_input_string.size()); + + std::string inflowwind_input_string = + this->JoinStringArray(inflowwind_input_string_array, '\0'); + inflowwind_input_string = inflowwind_input_string + '\0'; + int inflowwind_input_string_length = static_cast(inflowwind_input_string.size()); + + ADI_C_Init( + &sim_controls.aerodyn_input_passed, // input: AD input file is passed + aerodyn_input_string.data(), // input: AD input file as string + &aerodyn_input_string_length, // input: AD input file string length + &sim_controls.inflowwind_input_passed, // input: IfW input file is passed + inflowwind_input_string.data(), // input: IfW input file as string + &inflowwind_input_string_length, // input: IfW input file string length + sim_controls.output_root_name.data(), // input: rootname for ADI file writing + &env_conditions.gravity, // input: gravity + &air.density, // input: air density + &air.kinematic_viscosity, // input: kinematic viscosity + &air.sound_speed, // input: speed of sound + &env_conditions.atm_pressure, // input: atmospheric pressure + &air.vapor_pressure, // input: vapor pressure + &env_conditions.water_depth, // input: water depth + &env_conditions.msl_offset, // input: MSL to SWL offset + &sim_controls.interpolation_order, // input: interpolation order + &sim_controls.time_step, // input: time step + &sim_controls.max_time, // input: maximum simulation time + &sim_controls.store_HH_wind_speed, // input: store HH wind speed + &vtk_settings.write_vtk, // input: write VTK output + &vtk_settings.vtk_type, // input: VTK output type + vtk_nacelle_dim_flat.data(), // input: VTK nacelle dimensions + &vtk_settings.vtk_hub_radius, // input: VTK hub radius + &sim_controls.output_format, // input: output format + &sim_controls.output_time_step, // input: output time step + &sim_controls.n_channels, // output: number of channels + sim_controls.channel_names_c.data(), // output: output channel names + sim_controls.channel_units_c.data(), // output: output channel units + &error_handling.error_status, // output: error status + error_handling.error_message.data() // output: error message buffer + ); + + error_handling.CheckError(); + } + private: /// Method to flatten a 2D array into a 1D array for Fortran compatibility template @@ -196,6 +268,15 @@ struct AeroDynInflowLibrary { } return output; } + + /// Method to join a vector of strings into a single string with a delimiter + std::string JoinStringArray(const std::vector& input, char delimiter) { + std::string output; + for (const auto& str : input) { + output += str + delimiter; + } + return output; + } }; } // namespace openturbine::util From 6eabab7ede0a68abc2b47d09ed53592b2f3f1f4d Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Sat, 21 Sep 2024 17:12:27 -0600 Subject: [PATCH 09/87] Add first cut of the ADI_C_SetRotorMotion wrapper --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 16238461..b56e13c4 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -258,6 +258,71 @@ struct AeroDynInflowLibrary { error_handling.CheckError(); } + // Wrapper for ADI_SetRotorMotion routine to set rotor motion i.e. motion of the hub, nacelle, + // root, and mesh points from the structural mesh + + // Define structures for hub, nacelle, root, and mesh motions + struct MotionData { + std::vector position; + std::vector orientation; + std::vector velocity; + std::vector acceleration; + }; + + struct MeshMotionData { + std::vector> position; + std::vector> orientation; + std::vector> velocity; + std::vector> acceleration; + }; + + // Use the above structures in ADI_SetRotorMotion wrapper + void ADI_C_SetRotorMotion( + int turbine_number, MotionData hub_motion, MotionData nacelle_motion, + MeshMotionData root_motion, MeshMotionData mesh_motion + ) { + auto ADI_C_SetRotorMotion = this->lib.get_function< + void(int*, float*, double*, float*, float*, float*, double*, float*, float*, float*, double*, float*, float*, int*, float*, double*, float*, float*, int*, char*)>( + "ADI_C_SetRotorMotion" + ); + + // Flatten root and mesh motion arrays + auto root_pos_flat = FlattenArray(root_motion.position); + auto root_orient_flat = FlattenArray(root_motion.orientation); + auto root_vel_flat = FlattenArray(root_motion.velocity); + auto root_acc_flat = FlattenArray(root_motion.acceleration); + + auto mesh_pos_flat = FlattenArray(mesh_motion.position); + auto mesh_orient_flat = FlattenArray(mesh_motion.orientation); + auto mesh_vel_flat = FlattenArray(mesh_motion.velocity); + auto mesh_acc_flat = FlattenArray(mesh_motion.acceleration); + + ADI_C_SetRotorMotion( + &turbine_number, // input: current turbine number + hub_motion.position.data(), // input: hub position + hub_motion.orientation.data(), // input: hub orientation + hub_motion.velocity.data(), // input: hub velocity + hub_motion.acceleration.data(), // input: hub acceleration + nacelle_motion.position.data(), // input: nacelle position + nacelle_motion.orientation.data(), // input: nacelle orientation + nacelle_motion.velocity.data(), // input: nacelle velocity + nacelle_motion.acceleration.data(), // input: nacelle acceleration + root_pos_flat.data(), // input: root positions + root_orient_flat.data(), // input: root orientations + root_vel_flat.data(), // input: root velocities + root_acc_flat.data(), // input: root accelerations + &structural_mesh.n_mesh_points, // input: number of mesh points + mesh_pos_flat.data(), // input: mesh positions + mesh_orient_flat.data(), // input: mesh orientations + mesh_vel_flat.data(), // input: mesh velocities + mesh_acc_flat.data(), // input: mesh accelerations + &error_handling.error_status, // output: error status + error_handling.error_message.data() // output: error message buffer + ); + + error_handling.CheckError(); + } + private: /// Method to flatten a 2D array into a 1D array for Fortran compatibility template From 87daa980b2ca74614369e45695093dce9f22e465 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Mon, 23 Sep 2024 12:29:29 -0600 Subject: [PATCH 10/87] Add a description for the ADI wrapper --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index b56e13c4..383bdbb4 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -9,12 +9,32 @@ namespace openturbine::util { -// Notes from Derek: -// -// Premitive data types should match the Fortran data types in the shared library (SingPrec.f90), so: -// int should int_32_t etc. -// -// How are we providing the structural mesh data? +/** + * C++ wrapper for interfacing with the AeroDyn Inflow (ADI) shared library, a Fortran-based + * library that exposes C-bindings for the AeroDyn and InflowWind modules of OpenFAST. This + * wrapper simplifies interaction with the ADI library (particularly the C-based interface), + * providing a user-friendly interface for OpenTurbine developers to run AeroDyn with InflowWind. + * + * AeroDyn utilizes blade element momentum (BEM) theory to calculate aerodynamic forces acting + * on each blade section. It accounts for factors such as: + * - Dynamic stall + * - Unsteady aerodynamics + * - Tower shadow + * - Wind shear + * + * InflowWind simulates the inflow conditions around wind turbines by modeling spatially and + * temporally varying wind fields. It enables the simulation of complex wind phenomena, such as: + * - Turbulence + * - Wind shear + * - Gusts + * - Free vortex wake + * + * References: + * - AeroDyn InflowWind C bindings: + * https://github.com/OpenFAST/openfast/blob/dev/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 + * - AeroDyn InflowWind Python interface: + * https://github.com/OpenFAST/openfast/blob/dev/modules/aerodyn/python-lib/aerodyn_inflow_library.py + */ /// Struct for error handling settings struct ErrorHandling { From 17725890d68094815cece5bcbbbc00bf470fc5fc Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Mon, 23 Sep 2024 12:56:40 -0600 Subject: [PATCH 11/87] Add checks for motions --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 274 +++++++++++++----- 1 file changed, 203 insertions(+), 71 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 383bdbb4..931e81c9 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -47,13 +47,14 @@ struct ErrorHandling { kFatalError = 4 }; - static constexpr size_t kErrorMessagesLength = 1025U; //< Max error message length in Fortran + static constexpr size_t kErrorMessagesLength = 1025U; //< Max error message length int abort_error_level{ - static_cast(ErrorLevel::kFatalError)}; //< Error level at which to abort + static_cast(ErrorLevel::kFatalError) //< Error level at which to abort + }; int error_status{0}; //< Error status std::array error_message{}; //< Error message buffer - /// Check for errors and throw an exception if found + /// Checks for errors and throws an exception if found - otherwise returns true bool CheckError() const { return error_status == 0 ? true : throw std::runtime_error(error_message.data()); } @@ -61,98 +62,114 @@ struct ErrorHandling { /// Struct to hold the properties of the working fluid (air) struct FluidProperties { - float density{1.225f}; // Air density (kg/m^3) - float kinematic_viscosity{1.464E-5f}; // Kinematic viscosity (m^2/s) - float sound_speed{335.f}; // Speed of sound in the working fluid (m/s) - float vapor_pressure{1700.f}; // Vapor pressure of the working fluid (Pa) + float density{1.225f}; //< Air density (kg/m^3) + float kinematic_viscosity{1.464E-5f}; //< Kinematic viscosity (m^2/s) + float sound_speed{335.f}; //< Speed of sound in the working fluid (m/s) + float vapor_pressure{1700.f}; //< Vapor pressure of the working fluid (Pa) }; /// Struct to hold the environmental conditions struct EnvironmentalConditions { - float gravity{9.80665f}; // Gravitational acceleration (m/s^2) - float atm_pressure{103500.f}; // Atmospheric pressure (Pa) - float water_depth{0.f}; // Water depth (m) - float msl_offset{0.f}; // Mean sea level to still water level offset (m) + float gravity{9.80665f}; //< Gravitational acceleration (m/s^2) + float atm_pressure{103500.f}; //< Atmospheric pressure (Pa) + float water_depth{0.f}; //< Water depth (m) + float msl_offset{0.f}; //< Mean sea level to still water level offset (m) }; -/// Struct to hold the settings for the turbine (assuming a single turbine) +/// Struct to hold the initial settings for the turbine (assuming a single turbine and three blades) struct TurbineSettings { - int n_turbines{1}; // Number of turbines - 1 by default - int n_blades{3}; // Number of blades - 3 by default - std::array initial_hub_position{0.}; // Initial hub position - std::array initial_hub_orientation{0.}; // Initial hub orientation - std::array initial_nacelle_position{0.}; // Initial nacelle position - std::array initial_nacelle_orientation{0.}; // Initial nacelle orientation - std::array initial_root_position{0.}; // Initial root position - std::array initial_root_orientation{0.}; // Initial root orientation + int n_turbines{1}; //< Number of turbines - 1 by default + int n_blades{3}; //< Number of blades - 3 by default + std::array initial_hub_position{0.}; //< Initial hub position + std::array initial_hub_orientation{0.}; //< Initial hub orientation + std::array initial_nacelle_position{0.}; //< Initial nacelle position + std::array initial_nacelle_orientation{0.}; //< Initial nacelle orientation + std::array initial_root_position{0.}; //< Initial root position + std::array initial_root_orientation{0.}; //< Initial root orientation }; /// Struct to hold the structural mesh data struct StructuralMesh { - int n_mesh_points{1}; // Number of mesh points - std::vector> initial_mesh_position{}; // N x 3 array [x, y, z] + int n_mesh_points{1}; //< Number of mesh points + std::vector> initial_mesh_position{}; //< N x 3 array [x, y, z] std::vector> - initial_mesh_orientation{}; // N x 9 array [r11, r12, ..., r33] + initial_mesh_orientation{}; //< N x 9 array [r11, r12, ..., r33] std::vector - mesh_point_to_blade_num{}; // N x 1 array for mapping a mesh point to blade number + mesh_point_to_blade_num{}; //< N x 1 array for mapping a mesh point to blade number }; /// Struct to hold the settings for the simulation controls struct SimulationControls { - static constexpr size_t kDefaultStringLength{1025}; // Max length for output filenames + static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames // Input file handling - int aerodyn_input_passed{true}; // Input file passed for AeroDyn - int inflowwind_input_passed{true}; // Input file passed for InflowWind + int aerodyn_input_passed{true}; //< Flag to check if input file passed for AeroDyn module + int inflowwind_input_passed{true}; //< Flag to check if input file passed for InflowWind module // Interpolation order (must be either 1: linear, or 2: quadratic) - int interpolation_order{1}; // Interpolation order - linear by default + int interpolation_order{1}; //< Interpolation order - linear by default // Initial time related variables - double time_step{0.1}; // Simulation timestep (s) - double max_time{600.}; // Maximum simulation time (s) - double total_elapsed_time{0.}; // Total elapsed time (s) - int num_time_steps{0}; // Number of time steps + double time_step{0.1}; //< Simulation timestep (s) + double max_time{600.}; //< Maximum simulation time (s) + double total_elapsed_time{0.}; //< Total elapsed time (s) + int n_time_steps{0}; //< Number of time steps // Flags - int store_HH_wind_speed{1}; // Store hub-height wind speed? - int transpose_DCM{1}; // Transpose the direction cosine matrix? - int debug_level{0}; // Debug level (0-4) + int store_HH_wind_speed{1}; //< Flag to store HH wind speed + int transpose_DCM{1}; //< Flag to transpose the direction cosine matrix + int debug_level{0}; //< Debug level (0-4) // Outputs - int output_format{0}; // File format for writing outputs - float output_time_step{0.}; // Timestep for outputs to file + int output_format{0}; //< File format for writing outputs + float output_time_step{0.}; //< Timestep for outputs to file std::array output_root_name{ - "Output_ADIlib_default" // Root name for output files + "Output_ADIlib_default" //< Root name for output files }; - int n_channels{0}; // Number of channels returned - std::array channel_names_c{}; // Output channel names - std::array channel_units_c{}; // Output channel units + int n_channels{0}; //< Number of channels returned + std::array channel_names_c{}; //< Output channel names + std::array channel_units_c{}; //< Output channel units }; /// Struct to hold the settings for VTK output struct VTKSettings { - int write_vtk{false}; // Flag to write VTK output - int vtk_type{1}; // Type of VTK output (1: surface meshes) - std::array vtk_nacelle_dimensions{// Nacelle dimensions for VTK rendering + int write_vtk{false}; //< Flag to write VTK output + int vtk_type{1}; //< Type of VTK output (1: surface meshes) + std::array vtk_nacelle_dimensions{//< Nacelle dimensions for VTK rendering -2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}; - float vtk_hub_radius{1.5f}; // Hub radius for VTK rendering + float vtk_hub_radius{1.5f}; //< Hub radius for VTK rendering +}; + +// Define structures for hub, nacelle, root, and mesh motions +struct MotionData { + std::vector position; + std::vector orientation; + std::vector velocity; + std::vector acceleration; +}; + +struct MeshMotionData { + std::vector> position; + std::vector> orientation; + std::vector> velocity; + std::vector> acceleration; }; /// Wrapper class for the AeroDynInflow (ADI) shared library /// @details The AeroDynInflow (ADI) shared library is a Fortran library that provides C bindings -/// for interfacing with the AeroDyn Inflow module +/// for interfacing with the AeroDyn+InflowWind modules of OpenFAST struct AeroDynInflowLibrary { util::dylib lib{ "libaerodyn_inflow_c_binding.dylib", - util::dylib::no_filename_decorations}; //< Dynamic library object for AeroDyn Inflow - ErrorHandling error_handling; //< Error handling settings - FluidProperties air; //< Properties of the working fluid (air) - EnvironmentalConditions env_conditions; //< Environmental conditions - TurbineSettings turbine_settings; //< Turbine settings - StructuralMesh structural_mesh; //< Structural mesh data - SimulationControls sim_controls; //< Simulation control settings - VTKSettings vtk_settings; //< VTK output settings + util::dylib::no_filename_decorations //< Dynamic library object for AeroDyn Inflow + }; + ErrorHandling error_handling; //< Error handling settings + FluidProperties air; //< Properties of the working fluid (air) + EnvironmentalConditions env_conditions; //< Environmental conditions + TurbineSettings turbine_settings; //< Turbine settings + StructuralMesh structural_mesh; //< Structural mesh data + SimulationControls sim_controls; //< Simulation control settings + VTKSettings vtk_settings; //< VTK output settings AeroDynInflowLibrary(std::string shared_lib_path = "") { if (!shared_lib_path.empty()) { @@ -280,23 +297,6 @@ struct AeroDynInflowLibrary { // Wrapper for ADI_SetRotorMotion routine to set rotor motion i.e. motion of the hub, nacelle, // root, and mesh points from the structural mesh - - // Define structures for hub, nacelle, root, and mesh motions - struct MotionData { - std::vector position; - std::vector orientation; - std::vector velocity; - std::vector acceleration; - }; - - struct MeshMotionData { - std::vector> position; - std::vector> orientation; - std::vector> velocity; - std::vector> acceleration; - }; - - // Use the above structures in ADI_SetRotorMotion wrapper void ADI_C_SetRotorMotion( int turbine_number, MotionData hub_motion, MotionData nacelle_motion, MeshMotionData root_motion, MeshMotionData mesh_motion @@ -317,6 +317,26 @@ struct AeroDynInflowLibrary { auto mesh_vel_flat = FlattenArray(mesh_motion.velocity); auto mesh_acc_flat = FlattenArray(mesh_motion.acceleration); + // Checck the input motions + // CheckHubNacelleInputMotions( + // hub_motion.position, hub_motion.orientation, hub_motion.velocity, + // hub_motion.acceleration, "hub" + // ); + // CheckHubNacelleInputMotions( + // nacelle_motion.position, nacelle_motion.orientation, nacelle_motion.velocity, + // nacelle_motion.acceleration, "nacelle" + // ); + // CheckRootInputMotions( + // root_motion.position, root_motion.orientation, root_motion.velocity, + // root_motion.acceleration, structural_mesh.n_mesh_points, + // structural_mesh.initial_mesh_position.size() + // ); + // CheckMeshInputMotions( + // mesh_motion.position, mesh_motion.orientation, mesh_motion.velocity, + // mesh_motion.acceleration, structural_mesh.n_mesh_points, + // structural_mesh.initial_mesh_position.size() + // ); + ADI_C_SetRotorMotion( &turbine_number, // input: current turbine number hub_motion.position.data(), // input: hub position @@ -362,6 +382,118 @@ struct AeroDynInflowLibrary { } return output; } + + template + void CheckArraySize( + const std::vector>& array, size_t expected_rows, size_t expected_cols, + const std::string& array_name, const std::string& node_type + ) { + if (array.size() != expected_rows) { + std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " + << node_type << " " << array_name << " with " << expected_rows << " rows." + << std::endl; + // call ADI_End(); + } + + for (const auto& row : array) { + if (row.size() != expected_cols) { + std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " + << node_type << " " << array_name << " with " << expected_cols + << " columns." << std::endl; + // call ADI_End(); + } + } + } + + void CheckInputMotions( + const std::vector>& position_array, + const std::vector>& orientation_array, + const std::vector>& velocity_array, + const std::vector>& accleration_array, const std::string& node_type, + size_t expected_position_dim, size_t expected_orientation_dim, size_t expected_VelAcceln_dim, + size_t expected_number_of_nodes + ) { + CheckArraySize( + position_array, expected_number_of_nodes, expected_position_dim, "positions", node_type + ); + CheckArraySize( + orientation_array, expected_number_of_nodes, expected_orientation_dim, "orientations", + node_type + ); + CheckArraySize( + velocity_array, expected_number_of_nodes, expected_VelAcceln_dim, "velocities", node_type + ); + CheckArraySize( + accleration_array, expected_number_of_nodes, expected_VelAcceln_dim, "accelerations", + node_type + ); + } + + void CheckHubNacelleInputMotions( + const std::vector>& hubPos, + const std::vector>& hubOrient, + const std::vector>& hubVel, const std::vector>& hubAcc, + const std::string& nodeName + ) { + // Hub/Nacelle specific checks, where dimensions are 3, 9, and 6 for position, orientation, + // and velocities/accelerations + const size_t expected_position_dim = 3; + const size_t expected_orientation_dim = 9; + const size_t expected_VelAcceln_dim = 6; + const size_t expected_number_of_nodes = 1; // Since there is only 1 hub/nacelle node + + CheckInputMotions( + hubPos, hubOrient, hubVel, hubAcc, nodeName, expected_position_dim, + expected_orientation_dim, expected_VelAcceln_dim, expected_number_of_nodes + ); + } + + void CheckRootInputMotions( + const std::vector>& root_pos, + const std::vector>& root_orient, + const std::vector>& root_vel, + const std::vector>& root_acc, size_t num_blades, size_t init_num_blades + ) { + if (num_blades != init_num_blades) { + std::cerr << "The number of root points changed from the initial value of " + << init_num_blades << ". This is not permitted during the simulation." + << std::endl; + // call ADI_End(); + } + + const size_t expected_position_dim = 3; + const size_t expected_orientation_dim = 9; + const size_t expected_vel_acc_dim = 6; + + CheckInputMotions( + root_pos, root_orient, root_vel, root_acc, "root", expected_position_dim, + expected_orientation_dim, expected_vel_acc_dim, num_blades + ); + } + + void CheckMeshInputMotions( + const std::vector>& mesh_pos, + const std::vector>& mesh_orient, + const std::vector>& mesh_vel, + const std::vector>& mesh_acc, size_t num_mesh_pts, + size_t init_num_mesh_pts + ) { + if (num_mesh_pts != init_num_mesh_pts) { + std::cerr << "The number of mesh points changed from the initial value of " + << init_num_mesh_pts << ". This is not permitted during the simulation." + << std::endl; + // call ADI_End(); + } + + const size_t expected_position_dim = 3; + const size_t expected_orientation_dim = 9; + const size_t expected_vel_acc_dim = 6; + + CheckInputMotions( + mesh_pos, mesh_orient, mesh_vel, mesh_acc, "mesh", expected_position_dim, + expected_orientation_dim, expected_vel_acc_dim, num_mesh_pts + ); + } }; } // namespace openturbine::util From 7cffc5693bbf09b71144828c6d048b6ef3a8dcbf Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Mon, 23 Sep 2024 14:27:56 -0600 Subject: [PATCH 12/87] Add logic to flatten position, orient, velocity arrays --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 82 +++++++++++++++---- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 931e81c9..e6a810e3 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -88,16 +88,6 @@ struct TurbineSettings { std::array initial_root_orientation{0.}; //< Initial root orientation }; -/// Struct to hold the structural mesh data -struct StructuralMesh { - int n_mesh_points{1}; //< Number of mesh points - std::vector> initial_mesh_position{}; //< N x 3 array [x, y, z] - std::vector> - initial_mesh_orientation{}; //< N x 9 array [r11, r12, ..., r33] - std::vector - mesh_point_to_blade_num{}; //< N x 1 array for mapping a mesh point to blade number -}; - /// Struct to hold the settings for the simulation controls struct SimulationControls { static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames @@ -140,6 +130,16 @@ struct VTKSettings { float vtk_hub_radius{1.5f}; //< Hub radius for VTK rendering }; +/// Struct to hold the structural mesh data +struct StructuralMesh { + int n_mesh_points{1}; //< Number of mesh points + std::vector> initial_mesh_position{}; //< N x 3 array [x, y, z] + std::vector> + initial_mesh_orientation{}; //< N x 9 array [r11, r12, ..., r33] + std::vector + mesh_point_to_blade_num{}; //< N x 1 array for mapping a mesh point to blade number +}; + // Define structures for hub, nacelle, root, and mesh motions struct MotionData { std::vector position; @@ -374,6 +374,54 @@ struct AeroDynInflowLibrary { return output; } + std::vector FlattenPositionArray( + const std::vector>& position_array, size_t num_pts + ) { + if (position_array.size() != num_pts) { + std::cerr << "The number of mesh points changed from the initial value of " << num_pts + << ". This is not permitted during the simulation." << std::endl; + // call ADI_End(); + } + + std::vector mesh_pos_flat; + for (const auto& pos : position_array) { + mesh_pos_flat.insert(mesh_pos_flat.end(), pos.begin(), pos.end()); + } + return mesh_pos_flat; + } + + std::vector FlattenOrientationArray( + const std::vector>& orientation_array, size_t num_pts + ) { + if (orientation_array.size() != num_pts) { + std::cerr << "The number of mesh points changed from the initial value of " << num_pts + << ". This is not permitted during the simulation." << std::endl; + // call ADI_End(); + } + + std::vector mesh_orient_flat; + for (const auto& orient : orientation_array) { + mesh_orient_flat.insert(mesh_orient_flat.end(), orient.begin(), orient.end()); + } + return mesh_orient_flat; + } + + std::vector FlattenVelocityArray( + const std::vector>& velocity_array, size_t num_pts + ) { + if (velocity_array.size() != num_pts) { + std::cerr << "The number of mesh points changed from the initial value of " << num_pts + << ". This is not permitted during the simulation." << std::endl; + // call ADI_End(); + } + + std::vector mesh_vel_flat; + for (const auto& vel : velocity_array) { + mesh_vel_flat.insert(mesh_vel_flat.end(), vel.begin(), vel.end()); + } + return mesh_vel_flat; + } + /// Method to join a vector of strings into a single string with a delimiter std::string JoinStringArray(const std::vector& input, char delimiter) { std::string output; @@ -388,6 +436,7 @@ struct AeroDynInflowLibrary { const std::vector>& array, size_t expected_rows, size_t expected_cols, const std::string& array_name, const std::string& node_type ) { + // Check row count first if (array.size() != expected_rows) { std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " << node_type << " " << array_name << " with " << expected_rows << " rows." @@ -395,13 +444,12 @@ struct AeroDynInflowLibrary { // call ADI_End(); } - for (const auto& row : array) { - if (row.size() != expected_cols) { - std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " - << node_type << " " << array_name << " with " << expected_cols - << " columns." << std::endl; - // call ADI_End(); - } + // Check column count only on the first row to avoid redundant checks + if (!array.empty() && array[0].size() != expected_cols) { + std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " + << node_type << " " << array_name << " with " << expected_cols << " columns." + << std::endl; + // call ADI_End(); } } From fc8526a7f03a3318c5da6cbc733f185a6132a6f7 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 13:28:42 -0600 Subject: [PATCH 13/87] Minor fixes to doxygen comments --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index e6a810e3..4a7ac1fc 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -10,10 +10,11 @@ namespace openturbine::util { /** - * C++ wrapper for interfacing with the AeroDyn Inflow (ADI) shared library, a Fortran-based - * library that exposes C-bindings for the AeroDyn and InflowWind modules of OpenFAST. This - * wrapper simplifies interaction with the ADI library (particularly the C-based interface), - * providing a user-friendly interface for OpenTurbine developers to run AeroDyn with InflowWind. + * Following contains a C++ wrapper to interact with the AeroDyn Inflow (ADI) shared library, + * originally written in Fortran, that exposes C-bindings for the AeroDyn and InflowWind modules of + * OpenFAST. This wrapper simplifies interaction with the ADI library (particularly the C-based + * interface, with inspiration from the python interface), providing a modern interface for + * OpenTurbine devs to run AeroDyn x InflowWind. * * AeroDyn utilizes blade element momentum (BEM) theory to calculate aerodynamic forces acting * on each blade section. It accounts for factors such as: @@ -30,10 +31,10 @@ namespace openturbine::util { * - Free vortex wake * * References: + * - OpenFAST/AeroDyn: + * https://github.com/OpenFAST/openfast/tree/main/modules/aerodyn * - AeroDyn InflowWind C bindings: * https://github.com/OpenFAST/openfast/blob/dev/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 - * - AeroDyn InflowWind Python interface: - * https://github.com/OpenFAST/openfast/blob/dev/modules/aerodyn/python-lib/aerodyn_inflow_library.py */ /// Struct for error handling settings @@ -47,7 +48,7 @@ struct ErrorHandling { kFatalError = 4 }; - static constexpr size_t kErrorMessagesLength = 1025U; //< Max error message length + static constexpr size_t kErrorMessagesLength{1025U}; //< Max error message length int abort_error_level{ static_cast(ErrorLevel::kFatalError) //< Error level at which to abort }; @@ -93,8 +94,8 @@ struct SimulationControls { static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames // Input file handling - int aerodyn_input_passed{true}; //< Flag to check if input file passed for AeroDyn module - int inflowwind_input_passed{true}; //< Flag to check if input file passed for InflowWind module + int aerodyn_input_passed{1}; //< Input file passed for AeroDyn module? (1: passed) + int inflowwind_input_passed{1}; //< Input file passed for InflowWind module (1: passed) // Interpolation order (must be either 1: linear, or 2: quadratic) int interpolation_order{1}; //< Interpolation order - linear by default From 5fd26ce2a39a658512751b205f2b38863d677d77 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 13:29:37 -0600 Subject: [PATCH 14/87] Refactor MeshMotionData struct to include the array size and input motion checks as methods --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 125 +++++++++++++++--- 1 file changed, 110 insertions(+), 15 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 4a7ac1fc..37771735 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -141,24 +141,119 @@ struct StructuralMesh { mesh_point_to_blade_num{}; //< N x 1 array for mapping a mesh point to blade number }; -// Define structures for hub, nacelle, root, and mesh motions -struct MotionData { - std::vector position; - std::vector orientation; - std::vector velocity; - std::vector acceleration; -}; - struct MeshMotionData { - std::vector> position; - std::vector> orientation; - std::vector> velocity; - std::vector> acceleration; + std::vector> position; //< N x 3 array [x, y, z] + std::vector> orientation; //< N x 9 array [r11, r12, ..., r33] + std::vector> velocity; //< N x 6 array [u, v, w, p, q, r] + std::vector> + acceleration; //< N x 6 array [u_dot, v_dot, w_dot, p_dot, q_dot, r_dot] + + /// Method to check the sizes of the input arrays + template + void CheckArraySize( + const std::vector>& array, size_t expected_rows, size_t expected_cols, + const std::string& array_name, const std::string& node_type + ) const { + // Check row count first + if (array.size() != expected_rows) { + std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " + << node_type << " " << array_name << " with " << expected_rows << " rows." + << std::endl; + // call ADI_End(); + } + + // Check column count only on the first row to avoid redundant checks + if (!array.empty() && array[0].size() != expected_cols) { + std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " + << node_type << " " << array_name << " with " << expected_cols << " columns." + << std::endl; + // call ADI_End(); + } + } + + /// Method to check the input motions i.e. position, orientation, velocity, and acceleration + /// arrays + void CheckInputMotions( + const std::string& node_type, size_t expected_position_dim, size_t expected_orientation_dim, + size_t expected_vel_acc_dim, size_t expected_number_of_nodes + ) const { + CheckArraySize( + position, expected_number_of_nodes, expected_position_dim, "positions", node_type + ); + CheckArraySize( + orientation, expected_number_of_nodes, expected_orientation_dim, "orientations", + node_type + ); + CheckArraySize( + velocity, expected_number_of_nodes, expected_vel_acc_dim, "velocities", node_type + ); + CheckArraySize( + acceleration, expected_number_of_nodes, expected_vel_acc_dim, "accelerations", node_type + ); + } + + /// Method to check the hub/nacelle input motions + void CheckHubNacelleInputMotions(const std::string& node_name) const { + const size_t expected_position_dim{3}; + const size_t expected_orientation_dim{9}; + const size_t expected_vel_acc_dim{6}; + const size_t expected_number_of_nodes{1}; // Since there is only 1 hub/nacelle node + + CheckInputMotions( + node_name, expected_position_dim, expected_orientation_dim, expected_vel_acc_dim, + expected_number_of_nodes + ); + } + + /// Method to check the root input motions + void CheckRootInputMotions(size_t num_blades, size_t init_num_blades) const { + if (num_blades != init_num_blades) { + std::cerr << "The number of root points changed from the initial value of " + << init_num_blades << ". This is not permitted during the simulation." + << std::endl; + // call ADI_End(); + } + + const size_t expected_position_dim{3}; + const size_t expected_orientation_dim{9}; + const size_t expected_vel_acc_dim{6}; + + CheckInputMotions( + "root", expected_position_dim, expected_orientation_dim, expected_vel_acc_dim, num_blades + ); + } + + /// Method to check the mesh input motions + void CheckMeshInputMotions(size_t num_mesh_pts, size_t init_num_mesh_pts) const { + if (num_mesh_pts != init_num_mesh_pts) { + std::cerr << "The number of mesh points changed from the initial value of " + << init_num_mesh_pts << ". This is not permitted during the simulation." + << std::endl; + // call ADI_End(); + } + + const size_t expected_position_dim{3}; + const size_t expected_orientation_dim{9}; + const size_t expected_vel_acc_dim{6}; + + CheckInputMotions( + "mesh", expected_position_dim, expected_orientation_dim, expected_vel_acc_dim, + num_mesh_pts + ); + } }; -/// Wrapper class for the AeroDynInflow (ADI) shared library -/// @details The AeroDynInflow (ADI) shared library is a Fortran library that provides C bindings -/// for interfacing with the AeroDyn+InflowWind modules of OpenFAST +/** + * @brief Wrapper class for the AeroDynInflow (ADI) shared library + * + * @details This class provides an interface for interacting with the AeroDynInflow (ADI) shared + * library, which is a Fortran library offering C bindings for the AeroDyn x InflowWind modules of + * OpenFAST. Following functions are ported over from the AeroDyn_Inflow_C_Binding.f90 file: + * - ADI_C_PreInit: Handles the pre-initialization of the AeroDynInflow module + * - ADI_C_SetupRotor: Configures rotor-specific parameters before simulation + * - ADI_C_Init: Initializes the AeroDynInflow module + * - ADI_C_SetRotorMotion: Updates the rotor's motion during the simulation + */ struct AeroDynInflowLibrary { util::dylib lib{ "libaerodyn_inflow_c_binding.dylib", From 87e4a47784897b099cb95cce0bd020d6bb5430b6 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 13:30:39 -0600 Subject: [PATCH 15/87] Refactor ADI_C_SetRotorMotion wrapper --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 227 ++++-------------- 1 file changed, 52 insertions(+), 175 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 37771735..03ff72de 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -394,7 +394,7 @@ struct AeroDynInflowLibrary { // Wrapper for ADI_SetRotorMotion routine to set rotor motion i.e. motion of the hub, nacelle, // root, and mesh points from the structural mesh void ADI_C_SetRotorMotion( - int turbine_number, MotionData hub_motion, MotionData nacelle_motion, + int turbine_number, MeshMotionData hub_motion, MeshMotionData nacelle_motion, MeshMotionData root_motion, MeshMotionData mesh_motion ) { auto ADI_C_SetRotorMotion = this->lib.get_function< @@ -402,7 +402,29 @@ struct AeroDynInflowLibrary { "ADI_C_SetRotorMotion" ); - // Flatten root and mesh motion arrays + // Check the input motions for hub, nacelle, root, and mesh points + hub_motion.CheckHubNacelleInputMotions("hub"); + nacelle_motion.CheckHubNacelleInputMotions("nacelle"); + root_motion.CheckRootInputMotions( + static_cast(turbine_settings.n_blades), + static_cast(turbine_settings.initial_root_position.size()) + ); + mesh_motion.CheckMeshInputMotions( + static_cast(structural_mesh.n_mesh_points), + static_cast(structural_mesh.initial_mesh_position.size()) + ); + + // Flatten the arrays to pass + auto hub_pos_flat = FlattenArray(hub_motion.position); + auto hub_orient_flat = FlattenArray(hub_motion.orientation); + auto hub_vel_flat = FlattenArray(hub_motion.velocity); + auto hub_acc_flat = FlattenArray(hub_motion.acceleration); + + auto nacelle_pos_flat = FlattenArray(nacelle_motion.position); + auto nacelle_orient_flat = FlattenArray(nacelle_motion.orientation); + auto nacelle_vel_flat = FlattenArray(nacelle_motion.velocity); + auto nacelle_acc_flat = FlattenArray(nacelle_motion.acceleration); + auto root_pos_flat = FlattenArray(root_motion.position); auto root_orient_flat = FlattenArray(root_motion.orientation); auto root_vel_flat = FlattenArray(root_motion.velocity); @@ -413,36 +435,16 @@ struct AeroDynInflowLibrary { auto mesh_vel_flat = FlattenArray(mesh_motion.velocity); auto mesh_acc_flat = FlattenArray(mesh_motion.acceleration); - // Checck the input motions - // CheckHubNacelleInputMotions( - // hub_motion.position, hub_motion.orientation, hub_motion.velocity, - // hub_motion.acceleration, "hub" - // ); - // CheckHubNacelleInputMotions( - // nacelle_motion.position, nacelle_motion.orientation, nacelle_motion.velocity, - // nacelle_motion.acceleration, "nacelle" - // ); - // CheckRootInputMotions( - // root_motion.position, root_motion.orientation, root_motion.velocity, - // root_motion.acceleration, structural_mesh.n_mesh_points, - // structural_mesh.initial_mesh_position.size() - // ); - // CheckMeshInputMotions( - // mesh_motion.position, mesh_motion.orientation, mesh_motion.velocity, - // mesh_motion.acceleration, structural_mesh.n_mesh_points, - // structural_mesh.initial_mesh_position.size() - // ); - ADI_C_SetRotorMotion( &turbine_number, // input: current turbine number - hub_motion.position.data(), // input: hub position - hub_motion.orientation.data(), // input: hub orientation - hub_motion.velocity.data(), // input: hub velocity - hub_motion.acceleration.data(), // input: hub acceleration - nacelle_motion.position.data(), // input: nacelle position - nacelle_motion.orientation.data(), // input: nacelle orientation - nacelle_motion.velocity.data(), // input: nacelle velocity - nacelle_motion.acceleration.data(), // input: nacelle acceleration + hub_pos_flat.data(), // input: hub positions + hub_orient_flat.data(), // input: hub orientations + hub_vel_flat.data(), // input: hub velocities + hub_acc_flat.data(), // input: hub accelerations + nacelle_pos_flat.data(), // input: nacelle positions + nacelle_orient_flat.data(), // input: nacelle orientations + nacelle_vel_flat.data(), // input: nacelle velocities + nacelle_acc_flat.data(), // input: nacelle accelerations root_pos_flat.data(), // input: root positions root_orient_flat.data(), // input: root orientations root_vel_flat.data(), // input: root velocities @@ -470,52 +472,39 @@ struct AeroDynInflowLibrary { return output; } - std::vector FlattenPositionArray( - const std::vector>& position_array, size_t num_pts + /// Template method to validate array size and flatten it + template + std::vector ValidateAndFlattenArray( + const std::vector>& array, size_t num_pts, const std::string& array_name ) { - if (position_array.size() != num_pts) { - std::cerr << "The number of mesh points changed from the initial value of " << num_pts + if (array.size() != num_pts) { + std::cerr << "The number of mesh points in the " << array_name + << " array changed from the initial value of " << num_pts << ". This is not permitted during the simulation." << std::endl; // call ADI_End(); } + return FlattenArray(array); + } - std::vector mesh_pos_flat; - for (const auto& pos : position_array) { - mesh_pos_flat.insert(mesh_pos_flat.end(), pos.begin(), pos.end()); - } - return mesh_pos_flat; + /// Flatten and validate position array + std::vector FlattenPositionArray( + const std::vector>& position_array, size_t num_pts + ) { + return ValidateAndFlattenArray(position_array, num_pts, "position"); } + /// Flatten and validate orientation array std::vector FlattenOrientationArray( - const std::vector>& orientation_array, size_t num_pts + const std::vector>& orientation_array, size_t num_pts ) { - if (orientation_array.size() != num_pts) { - std::cerr << "The number of mesh points changed from the initial value of " << num_pts - << ". This is not permitted during the simulation." << std::endl; - // call ADI_End(); - } - - std::vector mesh_orient_flat; - for (const auto& orient : orientation_array) { - mesh_orient_flat.insert(mesh_orient_flat.end(), orient.begin(), orient.end()); - } - return mesh_orient_flat; + return ValidateAndFlattenArray(orientation_array, num_pts, "orientation"); } + /// Flatten and validate velocity array std::vector FlattenVelocityArray( - const std::vector>& velocity_array, size_t num_pts + const std::vector>& velocity_array, size_t num_pts ) { - if (velocity_array.size() != num_pts) { - std::cerr << "The number of mesh points changed from the initial value of " << num_pts - << ". This is not permitted during the simulation." << std::endl; - // call ADI_End(); - } - - std::vector mesh_vel_flat; - for (const auto& vel : velocity_array) { - mesh_vel_flat.insert(mesh_vel_flat.end(), vel.begin(), vel.end()); - } - return mesh_vel_flat; + return ValidateAndFlattenArray(velocity_array, num_pts, "velocity"); } /// Method to join a vector of strings into a single string with a delimiter @@ -526,118 +515,6 @@ struct AeroDynInflowLibrary { } return output; } - - template - void CheckArraySize( - const std::vector>& array, size_t expected_rows, size_t expected_cols, - const std::string& array_name, const std::string& node_type - ) { - // Check row count first - if (array.size() != expected_rows) { - std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " - << node_type << " " << array_name << " with " << expected_rows << " rows." - << std::endl; - // call ADI_End(); - } - - // Check column count only on the first row to avoid redundant checks - if (!array.empty() && array[0].size() != expected_cols) { - std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " - << node_type << " " << array_name << " with " << expected_cols << " columns." - << std::endl; - // call ADI_End(); - } - } - - void CheckInputMotions( - const std::vector>& position_array, - const std::vector>& orientation_array, - const std::vector>& velocity_array, - const std::vector>& accleration_array, const std::string& node_type, - size_t expected_position_dim, size_t expected_orientation_dim, size_t expected_VelAcceln_dim, - size_t expected_number_of_nodes - ) { - CheckArraySize( - position_array, expected_number_of_nodes, expected_position_dim, "positions", node_type - ); - CheckArraySize( - orientation_array, expected_number_of_nodes, expected_orientation_dim, "orientations", - node_type - ); - CheckArraySize( - velocity_array, expected_number_of_nodes, expected_VelAcceln_dim, "velocities", node_type - ); - CheckArraySize( - accleration_array, expected_number_of_nodes, expected_VelAcceln_dim, "accelerations", - node_type - ); - } - - void CheckHubNacelleInputMotions( - const std::vector>& hubPos, - const std::vector>& hubOrient, - const std::vector>& hubVel, const std::vector>& hubAcc, - const std::string& nodeName - ) { - // Hub/Nacelle specific checks, where dimensions are 3, 9, and 6 for position, orientation, - // and velocities/accelerations - const size_t expected_position_dim = 3; - const size_t expected_orientation_dim = 9; - const size_t expected_VelAcceln_dim = 6; - const size_t expected_number_of_nodes = 1; // Since there is only 1 hub/nacelle node - - CheckInputMotions( - hubPos, hubOrient, hubVel, hubAcc, nodeName, expected_position_dim, - expected_orientation_dim, expected_VelAcceln_dim, expected_number_of_nodes - ); - } - - void CheckRootInputMotions( - const std::vector>& root_pos, - const std::vector>& root_orient, - const std::vector>& root_vel, - const std::vector>& root_acc, size_t num_blades, size_t init_num_blades - ) { - if (num_blades != init_num_blades) { - std::cerr << "The number of root points changed from the initial value of " - << init_num_blades << ". This is not permitted during the simulation." - << std::endl; - // call ADI_End(); - } - - const size_t expected_position_dim = 3; - const size_t expected_orientation_dim = 9; - const size_t expected_vel_acc_dim = 6; - - CheckInputMotions( - root_pos, root_orient, root_vel, root_acc, "root", expected_position_dim, - expected_orientation_dim, expected_vel_acc_dim, num_blades - ); - } - - void CheckMeshInputMotions( - const std::vector>& mesh_pos, - const std::vector>& mesh_orient, - const std::vector>& mesh_vel, - const std::vector>& mesh_acc, size_t num_mesh_pts, - size_t init_num_mesh_pts - ) { - if (num_mesh_pts != init_num_mesh_pts) { - std::cerr << "The number of mesh points changed from the initial value of " - << init_num_mesh_pts << ". This is not permitted during the simulation." - << std::endl; - // call ADI_End(); - } - - const size_t expected_position_dim = 3; - const size_t expected_orientation_dim = 9; - const size_t expected_vel_acc_dim = 6; - - CheckInputMotions( - mesh_pos, mesh_orient, mesh_vel, mesh_acc, "mesh", expected_position_dim, - expected_orientation_dim, expected_vel_acc_dim, num_mesh_pts - ); - } }; } // namespace openturbine::util From 78fc562da7fb5546dac636eef24358ec6fa567de Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 14:07:19 -0600 Subject: [PATCH 16/87] Add wrapper for ADI_GetRotorLoads method --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 03ff72de..59eb2a73 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -461,6 +461,34 @@ struct AeroDynInflowLibrary { error_handling.CheckError(); } + // Wrapper for ADI_C_GetRotorLoads routine to get aerodynamic loads on the rotor + void ADI_GetRotorLoads( + int turbine_number, std::vector>& mesh_force_moment + ) { + auto ADI_C_GetRotorLoads = + this->lib.get_function("ADI_C_GetRotorLoads"); + + // Flatten the mesh force/moment array + auto mesh_force_moment_flat = FlattenArray(mesh_force_moment); + + ADI_C_GetRotorLoads( + &turbine_number, // input: current turbine number + &structural_mesh.n_mesh_points, // input: number of mesh points + mesh_force_moment_flat.data(), // output: mesh force/moment array + &error_handling.error_status, // output: error status + error_handling.error_message.data() // output: error message buffer + ); + + error_handling.CheckError(); + + // Copy the flattened array back to the original array + for (size_t i = 0; i < mesh_force_moment.size(); ++i) { + for (size_t j = 0; j < 6; ++j) { + mesh_force_moment[i][j] = mesh_force_moment_flat[i * 6 + j]; + } + } + } + private: /// Method to flatten a 2D array into a 1D array for Fortran compatibility template From a4ad7d4aa8728212af95ca36c3a62fe9d2665e13 Mon Sep 17 00:00:00 2001 From: dcdemen Date: Tue, 24 Sep 2024 13:51:00 -0600 Subject: [PATCH 17/87] Adds additional CI lines for building external codes where sanitizers are turned off --- .github/workflows/correctness-linux.yaml | 11 ++++++----- .github/workflows/correctness-macos.yaml | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/correctness-linux.yaml b/.github/workflows/correctness-linux.yaml index a8392fa4..07a1135f 100644 --- a/.github/workflows/correctness-linux.yaml +++ b/.github/workflows/correctness-linux.yaml @@ -14,6 +14,7 @@ jobs: matrix: compiler: [g++, clang++] build_type: [Release, Debug] + build_external: [all, none] steps: - name: Cache install Dependencies id: cache-dependencies @@ -47,13 +48,13 @@ jobs: mkdir build cd build cmake .. \ - -DOpenTurbine_ENABLE_SANITIZER_ADDRESS=ON \ - -DOpenTurbine_ENABLE_SANITIZER_LEAK=ON \ - -DOpenTurbine_ENABLE_SANITIZER_UNDEFINED=ON \ + -DOpenTurbine_ENABLE_SANITIZER_ADDRESS=${{ matrix.build_external == 'none' }} \ + -DOpenTurbine_ENABLE_SANITIZER_LEAK=${{ matrix.build_external == 'none' }} \ + -DOpenTurbine_ENABLE_SANITIZER_UNDEFINED=${{ matrix.build_external == 'none' }} \ -DOpenTurbine_ENABLE_CPPCHECK=ON \ -DOpenTurbine_ENABLE_CLANG_TIDY=ON \ - -DOpenTurbine_BUILD_OPENFAST_ADI=ON \ - -DOpenTurbine_BUILD_ROSCO_CONTROLLER=ON \ + -DOpenTurbine_BUILD_OPENFAST_ADI=${{ matrix.build_external == 'all' }} \ + -DOpenTurbine_BUILD_ROSCO_CONTROLLER=${{ matrix.build_external == 'all' }} \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} cmake --build . ctest --output-on-failure diff --git a/.github/workflows/correctness-macos.yaml b/.github/workflows/correctness-macos.yaml index b76bd40c..1bc40758 100644 --- a/.github/workflows/correctness-macos.yaml +++ b/.github/workflows/correctness-macos.yaml @@ -13,6 +13,7 @@ jobs: matrix: compiler: [g++, clang++] build_type: [Release, Debug] + build_external: [all, none] steps: - name: Cache install Dependencies id: cache-dependencies @@ -53,13 +54,13 @@ jobs: mkdir build cd build cmake .. \ - -DOpenTurbine_ENABLE_SANITIZER_ADDRESS=ON \ - -DOpenTurbine_ENABLE_SANITIZER_UNDEFINED=ON \ + -DOpenTurbine_ENABLE_SANITIZER_ADDRESS=${{ matrix.build_external == 'none' }} \ + -DOpenTurbine_ENABLE_SANITIZER_UNDEFINED=${{ matrix.build_external == 'none' }} \ -DOpenTurbine_ENABLE_CPPCHECK=ON \ -DOpenTurbine_ENABLE_CLANG_TIDY=ON \ -DOpenTurbine_ENABLE_VTK=ON \ - -DOpenTurbine_BUILD_OPENFAST_ADI=ON \ - -DOpenTurbine_BUILD_ROSCO_CONTROLLER=ON \ + -DOpenTurbine_BUILD_OPENFAST_ADI=${{ matrix.build_external == 'all' }} \ + -DOpenTurbine_BUILD_ROSCO_CONTROLLER=${{ matrix.build_external == 'all' }} \ -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} \ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} cmake --build . From c466afca7cdbd4c13d133096429b04182cd5590e Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 14:17:44 -0600 Subject: [PATCH 18/87] Add wrapper for ADI_C_CalcOutput method --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 59eb2a73..d3c0c2d7 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -489,6 +489,29 @@ struct AeroDynInflowLibrary { } } + // Wrapper for ADI_C_CalcOutput routine to calculate output channels at a given time + void ADI_C_CalcOutput(double time, std::vector& output_channel_values) { + auto ADI_C_CalcOutput = + this->lib.get_function("ADI_C_CalcOutput"); + + // Set up output channel values + auto output_channel_values_c = + std::vector(static_cast(sim_controls.n_channels)); + + // Run ADI_C_CalcOutput + ADI_C_CalcOutput( + &time, // input: time at which to calculate output forces + output_channel_values_c.data(), // output: output channel values + &error_handling.error_status, // output: error status + error_handling.error_message.data() // output: error message buffer + ); + + error_handling.CheckError(); + + // Copy the output channel values back to the original array + output_channel_values = output_channel_values_c; + } + private: /// Method to flatten a 2D array into a 1D array for Fortran compatibility template From 128424e1fa916baa79b7ee675cf7e6eeb4018f25 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 14:19:54 -0600 Subject: [PATCH 19/87] Add wrapper for ADI_C_UpdateStates method --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index d3c0c2d7..81d0c009 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -512,6 +512,22 @@ struct AeroDynInflowLibrary { output_channel_values = output_channel_values_c; } + // Wrapper for ADI_C_UpdateStates routine to calculate output forces at a given time + void ADI_C_UpdateStates(double time, double time_next) { + auto ADI_C_UpdateStates = + this->lib.get_function("ADI_C_UpdateStates"); + + // Run ADI_C_UpdateStates + ADI_C_UpdateStates( + &time, // input: time at which to calculate output forces + &time_next, // input: time T+dt we are stepping to + &error_handling.error_status, // output: error status + error_handling.error_message.data() // output: error message buffer + ); + + error_handling.CheckError(); + } + private: /// Method to flatten a 2D array into a 1D array for Fortran compatibility template From eddb8bc7cca8dbe363ca0f22a3f07f18a0a3ebd7 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 14:20:51 -0600 Subject: [PATCH 20/87] Add wrapper for ADI_End method --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 81d0c009..8778270e 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -528,6 +528,19 @@ struct AeroDynInflowLibrary { error_handling.CheckError(); } + // Wrapper for ADI_C_End routine to end the AeroDyn Inflow library + void ADI_End() { + auto ADI_C_End = this->lib.get_function("ADI_C_End"); + + // Run ADI_C_End + ADI_C_End( + &error_handling.error_status, // output: error status + error_handling.error_message.data() // output: error message buffer + ); + + error_handling.CheckError(); + } + private: /// Method to flatten a 2D array into a 1D array for Fortran compatibility template From c642c2f69fd7118650ead8eb20d38eacf4f7aba5 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 16:17:42 -0600 Subject: [PATCH 21/87] Refactor method names in AeroDynInflowLibrary for improved readability --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 57 +++++++++++++------ 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 8778270e..706e46b6 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -248,11 +248,36 @@ struct MeshMotionData { * * @details This class provides an interface for interacting with the AeroDynInflow (ADI) shared * library, which is a Fortran library offering C bindings for the AeroDyn x InflowWind modules of - * OpenFAST. Following functions are ported over from the AeroDyn_Inflow_C_Binding.f90 file: - * - ADI_C_PreInit: Handles the pre-initialization of the AeroDynInflow module - * - ADI_C_SetupRotor: Configures rotor-specific parameters before simulation - * - ADI_C_Init: Initializes the AeroDynInflow module - * - ADI_C_SetRotorMotion: Updates the rotor's motion during the simulation + * OpenFAST. + * + * Following functions are wrapped in this class: + * - ADI_C_PreInit: Handles the pre-initialization of the AeroDynInflow module + * - ADI_C_SetupRotor: Configures rotor-specific parameters before simulation + * - ADI_C_Init: Initializes the AeroDynInflow module + * - ADI_C_SetRotorMotion: Updates the rotor's motion during the simulation + * - ADI_C_GetRotorLoads: Retrieves the aerodynamic loads acting on the rotor + * - ADI_C_CalcOutput: Calculates the output channels at a given time + * - ADI_C_UpdateStates: Updates the states of the simulation at a given time + * - ADI_C_End: Ends the AeroDynInflow module + * + * Canonical workflow for using the AeroDyn x InflowWind C++ wrapper: + * 1. Instantiate the AeroDynInflowLibrary class + * - Modify the settings as needed + * - Set input files for AeroDyn and InflowWind (from file or as strings) + * 2. Initialize the AeroDyn Fortran library via the following steps: + * - PreInitialize() -- pre-initialize the AeroDyn library i.e. set number of turbines + * - SetupRotor() -- set up rotor-specific parameters i.e. initialize one rotor + * (iterate over turbines) + * - Initialize() -- initialize the AeroDyn library with input files i.e. actually + * call ADI to initialize the simulation + * 3. Perform the simulation by iterating over timesteps: + * - SetupRotorMotion() -- set motions of single turbine (iterate over turbines) + * - UpdateStates() -- update to next time step + * - CalculateOutputChannels() -- get outputs + * - GetRotorAerodynamicLoads() -- get loads per rotor (iterate over turbines) + * 4. Complete the simulation + * - Call Finalize() to close the AeroDyn library and free memory + * - Handle any resulting errors */ struct AeroDynInflowLibrary { util::dylib lib{ @@ -274,7 +299,7 @@ struct AeroDynInflowLibrary { } /// Wrapper for ADI_C_PreInit routine to initialize AeroDyn Inflow library - void ADI_PreInit() { + void PreInitialize() { auto ADI_C_PreInit = this->lib.get_function("ADI_C_PreInit"); ADI_C_PreInit( @@ -289,9 +314,7 @@ struct AeroDynInflowLibrary { } /// Wrapper for ADI_C_SetupRotor routine to set up the rotor - void ADI_SetupRotor( - int turbine_number, int is_horizontal_axis, std::vector turbine_ref_pos - ) { + void SetupRotor(int turbine_number, int is_horizontal_axis, std::vector turbine_ref_pos) { auto ADI_C_SetupRotor = this->lib.get_function< void(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, int*, float*, double*, int*, int*, char*)>( "ADI_C_SetupRotor" @@ -326,8 +349,8 @@ struct AeroDynInflowLibrary { error_handling.CheckError(); } - /// Wrapper for ADI_Init routine to initialize the AeroDyn Inflow library - void ADI_Init( + /// Wrapper for ADI_C_Init routine to initialize the AeroDyn Inflow library + void Initialize( std::vector aerodyn_input_string_array, std::vector inflowwind_input_string_array ) { @@ -391,9 +414,9 @@ struct AeroDynInflowLibrary { error_handling.CheckError(); } - // Wrapper for ADI_SetRotorMotion routine to set rotor motion i.e. motion of the hub, nacelle, + // Wrapper for ADI_C_SetRotorMotion routine to set rotor motion i.e. motion of the hub, nacelle, // root, and mesh points from the structural mesh - void ADI_C_SetRotorMotion( + void SetupRotorMotion( int turbine_number, MeshMotionData hub_motion, MeshMotionData nacelle_motion, MeshMotionData root_motion, MeshMotionData mesh_motion ) { @@ -462,7 +485,7 @@ struct AeroDynInflowLibrary { } // Wrapper for ADI_C_GetRotorLoads routine to get aerodynamic loads on the rotor - void ADI_GetRotorLoads( + void GetRotorAerodynamicLoads( int turbine_number, std::vector>& mesh_force_moment ) { auto ADI_C_GetRotorLoads = @@ -490,7 +513,7 @@ struct AeroDynInflowLibrary { } // Wrapper for ADI_C_CalcOutput routine to calculate output channels at a given time - void ADI_C_CalcOutput(double time, std::vector& output_channel_values) { + void CalculateOutputChannels(double time, std::vector& output_channel_values) { auto ADI_C_CalcOutput = this->lib.get_function("ADI_C_CalcOutput"); @@ -513,7 +536,7 @@ struct AeroDynInflowLibrary { } // Wrapper for ADI_C_UpdateStates routine to calculate output forces at a given time - void ADI_C_UpdateStates(double time, double time_next) { + void UpdateStates(double time, double time_next) { auto ADI_C_UpdateStates = this->lib.get_function("ADI_C_UpdateStates"); @@ -529,7 +552,7 @@ struct AeroDynInflowLibrary { } // Wrapper for ADI_C_End routine to end the AeroDyn Inflow library - void ADI_End() { + void Finalize() { auto ADI_C_End = this->lib.get_function("ADI_C_End"); // Run ADI_C_End From 15ea2d1599671e3cba30c0788bde0c374798c134 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 16:37:24 -0600 Subject: [PATCH 22/87] Fix sizes of root position and orientation arrays with n_blades --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 706e46b6..76b09a52 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -77,7 +77,7 @@ struct EnvironmentalConditions { float msl_offset{0.f}; //< Mean sea level to still water level offset (m) }; -/// Struct to hold the initial settings for the turbine (assuming a single turbine and three blades) +/// Struct to hold the initial settings for the turbine struct TurbineSettings { int n_turbines{1}; //< Number of turbines - 1 by default int n_blades{3}; //< Number of blades - 3 by default @@ -85,11 +85,13 @@ struct TurbineSettings { std::array initial_hub_orientation{0.}; //< Initial hub orientation std::array initial_nacelle_position{0.}; //< Initial nacelle position std::array initial_nacelle_orientation{0.}; //< Initial nacelle orientation - std::array initial_root_position{0.}; //< Initial root position - std::array initial_root_orientation{0.}; //< Initial root orientation + std::vector> initial_root_position{//< Initial root positions of blades + static_cast(n_blades)}; + std::vector> initial_root_orientation{//< Initial root orientations + static_cast(n_blades)}; }; -/// Struct to hold the settings for the simulation controls +/// Struct to hold the settings for simulation controls struct SimulationControls { static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames @@ -262,7 +264,7 @@ struct MeshMotionData { * * Canonical workflow for using the AeroDyn x InflowWind C++ wrapper: * 1. Instantiate the AeroDynInflowLibrary class - * - Modify the settings as needed + * - Modify the settings from defaults as needed * - Set input files for AeroDyn and InflowWind (from file or as strings) * 2. Initialize the AeroDyn Fortran library via the following steps: * - PreInitialize() -- pre-initialize the AeroDyn library i.e. set number of turbines @@ -320,7 +322,9 @@ struct AeroDynInflowLibrary { "ADI_C_SetupRotor" ); - // Flatten mesh arrays + // Flatten arrays to pass to the Fortran routine + auto initial_root_position_flat = FlattenArray(turbine_settings.initial_root_position); + auto initial_root_orientation_flat = FlattenArray(turbine_settings.initial_root_orientation); auto init_mesh_pos_flat = FlattenArray(structural_mesh.initial_mesh_position); auto init_mesh_orient_flat = FlattenArray(structural_mesh.initial_mesh_orientation); @@ -332,14 +336,13 @@ struct AeroDynInflowLibrary { turbine_settings.initial_hub_orientation.data(), // input: initial hub orientation turbine_settings.initial_nacelle_position.data(), // input: initial nacelle position turbine_settings.initial_nacelle_orientation.data( - ), // input: initial nacelle orientation - &turbine_settings.n_blades, // input: number of blades - turbine_settings.initial_root_position.data(), // input: initial blade root positions - turbine_settings.initial_root_orientation.data( - ), // input: initial blade root orientation - &structural_mesh.n_mesh_points, // input: number of mesh points - init_mesh_pos_flat.data(), // input: initial node positions - init_mesh_orient_flat.data(), // input: initial node orientation + ), // input: initial nacelle orientation + &turbine_settings.n_blades, // input: number of blades + initial_root_position_flat.data(), // input: initial blade root positions + initial_root_orientation_flat.data(), // input: initial blade root orientation + &structural_mesh.n_mesh_points, // input: number of mesh points + init_mesh_pos_flat.data(), // input: initial node positions + init_mesh_orient_flat.data(), // input: initial node orientation structural_mesh.mesh_point_to_blade_num.data( ), // input: initial mesh point to blade number mapping &error_handling.error_status, // output: Error status @@ -437,7 +440,7 @@ struct AeroDynInflowLibrary { static_cast(structural_mesh.initial_mesh_position.size()) ); - // Flatten the arrays to pass + // Flatten the arrays to pass to the Fortran routine auto hub_pos_flat = FlattenArray(hub_motion.position); auto hub_orient_flat = FlattenArray(hub_motion.orientation); auto hub_vel_flat = FlattenArray(hub_motion.velocity); From 332c3254da1dc4f69b554dc3e034050281445c4e Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 16:54:19 -0600 Subject: [PATCH 23/87] Quick refactor --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 76b09a52..5a8d3fed 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -5,6 +5,7 @@ #include #include +#include "src/math/quaternion_operations.hpp" #include "src/vendor/dylib/dylib.hpp" namespace openturbine::util { @@ -30,6 +31,25 @@ namespace openturbine::util { * - Gusts * - Free vortex wake * + * Canonical workflow for using the AeroDyn x InflowWind C++ wrapper: + * 1. Instantiate the AeroDynInflowLibrary class + * - Modify the settings from provided defaults as needed + * - Set input files for AeroDyn and InflowWind (from file or as strings) + * 2. Initialize the AeroDyn Fortran library via the following steps: + * - PreInitialize() -- pre-initialize the AeroDyn library i.e. set number of turbines + * - SetupRotor() -- set up rotor-specific parameters i.e. initialize one rotor + * (iterate over turbines) + * - Initialize() -- initialize the AeroDyn library with input files i.e. actually + * call ADI to initialize the simulation + * 3. Perform the simulation by iterating over timesteps: + * - SetupRotorMotion() -- set motions of single turbine (iterate over turbines) + * - UpdateStates() -- update to next time step + * - CalculateOutputChannels() -- get outputs + * - GetRotorAerodynamicLoads() -- get loads per rotor (iterate over turbines) + * 4. Complete the simulation + * - Call Finalize() to close the AeroDyn library and free memory + * - Handle any resulting errors + * * References: * - OpenFAST/AeroDyn: * https://github.com/OpenFAST/openfast/tree/main/modules/aerodyn @@ -253,33 +273,14 @@ struct MeshMotionData { * OpenFAST. * * Following functions are wrapped in this class: - * - ADI_C_PreInit: Handles the pre-initialization of the AeroDynInflow module - * - ADI_C_SetupRotor: Configures rotor-specific parameters before simulation - * - ADI_C_Init: Initializes the AeroDynInflow module - * - ADI_C_SetRotorMotion: Updates the rotor's motion during the simulation - * - ADI_C_GetRotorLoads: Retrieves the aerodynamic loads acting on the rotor - * - ADI_C_CalcOutput: Calculates the output channels at a given time - * - ADI_C_UpdateStates: Updates the states of the simulation at a given time - * - ADI_C_End: Ends the AeroDynInflow module - * - * Canonical workflow for using the AeroDyn x InflowWind C++ wrapper: - * 1. Instantiate the AeroDynInflowLibrary class - * - Modify the settings from defaults as needed - * - Set input files for AeroDyn and InflowWind (from file or as strings) - * 2. Initialize the AeroDyn Fortran library via the following steps: - * - PreInitialize() -- pre-initialize the AeroDyn library i.e. set number of turbines - * - SetupRotor() -- set up rotor-specific parameters i.e. initialize one rotor - * (iterate over turbines) - * - Initialize() -- initialize the AeroDyn library with input files i.e. actually - * call ADI to initialize the simulation - * 3. Perform the simulation by iterating over timesteps: - * - SetupRotorMotion() -- set motions of single turbine (iterate over turbines) - * - UpdateStates() -- update to next time step - * - CalculateOutputChannels() -- get outputs - * - GetRotorAerodynamicLoads() -- get loads per rotor (iterate over turbines) - * 4. Complete the simulation - * - Call Finalize() to close the AeroDyn library and free memory - * - Handle any resulting errors + * - PreInitialize -> ADI_C_PreInit: Handles the pre-initialization of the AeroDynInflow module + * - SetupRotor -> ADI_C_SetupRotor: Configures rotor-specific parameters before simulation + * - Initialize -> ADI_C_Init: Initializes the AeroDynInflow module for simulation + * - SetupRotorMotion -> ADI_C_SetRotorMotion: Sets rotor motion for a given time step + * - GetRotorAerodynamicLoads -> ADI_C_GetRotorLoads: Gets aerodynamic loads on the rotor + * - CalculateOutputChannels -> ADI_C_CalcOutput: Calculates output channels at a given time + * - UpdateStates -> ADI_C_UpdateStates: Updates states for the next time step + * - Finalize -> ADI_C_End: Ends the AeroDynInflow module by freeing memory */ struct AeroDynInflowLibrary { util::dylib lib{ From 99b125d661cb9773983b6159eda5b8c5e3db47ac Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 24 Sep 2024 18:26:46 -0600 Subject: [PATCH 24/87] Refactor to use 7x1 q vectors to provide motion data and break it into 3x1 position and 9x1 rotation matrix info --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 97 +++++++++++++++++-- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 5a8d3fed..85566f57 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -11,7 +11,7 @@ namespace openturbine::util { /** - * Following contains a C++ wrapper to interact with the AeroDyn Inflow (ADI) shared library, + * Following contains a C++ wrapper to interact with the AeroDyn/InflowWind (ADI) shared library, * originally written in Fortran, that exposes C-bindings for the AeroDyn and InflowWind modules of * OpenFAST. This wrapper simplifies interaction with the ADI library (particularly the C-based * interface, with inspiration from the python interface), providing a modern interface for @@ -31,7 +31,8 @@ namespace openturbine::util { * - Gusts * - Free vortex wake * - * Canonical workflow for using the AeroDyn x InflowWind C++ wrapper: + * Canonical workflow for using the AeroDyn x InflowWind C++ wrapper (see unit test directory for + * example): * 1. Instantiate the AeroDynInflowLibrary class * - Modify the settings from provided defaults as needed * - Set input files for AeroDyn and InflowWind (from file or as strings) @@ -97,6 +98,29 @@ struct EnvironmentalConditions { float msl_offset{0.f}; //< Mean sea level to still water level offset (m) }; +/// Function to break apart a 7x1 generalized coords vector into position (3x1 vector) and +/// orientation (9x1 vector) components +static void SetPositionAndOrientation( + const std::array& data, std::array& position, + std::array& orientation +) { + // Set position (first 3 elements) + for (size_t i = 0; i < 3; ++i) { + position[i] = static_cast(data[i]); + } + + // Set orientation (convert last 4 elements to 3x3 matrix) + auto orientation_2D = + QuaternionToRotationMatrix(std::array{data[3], data[4], data[5], data[6]}); + + // Flatten the 3x3 matrix to a 1D array + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + orientation[i * 3 + j] = orientation_2D[i][j]; + } + } +} + /// Struct to hold the initial settings for the turbine struct TurbineSettings { int n_turbines{1}; //< Number of turbines - 1 by default @@ -109,6 +133,34 @@ struct TurbineSettings { static_cast(n_blades)}; std::vector> initial_root_orientation{//< Initial root orientations static_cast(n_blades)}; + + /// Default constructor + TurbineSettings(); + + /// Constructor to initialize all data based on provided 7x1 inputs + TurbineSettings( + const std::array& hub_data, const std::array& nacelle_data, + const std::vector>& root_data, int n_turbines = 1, int n_blades = 3 + ) + : n_turbines(n_turbines), + n_blades(n_blades), + initial_root_position(static_cast(n_blades)), + initial_root_orientation(static_cast(n_blades)) { + // Set hub position and orientation + SetPositionAndOrientation(hub_data, initial_hub_position, initial_hub_orientation); + + // Set nacelle position and orientation + SetPositionAndOrientation( + nacelle_data, initial_nacelle_position, initial_nacelle_orientation + ); + + // Set root positions and orientations + for (size_t i = 0; i < static_cast(n_blades); ++i) { + SetPositionAndOrientation( + root_data[i], initial_root_position[i], initial_root_orientation[i] + ); + } + }; }; /// Struct to hold the settings for simulation controls @@ -153,7 +205,7 @@ struct VTKSettings { float vtk_hub_radius{1.5f}; //< Hub radius for VTK rendering }; -/// Struct to hold the structural mesh data +/// Struct to hold the initial motion of the structural mesh struct StructuralMesh { int n_mesh_points{1}; //< Number of mesh points std::vector> initial_mesh_position{}; //< N x 3 array [x, y, z] @@ -161,6 +213,23 @@ struct StructuralMesh { initial_mesh_orientation{}; //< N x 9 array [r11, r12, ..., r33] std::vector mesh_point_to_blade_num{}; //< N x 1 array for mapping a mesh point to blade number + + /// Default constructor + StructuralMesh() = default; + + /// Constructor to initialize all data based on provided 7x1 inputs + StructuralMesh(const std::vector>& mesh_data, int n_mesh_points = 1) + : n_mesh_points(n_mesh_points), + initial_mesh_position(static_cast(n_mesh_points)), + initial_mesh_orientation(static_cast(n_mesh_points)), + mesh_point_to_blade_num(static_cast(n_mesh_points)) { + // Set mesh position and orientation + for (size_t i = 0; i < static_cast(n_mesh_points); ++i) { + SetPositionAndOrientation( + mesh_data[i], initial_mesh_position[i], initial_mesh_orientation[i] + ); + } + } }; struct MeshMotionData { @@ -170,6 +239,20 @@ struct MeshMotionData { std::vector> acceleration; //< N x 6 array [u_dot, v_dot, w_dot, p_dot, q_dot, r_dot] + /// Default constructor + MeshMotionData() = default; + + /// Constructor to initialize all data based on provided 7x1 inputs + MeshMotionData(const std::vector>& mesh_data, int n_mesh_points = 1) + : position(static_cast(n_mesh_points)), + orientation(static_cast(n_mesh_points)), + velocity(static_cast(n_mesh_points)), + acceleration(static_cast(n_mesh_points)) { + // Set mesh position and orientation + for (size_t i = 0; i < static_cast(n_mesh_points); ++i) { + SetPositionAndOrientation(mesh_data[i], position[i], orientation[i]); + } + } /// Method to check the sizes of the input arrays template void CheckArraySize( @@ -269,8 +352,8 @@ struct MeshMotionData { * @brief Wrapper class for the AeroDynInflow (ADI) shared library * * @details This class provides an interface for interacting with the AeroDynInflow (ADI) shared - * library, which is a Fortran library offering C bindings for the AeroDyn x InflowWind modules of - * OpenFAST. + * library, which is a Fortran library offering C bindings for the AeroDyn x InflowWind modules + * of OpenFAST. * * Following functions are wrapped in this class: * - PreInitialize -> ADI_C_PreInit: Handles the pre-initialization of the AeroDynInflow module @@ -418,8 +501,8 @@ struct AeroDynInflowLibrary { error_handling.CheckError(); } - // Wrapper for ADI_C_SetRotorMotion routine to set rotor motion i.e. motion of the hub, nacelle, - // root, and mesh points from the structural mesh + // Wrapper for ADI_C_SetRotorMotion routine to set rotor motion i.e. motion of the hub, + // nacelle, root, and mesh points from the structural mesh void SetupRotorMotion( int turbine_number, MeshMotionData hub_motion, MeshMotionData nacelle_motion, MeshMotionData root_motion, MeshMotionData mesh_motion From 6ba5a14259f5d2b886bf77b9a2ac0711d80f472e Mon Sep 17 00:00:00 2001 From: dcdemen Date: Wed, 25 Sep 2024 14:27:52 -0600 Subject: [PATCH 25/87] Fixed compile errors when building for CUDA --- tests/unit_tests/model/test_copy_nodes_to_state.cpp | 6 ++++-- tests/unit_tests/regression/test_math.cpp | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/model/test_copy_nodes_to_state.cpp b/tests/unit_tests/model/test_copy_nodes_to_state.cpp index 8a0a9b2d..f545cdf4 100644 --- a/tests/unit_tests/model/test_copy_nodes_to_state.cpp +++ b/tests/unit_tests/model/test_copy_nodes_to_state.cpp @@ -26,7 +26,8 @@ TEST(CopyNodesToState, OneNode_Position) { constexpr auto q_exact = std::array{8., 9., 10., 11., 12., 13., 14.}; constexpr auto v_exact = std::array{}; constexpr auto vd_exact = std::array{}; - auto nodes = std::vector{std::make_shared(size_t{}, x0_exact, q_exact, v_exact, vd_exact)}; + auto nodes = + std::vector{std::make_shared(size_t{1234U}, x0_exact, q_exact, v_exact, vd_exact)}; CopyNodesToState(state, nodes); @@ -57,7 +58,8 @@ TEST(CopyNodesToState, OneNode_Velocity) { constexpr auto q_exact = std::array{}; constexpr auto v_exact = std::array{15., 16., 17., 18., 19., 20.}; constexpr auto vd_exact = std::array{21., 22., 23., 24., 25., 26.}; - auto nodes = std::vector{std::make_shared(size_t{}, x0_exact, q_exact, v_exact, vd_exact)}; + auto nodes = + std::vector{std::make_shared(size_t{1234U}, x0_exact, q_exact, v_exact, vd_exact)}; CopyNodesToState(state, nodes); diff --git a/tests/unit_tests/regression/test_math.cpp b/tests/unit_tests/regression/test_math.cpp index 08f13c90..cf231b92 100644 --- a/tests/unit_tests/regression/test_math.cpp +++ b/tests/unit_tests/regression/test_math.cpp @@ -203,7 +203,7 @@ TEST(QuaternionTest, RotationVectorToQuaternion_Set2) { TestRotationToQuaternion(phi, {0.707107, 0., 0., 0.707107}); } -TEST(QuaternionTest, QuaternionToRotationVector_1) { +void test_quaternion_to_rotation_vector_2() { const auto n = 100U; const auto dtheta = M_PI / static_cast(n); for (size_t i = 0; i < n; ++i) { @@ -223,6 +223,10 @@ TEST(QuaternionTest, QuaternionToRotationVector_1) { } } +TEST(QuaternionTest, QuaternionToRotationVector_1) { + test_quaternion_to_rotation_vector_2(); +} + TEST(QuaternionTest, QuaternionToRotationVector_2) { const auto n = 100U; const auto dtheta = M_PI / static_cast(n); From be883434e1bb55c3679aa5e1a26544e39239141b Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Wed, 25 Sep 2024 19:43:19 -0600 Subject: [PATCH 26/87] Quick refactor --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 168 ++++++++++++------ 1 file changed, 111 insertions(+), 57 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 85566f57..8b8ccdea 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -11,54 +11,63 @@ namespace openturbine::util { /** - * Following contains a C++ wrapper to interact with the AeroDyn/InflowWind (ADI) shared library, - * originally written in Fortran, that exposes C-bindings for the AeroDyn and InflowWind modules of - * OpenFAST. This wrapper simplifies interaction with the ADI library (particularly the C-based - * interface, with inspiration from the python interface), providing a modern interface for - * OpenTurbine devs to run AeroDyn x InflowWind. + * AeroDynInflowLibrary: A C++ wrapper for the AeroDyn/InflowWind (ADI) shared library + * + * ## Overview + * + * This wrapper simplifies interaction with the ADI library, providing a modern C++ interface for + * OpenTurbine developers to utilize AeroDyn and InflowWind functionality. It encapsulates the + * C-based interface of the Fortran library, drawing inspiration from the existing Python interface. + * + * ## Features * * AeroDyn utilizes blade element momentum (BEM) theory to calculate aerodynamic forces acting * on each blade section. It accounts for factors such as: - * - Dynamic stall + * - Dynamic stall (Beddoes-Leishman or OLAF models) * - Unsteady aerodynamics - * - Tower shadow + * - Tower shadow effects * - Wind shear + * - Tip and hub losses * * InflowWind simulates the inflow conditions around wind turbines by modeling spatially and - * temporally varying wind fields. It enables the simulation of complex wind phenomena, such as: - * - Turbulence - * - Wind shear - * - Gusts - * - Free vortex wake + * temporally varying wind fields. It enables the simulation of complex wind phenomena, including: + * - Atmospheric turbulence (e.g., Kaimal, von Karman spectra) + * - Wind shear (power law or logarithmic profiles) + * - Discrete gusts and extreme events + * - Various wind field types (uniform, full-field turbulence, user-defined) * - * Canonical workflow for using the AeroDyn x InflowWind C++ wrapper (see unit test directory for - * example): - * 1. Instantiate the AeroDynInflowLibrary class - * - Modify the settings from provided defaults as needed - * - Set input files for AeroDyn and InflowWind (from file or as strings) - * 2. Initialize the AeroDyn Fortran library via the following steps: - * - PreInitialize() -- pre-initialize the AeroDyn library i.e. set number of turbines - * - SetupRotor() -- set up rotor-specific parameters i.e. initialize one rotor - * (iterate over turbines) - * - Initialize() -- initialize the AeroDyn library with input files i.e. actually - * call ADI to initialize the simulation - * 3. Perform the simulation by iterating over timesteps: - * - SetupRotorMotion() -- set motions of single turbine (iterate over turbines) - * - UpdateStates() -- update to next time step - * - CalculateOutputChannels() -- get outputs - * - GetRotorAerodynamicLoads() -- get loads per rotor (iterate over turbines) - * 4. Complete the simulation - * - Call Finalize() to close the AeroDyn library and free memory - * - Handle any resulting errors + * ## Usage * - * References: - * - OpenFAST/AeroDyn: - * https://github.com/OpenFAST/openfast/tree/main/modules/aerodyn + * 1. Instantiate the AeroDynInflowLibrary class with the path to the shared library + * 2. Initialize the AeroDyn/InflowWind modules: + * - PreInitialize(): Set up general parameters + * - SetupRotor(): Configure rotor-specific settings (iterate over turbines) + * - Initialize(): Complete initialization with input files + * 3. Perform the simulation by iterating over timesteps: + * - SetupRotorMotion(): Update rotor motion (iterate over turbines) + * - UpdateStates(): Advance internal states + * - CalculateOutputChannels(): Compute output values + * - GetRotorAerodynamicLoads(): Retrieve aerodynamic forces and moments + * 4. Complete the simulation: + * - Finalize(): Clean up and release resources + * - Handle any resulting errors using the ErrorHandling struct + * + * ## References + * - OpenFAST/AeroDyn documentation: + * https://openfast.readthedocs.io/en/main/source/user/aerodyn/index.html + * - OpenFAST/InflowWind documentation: + * https://openfast.readthedocs.io/en/main/source/user/inflowwind/index.html * - AeroDyn InflowWind C bindings: * https://github.com/OpenFAST/openfast/blob/dev/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 */ -/// Struct for error handling settings +/** + * @brief Struct for error handling settings + * + * @details This struct holds the error handling settings for the AeroDynInflow library wrapper. It + * includes an error level enum, a maximum error message length, and methods for checking and + * handling errors. + */ struct ErrorHandling { /// Error levels used in InflowWind enum class ErrorLevel { @@ -76,9 +85,11 @@ struct ErrorHandling { int error_status{0}; //< Error status std::array error_message{}; //< Error message buffer - /// Checks for errors and throws an exception if found - otherwise returns true - bool CheckError() const { - return error_status == 0 ? true : throw std::runtime_error(error_message.data()); + /// Checks for errors and throws an exception if found + void CheckError() const { + if (error_status != 0) { + throw std::runtime_error(error_message.data()); + } } }; @@ -98,9 +109,14 @@ struct EnvironmentalConditions { float msl_offset{0.f}; //< Mean sea level to still water level offset (m) }; -/// Function to break apart a 7x1 generalized coords vector into position (3x1 vector) and -/// orientation (9x1 vector) components -static void SetPositionAndOrientation( +/** + * @brief Converts a 7-element array of position and quaternion to separate position and orientation + * arrays + * @param data Input array: [x, y, z, qw, qx, qy, qz] + * @param position Output array for position [x, y, z] + * @param orientation Output array for flattened 3x3 rotation matrix + */ +inline void SetPositionAndOrientation( const std::array& data, std::array& position, std::array& orientation ) { @@ -109,19 +125,14 @@ static void SetPositionAndOrientation( position[i] = static_cast(data[i]); } - // Set orientation (convert last 4 elements to 3x3 matrix) - auto orientation_2D = - QuaternionToRotationMatrix(std::array{data[3], data[4], data[5], data[6]}); + // Set orientation (convert last 4 elements to 3x3 rotation matrix) + auto orientation_2D = QuaternionToRotationMatrix({data[3], data[4], data[5], data[6]}); // Flatten the 3x3 matrix to a 1D array - for (size_t i = 0; i < 3; ++i) { - for (size_t j = 0; j < 3; ++j) { - orientation[i * 3 + j] = orientation_2D[i][j]; - } - } + std::copy(&orientation_2D[0][0], &orientation_2D[0][0] + 9, orientation.begin()); } -/// Struct to hold the initial settings for the turbine +/// Struct to hold the initial settings/motion for the turbine struct TurbineSettings { int n_turbines{1}; //< Number of turbines - 1 by default int n_blades{3}; //< Number of blades - 3 by default @@ -135,7 +146,7 @@ struct TurbineSettings { static_cast(n_blades)}; /// Default constructor - TurbineSettings(); + TurbineSettings() = default; /// Constructor to initialize all data based on provided 7x1 inputs TurbineSettings( @@ -146,6 +157,10 @@ struct TurbineSettings { n_blades(n_blades), initial_root_position(static_cast(n_blades)), initial_root_orientation(static_cast(n_blades)) { + if (root_data.size() != static_cast(n_blades)) { + throw std::invalid_argument("Number of root data entries must match n_blades"); + } + // Set hub position and orientation SetPositionAndOrientation(hub_data, initial_hub_position, initial_hub_orientation); @@ -163,7 +178,12 @@ struct TurbineSettings { }; }; -/// Struct to hold the settings for simulation controls +/** + * @brief Struct to hold the settings for simulation controls + * + * @details This struct holds the settings for simulation controls, including input file handling, + * interpolation order, time-related variables, and flags. + */ struct SimulationControls { static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames @@ -196,7 +216,12 @@ struct SimulationControls { std::array channel_units_c{}; //< Output channel units }; -/// Struct to hold the settings for VTK output +/** + * @brief Struct to hold the settings for VTK output + * + * @details This struct holds the settings for VTK output, including the flag to write VTK output, + * the type of VTK output, and the nacelle dimensions for VTK rendering. + */ struct VTKSettings { int write_vtk{false}; //< Flag to write VTK output int vtk_type{1}; //< Type of VTK output (1: surface meshes) @@ -205,7 +230,13 @@ struct VTKSettings { float vtk_hub_radius{1.5f}; //< Hub radius for VTK rendering }; -/// Struct to hold the initial motion of the structural mesh +/** + * @brief Struct to hold the initial motion of the structural mesh + * + * @details This struct holds the initial motion of the structural mesh, including the number of + * mesh points, the initial mesh position, the initial mesh orientation, and the mapping of mesh + * points to blade numbers. + */ struct StructuralMesh { int n_mesh_points{1}; //< Number of mesh points std::vector> initial_mesh_position{}; //< N x 3 array [x, y, z] @@ -232,6 +263,12 @@ struct StructuralMesh { } }; +/** + * @brief Struct to hold the motion data of the structural mesh + * + * @details This struct holds the motion data of the structural mesh, i.e. position, orientation, + * velocity, and acceleration of the mesh points. + */ struct MeshMotionData { std::vector> position; //< N x 3 array [x, y, z] std::vector> orientation; //< N x 9 array [r11, r12, ..., r33] @@ -253,7 +290,16 @@ struct MeshMotionData { SetPositionAndOrientation(mesh_data[i], position[i], orientation[i]); } } - /// Method to check the sizes of the input arrays + + /** + * @brief Method to check the dimensions of the input arrays + * + * @param array The input array to check + * @param expected_rows The expected number of rows + * @param expected_cols The expected number of columns + * @param array_name The name of the array + * @param node_type The type of node (e.g., "hub", "nacelle", "root", "mesh") + */ template void CheckArraySize( const std::vector>& array, size_t expected_rows, size_t expected_cols, @@ -276,8 +322,16 @@ struct MeshMotionData { } } - /// Method to check the input motions i.e. position, orientation, velocity, and acceleration - /// arrays + /** + * @brief Method to check the array sizes of the input motions for hub, nacelle, root, and mesh + * points + * + * @param node_type The type of node (e.g., "hub", "nacelle", "root", "mesh") + * @param expected_position_dim The expected dimension of the position array + * @param expected_orientation_dim The expected dimension of the orientation array + * @param expected_vel_acc_dim The expected dimension of the velocity and acceleration arrays + * @param expected_number_of_nodes The expected number of nodes for the input motions + */ void CheckInputMotions( const std::string& node_type, size_t expected_position_dim, size_t expected_orientation_dim, size_t expected_vel_acc_dim, size_t expected_number_of_nodes From 33d51fdd8fdaa574930e490164da4ead01a37717 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Wed, 25 Sep 2024 19:52:12 -0600 Subject: [PATCH 27/87] Add some boilerplate unit tests --- .../regression/test_aerodyn_inflow.cpp | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 1b304256..b9df50ef 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -2,6 +2,7 @@ #include "test_utilities.hpp" +#include "src/utilities/aerodynamics/aerodyn_inflow.hpp" #include "src/vendor/dylib/dylib.hpp" namespace openturbine::tests { @@ -24,7 +25,7 @@ TEST(AerodynInflowTest, ADI_C_PreInit) { auto ADI_C_PreInit = lib.get_function("ADI_C_PreInit"); // Call ADI_C_PreInit routine and expect the following outputs - int numTurbines{0}; // input: Number of turbines + int numTurbines{1}; // input: Number of turbines int transposeDCM{1}; // input: Transpose the direction cosine matrix int debuglevel{0}; // input: Debug level int error_status_c{0}; // output: error status @@ -34,12 +35,64 @@ TEST(AerodynInflowTest, ADI_C_PreInit) { static_cast(error_message_c) ); - EXPECT_EQ(numTurbines, 0); + EXPECT_EQ(numTurbines, 1); EXPECT_EQ(transposeDCM, 1); EXPECT_EQ(debuglevel, 0); EXPECT_EQ(error_status_c, 0); EXPECT_STREQ(static_cast(error_message_c), ""); } + +TEST(AerodynInflowTest, ErrorHandling_NoThrow) { + util::ErrorHandling error_handling; + EXPECT_NO_THROW(error_handling.CheckError()); +} + +TEST(AerodynInflowTest, ErrorHandling_Throw) { + util::ErrorHandling error_handling; + error_handling.error_status = 1; + EXPECT_THROW(error_handling.CheckError(), std::runtime_error); +} + +TEST(AerodynInflowTest, FluidProperties_Default) { + util::FluidProperties fluid_properties; + EXPECT_EQ(fluid_properties.density, 1.225f); + EXPECT_EQ(fluid_properties.kinematic_viscosity, 1.464E-5f); + EXPECT_EQ(fluid_properties.sound_speed, 335.f); + EXPECT_EQ(fluid_properties.vapor_pressure, 1700.f); +} + +TEST(AerodynInflowTest, FluidProperties_Set) { + util::FluidProperties fluid_properties; + fluid_properties.density = 1.1f; + fluid_properties.kinematic_viscosity = 1.5E-5f; + fluid_properties.sound_speed = 340.f; + fluid_properties.vapor_pressure = 1800.f; + EXPECT_EQ(fluid_properties.density, 1.1f); + EXPECT_EQ(fluid_properties.kinematic_viscosity, 1.5E-5f); + EXPECT_EQ(fluid_properties.sound_speed, 340.f); + EXPECT_EQ(fluid_properties.vapor_pressure, 1800.f); +} + +TEST(AerodynInflowTest, EnvironmentalConditions_Default) { + util::EnvironmentalConditions environmental_conditions; + EXPECT_EQ(environmental_conditions.gravity, 9.80665f); + EXPECT_EQ(environmental_conditions.atm_pressure, 103500.f); + EXPECT_EQ(environmental_conditions.water_depth, 0.f); + EXPECT_EQ(environmental_conditions.msl_offset, 0.f); +} + +TEST(AerodynInflowTest, EnvironmentalConditions_Set) { + util::EnvironmentalConditions environmental_conditions; + environmental_conditions.gravity = 9.79665f; + environmental_conditions.atm_pressure = 103000.f; + environmental_conditions.water_depth = 100.f; + environmental_conditions.msl_offset = 10.f; + EXPECT_EQ(environmental_conditions.gravity, 9.79665f); + EXPECT_EQ(environmental_conditions.atm_pressure, 103000.f); + EXPECT_EQ(environmental_conditions.water_depth, 100.f); + EXPECT_EQ(environmental_conditions.msl_offset, 10.f); +} + #endif } // namespace openturbine::tests \ No newline at end of file From b82b0edda856e55993deffb84f31d8c0957ecec5 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Wed, 25 Sep 2024 21:28:51 -0600 Subject: [PATCH 28/87] Add unit test for SetPositionAndOrientation function --- .../regression/test_aerodyn_inflow.cpp | 55 +++++++++++++------ 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index b9df50ef..91e13652 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -55,10 +55,10 @@ TEST(AerodynInflowTest, ErrorHandling_Throw) { TEST(AerodynInflowTest, FluidProperties_Default) { util::FluidProperties fluid_properties; - EXPECT_EQ(fluid_properties.density, 1.225f); - EXPECT_EQ(fluid_properties.kinematic_viscosity, 1.464E-5f); - EXPECT_EQ(fluid_properties.sound_speed, 335.f); - EXPECT_EQ(fluid_properties.vapor_pressure, 1700.f); + EXPECT_NEAR(fluid_properties.density, 1.225f, 1e-6f); + EXPECT_NEAR(fluid_properties.kinematic_viscosity, 1.464E-5f, 1e-6f); + EXPECT_NEAR(fluid_properties.sound_speed, 335.f, 1e-6f); + EXPECT_NEAR(fluid_properties.vapor_pressure, 1700.f, 1e-6f); } TEST(AerodynInflowTest, FluidProperties_Set) { @@ -67,18 +67,19 @@ TEST(AerodynInflowTest, FluidProperties_Set) { fluid_properties.kinematic_viscosity = 1.5E-5f; fluid_properties.sound_speed = 340.f; fluid_properties.vapor_pressure = 1800.f; - EXPECT_EQ(fluid_properties.density, 1.1f); - EXPECT_EQ(fluid_properties.kinematic_viscosity, 1.5E-5f); - EXPECT_EQ(fluid_properties.sound_speed, 340.f); - EXPECT_EQ(fluid_properties.vapor_pressure, 1800.f); + + EXPECT_NEAR(fluid_properties.density, 1.1f, 1e-6f); + EXPECT_NEAR(fluid_properties.kinematic_viscosity, 1.5E-5f, 1e-6f); + EXPECT_NEAR(fluid_properties.sound_speed, 340.f, 1e-6f); + EXPECT_NEAR(fluid_properties.vapor_pressure, 1800.f, 1e-6f); } TEST(AerodynInflowTest, EnvironmentalConditions_Default) { util::EnvironmentalConditions environmental_conditions; - EXPECT_EQ(environmental_conditions.gravity, 9.80665f); - EXPECT_EQ(environmental_conditions.atm_pressure, 103500.f); - EXPECT_EQ(environmental_conditions.water_depth, 0.f); - EXPECT_EQ(environmental_conditions.msl_offset, 0.f); + EXPECT_NEAR(environmental_conditions.gravity, 9.80665f, 1e-6f); + EXPECT_NEAR(environmental_conditions.atm_pressure, 103500.f, 1e-6f); + EXPECT_NEAR(environmental_conditions.water_depth, 0.f, 1e-6f); + EXPECT_NEAR(environmental_conditions.msl_offset, 0.f, 1e-6f); } TEST(AerodynInflowTest, EnvironmentalConditions_Set) { @@ -87,10 +88,32 @@ TEST(AerodynInflowTest, EnvironmentalConditions_Set) { environmental_conditions.atm_pressure = 103000.f; environmental_conditions.water_depth = 100.f; environmental_conditions.msl_offset = 10.f; - EXPECT_EQ(environmental_conditions.gravity, 9.79665f); - EXPECT_EQ(environmental_conditions.atm_pressure, 103000.f); - EXPECT_EQ(environmental_conditions.water_depth, 100.f); - EXPECT_EQ(environmental_conditions.msl_offset, 10.f); + + EXPECT_NEAR(environmental_conditions.gravity, 9.79665f, 1e-6f); + EXPECT_NEAR(environmental_conditions.atm_pressure, 103000.f, 1e-6f); + EXPECT_NEAR(environmental_conditions.water_depth, 100.f, 1e-6f); + EXPECT_NEAR(environmental_conditions.msl_offset, 10.f, 1e-6f); +} + +TEST(AerodynInflowTest, SetPositionAndOrientation) { + std::array data = {1., 2., 3., 0.707107, 0.707107, 0., 0.}; + std::array position; + std::array orientation; + + util::SetPositionAndOrientation(data, position, orientation); + + EXPECT_EQ(position[0], 1.0f); + EXPECT_EQ(position[1], 2.0f); + EXPECT_EQ(position[2], 3.0f); + EXPECT_NEAR(orientation[0], 1., 1.0E-6); + EXPECT_NEAR(orientation[1], 0., 1.0E-6); + EXPECT_NEAR(orientation[2], 0., 1.0E-6); + EXPECT_NEAR(orientation[3], 0., 1.0E-6); + EXPECT_NEAR(orientation[4], 0., 1.0E-6); + EXPECT_NEAR(orientation[5], -1., 1.0E-6); + EXPECT_NEAR(orientation[6], 0., 1.0E-6); + EXPECT_NEAR(orientation[7], 1., 1.0E-6); + EXPECT_NEAR(orientation[8], 0., 1.0E-6); } #endif From f02efd05d210d458156b48d11d1414c3e6fbc814 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Wed, 25 Sep 2024 22:45:23 -0600 Subject: [PATCH 29/87] Add a method ExpectArrayNear() to assert the elements of an std::array and refactor previous test to use the method --- .../regression/test_aerodyn_inflow.cpp | 59 +++++-------------- 1 file changed, 14 insertions(+), 45 deletions(-) diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 91e13652..7e5b101c 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -8,39 +8,6 @@ namespace openturbine::tests { #ifdef OpenTurbine_BUILD_OPENFAST_ADI -TEST(AerodynInflowTest, ADI_C_PreInit) { - // Use dylib to load the dynamic library and get access to the aerodyn inflow c binding functions - const std::filesystem::path project_root = FindProjectRoot(); - const std::filesystem::path full_path = - project_root / "build/tests/unit_tests/libaerodyn_inflow_c_binding."; - std::string path = full_path.string(); -#ifdef __APPLE__ - path += "dylib"; -#elif __linux__ - path += "so"; -#else // Windows - path += "dll"; -#endif - const util::dylib lib(path, util::dylib::no_filename_decorations); - auto ADI_C_PreInit = lib.get_function("ADI_C_PreInit"); - - // Call ADI_C_PreInit routine and expect the following outputs - int numTurbines{1}; // input: Number of turbines - int transposeDCM{1}; // input: Transpose the direction cosine matrix - int debuglevel{0}; // input: Debug level - int error_status_c{0}; // output: error status - char error_message_c[] = {'\0'}; // output: error message - ADI_C_PreInit( - &numTurbines, &transposeDCM, &debuglevel, &error_status_c, - static_cast(error_message_c) - ); - - EXPECT_EQ(numTurbines, 1); - EXPECT_EQ(transposeDCM, 1); - EXPECT_EQ(debuglevel, 0); - EXPECT_EQ(error_status_c, 0); - EXPECT_STREQ(static_cast(error_message_c), ""); -} TEST(AerodynInflowTest, ErrorHandling_NoThrow) { util::ErrorHandling error_handling; @@ -95,6 +62,18 @@ TEST(AerodynInflowTest, EnvironmentalConditions_Set) { EXPECT_NEAR(environmental_conditions.msl_offset, 10.f, 1e-6f); } +/// Check if members of the provided array is equal to the provided expected array +template +void ExpectArrayNear( + const std::array& actual, const std::array& expected, + T epsilon = static_cast(1e-6) +) { + ASSERT_EQ(actual.size(), expected.size()); + for (size_t i = 0; i < N; ++i) { + EXPECT_NEAR(actual[i], expected[i], epsilon) << "Element mismatch at index " << i; + } +} + TEST(AerodynInflowTest, SetPositionAndOrientation) { std::array data = {1., 2., 3., 0.707107, 0.707107, 0., 0.}; std::array position; @@ -102,18 +81,8 @@ TEST(AerodynInflowTest, SetPositionAndOrientation) { util::SetPositionAndOrientation(data, position, orientation); - EXPECT_EQ(position[0], 1.0f); - EXPECT_EQ(position[1], 2.0f); - EXPECT_EQ(position[2], 3.0f); - EXPECT_NEAR(orientation[0], 1., 1.0E-6); - EXPECT_NEAR(orientation[1], 0., 1.0E-6); - EXPECT_NEAR(orientation[2], 0., 1.0E-6); - EXPECT_NEAR(orientation[3], 0., 1.0E-6); - EXPECT_NEAR(orientation[4], 0., 1.0E-6); - EXPECT_NEAR(orientation[5], -1., 1.0E-6); - EXPECT_NEAR(orientation[6], 0., 1.0E-6); - EXPECT_NEAR(orientation[7], 1., 1.0E-6); - EXPECT_NEAR(orientation[8], 0., 1.0E-6); + ExpectArrayNear(position, {1.0f, 2.0f, 3.0f}); + ExpectArrayNear(orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.}); } #endif From 25ba8367d023cc25b49ba4a3b6ade4a687329911 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Wed, 25 Sep 2024 22:46:53 -0600 Subject: [PATCH 30/87] Add unit tests for TurbineSettings struct --- .../regression/test_aerodyn_inflow.cpp | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 7e5b101c..00bf75ff 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -85,6 +85,47 @@ TEST(AerodynInflowTest, SetPositionAndOrientation) { ExpectArrayNear(orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.}); } +TEST(AerodynInflowTest, TurbineSettings_Default) { + util::TurbineSettings turbine_settings; + EXPECT_EQ(turbine_settings.n_turbines, 1); + EXPECT_EQ(turbine_settings.n_blades, 3); + ExpectArrayNear(turbine_settings.initial_hub_position, {0.f, 0.f, 0.f}); + ExpectArrayNear(turbine_settings.initial_hub_orientation, {0., 0., 0., 0., 0., 0., 0., 0., 0.}); + ExpectArrayNear(turbine_settings.initial_nacelle_position, {0.f, 0.f, 0.f}); + ExpectArrayNear( + turbine_settings.initial_nacelle_orientation, {0., 0., 0., 0., 0., 0., 0., 0., 0.} + ); + for (size_t i = 0; i < turbine_settings.initial_root_position.size(); ++i) { + ExpectArrayNear(turbine_settings.initial_root_position[i], {0.f, 0.f, 0.f}); + ExpectArrayNear( + turbine_settings.initial_root_orientation[i], {0., 0., 0., 0., 0., 0., 0., 0., 0.} + ); + } +} + +TEST(AerodynInflowTest, TurbineSettings_Set_1T1B) { + int n_turbines{1}; + int n_blades{1}; + std::array hub_data = {1., 2., 3., 0.707107, 0.707107, 0., 0.}; + std::array nacelle_data = {4., 5., 6., 0.707107, 0.707107, 0., 0.}; + std::vector> root_data = {{7., 8., 9., 0.707107, 0.707107, 0., 0.}}; + + util::TurbineSettings turbine_settings(hub_data, nacelle_data, root_data, n_turbines, n_blades); + + EXPECT_EQ(turbine_settings.n_turbines, 1); + EXPECT_EQ(turbine_settings.n_blades, 1); + ExpectArrayNear(turbine_settings.initial_hub_position, {1.f, 2.f, 3.f}); + ExpectArrayNear(turbine_settings.initial_hub_orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.}); + ExpectArrayNear(turbine_settings.initial_nacelle_position, {4.f, 5.f, 6.f}); + ExpectArrayNear( + turbine_settings.initial_nacelle_orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.} + ); + ExpectArrayNear(turbine_settings.initial_root_position[0], {7.f, 8.f, 9.f}); + ExpectArrayNear( + turbine_settings.initial_root_orientation[0], {1., 0., 0., 0., 0., -1., 0., 1., 0.} + ); +} + #endif } // namespace openturbine::tests \ No newline at end of file From f2f755a4c7c7ce174e080c057859cc23c441d28c Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 00:28:56 -0600 Subject: [PATCH 31/87] Refactor to provide default values to TurbineSettings and StructuralMesh structs --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 8b8ccdea..d3b8ba74 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -134,16 +134,23 @@ inline void SetPositionAndOrientation( /// Struct to hold the initial settings/motion for the turbine struct TurbineSettings { - int n_turbines{1}; //< Number of turbines - 1 by default - int n_blades{3}; //< Number of blades - 3 by default - std::array initial_hub_position{0.}; //< Initial hub position - std::array initial_hub_orientation{0.}; //< Initial hub orientation - std::array initial_nacelle_position{0.}; //< Initial nacelle position - std::array initial_nacelle_orientation{0.}; //< Initial nacelle orientation - std::vector> initial_root_position{//< Initial root positions of blades - static_cast(n_blades)}; - std::vector> initial_root_orientation{//< Initial root orientations - static_cast(n_blades)}; + int n_turbines{1}; //< Number of turbines - 1 by default + int n_blades{3}; //< Number of blades - 3 by default + std::array initial_hub_position{0.f}; //< Initial hub position + std::array initial_hub_orientation{ + 1., 0., 0., 0., 1., 0., 0., 0., 1. //< Initial hub orientation + }; + std::array initial_nacelle_position{0.f}; //< Initial nacelle position + std::array initial_nacelle_orientation{ + 1., 0., 0., 0., 1., 0., 0., 0., 1. //< Initial nacelle orientation + }; + std::vector> initial_root_position{ + {0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}, {0.f, 0.f, 0.f} //< Initial root positions of blades + }; + std::vector> initial_root_orientation{ + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, //< Initial root orientations + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; /// Default constructor TurbineSettings() = default; @@ -238,12 +245,16 @@ struct VTKSettings { * points to blade numbers. */ struct StructuralMesh { - int n_mesh_points{1}; //< Number of mesh points - std::vector> initial_mesh_position{}; //< N x 3 array [x, y, z] - std::vector> - initial_mesh_orientation{}; //< N x 9 array [r11, r12, ..., r33] - std::vector - mesh_point_to_blade_num{}; //< N x 1 array for mapping a mesh point to blade number + int n_mesh_points{1}; //< Number of mesh points + std::vector> initial_mesh_position{ + {0.f, 0.f, 0.f} //< N x 3 array [x, y, z] + }; + std::vector> initial_mesh_orientation{ + {1., 0., 0., 0., 1., 0., 0., 0., 1.} //< N x 9 array [r11, r12, ..., r33] + }; + std::vector mesh_point_to_blade_num{ + 1 //< N x 1 array for mapping a mesh point to blade number + }; /// Default constructor StructuralMesh() = default; @@ -323,8 +334,8 @@ struct MeshMotionData { } /** - * @brief Method to check the array sizes of the input motions for hub, nacelle, root, and mesh - * points + * @brief Method to check the array sizes of the input motions for hub, nacelle, root, and + * mesh points * * @param node_type The type of node (e.g., "hub", "nacelle", "root", "mesh") * @param expected_position_dim The expected dimension of the position array From b3a9e83420885b9a34fcb27d0c1d962462b4f1d7 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 00:29:54 -0600 Subject: [PATCH 32/87] Add unit tests --- .../regression/test_aerodyn_inflow.cpp | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 00bf75ff..0e5197f1 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -90,15 +90,15 @@ TEST(AerodynInflowTest, TurbineSettings_Default) { EXPECT_EQ(turbine_settings.n_turbines, 1); EXPECT_EQ(turbine_settings.n_blades, 3); ExpectArrayNear(turbine_settings.initial_hub_position, {0.f, 0.f, 0.f}); - ExpectArrayNear(turbine_settings.initial_hub_orientation, {0., 0., 0., 0., 0., 0., 0., 0., 0.}); + ExpectArrayNear(turbine_settings.initial_hub_orientation, {1., 0., 0., 0., 1., 0., 0., 0., 1.}); ExpectArrayNear(turbine_settings.initial_nacelle_position, {0.f, 0.f, 0.f}); ExpectArrayNear( - turbine_settings.initial_nacelle_orientation, {0., 0., 0., 0., 0., 0., 0., 0., 0.} + turbine_settings.initial_nacelle_orientation, {1., 0., 0., 0., 1., 0., 0., 0., 1.} ); for (size_t i = 0; i < turbine_settings.initial_root_position.size(); ++i) { ExpectArrayNear(turbine_settings.initial_root_position[i], {0.f, 0.f, 0.f}); ExpectArrayNear( - turbine_settings.initial_root_orientation[i], {0., 0., 0., 0., 0., 0., 0., 0., 0.} + turbine_settings.initial_root_orientation[i], {1., 0., 0., 0., 1., 0., 0., 0., 1.} ); } } @@ -126,6 +126,34 @@ TEST(AerodynInflowTest, TurbineSettings_Set_1T1B) { ); } +TEST(AerodynInflowTest, SimulationControls_Default) { + util::SimulationControls simulation_controls; + EXPECT_EQ(simulation_controls.aerodyn_input_passed, 1); + EXPECT_EQ(simulation_controls.inflowwind_input_passed, 1); + EXPECT_EQ(simulation_controls.interpolation_order, 1); + EXPECT_EQ(simulation_controls.time_step, 0.1); + EXPECT_EQ(simulation_controls.max_time, 600.0); + EXPECT_EQ(simulation_controls.total_elapsed_time, 0.0); + EXPECT_EQ(simulation_controls.n_time_steps, 0); + EXPECT_EQ(simulation_controls.store_HH_wind_speed, 1); + EXPECT_EQ(simulation_controls.transpose_DCM, 1); + EXPECT_EQ(simulation_controls.debug_level, 0); + EXPECT_EQ(simulation_controls.output_format, 0); + EXPECT_EQ(simulation_controls.output_time_step, 0.0); + EXPECT_STREQ(simulation_controls.output_root_name.data(), "Output_ADIlib_default"); + EXPECT_EQ(simulation_controls.n_channels, 0); + EXPECT_STREQ(simulation_controls.channel_names_c.data(), ""); + EXPECT_STREQ(simulation_controls.channel_units_c.data(), ""); +} + +TEST(AerodynInflowTest, VTKSettings_Default) { + util::VTKSettings vtk_settings; + EXPECT_EQ(vtk_settings.write_vtk, 0); + EXPECT_EQ(vtk_settings.vtk_type, 1); + ExpectArrayNear(vtk_settings.vtk_nacelle_dimensions, {-2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}); + EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.5f); +} + #endif } // namespace openturbine::tests \ No newline at end of file From 7f7ca55e61a6c6d4b372a46583bf9824656a3901 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 10:06:28 -0600 Subject: [PATCH 33/87] Quick refactor to provide appropriate size of arrays to StructuralMesh and TurbineSettings structs --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 137 ++++++++++-------- 1 file changed, 78 insertions(+), 59 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index d3b8ba74..dd3bb349 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -17,7 +17,8 @@ namespace openturbine::util { * * This wrapper simplifies interaction with the ADI library, providing a modern C++ interface for * OpenTurbine developers to utilize AeroDyn and InflowWind functionality. It encapsulates the - * C-based interface of the Fortran library, drawing inspiration from the existing Python interface. + * C-bindings of the Fortran library, drawing inspiration from the existing Python + * interface. * * ## Features * @@ -53,6 +54,7 @@ namespace openturbine::util { * - Handle any resulting errors using the ErrorHandling struct * * ## References + * * - OpenFAST/AeroDyn documentation: * https://openfast.readthedocs.io/en/main/source/user/aerodyn/index.html * - OpenFAST/InflowWind documentation: @@ -145,12 +147,15 @@ struct TurbineSettings { 1., 0., 0., 0., 1., 0., 0., 0., 1. //< Initial nacelle orientation }; std::vector> initial_root_position{ - {0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}, {0.f, 0.f, 0.f} //< Initial root positions of blades - }; + {0.f, 0.f, 0.f}, // blade 1 + {0.f, 0.f, 0.f}, // blade 2 + {0.f, 0.f, 0.f} // blade 3 + }; //< Initial root positions of blades std::vector> initial_root_orientation{ - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, //< Initial root orientations - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, // blade 1 + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, // blade 2 + {1., 0., 0., 0., 1., 0., 0., 0., 1.} // blade 3 + }; //< Initial root orientations of blades /// Default constructor TurbineSettings() = default; @@ -185,58 +190,6 @@ struct TurbineSettings { }; }; -/** - * @brief Struct to hold the settings for simulation controls - * - * @details This struct holds the settings for simulation controls, including input file handling, - * interpolation order, time-related variables, and flags. - */ -struct SimulationControls { - static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames - - // Input file handling - int aerodyn_input_passed{1}; //< Input file passed for AeroDyn module? (1: passed) - int inflowwind_input_passed{1}; //< Input file passed for InflowWind module (1: passed) - - // Interpolation order (must be either 1: linear, or 2: quadratic) - int interpolation_order{1}; //< Interpolation order - linear by default - - // Initial time related variables - double time_step{0.1}; //< Simulation timestep (s) - double max_time{600.}; //< Maximum simulation time (s) - double total_elapsed_time{0.}; //< Total elapsed time (s) - int n_time_steps{0}; //< Number of time steps - - // Flags - int store_HH_wind_speed{1}; //< Flag to store HH wind speed - int transpose_DCM{1}; //< Flag to transpose the direction cosine matrix - int debug_level{0}; //< Debug level (0-4) - - // Outputs - int output_format{0}; //< File format for writing outputs - float output_time_step{0.}; //< Timestep for outputs to file - std::array output_root_name{ - "Output_ADIlib_default" //< Root name for output files - }; - int n_channels{0}; //< Number of channels returned - std::array channel_names_c{}; //< Output channel names - std::array channel_units_c{}; //< Output channel units -}; - -/** - * @brief Struct to hold the settings for VTK output - * - * @details This struct holds the settings for VTK output, including the flag to write VTK output, - * the type of VTK output, and the nacelle dimensions for VTK rendering. - */ -struct VTKSettings { - int write_vtk{false}; //< Flag to write VTK output - int vtk_type{1}; //< Type of VTK output (1: surface meshes) - std::array vtk_nacelle_dimensions{//< Nacelle dimensions for VTK rendering - -2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}; - float vtk_hub_radius{1.5f}; //< Hub radius for VTK rendering -}; - /** * @brief Struct to hold the initial motion of the structural mesh * @@ -260,17 +213,31 @@ struct StructuralMesh { StructuralMesh() = default; /// Constructor to initialize all data based on provided 7x1 inputs - StructuralMesh(const std::vector>& mesh_data, int n_mesh_points = 1) + StructuralMesh( + const std::vector>& mesh_data, + std::vector mesh_point_to_blade_num, int n_mesh_points = 1 + ) : n_mesh_points(n_mesh_points), initial_mesh_position(static_cast(n_mesh_points)), initial_mesh_orientation(static_cast(n_mesh_points)), mesh_point_to_blade_num(static_cast(n_mesh_points)) { + if (mesh_data.size() != static_cast(n_mesh_points) || + mesh_point_to_blade_num.size() != static_cast(n_mesh_points)) { + throw std::invalid_argument( + "Number of mesh data entries and mesh point to blade number entries must match " + "n_mesh_points" + ); + } + // Set mesh position and orientation for (size_t i = 0; i < static_cast(n_mesh_points); ++i) { SetPositionAndOrientation( mesh_data[i], initial_mesh_position[i], initial_mesh_orientation[i] ); } + + // Set mesh point to blade number mapping + this->mesh_point_to_blade_num = mesh_point_to_blade_num; } }; @@ -413,6 +380,58 @@ struct MeshMotionData { } }; +/** + * @brief Struct to hold the settings for simulation controls + * + * @details This struct holds the settings for simulation controls, including input file handling, + * interpolation order, time-related variables, and flags. + */ +struct SimulationControls { + static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames + + // Input file handling + int aerodyn_input_passed{1}; //< Input file passed for AeroDyn module? (1: passed) + int inflowwind_input_passed{1}; //< Input file passed for InflowWind module (1: passed) + + // Interpolation order (must be either 1: linear, or 2: quadratic) + int interpolation_order{1}; //< Interpolation order - linear by default + + // Initial time related variables + double time_step{0.1}; //< Simulation timestep (s) + double max_time{600.}; //< Maximum simulation time (s) + double total_elapsed_time{0.}; //< Total elapsed time (s) + int n_time_steps{0}; //< Number of time steps + + // Flags + int store_HH_wind_speed{1}; //< Flag to store HH wind speed + int transpose_DCM{1}; //< Flag to transpose the direction cosine matrix + int debug_level{0}; //< Debug level (0-4) + + // Outputs + int output_format{0}; //< File format for writing outputs + float output_time_step{0.}; //< Timestep for outputs to file + std::array output_root_name{ + "Output_ADIlib_default" //< Root name for output files + }; + int n_channels{0}; //< Number of channels returned + std::array channel_names_c{}; //< Output channel names + std::array channel_units_c{}; //< Output channel units +}; + +/** + * @brief Struct to hold the settings for VTK output + * + * @details This struct holds the settings for VTK output, including the flag to write VTK output, + * the type of VTK output, and the nacelle dimensions for VTK rendering. + */ +struct VTKSettings { + int write_vtk{false}; //< Flag to write VTK output + int vtk_type{1}; //< Type of VTK output (1: surface meshes) + std::array vtk_nacelle_dimensions{//< Nacelle dimensions for VTK rendering + -2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}; + float vtk_hub_radius{1.5f}; //< Hub radius for VTK rendering +}; + /** * @brief Wrapper class for the AeroDynInflow (ADI) shared library * From c4ddc01d6c195a7d023152fffdd40340ec93a3f6 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 10:07:26 -0600 Subject: [PATCH 34/87] Add unit tests for StructuralMesh --- .../regression/test_aerodyn_inflow.cpp | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 0e5197f1..4a52f038 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -154,6 +154,35 @@ TEST(AerodynInflowTest, VTKSettings_Default) { EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.5f); } +TEST(AerodynInflowTest, StructuralMesh_Default) { + util::StructuralMesh structural_mesh; + EXPECT_EQ(structural_mesh.n_mesh_points, 1); + EXPECT_EQ(structural_mesh.initial_mesh_position.size(), 1); + EXPECT_EQ(structural_mesh.initial_mesh_orientation.size(), 1); + EXPECT_EQ(structural_mesh.mesh_point_to_blade_num.size(), 1); + ExpectArrayNear(structural_mesh.initial_mesh_position[0], {0.f, 0.f, 0.f}); + ExpectArrayNear( + structural_mesh.initial_mesh_orientation[0], {1., 0., 0., 0., 1., 0., 0., 0., 1.} + ); + EXPECT_EQ(structural_mesh.mesh_point_to_blade_num[0], 1); +} + +TEST(AerodynInflowTest, StructuralMesh_Set) { + std::vector> mesh_data = {{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; + std::vector mesh_point_to_blade_num = {1}; + util::StructuralMesh structural_mesh(mesh_data, mesh_point_to_blade_num, 1); + + EXPECT_EQ(structural_mesh.n_mesh_points, 1); + EXPECT_EQ(structural_mesh.initial_mesh_position.size(), 1); + EXPECT_EQ(structural_mesh.initial_mesh_orientation.size(), 1); + EXPECT_EQ(structural_mesh.mesh_point_to_blade_num.size(), 1); + ExpectArrayNear(structural_mesh.initial_mesh_position[0], {1.f, 2.f, 3.f}); + ExpectArrayNear( + structural_mesh.initial_mesh_orientation[0], {1., 0., 0., 0., 0., -1., 0., 1., 0.} + ); + EXPECT_EQ(structural_mesh.mesh_point_to_blade_num[0], 1); +} + #endif } // namespace openturbine::tests \ No newline at end of file From 6f389a6273b8e579d35aed4abaa4dd080f4bc03d Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 10:32:42 -0600 Subject: [PATCH 35/87] Add tests for c-tor of MeshMotionData struct --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 18 ++++++++++---- .../regression/test_aerodyn_inflow.cpp | 24 +++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index dd3bb349..7b5a58dd 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -258,7 +258,11 @@ struct MeshMotionData { MeshMotionData() = default; /// Constructor to initialize all data based on provided 7x1 inputs - MeshMotionData(const std::vector>& mesh_data, int n_mesh_points = 1) + MeshMotionData( + const std::vector>& mesh_data, + std::vector> mesh_velocities, + std::vector> mesh_accelerations, int n_mesh_points = 1 + ) : position(static_cast(n_mesh_points)), orientation(static_cast(n_mesh_points)), velocity(static_cast(n_mesh_points)), @@ -267,6 +271,10 @@ struct MeshMotionData { for (size_t i = 0; i < static_cast(n_mesh_points); ++i) { SetPositionAndOrientation(mesh_data[i], position[i], orientation[i]); } + + // Set mesh velocities and accelerations + this->velocity = mesh_velocities; + this->acceleration = mesh_accelerations; } /** @@ -383,8 +391,8 @@ struct MeshMotionData { /** * @brief Struct to hold the settings for simulation controls * - * @details This struct holds the settings for simulation controls, including input file handling, - * interpolation order, time-related variables, and flags. + * @details This struct holds the settings for simulation controls, including input file + * handling, interpolation order, time-related variables, and flags. */ struct SimulationControls { static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames @@ -421,8 +429,8 @@ struct SimulationControls { /** * @brief Struct to hold the settings for VTK output * - * @details This struct holds the settings for VTK output, including the flag to write VTK output, - * the type of VTK output, and the nacelle dimensions for VTK rendering. + * @details This struct holds the settings for VTK output, including the flag to write VTK + * output, the type of VTK output, and the nacelle dimensions for VTK rendering. */ struct VTKSettings { int write_vtk{false}; //< Flag to write VTK output diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 4a52f038..a94225c2 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -183,6 +183,30 @@ TEST(AerodynInflowTest, StructuralMesh_Set) { EXPECT_EQ(structural_mesh.mesh_point_to_blade_num[0], 1); } +TEST(AerodynInflowTest, MeshMotionData_Default) { + util::MeshMotionData mesh_motion_data; + EXPECT_EQ(mesh_motion_data.position.size(), 0); + EXPECT_EQ(mesh_motion_data.orientation.size(), 0); + EXPECT_EQ(mesh_motion_data.velocity.size(), 0); + EXPECT_EQ(mesh_motion_data.acceleration.size(), 0); +} + +TEST(AerodynInflowTest, MeshMotionData_Set) { + std::vector> mesh_data = {{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; + std::vector> mesh_velocities = {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; + std::vector> mesh_accelerations = {{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + util::MeshMotionData mesh_motion_data(mesh_data, mesh_velocities, mesh_accelerations, 1); + + EXPECT_EQ(mesh_motion_data.position.size(), 1); + EXPECT_EQ(mesh_motion_data.orientation.size(), 1); + EXPECT_EQ(mesh_motion_data.velocity.size(), 1); + EXPECT_EQ(mesh_motion_data.acceleration.size(), 1); + ExpectArrayNear(mesh_motion_data.position[0], {1.f, 2.f, 3.f}); + ExpectArrayNear(mesh_motion_data.orientation[0], {1., 0., 0., 0., 0., -1., 0., 1., 0.}); + ExpectArrayNear(mesh_motion_data.velocity[0], {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}); + ExpectArrayNear(mesh_motion_data.acceleration[0], {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}); +} + #endif } // namespace openturbine::tests \ No newline at end of file From b705c30d683f29d9fb9436eb04efd3b07b22bb57 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 13:22:12 -0600 Subject: [PATCH 36/87] Another quick refactor --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 7b5a58dd..b928a543 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -53,6 +53,8 @@ namespace openturbine::util { * - Finalize(): Clean up and release resources * - Handle any resulting errors using the ErrorHandling struct * + * Note: Refer to the unit tests for more detailed examples of how to use this wrapper. + * * ## References * * - OpenFAST/AeroDyn documentation: @@ -134,7 +136,14 @@ inline void SetPositionAndOrientation( std::copy(&orientation_2D[0][0], &orientation_2D[0][0] + 9, orientation.begin()); } -/// Struct to hold the initial settings/motion for the turbine +/** + * @brief Struct to hold the initial settings/motion for the turbine + * + * @details This struct holds the initial settings/motion for the turbine, including the number of + * turbines, the number of blades, the initial hub position, the initial hub orientation, the initial + * nacelle position, the initial nacelle orientation, the initial root positions, and the initial + * root orientations. + */ struct TurbineSettings { int n_turbines{1}; //< Number of turbines - 1 by default int n_blades{3}; //< Number of blades - 3 by default @@ -194,8 +203,8 @@ struct TurbineSettings { * @brief Struct to hold the initial motion of the structural mesh * * @details This struct holds the initial motion of the structural mesh, including the number of - * mesh points, the initial mesh position, the initial mesh orientation, and the mapping of mesh - * points to blade numbers. + * mesh points i.e. nodes, the initial mesh position, the initial mesh orientation, and the mapping + * of mesh points to blade numbers. */ struct StructuralMesh { int n_mesh_points{1}; //< Number of mesh points @@ -212,7 +221,8 @@ struct StructuralMesh { /// Default constructor StructuralMesh() = default; - /// Constructor to initialize all data based on provided 7x1 inputs + /// Constructor to initialize all data based on provided 7x1 inputs and mapping of mesh points + /// to blade numbers StructuralMesh( const std::vector>& mesh_data, std::vector mesh_point_to_blade_num, int n_mesh_points = 1 @@ -220,9 +230,9 @@ struct StructuralMesh { : n_mesh_points(n_mesh_points), initial_mesh_position(static_cast(n_mesh_points)), initial_mesh_orientation(static_cast(n_mesh_points)), - mesh_point_to_blade_num(static_cast(n_mesh_points)) { + mesh_point_to_blade_num(std::move(mesh_point_to_blade_num)) { if (mesh_data.size() != static_cast(n_mesh_points) || - mesh_point_to_blade_num.size() != static_cast(n_mesh_points)) { + this->mesh_point_to_blade_num.size() != static_cast(n_mesh_points)) { throw std::invalid_argument( "Number of mesh data entries and mesh point to blade number entries must match " "n_mesh_points" @@ -235,9 +245,6 @@ struct StructuralMesh { mesh_data[i], initial_mesh_position[i], initial_mesh_orientation[i] ); } - - // Set mesh point to blade number mapping - this->mesh_point_to_blade_num = mesh_point_to_blade_num; } }; From f5ca15b9ddcdcccf27bac6f5c88464957014cf32 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 13:22:34 -0600 Subject: [PATCH 37/87] Add unit tests for SimulationControls and VTKSettings structs --- .../regression/test_aerodyn_inflow.cpp | 118 +++++++++++++----- 1 file changed, 90 insertions(+), 28 deletions(-) diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index a94225c2..97f4d340 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -126,34 +126,6 @@ TEST(AerodynInflowTest, TurbineSettings_Set_1T1B) { ); } -TEST(AerodynInflowTest, SimulationControls_Default) { - util::SimulationControls simulation_controls; - EXPECT_EQ(simulation_controls.aerodyn_input_passed, 1); - EXPECT_EQ(simulation_controls.inflowwind_input_passed, 1); - EXPECT_EQ(simulation_controls.interpolation_order, 1); - EXPECT_EQ(simulation_controls.time_step, 0.1); - EXPECT_EQ(simulation_controls.max_time, 600.0); - EXPECT_EQ(simulation_controls.total_elapsed_time, 0.0); - EXPECT_EQ(simulation_controls.n_time_steps, 0); - EXPECT_EQ(simulation_controls.store_HH_wind_speed, 1); - EXPECT_EQ(simulation_controls.transpose_DCM, 1); - EXPECT_EQ(simulation_controls.debug_level, 0); - EXPECT_EQ(simulation_controls.output_format, 0); - EXPECT_EQ(simulation_controls.output_time_step, 0.0); - EXPECT_STREQ(simulation_controls.output_root_name.data(), "Output_ADIlib_default"); - EXPECT_EQ(simulation_controls.n_channels, 0); - EXPECT_STREQ(simulation_controls.channel_names_c.data(), ""); - EXPECT_STREQ(simulation_controls.channel_units_c.data(), ""); -} - -TEST(AerodynInflowTest, VTKSettings_Default) { - util::VTKSettings vtk_settings; - EXPECT_EQ(vtk_settings.write_vtk, 0); - EXPECT_EQ(vtk_settings.vtk_type, 1); - ExpectArrayNear(vtk_settings.vtk_nacelle_dimensions, {-2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}); - EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.5f); -} - TEST(AerodynInflowTest, StructuralMesh_Default) { util::StructuralMesh structural_mesh; EXPECT_EQ(structural_mesh.n_mesh_points, 1); @@ -207,6 +179,96 @@ TEST(AerodynInflowTest, MeshMotionData_Set) { ExpectArrayNear(mesh_motion_data.acceleration[0], {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}); } +TEST(AerodynInflowTest, SimulationControls_Default) { + util::SimulationControls simulation_controls; + EXPECT_EQ(simulation_controls.aerodyn_input_passed, 1); + EXPECT_EQ(simulation_controls.inflowwind_input_passed, 1); + EXPECT_EQ(simulation_controls.interpolation_order, 1); + EXPECT_EQ(simulation_controls.time_step, 0.1); + EXPECT_EQ(simulation_controls.max_time, 600.0); + EXPECT_EQ(simulation_controls.total_elapsed_time, 0.0); + EXPECT_EQ(simulation_controls.n_time_steps, 0); + EXPECT_EQ(simulation_controls.store_HH_wind_speed, 1); + EXPECT_EQ(simulation_controls.transpose_DCM, 1); + EXPECT_EQ(simulation_controls.debug_level, 0); + EXPECT_EQ(simulation_controls.output_format, 0); + EXPECT_EQ(simulation_controls.output_time_step, 0.0); + EXPECT_STREQ(simulation_controls.output_root_name.data(), "Output_ADIlib_default"); + EXPECT_EQ(simulation_controls.n_channels, 0); + EXPECT_STREQ(simulation_controls.channel_names_c.data(), ""); + EXPECT_STREQ(simulation_controls.channel_units_c.data(), ""); +} + +TEST(AerodynInflowTest, SimulationControls_Set) { + util::SimulationControls simulation_controls; + simulation_controls.aerodyn_input_passed = 0; + simulation_controls.inflowwind_input_passed = 0; + simulation_controls.interpolation_order = 2; + simulation_controls.time_step = 0.2; + simulation_controls.max_time = 1200.0; + simulation_controls.total_elapsed_time = 100.0; + simulation_controls.n_time_steps = 10; + simulation_controls.store_HH_wind_speed = 0; + simulation_controls.transpose_DCM = 0; + simulation_controls.debug_level = 1; + simulation_controls.output_format = 1; + simulation_controls.output_time_step = 1.0; + std::strncpy( + simulation_controls.output_root_name.data(), "Output_ADIlib_test", + simulation_controls.output_root_name.size() - 1 + ); + simulation_controls.output_root_name[simulation_controls.output_root_name.size() - 1] = '\0'; + simulation_controls.n_channels = 1; + std::strncpy( + simulation_controls.channel_names_c.data(), "test_channel", + simulation_controls.channel_names_c.size() - 1 + ); + simulation_controls.channel_names_c[simulation_controls.channel_names_c.size() - 1] = '\0'; + std::strncpy( + simulation_controls.channel_units_c.data(), "test_unit", + simulation_controls.channel_units_c.size() - 1 + ); + simulation_controls.channel_units_c[simulation_controls.channel_units_c.size() - 1] = '\0'; + + EXPECT_EQ(simulation_controls.aerodyn_input_passed, 0); + EXPECT_EQ(simulation_controls.inflowwind_input_passed, 0); + EXPECT_EQ(simulation_controls.interpolation_order, 2); + EXPECT_EQ(simulation_controls.time_step, 0.2); + EXPECT_EQ(simulation_controls.max_time, 1200.0); + EXPECT_EQ(simulation_controls.total_elapsed_time, 100.0); + EXPECT_EQ(simulation_controls.n_time_steps, 10); + EXPECT_EQ(simulation_controls.store_HH_wind_speed, 0); + EXPECT_EQ(simulation_controls.transpose_DCM, 0); + EXPECT_EQ(simulation_controls.debug_level, 1); + EXPECT_EQ(simulation_controls.output_format, 1); + EXPECT_EQ(simulation_controls.output_time_step, 1.0); + EXPECT_STREQ(simulation_controls.output_root_name.data(), "Output_ADIlib_test"); + EXPECT_EQ(simulation_controls.n_channels, 1); + EXPECT_STREQ(simulation_controls.channel_names_c.data(), "test_channel"); + EXPECT_STREQ(simulation_controls.channel_units_c.data(), "test_unit"); +} + +TEST(AerodynInflowTest, VTKSettings_Default) { + util::VTKSettings vtk_settings; + EXPECT_EQ(vtk_settings.write_vtk, 0); + EXPECT_EQ(vtk_settings.vtk_type, 1); + ExpectArrayNear(vtk_settings.vtk_nacelle_dimensions, {-2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}); + EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.5f); +} + +TEST(AerodynInflowTest, VTKSettings_Set) { + util::VTKSettings vtk_settings; + vtk_settings.write_vtk = 1; + vtk_settings.vtk_type = 2; + vtk_settings.vtk_nacelle_dimensions = {-1.5f, -1.5f, 0.f, 5.f, 2.5f, 2.5f}; + vtk_settings.vtk_hub_radius = 1.0f; + + EXPECT_EQ(vtk_settings.write_vtk, 1); + EXPECT_EQ(vtk_settings.vtk_type, 2); + ExpectArrayNear(vtk_settings.vtk_nacelle_dimensions, {-1.5f, -1.5f, 0.f, 5.f, 2.5f, 2.5f}); + EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.0f); +} + #endif } // namespace openturbine::tests \ No newline at end of file From d4cc48ffe9e9313b144fee1c04eaa2d0fef766cc Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 14:05:12 -0600 Subject: [PATCH 38/87] Refactor the MeshMotionData struct --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 99 +++++++++---------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index b928a543..1baa90f4 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -249,10 +249,11 @@ struct StructuralMesh { }; /** - * @brief Struct to hold the motion data of the structural mesh + * @brief Struct to hold the motion data of any structural mesh component * - * @details This struct holds the motion data of the structural mesh, i.e. position, orientation, - * velocity, and acceleration of the mesh points. + * @details This struct holds the motion data (i.e. position, orientation, + * velocity, and acceleration) of the structural mesh, which can be the hub, nacelle, root, or + * mesh points/nodes. */ struct MeshMotionData { std::vector> position; //< N x 3 array [x, y, z] @@ -264,24 +265,25 @@ struct MeshMotionData { /// Default constructor MeshMotionData() = default; - /// Constructor to initialize all data based on provided 7x1 inputs + /// Constructor to initialize all data based on provided inputs MeshMotionData( const std::vector>& mesh_data, - std::vector> mesh_velocities, - std::vector> mesh_accelerations, int n_mesh_points = 1 + const std::vector>& mesh_velocities, + const std::vector>& mesh_accelerations, size_t n_mesh_points = 1 ) - : position(static_cast(n_mesh_points)), - orientation(static_cast(n_mesh_points)), - velocity(static_cast(n_mesh_points)), - acceleration(static_cast(n_mesh_points)) { + : position(n_mesh_points), + orientation(n_mesh_points), + velocity(std::move(mesh_velocities)), + acceleration(std::move(mesh_accelerations)) { + if (mesh_data.size() != n_mesh_points || mesh_velocities.size() != n_mesh_points || + mesh_accelerations.size() != n_mesh_points) { + throw std::invalid_argument("Input vector sizes must match n_mesh_points"); + } + // Set mesh position and orientation - for (size_t i = 0; i < static_cast(n_mesh_points); ++i) { + for (size_t i = 0; i < n_mesh_points; ++i) { SetPositionAndOrientation(mesh_data[i], position[i], orientation[i]); } - - // Set mesh velocities and accelerations - this->velocity = mesh_velocities; - this->acceleration = mesh_accelerations; } /** @@ -291,60 +293,51 @@ struct MeshMotionData { * @param expected_rows The expected number of rows * @param expected_cols The expected number of columns * @param array_name The name of the array - * @param node_type The type of node (e.g., "hub", "nacelle", "root", "mesh") + * @param node_label The label of the node (e.g., "hub", "nacelle", "root", "mesh") */ template void CheckArraySize( const std::vector>& array, size_t expected_rows, size_t expected_cols, - const std::string& array_name, const std::string& node_type + const std::string& array_name, const std::string& node_label ) const { - // Check row count first if (array.size() != expected_rows) { - std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " - << node_type << " " << array_name << " with " << expected_rows << " rows." - << std::endl; - // call ADI_End(); + throw std::invalid_argument( + "Expecting a " + std::to_string(expected_rows) + "x" + + std::to_string(expected_cols) + " array of " + node_label + " " + array_name + + " with " + std::to_string(expected_rows) + " rows, but got " + + std::to_string(array.size()) + " rows." + ); } - // Check column count only on the first row to avoid redundant checks if (!array.empty() && array[0].size() != expected_cols) { - std::cerr << "Expecting a " << expected_rows << "x" << expected_cols << " array of " - << node_type << " " << array_name << " with " << expected_cols << " columns." - << std::endl; - // call ADI_End(); + throw std::invalid_argument( + "Expecting a " + std::to_string(expected_rows) + "x" + + std::to_string(expected_cols) + " array of " + node_label + " " + array_name + + " with " + std::to_string(expected_cols) + " columns, but got " + + std::to_string(array[0].size()) + " columns." + ); } } - /** - * @brief Method to check the array sizes of the input motions for hub, nacelle, root, and - * mesh points - * - * @param node_type The type of node (e.g., "hub", "nacelle", "root", "mesh") - * @param expected_position_dim The expected dimension of the position array - * @param expected_orientation_dim The expected dimension of the orientation array - * @param expected_vel_acc_dim The expected dimension of the velocity and acceleration arrays - * @param expected_number_of_nodes The expected number of nodes for the input motions - */ void CheckInputMotions( - const std::string& node_type, size_t expected_position_dim, size_t expected_orientation_dim, + const std::string& node_label, size_t expected_position_dim, size_t expected_orientation_dim, size_t expected_vel_acc_dim, size_t expected_number_of_nodes ) const { CheckArraySize( - position, expected_number_of_nodes, expected_position_dim, "positions", node_type + position, expected_number_of_nodes, expected_position_dim, "positions", node_label ); CheckArraySize( orientation, expected_number_of_nodes, expected_orientation_dim, "orientations", - node_type + node_label ); CheckArraySize( - velocity, expected_number_of_nodes, expected_vel_acc_dim, "velocities", node_type + velocity, expected_number_of_nodes, expected_vel_acc_dim, "velocities", node_label ); CheckArraySize( - acceleration, expected_number_of_nodes, expected_vel_acc_dim, "accelerations", node_type + acceleration, expected_number_of_nodes, expected_vel_acc_dim, "accelerations", node_label ); } - /// Method to check the hub/nacelle input motions void CheckHubNacelleInputMotions(const std::string& node_name) const { const size_t expected_position_dim{3}; const size_t expected_orientation_dim{9}; @@ -357,13 +350,13 @@ struct MeshMotionData { ); } - /// Method to check the root input motions void CheckRootInputMotions(size_t num_blades, size_t init_num_blades) const { if (num_blades != init_num_blades) { - std::cerr << "The number of root points changed from the initial value of " - << init_num_blades << ". This is not permitted during the simulation." - << std::endl; - // call ADI_End(); + throw std::invalid_argument( + "The number of root points changed from the initial value of " + + std::to_string(init_num_blades) + " to " + std::to_string(num_blades) + + ". This is not permitted during the simulation." + ); } const size_t expected_position_dim{3}; @@ -375,13 +368,13 @@ struct MeshMotionData { ); } - /// Method to check the mesh input motions void CheckMeshInputMotions(size_t num_mesh_pts, size_t init_num_mesh_pts) const { if (num_mesh_pts != init_num_mesh_pts) { - std::cerr << "The number of mesh points changed from the initial value of " - << init_num_mesh_pts << ". This is not permitted during the simulation." - << std::endl; - // call ADI_End(); + throw std::invalid_argument( + "The number of mesh points changed from the initial value of " + + std::to_string(init_num_mesh_pts) + " to " + std::to_string(num_mesh_pts) + + ". This is not permitted during the simulation." + ); } const size_t expected_position_dim{3}; From 07643f4111996c397f39d7ae663f71c61994c3ca Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 14:05:34 -0600 Subject: [PATCH 39/87] Add unit tests for the MeshMotionData_CheckArraySize method --- .../regression/test_aerodyn_inflow.cpp | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 97f4d340..ccfbdd2b 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -179,6 +179,36 @@ TEST(AerodynInflowTest, MeshMotionData_Set) { ExpectArrayNear(mesh_motion_data.acceleration[0], {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}); } +TEST(AerodynInflowTest, MeshMotionData_CheckArraySize_NoThrow) { + std::vector> mesh_data = {{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; + std::vector> mesh_velocities = {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; + std::vector> mesh_accelerations = {{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + util::MeshMotionData mesh_motion_data(mesh_data, mesh_velocities, mesh_accelerations, 1); + + mesh_motion_data.CheckArraySize(mesh_motion_data.position, 1, 3, "position", "mesh motion data"); + mesh_motion_data.CheckArraySize( + mesh_motion_data.orientation, 1, 9, "orientation", "mesh motion data" + ); + mesh_motion_data.CheckArraySize(mesh_motion_data.velocity, 1, 6, "velocity", "mesh motion data"); + mesh_motion_data.CheckArraySize( + mesh_motion_data.acceleration, 1, 6, "acceleration", "mesh motion data" + ); +} + +TEST(AerodynInflowTest, MeshMotionData_CheckArraySize_ExpectThrow) { + std::vector> mesh_data = {{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; + std::vector> mesh_velocities = {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; + std::vector> mesh_accelerations = {{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + util::MeshMotionData mesh_motion_data(mesh_data, mesh_velocities, mesh_accelerations, 1); + + EXPECT_THROW( + mesh_motion_data.CheckArraySize( + mesh_motion_data.position, 2, 3, "position", "mesh motion data" // Expected 1 row + ), + std::invalid_argument + ); +} + TEST(AerodynInflowTest, SimulationControls_Default) { util::SimulationControls simulation_controls; EXPECT_EQ(simulation_controls.aerodyn_input_passed, 1); From 716829b55cde6580d03269ba997e68f9c143f858 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 15:02:17 -0600 Subject: [PATCH 40/87] Refactor c-tor for AeroDynInflowLibrary + unit test --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 26 +++++++++------- .../regression/test_aerodyn_inflow.cpp | 30 +++++++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 1baa90f4..edb940c2 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -447,15 +447,15 @@ struct VTKSettings { * library, which is a Fortran library offering C bindings for the AeroDyn x InflowWind modules * of OpenFAST. * - * Following functions are wrapped in this class: - * - PreInitialize -> ADI_C_PreInit: Handles the pre-initialization of the AeroDynInflow module - * - SetupRotor -> ADI_C_SetupRotor: Configures rotor-specific parameters before simulation - * - Initialize -> ADI_C_Init: Initializes the AeroDynInflow module for simulation - * - SetupRotorMotion -> ADI_C_SetRotorMotion: Sets rotor motion for a given time step - * - GetRotorAerodynamicLoads -> ADI_C_GetRotorLoads: Gets aerodynamic loads on the rotor - * - CalculateOutputChannels -> ADI_C_CalcOutput: Calculates output channels at a given time - * - UpdateStates -> ADI_C_UpdateStates: Updates states for the next time step - * - Finalize -> ADI_C_End: Ends the AeroDynInflow module by freeing memory + * The class encapsulates the following key functions: + * - PreInitialize (ADI_C_PreInit): Pre-initializes the AeroDynInflow module + * - SetupRotor (ADI_C_SetupRotor): Configures rotor-specific parameters before simulation + * - Initialize (ADI_C_Init): Initializes the AeroDynInflow module for simulation + * - SetupRotorMotion (ADI_C_SetRotorMotion): Sets rotor motion for a given time step + * - GetRotorAerodynamicLoads (ADI_C_GetRotorLoads): Retrieves aerodynamic loads on the rotor + * - CalculateOutputChannels (ADI_C_CalcOutput): Calculates output channels at a given time + * - UpdateStates (ADI_C_UpdateStates): Updates states for the next time step + * - Finalize (ADI_C_End): Ends the AeroDynInflow module and frees memory */ struct AeroDynInflowLibrary { util::dylib lib{ @@ -470,7 +470,13 @@ struct AeroDynInflowLibrary { SimulationControls sim_controls; //< Simulation control settings VTKSettings vtk_settings; //< VTK output settings - AeroDynInflowLibrary(std::string shared_lib_path = "") { + /// Constructor to initialize AeroDyn Inflow library with default settings and optional path + AeroDynInflowLibrary( + std::string shared_lib_path = "", TurbineSettings ts = TurbineSettings{}, + StructuralMesh sm = StructuralMesh{}, SimulationControls sc = SimulationControls{}, + VTKSettings vtk = VTKSettings{} + ) + : turbine_settings(ts), structural_mesh(sm), sim_controls(sc), vtk_settings(vtk) { if (!shared_lib_path.empty()) { lib = util::dylib(shared_lib_path, util::dylib::no_filename_decorations); } diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index ccfbdd2b..45d2006c 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -299,6 +299,36 @@ TEST(AerodynInflowTest, VTKSettings_Set) { EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.0f); } +/// Helper function to get the shared library path +std::string GetSharedLibraryPath() { + const std::filesystem::path project_root = FindProjectRoot(); + const std::filesystem::path full_path = + project_root / "build/tests/unit_tests/libaerodyn_inflow_c_binding"; + +#ifdef __APPLE__ + return full_path.string() + ".dylib"; +#elif __linux__ + return full_path.string() + ".so"; +#else // Windows + return full_path.string() + ".dll"; +#endif +} + +TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { + // Load the shared library + const std::string path = GetSharedLibraryPath(); + util::AeroDynInflowLibrary aerodyn_inflow_library(path); + + // Check initial error handling state + EXPECT_EQ(aerodyn_inflow_library.error_handling.error_status, 0); + EXPECT_STREQ(aerodyn_inflow_library.error_handling.error_message.data(), ""); + + // Check default values for other important members + EXPECT_EQ(aerodyn_inflow_library.turbine_settings.n_turbines, 1); + EXPECT_EQ(aerodyn_inflow_library.sim_controls.debug_level, 0); + EXPECT_EQ(aerodyn_inflow_library.sim_controls.transpose_DCM, 1); +} + #endif } // namespace openturbine::tests \ No newline at end of file From f43e23d7fa364fca45c5aea3a43cc8bfb37b4477 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 26 Sep 2024 15:34:34 -0600 Subject: [PATCH 41/87] Refactor to convert AeroDynInflowLibrary from struct -> class to enable data hiding --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 274 ++++++++++-------- .../regression/test_aerodyn_inflow.cpp | 14 +- 2 files changed, 155 insertions(+), 133 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index edb940c2..88d57757 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -457,81 +457,87 @@ struct VTKSettings { * - UpdateStates (ADI_C_UpdateStates): Updates states for the next time step * - Finalize (ADI_C_End): Ends the AeroDynInflow module and frees memory */ -struct AeroDynInflowLibrary { - util::dylib lib{ - "libaerodyn_inflow_c_binding.dylib", - util::dylib::no_filename_decorations //< Dynamic library object for AeroDyn Inflow - }; - ErrorHandling error_handling; //< Error handling settings - FluidProperties air; //< Properties of the working fluid (air) - EnvironmentalConditions env_conditions; //< Environmental conditions - TurbineSettings turbine_settings; //< Turbine settings - StructuralMesh structural_mesh; //< Structural mesh data - SimulationControls sim_controls; //< Simulation control settings - VTKSettings vtk_settings; //< VTK output settings - +class AeroDynInflowLibrary { +public: /// Constructor to initialize AeroDyn Inflow library with default settings and optional path AeroDynInflowLibrary( - std::string shared_lib_path = "", TurbineSettings ts = TurbineSettings{}, - StructuralMesh sm = StructuralMesh{}, SimulationControls sc = SimulationControls{}, - VTKSettings vtk = VTKSettings{} + std::string shared_lib_path = "", ErrorHandling eh = ErrorHandling{}, + FluidProperties fp = FluidProperties{}, + EnvironmentalConditions ec = EnvironmentalConditions{}, + TurbineSettings ts = TurbineSettings{}, StructuralMesh sm = StructuralMesh{}, + SimulationControls sc = SimulationControls{}, VTKSettings vtk = VTKSettings{} ) - : turbine_settings(ts), structural_mesh(sm), sim_controls(sc), vtk_settings(vtk) { + : error_handling_(std::move(eh)), + air_(std::move(fp)), + env_conditions_(std::move(ec)), + turbine_settings_(std::move(ts)), + structural_mesh_(std::move(sm)), + sim_controls_(std::move(sc)), + vtk_settings_(std::move(vtk)) { if (!shared_lib_path.empty()) { - lib = util::dylib(shared_lib_path, util::dylib::no_filename_decorations); + lib_ = util::dylib(shared_lib_path, util::dylib::no_filename_decorations); } } + /// Getter methods + const ErrorHandling& GetErrorHandling() const { return error_handling_; } + const EnvironmentalConditions& GetEnvironmentalConditions() const { return env_conditions_; } + const FluidProperties& GetFluidProperties() const { return air_; } + const TurbineSettings& GetTurbineSettings() const { return turbine_settings_; } + const StructuralMesh& GetStructuralMesh() const { return structural_mesh_; } + const SimulationControls& GetSimulationControls() const { return sim_controls_; } + const VTKSettings& GetVTKSettings() const { return vtk_settings_; } + /// Wrapper for ADI_C_PreInit routine to initialize AeroDyn Inflow library void PreInitialize() { - auto ADI_C_PreInit = - this->lib.get_function("ADI_C_PreInit"); + auto ADI_C_PreInit = lib_.get_function("ADI_C_PreInit"); ADI_C_PreInit( - &turbine_settings.n_turbines, // input: Number of turbines - &sim_controls.transpose_DCM, // input: Transpose DCM? - &sim_controls.debug_level, // input: Debug level - &error_handling.error_status, // output: Error status - error_handling.error_message.data() // output: Error message + &turbine_settings_.n_turbines, // input: Number of turbines + &sim_controls_.transpose_DCM, // input: Transpose DCM? + &sim_controls_.debug_level, // input: Debug level + &error_handling_.error_status, // output: Error status + error_handling_.error_message.data() // output: Error message ); - error_handling.CheckError(); + error_handling_.CheckError(); } /// Wrapper for ADI_C_SetupRotor routine to set up the rotor void SetupRotor(int turbine_number, int is_horizontal_axis, std::vector turbine_ref_pos) { - auto ADI_C_SetupRotor = this->lib.get_function< + auto ADI_C_SetupRotor = lib_.get_function< void(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, int*, float*, double*, int*, int*, char*)>( "ADI_C_SetupRotor" ); // Flatten arrays to pass to the Fortran routine - auto initial_root_position_flat = FlattenArray(turbine_settings.initial_root_position); - auto initial_root_orientation_flat = FlattenArray(turbine_settings.initial_root_orientation); - auto init_mesh_pos_flat = FlattenArray(structural_mesh.initial_mesh_position); - auto init_mesh_orient_flat = FlattenArray(structural_mesh.initial_mesh_orientation); + auto initial_root_position_flat = FlattenArray(turbine_settings_.initial_root_position); + auto initial_root_orientation_flat = + FlattenArray(turbine_settings_.initial_root_orientation); + auto init_mesh_pos_flat = FlattenArray(structural_mesh_.initial_mesh_position); + auto init_mesh_orient_flat = FlattenArray(structural_mesh_.initial_mesh_orientation); ADI_C_SetupRotor( - &turbine_number, // input: current turbine number - &is_horizontal_axis, // input: 1: HAWT, 0: VAWT or cross-flow - turbine_ref_pos.data(), // input: turbine reference position - turbine_settings.initial_hub_position.data(), // input: initial hub position - turbine_settings.initial_hub_orientation.data(), // input: initial hub orientation - turbine_settings.initial_nacelle_position.data(), // input: initial nacelle position - turbine_settings.initial_nacelle_orientation.data( + &turbine_number, // input: current turbine number + &is_horizontal_axis, // input: 1: HAWT, 0: VAWT or cross-flow + turbine_ref_pos.data(), // input: turbine reference position + turbine_settings_.initial_hub_position.data(), // input: initial hub position + turbine_settings_.initial_hub_orientation.data(), // input: initial hub orientation + turbine_settings_.initial_nacelle_position.data(), // input: initial nacelle position + turbine_settings_.initial_nacelle_orientation.data( ), // input: initial nacelle orientation - &turbine_settings.n_blades, // input: number of blades + &turbine_settings_.n_blades, // input: number of blades initial_root_position_flat.data(), // input: initial blade root positions initial_root_orientation_flat.data(), // input: initial blade root orientation - &structural_mesh.n_mesh_points, // input: number of mesh points + &structural_mesh_.n_mesh_points, // input: number of mesh points init_mesh_pos_flat.data(), // input: initial node positions init_mesh_orient_flat.data(), // input: initial node orientation - structural_mesh.mesh_point_to_blade_num.data( - ), // input: initial mesh point to blade number mapping - &error_handling.error_status, // output: Error status - error_handling.error_message.data() // output: Error message buffer + structural_mesh_.mesh_point_to_blade_num.data( + ), // input: initial mesh point to blade number mapping + &error_handling_.error_status, // output: Error status + error_handling_.error_message.data() // output: Error message buffer ); - error_handling.CheckError(); + error_handling_.CheckError(); } /// Wrapper for ADI_C_Init routine to initialize the AeroDyn Inflow library @@ -540,7 +546,7 @@ struct AeroDynInflowLibrary { std::vector inflowwind_input_string_array ) { auto ADI_C_Init = - this->lib + lib_ .get_function< void(int*, const char*, int*, int*, const char*, int*, char*, float*, float*, float*, float*, float*, float*, float*, float*, int*, double*, double*, int*, int*, int*, float*, float*, int*, float*, int*, char*, char*, int*, char*)>( "ADI_C_Init" @@ -549,7 +555,7 @@ struct AeroDynInflowLibrary { // Flatten arrays to pass auto vtk_nacelle_dim_flat = std::array{}; std::copy( - vtk_settings.vtk_nacelle_dimensions.begin(), vtk_settings.vtk_nacelle_dimensions.end(), + vtk_settings_.vtk_nacelle_dimensions.begin(), vtk_settings_.vtk_nacelle_dimensions.end(), vtk_nacelle_dim_flat.begin() ); @@ -564,39 +570,39 @@ struct AeroDynInflowLibrary { int inflowwind_input_string_length = static_cast(inflowwind_input_string.size()); ADI_C_Init( - &sim_controls.aerodyn_input_passed, // input: AD input file is passed - aerodyn_input_string.data(), // input: AD input file as string - &aerodyn_input_string_length, // input: AD input file string length - &sim_controls.inflowwind_input_passed, // input: IfW input file is passed - inflowwind_input_string.data(), // input: IfW input file as string - &inflowwind_input_string_length, // input: IfW input file string length - sim_controls.output_root_name.data(), // input: rootname for ADI file writing - &env_conditions.gravity, // input: gravity - &air.density, // input: air density - &air.kinematic_viscosity, // input: kinematic viscosity - &air.sound_speed, // input: speed of sound - &env_conditions.atm_pressure, // input: atmospheric pressure - &air.vapor_pressure, // input: vapor pressure - &env_conditions.water_depth, // input: water depth - &env_conditions.msl_offset, // input: MSL to SWL offset - &sim_controls.interpolation_order, // input: interpolation order - &sim_controls.time_step, // input: time step - &sim_controls.max_time, // input: maximum simulation time - &sim_controls.store_HH_wind_speed, // input: store HH wind speed - &vtk_settings.write_vtk, // input: write VTK output - &vtk_settings.vtk_type, // input: VTK output type - vtk_nacelle_dim_flat.data(), // input: VTK nacelle dimensions - &vtk_settings.vtk_hub_radius, // input: VTK hub radius - &sim_controls.output_format, // input: output format - &sim_controls.output_time_step, // input: output time step - &sim_controls.n_channels, // output: number of channels - sim_controls.channel_names_c.data(), // output: output channel names - sim_controls.channel_units_c.data(), // output: output channel units - &error_handling.error_status, // output: error status - error_handling.error_message.data() // output: error message buffer + &sim_controls_.aerodyn_input_passed, // input: AD input file is passed + aerodyn_input_string.data(), // input: AD input file as string + &aerodyn_input_string_length, // input: AD input file string length + &sim_controls_.inflowwind_input_passed, // input: IfW input file is passed + inflowwind_input_string.data(), // input: IfW input file as string + &inflowwind_input_string_length, // input: IfW input file string length + sim_controls_.output_root_name.data(), // input: rootname for ADI file writing + &env_conditions_.gravity, // input: gravity + &air_.density, // input: air density + &air_.kinematic_viscosity, // input: kinematic viscosity + &air_.sound_speed, // input: speed of sound + &env_conditions_.atm_pressure, // input: atmospheric pressure + &air_.vapor_pressure, // input: vapor pressure + &env_conditions_.water_depth, // input: water depth + &env_conditions_.msl_offset, // input: MSL to SWL offset + &sim_controls_.interpolation_order, // input: interpolation order + &sim_controls_.time_step, // input: time step + &sim_controls_.max_time, // input: maximum simulation time + &sim_controls_.store_HH_wind_speed, // input: store HH wind speed + &vtk_settings_.write_vtk, // input: write VTK output + &vtk_settings_.vtk_type, // input: VTK output type + vtk_nacelle_dim_flat.data(), // input: VTK nacelle dimensions + &vtk_settings_.vtk_hub_radius, // input: VTK hub radius + &sim_controls_.output_format, // input: output format + &sim_controls_.output_time_step, // input: output time step + &sim_controls_.n_channels, // output: number of channels + sim_controls_.channel_names_c.data(), // output: output channel names + sim_controls_.channel_units_c.data(), // output: output channel units + &error_handling_.error_status, // output: error status + error_handling_.error_message.data() // output: error message buffer ); - error_handling.CheckError(); + error_handling_.CheckError(); } // Wrapper for ADI_C_SetRotorMotion routine to set rotor motion i.e. motion of the hub, @@ -605,7 +611,7 @@ struct AeroDynInflowLibrary { int turbine_number, MeshMotionData hub_motion, MeshMotionData nacelle_motion, MeshMotionData root_motion, MeshMotionData mesh_motion ) { - auto ADI_C_SetRotorMotion = this->lib.get_function< + auto ADI_C_SetRotorMotion = lib_.get_function< void(int*, float*, double*, float*, float*, float*, double*, float*, float*, float*, double*, float*, float*, int*, float*, double*, float*, float*, int*, char*)>( "ADI_C_SetRotorMotion" ); @@ -614,12 +620,12 @@ struct AeroDynInflowLibrary { hub_motion.CheckHubNacelleInputMotions("hub"); nacelle_motion.CheckHubNacelleInputMotions("nacelle"); root_motion.CheckRootInputMotions( - static_cast(turbine_settings.n_blades), - static_cast(turbine_settings.initial_root_position.size()) + static_cast(turbine_settings_.n_blades), + static_cast(turbine_settings_.initial_root_position.size()) ); mesh_motion.CheckMeshInputMotions( - static_cast(structural_mesh.n_mesh_points), - static_cast(structural_mesh.initial_mesh_position.size()) + static_cast(structural_mesh_.n_mesh_points), + static_cast(structural_mesh_.initial_mesh_position.size()) ); // Flatten the arrays to pass to the Fortran routine @@ -644,29 +650,29 @@ struct AeroDynInflowLibrary { auto mesh_acc_flat = FlattenArray(mesh_motion.acceleration); ADI_C_SetRotorMotion( - &turbine_number, // input: current turbine number - hub_pos_flat.data(), // input: hub positions - hub_orient_flat.data(), // input: hub orientations - hub_vel_flat.data(), // input: hub velocities - hub_acc_flat.data(), // input: hub accelerations - nacelle_pos_flat.data(), // input: nacelle positions - nacelle_orient_flat.data(), // input: nacelle orientations - nacelle_vel_flat.data(), // input: nacelle velocities - nacelle_acc_flat.data(), // input: nacelle accelerations - root_pos_flat.data(), // input: root positions - root_orient_flat.data(), // input: root orientations - root_vel_flat.data(), // input: root velocities - root_acc_flat.data(), // input: root accelerations - &structural_mesh.n_mesh_points, // input: number of mesh points - mesh_pos_flat.data(), // input: mesh positions - mesh_orient_flat.data(), // input: mesh orientations - mesh_vel_flat.data(), // input: mesh velocities - mesh_acc_flat.data(), // input: mesh accelerations - &error_handling.error_status, // output: error status - error_handling.error_message.data() // output: error message buffer + &turbine_number, // input: current turbine number + hub_pos_flat.data(), // input: hub positions + hub_orient_flat.data(), // input: hub orientations + hub_vel_flat.data(), // input: hub velocities + hub_acc_flat.data(), // input: hub accelerations + nacelle_pos_flat.data(), // input: nacelle positions + nacelle_orient_flat.data(), // input: nacelle orientations + nacelle_vel_flat.data(), // input: nacelle velocities + nacelle_acc_flat.data(), // input: nacelle accelerations + root_pos_flat.data(), // input: root positions + root_orient_flat.data(), // input: root orientations + root_vel_flat.data(), // input: root velocities + root_acc_flat.data(), // input: root accelerations + &structural_mesh_.n_mesh_points, // input: number of mesh points + mesh_pos_flat.data(), // input: mesh positions + mesh_orient_flat.data(), // input: mesh orientations + mesh_vel_flat.data(), // input: mesh velocities + mesh_acc_flat.data(), // input: mesh accelerations + &error_handling_.error_status, // output: error status + error_handling_.error_message.data() // output: error message buffer ); - error_handling.CheckError(); + error_handling_.CheckError(); } // Wrapper for ADI_C_GetRotorLoads routine to get aerodynamic loads on the rotor @@ -674,20 +680,20 @@ struct AeroDynInflowLibrary { int turbine_number, std::vector>& mesh_force_moment ) { auto ADI_C_GetRotorLoads = - this->lib.get_function("ADI_C_GetRotorLoads"); + lib_.get_function("ADI_C_GetRotorLoads"); // Flatten the mesh force/moment array auto mesh_force_moment_flat = FlattenArray(mesh_force_moment); ADI_C_GetRotorLoads( - &turbine_number, // input: current turbine number - &structural_mesh.n_mesh_points, // input: number of mesh points - mesh_force_moment_flat.data(), // output: mesh force/moment array - &error_handling.error_status, // output: error status - error_handling.error_message.data() // output: error message buffer + &turbine_number, // input: current turbine number + &structural_mesh_.n_mesh_points, // input: number of mesh points + mesh_force_moment_flat.data(), // output: mesh force/moment array + &error_handling_.error_status, // output: error status + error_handling_.error_message.data() // output: error message buffer ); - error_handling.CheckError(); + error_handling_.CheckError(); // Copy the flattened array back to the original array for (size_t i = 0; i < mesh_force_moment.size(); ++i) { @@ -700,21 +706,21 @@ struct AeroDynInflowLibrary { // Wrapper for ADI_C_CalcOutput routine to calculate output channels at a given time void CalculateOutputChannels(double time, std::vector& output_channel_values) { auto ADI_C_CalcOutput = - this->lib.get_function("ADI_C_CalcOutput"); + lib_.get_function("ADI_C_CalcOutput"); // Set up output channel values auto output_channel_values_c = - std::vector(static_cast(sim_controls.n_channels)); + std::vector(static_cast(sim_controls_.n_channels)); // Run ADI_C_CalcOutput ADI_C_CalcOutput( - &time, // input: time at which to calculate output forces - output_channel_values_c.data(), // output: output channel values - &error_handling.error_status, // output: error status - error_handling.error_message.data() // output: error message buffer + &time, // input: time at which to calculate output forces + output_channel_values_c.data(), // output: output channel values + &error_handling_.error_status, // output: error status + error_handling_.error_message.data() // output: error message buffer ); - error_handling.CheckError(); + error_handling_.CheckError(); // Copy the output channel values back to the original array output_channel_values = output_channel_values_c; @@ -723,33 +729,45 @@ struct AeroDynInflowLibrary { // Wrapper for ADI_C_UpdateStates routine to calculate output forces at a given time void UpdateStates(double time, double time_next) { auto ADI_C_UpdateStates = - this->lib.get_function("ADI_C_UpdateStates"); + lib_.get_function("ADI_C_UpdateStates"); // Run ADI_C_UpdateStates ADI_C_UpdateStates( - &time, // input: time at which to calculate output forces - &time_next, // input: time T+dt we are stepping to - &error_handling.error_status, // output: error status - error_handling.error_message.data() // output: error message buffer + &time, // input: time at which to calculate output forces + &time_next, // input: time T+dt we are stepping to + &error_handling_.error_status, // output: error status + error_handling_.error_message.data() // output: error message buffer ); - error_handling.CheckError(); + error_handling_.CheckError(); } // Wrapper for ADI_C_End routine to end the AeroDyn Inflow library void Finalize() { - auto ADI_C_End = this->lib.get_function("ADI_C_End"); + auto ADI_C_End = lib_.get_function("ADI_C_End"); // Run ADI_C_End ADI_C_End( - &error_handling.error_status, // output: error status - error_handling.error_message.data() // output: error message buffer + &error_handling_.error_status, // output: error status + error_handling_.error_message.data() // output: error message buffer ); - error_handling.CheckError(); + error_handling_.CheckError(); } private: + util::dylib lib_{ + "libaerodyn_inflow_c_binding.dylib", + util::dylib::no_filename_decorations //< Dynamic library object for AeroDyn Inflow + }; + ErrorHandling error_handling_; //< Error handling settings + FluidProperties air_; //< Properties of the working fluid (air) + EnvironmentalConditions env_conditions_; //< Environmental conditions + TurbineSettings turbine_settings_; //< Turbine settings + StructuralMesh structural_mesh_; //< Structural mesh data + SimulationControls sim_controls_; //< Simulation control settings + VTKSettings vtk_settings_; //< VTK output settings + /// Method to flatten a 2D array into a 1D array for Fortran compatibility template std::vector FlattenArray(const std::vector>& input) { diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 45d2006c..3b2bf145 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -320,13 +320,17 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { util::AeroDynInflowLibrary aerodyn_inflow_library(path); // Check initial error handling state - EXPECT_EQ(aerodyn_inflow_library.error_handling.error_status, 0); - EXPECT_STREQ(aerodyn_inflow_library.error_handling.error_message.data(), ""); + EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); + EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); // Check default values for other important members - EXPECT_EQ(aerodyn_inflow_library.turbine_settings.n_turbines, 1); - EXPECT_EQ(aerodyn_inflow_library.sim_controls.debug_level, 0); - EXPECT_EQ(aerodyn_inflow_library.sim_controls.transpose_DCM, 1); + EXPECT_EQ(aerodyn_inflow_library.GetFluidProperties().density, 1.225f); + EXPECT_EQ(aerodyn_inflow_library.GetEnvironmentalConditions().gravity, 9.80665f); + EXPECT_EQ(aerodyn_inflow_library.GetTurbineSettings().n_turbines, 1); + EXPECT_EQ(aerodyn_inflow_library.GetSimulationControls().debug_level, 0); + EXPECT_EQ(aerodyn_inflow_library.GetSimulationControls().transpose_DCM, 1); + EXPECT_EQ(aerodyn_inflow_library.GetStructuralMesh().n_mesh_points, 1); + EXPECT_EQ(aerodyn_inflow_library.GetVTKSettings().write_vtk, 0); } #endif From 558461f78fae234b0a640f4fbe31f81744314083 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 13:42:37 -0600 Subject: [PATCH 42/87] Shorten the doxygen comment for wrapper intro --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 66 ++++++------------- 1 file changed, 19 insertions(+), 47 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 88d57757..02d881b1 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -11,58 +11,30 @@ namespace openturbine::util { /** - * AeroDynInflowLibrary: A C++ wrapper for the AeroDyn/InflowWind (ADI) shared library + * AeroDynInflowLibrary: C++ wrapper for the AeroDyn/InflowWind (ADI) shared library * - * ## Overview + * Provides a modern C++ interface to AeroDyn x InflowWind functionality, encapsulating + * the C-bindings of the Fortran library. * - * This wrapper simplifies interaction with the ADI library, providing a modern C++ interface for - * OpenTurbine developers to utilize AeroDyn and InflowWind functionality. It encapsulates the - * C-bindings of the Fortran library, drawing inspiration from the existing Python - * interface. + * Key Features: + * - AeroDyn: Blade element momentum (BEM) theory for aerodynamic force calculations + * (dynamic stall, unsteady aerodynamics, tower shadow, wind shear, tip/hub losses) + * - InflowWind: Simulates complex inflow conditions (turbulence, wind shear, gusts) * - * ## Features + * Usage: + * 1. Instantiate AeroDynInflowLibrary with shared library path + * 2. Initialize: PreInitialize() -> SetupRotor() -> Initialize() + * 3. Simulate: SetupRotorMotion() -> UpdateStates() -> CalculateOutputChannels() -> + * GetRotorAerodynamicLoads() + * 4. Finalize() and handle errors * - * AeroDyn utilizes blade element momentum (BEM) theory to calculate aerodynamic forces acting - * on each blade section. It accounts for factors such as: - * - Dynamic stall (Beddoes-Leishman or OLAF models) - * - Unsteady aerodynamics - * - Tower shadow effects - * - Wind shear - * - Tip and hub losses + * See unit tests for detailed usage examples. * - * InflowWind simulates the inflow conditions around wind turbines by modeling spatially and - * temporally varying wind fields. It enables the simulation of complex wind phenomena, including: - * - Atmospheric turbulence (e.g., Kaimal, von Karman spectra) - * - Wind shear (power law or logarithmic profiles) - * - Discrete gusts and extreme events - * - Various wind field types (uniform, full-field turbulence, user-defined) - * - * ## Usage - * - * 1. Instantiate the AeroDynInflowLibrary class with the path to the shared library - * 2. Initialize the AeroDyn/InflowWind modules: - * - PreInitialize(): Set up general parameters - * - SetupRotor(): Configure rotor-specific settings (iterate over turbines) - * - Initialize(): Complete initialization with input files - * 3. Perform the simulation by iterating over timesteps: - * - SetupRotorMotion(): Update rotor motion (iterate over turbines) - * - UpdateStates(): Advance internal states - * - CalculateOutputChannels(): Compute output values - * - GetRotorAerodynamicLoads(): Retrieve aerodynamic forces and moments - * 4. Complete the simulation: - * - Finalize(): Clean up and release resources - * - Handle any resulting errors using the ErrorHandling struct - * - * Note: Refer to the unit tests for more detailed examples of how to use this wrapper. - * - * ## References - * - * - OpenFAST/AeroDyn documentation: - * https://openfast.readthedocs.io/en/main/source/user/aerodyn/index.html - * - OpenFAST/InflowWind documentation: - * https://openfast.readthedocs.io/en/main/source/user/inflowwind/index.html - * - AeroDyn InflowWind C bindings: - * https://github.com/OpenFAST/openfast/blob/dev/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 + * References: + * - OpenFAST/AeroDyn: https://openfast.readthedocs.io/en/main/source/user/aerodyn/index.html + * - OpenFAST/InflowWind: https://openfast.readthedocs.io/en/main/source/user/inflowwind/index.html + * - C bindings: + * https://github.com/OpenFAST/openfast/blob/dev/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 */ /** From c66cc8f92c5c30864a4bcafab8cde20484f0f1b2 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 13:43:49 -0600 Subject: [PATCH 43/87] Refactor MeshMotionData/CheckInputMotions methods --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 61 ++++++------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 02d881b1..2b59328f 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -101,7 +101,7 @@ inline void SetPositionAndOrientation( position[i] = static_cast(data[i]); } - // Set orientation (convert last 4 elements to 3x3 rotation matrix) + // Set orientation (convert last 4 elements i.e. quaternion to 3x3 rotation matrix) auto orientation_2D = QuaternionToRotationMatrix({data[3], data[4], data[5], data[6]}); // Flatten the 3x3 matrix to a 1D array @@ -292,8 +292,9 @@ struct MeshMotionData { } void CheckInputMotions( - const std::string& node_label, size_t expected_position_dim, size_t expected_orientation_dim, - size_t expected_vel_acc_dim, size_t expected_number_of_nodes + const std::string& node_label, size_t expected_number_of_nodes, + size_t expected_position_dim = 3, size_t expected_orientation_dim = 9, + size_t expected_vel_acc_dim = 6 ) const { CheckArraySize( position, expected_number_of_nodes, expected_position_dim, "positions", node_label @@ -310,53 +311,31 @@ struct MeshMotionData { ); } - void CheckHubNacelleInputMotions(const std::string& node_name) const { - const size_t expected_position_dim{3}; - const size_t expected_orientation_dim{9}; - const size_t expected_vel_acc_dim{6}; - const size_t expected_number_of_nodes{1}; // Since there is only 1 hub/nacelle node - - CheckInputMotions( - node_name, expected_position_dim, expected_orientation_dim, expected_vel_acc_dim, - expected_number_of_nodes - ); - } - - void CheckRootInputMotions(size_t num_blades, size_t init_num_blades) const { - if (num_blades != init_num_blades) { + void CheckNodeInputMotions( + const std::string& node_name, size_t expected_number_of_nodes, size_t initial_number_of_nodes + ) const { + if (expected_number_of_nodes != initial_number_of_nodes) { throw std::invalid_argument( - "The number of root points changed from the initial value of " + - std::to_string(init_num_blades) + " to " + std::to_string(num_blades) + + "The number of " + node_name + " points changed from the initial value of " + + std::to_string(initial_number_of_nodes) + " to " + + std::to_string(expected_number_of_nodes) + ". This is not permitted during the simulation." ); } - const size_t expected_position_dim{3}; - const size_t expected_orientation_dim{9}; - const size_t expected_vel_acc_dim{6}; - - CheckInputMotions( - "root", expected_position_dim, expected_orientation_dim, expected_vel_acc_dim, num_blades - ); + CheckInputMotions(node_name, expected_number_of_nodes); } - void CheckMeshInputMotions(size_t num_mesh_pts, size_t init_num_mesh_pts) const { - if (num_mesh_pts != init_num_mesh_pts) { - throw std::invalid_argument( - "The number of mesh points changed from the initial value of " + - std::to_string(init_num_mesh_pts) + " to " + std::to_string(num_mesh_pts) + - ". This is not permitted during the simulation." - ); - } + void CheckHubNacelleInputMotions(const std::string& node_name) const { + CheckInputMotions(node_name, 1); + } - const size_t expected_position_dim{3}; - const size_t expected_orientation_dim{9}; - const size_t expected_vel_acc_dim{6}; + void CheckRootInputMotions(size_t n_blades, size_t init_n_blades) const { + CheckNodeInputMotions("root", n_blades, init_n_blades); + } - CheckInputMotions( - "mesh", expected_position_dim, expected_orientation_dim, expected_vel_acc_dim, - num_mesh_pts - ); + void CheckMeshInputMotions(size_t n_mesh_pts, size_t init_n_mesh_pts) const { + CheckNodeInputMotions("mesh", n_mesh_pts, init_n_mesh_pts); } }; From 8d8d5561a722e5dfbd7522c08cf3bc9d4feef91d Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 13:44:24 -0600 Subject: [PATCH 44/87] Update doxygen comment for AeroDynInflowLibrary class --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 2b59328f..7030e1a4 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -398,15 +398,19 @@ struct VTKSettings { * library, which is a Fortran library offering C bindings for the AeroDyn x InflowWind modules * of OpenFAST. * - * The class encapsulates the following key functions: - * - PreInitialize (ADI_C_PreInit): Pre-initializes the AeroDynInflow module - * - SetupRotor (ADI_C_SetupRotor): Configures rotor-specific parameters before simulation - * - Initialize (ADI_C_Init): Initializes the AeroDynInflow module for simulation - * - SetupRotorMotion (ADI_C_SetRotorMotion): Sets rotor motion for a given time step - * - GetRotorAerodynamicLoads (ADI_C_GetRotorLoads): Retrieves aerodynamic loads on the rotor - * - CalculateOutputChannels (ADI_C_CalcOutput): Calculates output channels at a given time - * - UpdateStates (ADI_C_UpdateStates): Updates states for the next time step - * - Finalize (ADI_C_End): Ends the AeroDynInflow module and frees memory + * The class encapsulates key functions for AeroDyn/InflowWind simulation: + * + * - PreInitialize (ADI_C_PreInit): Set up general parameters + * - SetupRotor (ADI_C_SetupRotor): Configure rotor-specific settings + * - Initialize (ADI_C_Init): Complete initialization with input files + * - SetupRotorMotion (ADI_C_SetRotorMotion): Update rotor motion for each timestep + * - UpdateStates (ADI_C_UpdateStates): Advance internal states + * - CalculateOutputChannels (ADI_C_CalcOutput): Compute output values + * - GetRotorAerodynamicLoads (ADI_C_GetRotorLoads): Retrieve aerodynamic forces and moments + * - Finalize (ADI_C_End): Clean up and release resources + * + * Usage: Instantiate the class, call functions in the order listed above (iterating over + * turbines/timesteps as needed), and handle any errors using the ErrorHandling struct. */ class AeroDynInflowLibrary { public: From 87b42a19a553f420cd358b870d10dafdb2b72e1e Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 13:45:03 -0600 Subject: [PATCH 45/87] Add a d-tor for AeroDynInflowLibrary to handle resource release process automatically --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 7030e1a4..1631220c 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -434,6 +434,17 @@ class AeroDynInflowLibrary { } } + /// Destructor to take care of Fortran-side cleanup if the library is initialized + ~AeroDynInflowLibrary() noexcept { + try { + if (is_initialized_) { + Finalize(); + } + } catch (const std::exception& e) { + std::cerr << "Error during AeroDynInflowLibrary destruction: " << e.what() << std::endl; + } + } + /// Getter methods const ErrorHandling& GetErrorHandling() const { return error_handling_; } const EnvironmentalConditions& GetEnvironmentalConditions() const { return env_conditions_; } From 56ea3de7016f8515cd70f25137580d73fc307c7b Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 13:47:08 -0600 Subject: [PATCH 46/87] Add a flag to keep track if AeroDynInflowLibrary has been initialized --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 1631220c..c4dd9a04 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -504,6 +504,7 @@ class AeroDynInflowLibrary { ); error_handling_.CheckError(); + is_initialized_ = true; } /// Wrapper for ADI_C_Init routine to initialize the AeroDyn Inflow library @@ -722,6 +723,7 @@ class AeroDynInflowLibrary { } private: + bool is_initialized_{false}; //< Flag to check if the library is initialized util::dylib lib_{ "libaerodyn_inflow_c_binding.dylib", util::dylib::no_filename_decorations //< Dynamic library object for AeroDyn Inflow From fd354c792b078bb1f081b692772e2196faf35a9c Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 13:47:40 -0600 Subject: [PATCH 47/87] Refactor the 2D array flattening + validation methods to make things compact --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 183 +++++++++--------- 1 file changed, 89 insertions(+), 94 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index c4dd9a04..df457587 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -454,9 +454,10 @@ class AeroDynInflowLibrary { const SimulationControls& GetSimulationControls() const { return sim_controls_; } const VTKSettings& GetVTKSettings() const { return vtk_settings_; } - /// Wrapper for ADI_C_PreInit routine to initialize AeroDyn Inflow library + /// Wrapper for ADI_C_PreInit routine to pre-initialize AeroDyn Inflow library void PreInitialize() { auto ADI_C_PreInit = lib_.get_function("ADI_C_PreInit"); + ADI_C_PreInit( &turbine_settings_.n_turbines, // input: Number of turbines &sim_controls_.transpose_DCM, // input: Transpose DCM? @@ -476,11 +477,21 @@ class AeroDynInflowLibrary { ); // Flatten arrays to pass to the Fortran routine - auto initial_root_position_flat = FlattenArray(turbine_settings_.initial_root_position); - auto initial_root_orientation_flat = - FlattenArray(turbine_settings_.initial_root_orientation); - auto init_mesh_pos_flat = FlattenArray(structural_mesh_.initial_mesh_position); - auto init_mesh_orient_flat = FlattenArray(structural_mesh_.initial_mesh_orientation); + auto initial_root_position_flat = ValidateAndFlattenArray( + turbine_settings_.initial_root_position, static_cast(turbine_settings_.n_blades) + ); + auto initial_root_orientation_flat = ValidateAndFlattenArray( + turbine_settings_.initial_root_orientation, + static_cast(turbine_settings_.n_blades) + ); + auto init_mesh_pos_flat = ValidateAndFlattenArray( + structural_mesh_.initial_mesh_position, + static_cast(structural_mesh_.n_mesh_points) + ); + auto init_mesh_orient_flat = ValidateAndFlattenArray( + structural_mesh_.initial_mesh_orientation, + static_cast(structural_mesh_.n_mesh_points) + ); ADI_C_SetupRotor( &turbine_number, // input: current turbine number @@ -519,13 +530,6 @@ class AeroDynInflowLibrary { "ADI_C_Init" ); - // Flatten arrays to pass - auto vtk_nacelle_dim_flat = std::array{}; - std::copy( - vtk_settings_.vtk_nacelle_dimensions.begin(), vtk_settings_.vtk_nacelle_dimensions.end(), - vtk_nacelle_dim_flat.begin() - ); - // Primary input files will be passed as a single string joined by C_NULL_CHAR i.e. '\0' std::string aerodyn_input_string = this->JoinStringArray(aerodyn_input_string_array, '\0'); aerodyn_input_string = aerodyn_input_string + '\0'; @@ -537,36 +541,36 @@ class AeroDynInflowLibrary { int inflowwind_input_string_length = static_cast(inflowwind_input_string.size()); ADI_C_Init( - &sim_controls_.aerodyn_input_passed, // input: AD input file is passed - aerodyn_input_string.data(), // input: AD input file as string - &aerodyn_input_string_length, // input: AD input file string length - &sim_controls_.inflowwind_input_passed, // input: IfW input file is passed - inflowwind_input_string.data(), // input: IfW input file as string - &inflowwind_input_string_length, // input: IfW input file string length - sim_controls_.output_root_name.data(), // input: rootname for ADI file writing - &env_conditions_.gravity, // input: gravity - &air_.density, // input: air density - &air_.kinematic_viscosity, // input: kinematic viscosity - &air_.sound_speed, // input: speed of sound - &env_conditions_.atm_pressure, // input: atmospheric pressure - &air_.vapor_pressure, // input: vapor pressure - &env_conditions_.water_depth, // input: water depth - &env_conditions_.msl_offset, // input: MSL to SWL offset - &sim_controls_.interpolation_order, // input: interpolation order - &sim_controls_.time_step, // input: time step - &sim_controls_.max_time, // input: maximum simulation time - &sim_controls_.store_HH_wind_speed, // input: store HH wind speed - &vtk_settings_.write_vtk, // input: write VTK output - &vtk_settings_.vtk_type, // input: VTK output type - vtk_nacelle_dim_flat.data(), // input: VTK nacelle dimensions - &vtk_settings_.vtk_hub_radius, // input: VTK hub radius - &sim_controls_.output_format, // input: output format - &sim_controls_.output_time_step, // input: output time step - &sim_controls_.n_channels, // output: number of channels - sim_controls_.channel_names_c.data(), // output: output channel names - sim_controls_.channel_units_c.data(), // output: output channel units - &error_handling_.error_status, // output: error status - error_handling_.error_message.data() // output: error message buffer + &sim_controls_.aerodyn_input_passed, // input: AD input file is passed + aerodyn_input_string.data(), // input: AD input file as string + &aerodyn_input_string_length, // input: AD input file string length + &sim_controls_.inflowwind_input_passed, // input: IfW input file is passed + inflowwind_input_string.data(), // input: IfW input file as string + &inflowwind_input_string_length, // input: IfW input file string length + sim_controls_.output_root_name.data(), // input: rootname for ADI file writing + &env_conditions_.gravity, // input: gravity + &air_.density, // input: air density + &air_.kinematic_viscosity, // input: kinematic viscosity + &air_.sound_speed, // input: speed of sound + &env_conditions_.atm_pressure, // input: atmospheric pressure + &air_.vapor_pressure, // input: vapor pressure + &env_conditions_.water_depth, // input: water depth + &env_conditions_.msl_offset, // input: MSL to SWL offset + &sim_controls_.interpolation_order, // input: interpolation order + &sim_controls_.time_step, // input: time step + &sim_controls_.max_time, // input: maximum simulation time + &sim_controls_.store_HH_wind_speed, // input: store HH wind speed + &vtk_settings_.write_vtk, // input: write VTK output + &vtk_settings_.vtk_type, // input: VTK output type + vtk_settings_.vtk_nacelle_dimensions.data(), // input: VTK nacelle dimensions + &vtk_settings_.vtk_hub_radius, // input: VTK hub radius + &sim_controls_.output_format, // input: output format + &sim_controls_.output_time_step, // input: output time step + &sim_controls_.n_channels, // output: number of channels + sim_controls_.channel_names_c.data(), // output: output channel names + sim_controls_.channel_units_c.data(), // output: output channel units + &error_handling_.error_status, // output: error status + error_handling_.error_message.data() // output: error message buffer ); error_handling_.CheckError(); @@ -595,26 +599,29 @@ class AeroDynInflowLibrary { static_cast(structural_mesh_.initial_mesh_position.size()) ); - // Flatten the arrays to pass to the Fortran routine - auto hub_pos_flat = FlattenArray(hub_motion.position); - auto hub_orient_flat = FlattenArray(hub_motion.orientation); - auto hub_vel_flat = FlattenArray(hub_motion.velocity); - auto hub_acc_flat = FlattenArray(hub_motion.acceleration); + // Flatten arrays to pass to the Fortran routine + auto flatten_and_validate = [this](const auto& motion, int n_pts) { + return std::make_tuple( + ValidateAndFlattenArray(motion.position, static_cast(n_pts)), + ValidateAndFlattenArray(motion.orientation, static_cast(n_pts)), + ValidateAndFlattenArray(motion.velocity, static_cast(n_pts)), + ValidateAndFlattenArray(motion.acceleration, static_cast(n_pts)) + ); + }; - auto nacelle_pos_flat = FlattenArray(nacelle_motion.position); - auto nacelle_orient_flat = FlattenArray(nacelle_motion.orientation); - auto nacelle_vel_flat = FlattenArray(nacelle_motion.velocity); - auto nacelle_acc_flat = FlattenArray(nacelle_motion.acceleration); + // Hub and nacelle (1 point each) + auto [hub_pos_flat, hub_orient_flat, hub_vel_flat, hub_acc_flat] = + flatten_and_validate(hub_motion, 1); + auto [nacelle_pos_flat, nacelle_orient_flat, nacelle_vel_flat, nacelle_acc_flat] = + flatten_and_validate(nacelle_motion, 1); - auto root_pos_flat = FlattenArray(root_motion.position); - auto root_orient_flat = FlattenArray(root_motion.orientation); - auto root_vel_flat = FlattenArray(root_motion.velocity); - auto root_acc_flat = FlattenArray(root_motion.acceleration); + // Root (n_blades points) + auto [root_pos_flat, root_orient_flat, root_vel_flat, root_acc_flat] = + flatten_and_validate(root_motion, turbine_settings_.n_blades); - auto mesh_pos_flat = FlattenArray(mesh_motion.position); - auto mesh_orient_flat = FlattenArray(mesh_motion.orientation); - auto mesh_vel_flat = FlattenArray(mesh_motion.velocity); - auto mesh_acc_flat = FlattenArray(mesh_motion.acceleration); + // Mesh (n_mesh_points) + auto [mesh_pos_flat, mesh_orient_flat, mesh_vel_flat, mesh_acc_flat] = + flatten_and_validate(mesh_motion, structural_mesh_.n_mesh_points); ADI_C_SetRotorMotion( &turbine_number, // input: current turbine number @@ -679,7 +686,6 @@ class AeroDynInflowLibrary { auto output_channel_values_c = std::vector(static_cast(sim_controls_.n_channels)); - // Run ADI_C_CalcOutput ADI_C_CalcOutput( &time, // input: time at which to calculate output forces output_channel_values_c.data(), // output: output channel values @@ -688,8 +694,6 @@ class AeroDynInflowLibrary { ); error_handling_.CheckError(); - - // Copy the output channel values back to the original array output_channel_values = output_channel_values_c; } @@ -698,7 +702,6 @@ class AeroDynInflowLibrary { auto ADI_C_UpdateStates = lib_.get_function("ADI_C_UpdateStates"); - // Run ADI_C_UpdateStates ADI_C_UpdateStates( &time, // input: time at which to calculate output forces &time_next, // input: time T+dt we are stepping to @@ -709,11 +712,10 @@ class AeroDynInflowLibrary { error_handling_.CheckError(); } - // Wrapper for ADI_C_End routine to end the AeroDyn Inflow library + // Wrapper for ADI_C_End routine to end simulation and free memory void Finalize() { auto ADI_C_End = lib_.get_function("ADI_C_End"); - // Run ADI_C_End ADI_C_End( &error_handling_.error_status, // output: error status error_handling_.error_message.data() // output: error message buffer @@ -740,45 +742,38 @@ class AeroDynInflowLibrary { template std::vector FlattenArray(const std::vector>& input) { std::vector output; + output.reserve(input.size() * N); for (const auto& arr : input) { output.insert(output.end(), arr.begin(), arr.end()); } return output; } - /// Template method to validate array size and flatten it + /// Method to validate a 2D array for the correct number of points and then flatten it to 1D template std::vector ValidateAndFlattenArray( - const std::vector>& array, size_t num_pts, const std::string& array_name + const std::vector>& array, size_t expected_size ) { - if (array.size() != num_pts) { - std::cerr << "The number of mesh points in the " << array_name - << " array changed from the initial value of " << num_pts - << ". This is not permitted during the simulation." << std::endl; - // call ADI_End(); + std::string array_name; + if constexpr (std::is_same_v && N == 3) { + array_name = "position"; + } else if constexpr (std::is_same_v && N == 9) { + array_name = "orientation"; + } else if constexpr (std::is_same_v && N == 6) { + array_name = "velocity/acceleration"; + } else { + array_name = "unknown"; } - return FlattenArray(array); - } - /// Flatten and validate position array - std::vector FlattenPositionArray( - const std::vector>& position_array, size_t num_pts - ) { - return ValidateAndFlattenArray(position_array, num_pts, "position"); - } - - /// Flatten and validate orientation array - std::vector FlattenOrientationArray( - const std::vector>& orientation_array, size_t num_pts - ) { - return ValidateAndFlattenArray(orientation_array, num_pts, "orientation"); - } - - /// Flatten and validate velocity array - std::vector FlattenVelocityArray( - const std::vector>& velocity_array, size_t num_pts - ) { - return ValidateAndFlattenArray(velocity_array, num_pts, "velocity"); + if (array.size() != expected_size) { + throw std::runtime_error( + "The number of mesh points in the " + array_name + + " array changed from the initial value of " + std::to_string(expected_size) + + " to " + std::to_string(array.size()) + + ". This is not permitted during the simulation." + ); + } + return FlattenArray(array); } /// Method to join a vector of strings into a single string with a delimiter From 472edab013dddc4f61a7d9040e50f8eb7a0e51e9 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 15:05:21 -0600 Subject: [PATCH 48/87] Convert int -> bool for clarity where appropriate --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 121 ++++++++++++++---- 1 file changed, 98 insertions(+), 23 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index df457587..5e7b5f82 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -349,8 +349,8 @@ struct SimulationControls { static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames // Input file handling - int aerodyn_input_passed{1}; //< Input file passed for AeroDyn module? (1: passed) - int inflowwind_input_passed{1}; //< Input file passed for InflowWind module (1: passed) + bool aerodyn_input_passed{true}; //< Input file passed for AeroDyn module? (1: passed) + bool inflowwind_input_passed{true}; //< Input file passed for InflowWind module? (1: passed) // Interpolation order (must be either 1: linear, or 2: quadratic) int interpolation_order{1}; //< Interpolation order - linear by default @@ -362,9 +362,9 @@ struct SimulationControls { int n_time_steps{0}; //< Number of time steps // Flags - int store_HH_wind_speed{1}; //< Flag to store HH wind speed - int transpose_DCM{1}; //< Flag to transpose the direction cosine matrix - int debug_level{0}; //< Debug level (0-4) + bool store_HH_wind_speed{true}; //< Flag to store HH wind speed + bool transpose_DCM{true}; //< Flag to transpose the direction cosine matrix + int debug_level{0}; //< Debug level (0-4) // Outputs int output_format{0}; //< File format for writing outputs @@ -384,7 +384,7 @@ struct SimulationControls { * output, the type of VTK output, and the nacelle dimensions for VTK rendering. */ struct VTKSettings { - int write_vtk{false}; //< Flag to write VTK output + bool write_vtk{false}; //< Flag to write VTK output int vtk_type{1}; //< Type of VTK output (1: surface meshes) std::array vtk_nacelle_dimensions{//< Nacelle dimensions for VTK rendering -2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}; @@ -445,7 +445,7 @@ class AeroDynInflowLibrary { } } - /// Getter methods + /// Getter methods for the private member variables const ErrorHandling& GetErrorHandling() const { return error_handling_; } const EnvironmentalConditions& GetEnvironmentalConditions() const { return env_conditions_; } const FluidProperties& GetFluidProperties() const { return air_; } @@ -454,13 +454,21 @@ class AeroDynInflowLibrary { const SimulationControls& GetSimulationControls() const { return sim_controls_; } const VTKSettings& GetVTKSettings() const { return vtk_settings_; } - /// Wrapper for ADI_C_PreInit routine to pre-initialize AeroDyn Inflow library + /** + * @brief Pre-initializes the AeroDyn Inflow library + * + * @details This function pre-initializes the AeroDyn Inflow library by setting up the number + * of turbines, the number of blades, and the transpose DCM flag. + */ void PreInitialize() { auto ADI_C_PreInit = lib_.get_function("ADI_C_PreInit"); + // Convert bool -> int to pass to Fortran + int transpose_DCM_int = sim_controls_.transpose_DCM ? 1 : 0; + ADI_C_PreInit( &turbine_settings_.n_turbines, // input: Number of turbines - &sim_controls_.transpose_DCM, // input: Transpose DCM? + &transpose_DCM_int, // input: Transpose DCM? &sim_controls_.debug_level, // input: Debug level &error_handling_.error_status, // output: Error status error_handling_.error_message.data() // output: Error message @@ -469,8 +477,19 @@ class AeroDynInflowLibrary { error_handling_.CheckError(); } - /// Wrapper for ADI_C_SetupRotor routine to set up the rotor - void SetupRotor(int turbine_number, int is_horizontal_axis, std::vector turbine_ref_pos) { + /** + * @brief Sets up the rotor for the AeroDyn Inflow library + * + * @details This function sets up the rotor for the AeroDyn Inflow library by initializing the + * rotor motion data and passing it to the Fortran routine. + * + * @param turbine_number Number of the current turbine + * @param is_horizontal_axis Flag to indicate if the turbine is a horizontal axis turbine + * @param turbine_ref_pos Reference position of the turbine + */ + void SetupRotor( + int turbine_number, bool is_horizontal_axis, std::vector turbine_ref_pos + ) { auto ADI_C_SetupRotor = lib_.get_function< void(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, int*, float*, double*, int*, int*, char*)>( "ADI_C_SetupRotor" @@ -493,9 +512,12 @@ class AeroDynInflowLibrary { static_cast(structural_mesh_.n_mesh_points) ); + // Convert bool -> int to pass to Fortran + int is_horizontal_axis_int = is_horizontal_axis ? 1 : 0; + ADI_C_SetupRotor( &turbine_number, // input: current turbine number - &is_horizontal_axis, // input: 1: HAWT, 0: VAWT or cross-flow + &is_horizontal_axis_int, // input: 1: HAWT, 0: VAWT or cross-flow turbine_ref_pos.data(), // input: turbine reference position turbine_settings_.initial_hub_position.data(), // input: initial hub position turbine_settings_.initial_hub_orientation.data(), // input: initial hub orientation @@ -518,7 +540,15 @@ class AeroDynInflowLibrary { is_initialized_ = true; } - /// Wrapper for ADI_C_Init routine to initialize the AeroDyn Inflow library + /** + * @brief Initializes the AeroDyn Inflow library + * + * @details This function initializes the AeroDyn Inflow library by passing the input files and + * other parameters to the Fortran routine. + * + * @param aerodyn_input_string_array Input file for the AeroDyn module + * @param inflowwind_input_string_array Input file for the InflowWind module + */ void Initialize( std::vector aerodyn_input_string_array, std::vector inflowwind_input_string_array @@ -540,11 +570,17 @@ class AeroDynInflowLibrary { inflowwind_input_string = inflowwind_input_string + '\0'; int inflowwind_input_string_length = static_cast(inflowwind_input_string.size()); + // Convert bool -> int to pass to Fortran + int aerodyn_input_passed_int = sim_controls_.aerodyn_input_passed ? 1 : 0; + int inflowwind_input_passed_int = sim_controls_.inflowwind_input_passed ? 1 : 0; + int store_HH_wind_speed_int = sim_controls_.store_HH_wind_speed ? 1 : 0; + int write_vtk_int = vtk_settings_.write_vtk ? 1 : 0; + ADI_C_Init( - &sim_controls_.aerodyn_input_passed, // input: AD input file is passed + &aerodyn_input_passed_int, // input: AD input file is passed? aerodyn_input_string.data(), // input: AD input file as string &aerodyn_input_string_length, // input: AD input file string length - &sim_controls_.inflowwind_input_passed, // input: IfW input file is passed + &inflowwind_input_passed_int, // input: IfW input file is passed? inflowwind_input_string.data(), // input: IfW input file as string &inflowwind_input_string_length, // input: IfW input file string length sim_controls_.output_root_name.data(), // input: rootname for ADI file writing @@ -559,8 +595,8 @@ class AeroDynInflowLibrary { &sim_controls_.interpolation_order, // input: interpolation order &sim_controls_.time_step, // input: time step &sim_controls_.max_time, // input: maximum simulation time - &sim_controls_.store_HH_wind_speed, // input: store HH wind speed - &vtk_settings_.write_vtk, // input: write VTK output + &store_HH_wind_speed_int, // input: store HH wind speed? + &write_vtk_int, // input: write VTK output? &vtk_settings_.vtk_type, // input: VTK output type vtk_settings_.vtk_nacelle_dimensions.data(), // input: VTK nacelle dimensions &vtk_settings_.vtk_hub_radius, // input: VTK hub radius @@ -576,8 +612,18 @@ class AeroDynInflowLibrary { error_handling_.CheckError(); } - // Wrapper for ADI_C_SetRotorMotion routine to set rotor motion i.e. motion of the hub, - // nacelle, root, and mesh points from the structural mesh + /** + * @brief Sets up the rotor motion for the AeroDyn Inflow library + * + * @details This function sets up the rotor motion for the AeroDyn Inflow library by passing + * the motion data for the hub, nacelle, root, and mesh points to the Fortran routine. + * + * @param turbine_number Number of the current turbine + * @param hub_motion Motion data for the hub + * @param nacelle_motion Motion data for the nacelle + * @param root_motion Motion data for the blade roots + * @param mesh_motion Motion data for the mesh points + */ void SetupRotorMotion( int turbine_number, MeshMotionData hub_motion, MeshMotionData nacelle_motion, MeshMotionData root_motion, MeshMotionData mesh_motion @@ -649,7 +695,15 @@ class AeroDynInflowLibrary { error_handling_.CheckError(); } - // Wrapper for ADI_C_GetRotorLoads routine to get aerodynamic loads on the rotor + /** + * @brief Gets the aerodynamic loads on the rotor + * + * @details This function gets the aerodynamic loads on the rotor by passing the mesh + * force/moment array to the Fortran routine. + * + * @param turbine_number Number of the current turbine + * @param mesh_force_moment Mesh force/moment array + */ void GetRotorAerodynamicLoads( int turbine_number, std::vector>& mesh_force_moment ) { @@ -677,7 +731,15 @@ class AeroDynInflowLibrary { } } - // Wrapper for ADI_C_CalcOutput routine to calculate output channels at a given time + /** + * @brief Calculates the output channels at a given time + * + * @details This function calculates the output channels at a given time by passing the time + * and the output channel values to the Fortran routine. + * + * @param time Time at which to calculate the output channels + * @param output_channel_values Output channel values + */ void CalculateOutputChannels(double time, std::vector& output_channel_values) { auto ADI_C_CalcOutput = lib_.get_function("ADI_C_CalcOutput"); @@ -697,7 +759,15 @@ class AeroDynInflowLibrary { output_channel_values = output_channel_values_c; } - // Wrapper for ADI_C_UpdateStates routine to calculate output forces at a given time + /** + * @brief Updates the states of the AeroDyn Inflow library + * + * @details This function updates the states of the AeroDyn Inflow library by passing the current + * time and the next time to the Fortran routine. + * + * @param time Current time + * @param time_next Next time + */ void UpdateStates(double time, double time_next) { auto ADI_C_UpdateStates = lib_.get_function("ADI_C_UpdateStates"); @@ -712,7 +782,12 @@ class AeroDynInflowLibrary { error_handling_.CheckError(); } - // Wrapper for ADI_C_End routine to end simulation and free memory + /** + * @brief Ends the simulation and frees memory + * + * @details This function ends the simulation and frees memory by passing the error status + * and error message buffer to the Fortran routine. + */ void Finalize() { auto ADI_C_End = lib_.get_function("ADI_C_End"); From d3a07f717053ca09844f794239ae365942d95cad Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 15:14:35 -0600 Subject: [PATCH 49/87] Fix turbine ref position type --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 5e7b5f82..6f8e28e7 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -463,7 +463,7 @@ class AeroDynInflowLibrary { void PreInitialize() { auto ADI_C_PreInit = lib_.get_function("ADI_C_PreInit"); - // Convert bool -> int to pass to Fortran + // Convert bool -> int to pass to the Fortran routine int transpose_DCM_int = sim_controls_.transpose_DCM ? 1 : 0; ADI_C_PreInit( @@ -488,7 +488,7 @@ class AeroDynInflowLibrary { * @param turbine_ref_pos Reference position of the turbine */ void SetupRotor( - int turbine_number, bool is_horizontal_axis, std::vector turbine_ref_pos + int turbine_number, bool is_horizontal_axis, std::array turbine_ref_pos ) { auto ADI_C_SetupRotor = lib_.get_function< void(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, int*, float*, double*, int*, int*, char*)>( @@ -512,7 +512,7 @@ class AeroDynInflowLibrary { static_cast(structural_mesh_.n_mesh_points) ); - // Convert bool -> int to pass to Fortran + // Convert bool -> int to pass to the Fortran routine int is_horizontal_axis_int = is_horizontal_axis ? 1 : 0; ADI_C_SetupRotor( @@ -570,7 +570,7 @@ class AeroDynInflowLibrary { inflowwind_input_string = inflowwind_input_string + '\0'; int inflowwind_input_string_length = static_cast(inflowwind_input_string.size()); - // Convert bool -> int to pass to Fortran + // Convert bool -> int to pass to the Fortran routine int aerodyn_input_passed_int = sim_controls_.aerodyn_input_passed ? 1 : 0; int inflowwind_input_passed_int = sim_controls_.inflowwind_input_passed ? 1 : 0; int store_HH_wind_speed_int = sim_controls_.store_HH_wind_speed ? 1 : 0; From e6747dea52b1e5215a9e23fadbaa9f32d5b974db Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 15:33:47 -0600 Subject: [PATCH 50/87] Add Validate() method to TurbineSettings and StructuralMesh structs to validate the array sizes --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 6f8e28e7..7b9d633c 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -169,6 +169,71 @@ struct TurbineSettings { ); } }; + + /** + * @brief Validates the turbine settings + * + * @details This method validates the turbine settings, including the number of blades, the + * initial root positions, and the initial root orientations. + */ + void Validate() const { + if (n_blades < 1) { + throw std::runtime_error("No blades. Set n_blades to number of AD blades in the model"); + } + + if (initial_root_position.size() != static_cast(n_blades)) { + throw std::invalid_argument("Number of blade root positions must match n_blades"); + } + + if (initial_root_orientation.size() != static_cast(n_blades)) { + throw std::invalid_argument("Number of blade root orientations must match n_blades"); + } + + for (const auto& pos : initial_root_position) { + if (pos.size() != 3) { + throw std::invalid_argument( + "Expecting a 3 element array for blade root positions (initial_root_position) " + "with second index for [x,y,z]" + ); + } + } + + for (const auto& orient : initial_root_orientation) { + if (orient.size() != 9) { + throw std::invalid_argument( + "Expecting a 9 element array for blade root orientations as DCMs " + "(initial_root_orientation) with second index for " + "[r11,r12,r13,r21,r22,r23,r31,r32,r33]" + ); + } + } + + if (initial_hub_position.size() != 3) { + throw std::invalid_argument( + "Expecting a 3 element array for initial_hub_position [x,y,z]" + ); + } + + if (initial_hub_orientation.size() != 9) { + throw std::invalid_argument( + "Expecting a 9 element array for initial_hub_orientation DCM " + "[r11,r12,r13,r21,r22,r23,r31,r32,r33]" + ); + } + + if (initial_nacelle_position.size() != 3) { + throw std::invalid_argument( + "Expecting a 3 element array for initial_nacelle_position [x,y,z]" + ); + } + + if (initial_nacelle_orientation.size() != 9) { + throw std::invalid_argument( + "Expecting a 9 element array for initial_nacelle_orientation DCM " + "[r11,r12,r13,r21,r22,r23,r31,r32,r33]" + ); + } + } }; /** @@ -218,6 +283,54 @@ struct StructuralMesh { ); } } + + /** + * @brief Validates the structural mesh + * + * @details This method validates the structural mesh, including the number of mesh points, the + * initial mesh position, the initial mesh orientation, and the mapping of mesh points to blade + * numbers. + */ + void Validate() const { + if (initial_mesh_position.size() != static_cast(n_mesh_points)) { + throw std::invalid_argument("Number of mesh positions must match n_mesh_points"); + } + + if (initial_mesh_orientation.size() != static_cast(n_mesh_points)) { + throw std::invalid_argument("Number of mesh orientations must match n_mesh_points"); + } + + if (initial_mesh_position.size() != initial_mesh_orientation.size()) { + throw std::invalid_argument( + "Different number of meshes in initial position and orientation arrays" + ); + } + + for (const auto& pos : initial_mesh_position) { + if (pos.size() != 3) { + throw std::invalid_argument( + "Expecting a Nx3 array of initial mesh positions (initial_mesh_position) with " + "second index for [x,y,z]" + ); + } + } + + for (const auto& orient : initial_mesh_orientation) { + if (orient.size() != 9) { + throw std::invalid_argument( + "Expecting a Nx9 array of initial mesh orientations as DCMs " + "(initial_mesh_orientation) with second index for " + "[r11,r12,r13,r21,r22,r23,r31,r32,r33]" + ); + } + } + + if (mesh_point_to_blade_num.size() != static_cast(n_mesh_points)) { + throw std::invalid_argument( + "Number of mesh point to blade number mappings must match n_mesh_points" + ); + } + } }; /** @@ -495,6 +608,10 @@ class AeroDynInflowLibrary { "ADI_C_SetupRotor" ); + // Validate the turbine settings and structural mesh + turbine_settings_.Validate(); + structural_mesh_.Validate(); + // Flatten arrays to pass to the Fortran routine auto initial_root_position_flat = ValidateAndFlattenArray( turbine_settings_.initial_root_position, static_cast(turbine_settings_.n_blades) From d7e287424e14fff77788ba40d7d242680a01c8e7 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 16:08:13 -0600 Subject: [PATCH 51/87] Remove redundant checks from Validate() methods + add unit tests --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 85 ++------- .../regression/test_aerodyn_inflow.cpp | 165 +++++++++++++++++- 2 files changed, 175 insertions(+), 75 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 7b9d633c..d3c8a1aa 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -188,51 +188,6 @@ struct TurbineSettings { if (initial_root_orientation.size() != static_cast(n_blades)) { throw std::invalid_argument("Number of blade root orientations must match n_blades"); } - - for (const auto& pos : initial_root_position) { - if (pos.size() != 3) { - throw std::invalid_argument( - "Expecting a 3 element array for blade root positions (initial_root_position) " - "with second index for [x,y,z]" - ); - } - } - - for (const auto& orient : initial_root_orientation) { - if (orient.size() != 9) { - throw std::invalid_argument( - "Expecting a 9 element array for blade root orientations as DCMs " - "(initial_root_orientation) with second index for " - "[r11,r12,r13,r21,r22,r23,r31,r32,r33]" - ); - } - } - - if (initial_hub_position.size() != 3) { - throw std::invalid_argument( - "Expecting a 3 element array for initial_hub_position [x,y,z]" - ); - } - - if (initial_hub_orientation.size() != 9) { - throw std::invalid_argument( - "Expecting a 9 element array for initial_hub_orientation DCM " - "[r11,r12,r13,r21,r22,r23,r31,r32,r33]" - ); - } - - if (initial_nacelle_position.size() != 3) { - throw std::invalid_argument( - "Expecting a 3 element array for initial_nacelle_position [x,y,z]" - ); - } - - if (initial_nacelle_orientation.size() != 9) { - throw std::invalid_argument( - "Expecting a 9 element array for initial_nacelle_orientation DCM " - "[r11,r12,r13,r21,r22,r23,r31,r32,r33]" - ); - } } }; @@ -305,31 +260,6 @@ struct StructuralMesh { "Different number of meshes in initial position and orientation arrays" ); } - - for (const auto& pos : initial_mesh_position) { - if (pos.size() != 3) { - throw std::invalid_argument( - "Expecting a Nx3 array of initial mesh positions (initial_mesh_position) with " - "second index for [x,y,z]" - ); - } - } - - for (const auto& orient : initial_mesh_orientation) { - if (orient.size() != 9) { - throw std::invalid_argument( - "Expecting a Nx9 array of initial mesh orientations as DCMs " - "(initial_mesh_orientation) with second index for " - "[r11,r12,r13,r21,r22,r23,r31,r32,r33]" - ); - } - } - - if (mesh_point_to_blade_num.size() != static_cast(n_mesh_points)) { - throw std::invalid_argument( - "Number of mesh point to blade number mappings must match n_mesh_points" - ); - } } }; @@ -970,11 +900,18 @@ class AeroDynInflowLibrary { /// Method to join a vector of strings into a single string with a delimiter std::string JoinStringArray(const std::vector& input, char delimiter) { - std::string output; - for (const auto& str : input) { - output += str + delimiter; + if (input.empty()) { + return ""; } - return output; + + std::ostringstream result; + std::copy( + input.begin(), input.end() - 1, + std::ostream_iterator(result, std::string(1, delimiter).c_str()) + ); + result << input.back(); + + return result.str(); } }; diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 3b2bf145..126fe4c3 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -16,7 +16,7 @@ TEST(AerodynInflowTest, ErrorHandling_NoThrow) { TEST(AerodynInflowTest, ErrorHandling_Throw) { util::ErrorHandling error_handling; - error_handling.error_status = 1; + error_handling.error_status = 1; // Set error status to 1 to trigger an error EXPECT_THROW(error_handling.CheckError(), std::runtime_error); } @@ -126,6 +126,45 @@ TEST(AerodynInflowTest, TurbineSettings_Set_1T1B) { ); } +TEST(AerodynInflowTest, TurbineSettings_Validate_ExpectNoThrow) { + util::TurbineSettings turbine_settings; + turbine_settings.n_blades = 3; + turbine_settings.initial_root_position = {{0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}}; + turbine_settings.initial_root_orientation = { + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; + + EXPECT_NO_THROW(turbine_settings.Validate()); +} + +TEST(AerodynInflowTest, TurbineSettings_Validate_InvalidBlades) { + util::TurbineSettings turbine_settings; + turbine_settings.n_blades = 0; // Invalid number of blades + + EXPECT_THROW(turbine_settings.Validate(), std::runtime_error); +} + +TEST(AerodynInflowTest, TurbineSettings_Validate_InvalidRootPositionSize) { + util::TurbineSettings turbine_settings; + turbine_settings.n_blades = 3; + turbine_settings.initial_root_position = {{0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}}; // Only 2 positions + + EXPECT_THROW(turbine_settings.Validate(), std::invalid_argument); +} + +TEST(AerodynInflowTest, TurbineSettings_Validate_InvalidRootOrientationSize) { + util::TurbineSettings turbine_settings; + turbine_settings.n_blades = 3; + turbine_settings.initial_root_position = {{0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}}; + turbine_settings.initial_root_orientation = { + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.} // Only 2 orientations + }; + + EXPECT_THROW(turbine_settings.Validate(), std::invalid_argument); +} + TEST(AerodynInflowTest, StructuralMesh_Default) { util::StructuralMesh structural_mesh; EXPECT_EQ(structural_mesh.n_mesh_points, 1); @@ -155,6 +194,54 @@ TEST(AerodynInflowTest, StructuralMesh_Set) { EXPECT_EQ(structural_mesh.mesh_point_to_blade_num[0], 1); } +TEST(AerodynInflowTest, StructuralMesh_Validate_ExpectNoThrow) { + util::StructuralMesh structural_mesh; + structural_mesh.n_mesh_points = 3; + structural_mesh.initial_mesh_position = {{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {2.f, 2.f, 2.f}}; + structural_mesh.initial_mesh_orientation = { + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; + + EXPECT_NO_THROW(structural_mesh.Validate()); +} + +TEST(AerodynInflowTest, StructuralMesh_Validate_InvalidMeshPositionSize) { + util::StructuralMesh structural_mesh; + structural_mesh.n_mesh_points = 3; + structural_mesh.initial_mesh_position = {{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}; // Only 2 positions + structural_mesh.initial_mesh_orientation = { + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; + + EXPECT_THROW(structural_mesh.Validate(), std::invalid_argument); +} + +TEST(AerodynInflowTest, StructuralMesh_Validate_InvalidMeshOrientationSize) { + util::StructuralMesh structural_mesh; + structural_mesh.n_mesh_points = 3; + structural_mesh.initial_mesh_position = {{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {2.f, 2.f, 2.f}}; + structural_mesh.initial_mesh_orientation = { + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.} // Only 2 orientations + }; + + EXPECT_THROW(structural_mesh.Validate(), std::invalid_argument); +} + +TEST(AerodynInflowTest, StructuralMesh_Validate_MismatchedSizes) { + util::StructuralMesh structural_mesh; + structural_mesh.n_mesh_points = 3; + structural_mesh.initial_mesh_position = {{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {2.f, 2.f, 2.f}}; + structural_mesh.initial_mesh_orientation = { + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.} // Only 2 orientations + }; + + EXPECT_THROW(structural_mesh.Validate(), std::invalid_argument); +} + TEST(AerodynInflowTest, MeshMotionData_Default) { util::MeshMotionData mesh_motion_data; EXPECT_EQ(mesh_motion_data.position.size(), 0); @@ -333,6 +420,82 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { EXPECT_EQ(aerodyn_inflow_library.GetVTKSettings().write_vtk, 0); } +TEST(AerodynInflowTest, AeroDynInflowLibrary_PreInitialize) { + // Load the shared library + const std::string path = GetSharedLibraryPath(); + util::AeroDynInflowLibrary aerodyn_inflow_library(path); + + // Check initial error handling state + EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); + EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); + + // Call PreInitialize + aerodyn_inflow_library.PreInitialize(); + + // Check error handling state after PreInitialize + EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); + EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); +} + +// Write test based on py_ad_driver.py to complete a full loop of initialization, simulation, and +// cleanup +TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { + // Set up simulation parameters + util::SimulationControls sim_controls; + sim_controls.time_step = 0.0125; + sim_controls.max_time = 10.0; + + // Set up environmental conditions + util::EnvironmentalConditions env_conditions; + env_conditions.gravity = 9.80665f; + env_conditions.atm_pressure = 103500.0f; + + // Set up fluid properties + util::FluidProperties fluid_props; + fluid_props.density = 1.225f; + fluid_props.kinematic_viscosity = 1.464E-05f; + fluid_props.sound_speed = 335.0f; + fluid_props.vapor_pressure = 1700.0f; + + // Set up turbine settings + util::TurbineSettings turbine_settings; + turbine_settings.n_turbines = 1; + turbine_settings.n_blades = 3; + turbine_settings.initial_hub_position = {0.0f, 0.0f, 90.0f}; + turbine_settings.initial_hub_orientation = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}; + turbine_settings.initial_nacelle_position = {0.0f, 0.0f, 90.0f}; + turbine_settings.initial_nacelle_orientation = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}; + std::vector> root_positions = { + {0.0f, 0.0f, 90.0f}, {0.0f, 0.0f, 90.0f}, {0.0f, 0.0f, 90.0f}}; + turbine_settings.initial_root_position = root_positions; + std::vector> root_orientations( + 3, {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0} + ); + turbine_settings.initial_root_orientation = root_orientations; + + // Set up structural mesh + // Assuming 5 mesh points per blade + /* std::vector> mesh_positions(15); + std::vector> mesh_orientations( + 15, {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0} + ); + std::vector mesh_point_to_blade_num(15); + + for (int i = 0; i < 15; ++i) { + mesh_positions[static_cast(i)] = {0.0f, 0.0f, 90.0f + i * 2.0f}; + mesh_point_to_blade_num[static_cast(i)] = i / 5 + 1; + } + + util::StructuralMesh structural_mesh(mesh_positions, mesh_point_to_blade_num, 15); + + // Load the shared library + const std::string path = GetSharedLibraryPath(); + util::AeroDynInflowLibrary aerodyn_inflow_library( + path, util::ErrorHandling{}, fluid_props, env_conditions, turbine_settings, + util::StructuralMesh{}, sim_controls, util::VTKSettings{} + ); */ +} + #endif } // namespace openturbine::tests \ No newline at end of file From 3685e8bbcf6cb718607d686756479359fa052d4d Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 16:23:33 -0600 Subject: [PATCH 52/87] Add unit tests for FlattenArray and ValidateAndFlattenArray methods --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 28 +++---- .../regression/test_aerodyn_inflow.cpp | 74 +++++++++++++++++++ 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index d3c8a1aa..af5484f2 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -846,20 +846,6 @@ class AeroDynInflowLibrary { error_handling_.CheckError(); } -private: - bool is_initialized_{false}; //< Flag to check if the library is initialized - util::dylib lib_{ - "libaerodyn_inflow_c_binding.dylib", - util::dylib::no_filename_decorations //< Dynamic library object for AeroDyn Inflow - }; - ErrorHandling error_handling_; //< Error handling settings - FluidProperties air_; //< Properties of the working fluid (air) - EnvironmentalConditions env_conditions_; //< Environmental conditions - TurbineSettings turbine_settings_; //< Turbine settings - StructuralMesh structural_mesh_; //< Structural mesh data - SimulationControls sim_controls_; //< Simulation control settings - VTKSettings vtk_settings_; //< VTK output settings - /// Method to flatten a 2D array into a 1D array for Fortran compatibility template std::vector FlattenArray(const std::vector>& input) { @@ -913,6 +899,20 @@ class AeroDynInflowLibrary { return result.str(); } + +private: + bool is_initialized_{false}; //< Flag to check if the library is initialized + util::dylib lib_{ + "libaerodyn_inflow_c_binding.dylib", + util::dylib::no_filename_decorations //< Dynamic library object for AeroDyn Inflow + }; + ErrorHandling error_handling_; //< Error handling settings + FluidProperties air_; //< Properties of the working fluid (air) + EnvironmentalConditions env_conditions_; //< Environmental conditions + TurbineSettings turbine_settings_; //< Turbine settings + StructuralMesh structural_mesh_; //< Structural mesh data + SimulationControls sim_controls_; //< Simulation control settings + VTKSettings vtk_settings_; //< VTK output settings }; } // namespace openturbine::util diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 126fe4c3..d643f0ed 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -437,6 +437,80 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_PreInitialize) { EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); } +TEST(AerodynInflowTest, AeroDynInflowLibrary_FlattenArray) { + // Load the shared library + const std::string path = GetSharedLibraryPath(); + util::AeroDynInflowLibrary aerodyn_inflow_library(path); + + std::vector> input = {{1.0f, 2.0f, 3.0f}, {4.0f, 5.0f, 6.0f}}; + std::vector expected = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; + + auto result = aerodyn_inflow_library.FlattenArray(input); + + ASSERT_EQ(result.size(), expected.size()); + for (size_t i = 0; i < expected.size(); ++i) { + EXPECT_FLOAT_EQ(result[i], expected[i]); + } +} + +class AerodynInflowValidateAndFlattenArrayTest : public ::testing::Test { +protected: + util::AeroDynInflowLibrary aerodyn_inflow_library; + + AerodynInflowValidateAndFlattenArrayTest() : aerodyn_inflow_library(GetSharedLibraryPath()) {} +}; + +TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidPositionArray) { + std::vector> position_array = {{1.0f, 2.0f, 3.0f}, {4.0f, 5.0f, 6.0f}}; + size_t expected_size = 2; + std::vector expected = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; + + auto result = aerodyn_inflow_library.ValidateAndFlattenArray(position_array, expected_size); + ASSERT_EQ(result, expected); +} + +TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidOrientationArray) { + std::vector> orientation_array = { + {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, + {10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0}}; + size_t expected_size = 2; + std::vector expected = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, + 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0}; + + auto result = aerodyn_inflow_library.ValidateAndFlattenArray(orientation_array, expected_size); + ASSERT_EQ(result, expected); +} + +TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidVelocityAccelerationArray) { + std::vector> velocity_array = { + {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}, {7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f}}; + size_t expected_size = 2; + std::vector expected = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, + 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f}; + + auto result = aerodyn_inflow_library.ValidateAndFlattenArray(velocity_array, expected_size); + ASSERT_EQ(result, expected); +} + +TEST_F(AerodynInflowValidateAndFlattenArrayTest, InvalidArraySize) { + std::vector> position_array = {{1.0f, 2.0f, 3.0f}, {4.0f, 5.0f, 6.0f}}; + size_t expected_size = 3; // Incorrect size + + EXPECT_THROW( + { aerodyn_inflow_library.ValidateAndFlattenArray(position_array, expected_size); }, + std::runtime_error + ); +} + +TEST_F(AerodynInflowValidateAndFlattenArrayTest, UnknownArrayType) { + std::vector> unknown_array = {{1, 2}, {3, 4}}; + size_t expected_size = 2; + + auto result = aerodyn_inflow_library.ValidateAndFlattenArray(unknown_array, expected_size); + std::vector expected = {1, 2, 3, 4}; + ASSERT_EQ(result, expected); +} + // Write test based on py_ad_driver.py to complete a full loop of initialization, simulation, and // cleanup TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { From ac8cca5bbd71c4cd9c5917b2d67125f40456f117 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 27 Sep 2024 16:27:30 -0600 Subject: [PATCH 53/87] Add unit tests for JoinStringArray method --- .../regression/test_aerodyn_inflow.cpp | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index d643f0ed..15c11ca2 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -511,6 +511,43 @@ TEST_F(AerodynInflowValidateAndFlattenArrayTest, UnknownArrayType) { ASSERT_EQ(result, expected); } +class AerodynInflowJoinStringArrayTest : public ::testing::Test { +protected: + util::AeroDynInflowLibrary aerodyn_inflow_library; + + AerodynInflowJoinStringArrayTest() : aerodyn_inflow_library(GetSharedLibraryPath()) {} +}; + +TEST_F(AerodynInflowJoinStringArrayTest, NormalCase) { + std::vector input = {"apple", "banana", "cherry"}; + std::string expected = "apple,banana,cherry"; + EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, ','), expected); +} + +TEST_F(AerodynInflowJoinStringArrayTest, EmptyInput) { + std::vector input = {}; + std::string expected = ""; + EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, ','), expected); +} + +TEST_F(AerodynInflowJoinStringArrayTest, SingleElement) { + std::vector input = {"solo"}; + std::string expected = "solo"; + EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, ','), expected); +} + +TEST_F(AerodynInflowJoinStringArrayTest, DifferentDelimiter) { + std::vector input = {"one", "two", "three"}; + std::string expected = "one|two|three"; + EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, '|'), expected); +} + +TEST_F(AerodynInflowJoinStringArrayTest, StringsContainingDelimiter) { + std::vector input = {"com,ma", "semi;colon", "pipe|symbol"}; + std::string expected = "com,ma;semi;colon;pipe|symbol"; + EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, ';'), expected); +} + // Write test based on py_ad_driver.py to complete a full loop of initialization, simulation, and // cleanup TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { From 6059baf6ceb537b1f0b6a05629e53dc16cabb7af Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Mon, 30 Sep 2024 16:52:50 -0600 Subject: [PATCH 54/87] Refactor cycle as part of TDD --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 130 +++++++++++++----- .../regression/test_aerodyn_inflow.cpp | 116 +++++----------- 2 files changed, 128 insertions(+), 118 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index af5484f2..2299d28a 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -181,12 +181,11 @@ struct TurbineSettings { throw std::runtime_error("No blades. Set n_blades to number of AD blades in the model"); } - if (initial_root_position.size() != static_cast(n_blades)) { - throw std::invalid_argument("Number of blade root positions must match n_blades"); - } - - if (initial_root_orientation.size() != static_cast(n_blades)) { - throw std::invalid_argument("Number of blade root orientations must match n_blades"); + if (initial_root_position.size() != static_cast(n_blades) || + initial_root_orientation.size() != static_cast(n_blades)) { + throw std::invalid_argument( + "Number of blade root positions and orientations must match n_blades" + ); } } }; @@ -242,24 +241,22 @@ struct StructuralMesh { /** * @brief Validates the structural mesh * - * @details This method validates the structural mesh, including the number of mesh points, the - * initial mesh position, the initial mesh orientation, and the mapping of mesh points to blade - * numbers. + * @details This method validates the structural mesh by checking: + * - The number of mesh points matches the size of initial position and orientation arrays + * - The size of the mesh point to blade number mapping matches the number of mesh points + * - All blade numbers in the mapping are valid (between 1 and the number of blades) */ void Validate() const { - if (initial_mesh_position.size() != static_cast(n_mesh_points)) { - throw std::invalid_argument("Number of mesh positions must match n_mesh_points"); - } - - if (initial_mesh_orientation.size() != static_cast(n_mesh_points)) { - throw std::invalid_argument("Number of mesh orientations must match n_mesh_points"); - } - - if (initial_mesh_position.size() != initial_mesh_orientation.size()) { + if (initial_mesh_position.size() != static_cast(n_mesh_points) || + initial_mesh_orientation.size() != static_cast(n_mesh_points)) { throw std::invalid_argument( - "Different number of meshes in initial position and orientation arrays" + "Number of mesh positions and orientations must match n_mesh_points" ); } + + if (mesh_point_to_blade_num.size() != static_cast(n_mesh_points)) { + throw std::invalid_argument("Size of mesh_point_to_blade_num must match n_mesh_points"); + } } }; @@ -584,7 +581,7 @@ class AeroDynInflowLibrary { ); error_handling_.CheckError(); - is_initialized_ = true; + // is_initialized_ = true; } /** @@ -757,6 +754,15 @@ class AeroDynInflowLibrary { auto ADI_C_GetRotorLoads = lib_.get_function("ADI_C_GetRotorLoads"); + // Ensure the input vector has the correct size + if (mesh_force_moment.size() != static_cast(structural_mesh_.n_mesh_points)) { + throw std::invalid_argument( + "mesh_force_moment size (" + std::to_string(mesh_force_moment.size()) + + ") does not match n_mesh_points (" + std::to_string(structural_mesh_.n_mesh_points) + + ")" + ); + } + // Flatten the mesh force/moment array auto mesh_force_moment_flat = FlattenArray(mesh_force_moment); @@ -771,11 +777,7 @@ class AeroDynInflowLibrary { error_handling_.CheckError(); // Copy the flattened array back to the original array - for (size_t i = 0; i < mesh_force_moment.size(); ++i) { - for (size_t j = 0; j < 6; ++j) { - mesh_force_moment[i][j] = mesh_force_moment_flat[i * 6 + j]; - } - } + mesh_force_moment = UnflattenArray(mesh_force_moment_flat); } /** @@ -793,7 +795,7 @@ class AeroDynInflowLibrary { // Set up output channel values auto output_channel_values_c = - std::vector(static_cast(sim_controls_.n_channels)); + std::vector(static_cast(sim_controls_.n_channels), 0.f); ADI_C_CalcOutput( &time, // input: time at which to calculate output forces @@ -803,6 +805,8 @@ class AeroDynInflowLibrary { ); error_handling_.CheckError(); + + // Convert output channel values back into the output vector output_channel_values = output_channel_values_c; } @@ -812,16 +816,16 @@ class AeroDynInflowLibrary { * @details This function updates the states of the AeroDyn Inflow library by passing the current * time and the next time to the Fortran routine. * - * @param time Current time - * @param time_next Next time + * @param current_time Current time + * @param next_time Next time */ - void UpdateStates(double time, double time_next) { + void UpdateStates(double current_time, double next_time) { auto ADI_C_UpdateStates = lib_.get_function("ADI_C_UpdateStates"); ADI_C_UpdateStates( - &time, // input: time at which to calculate output forces - &time_next, // input: time T+dt we are stepping to + ¤t_time, // input: current time + &next_time, // input: next time step &error_handling_.error_status, // output: error status error_handling_.error_message.data() // output: error message buffer ); @@ -846,9 +850,19 @@ class AeroDynInflowLibrary { error_handling_.CheckError(); } - /// Method to flatten a 2D array into a 1D array for Fortran compatibility + /** + * @brief Flattens a 2D array into a 1D array for Fortran compatibility + * + * @details This function flattens a 2D array into a 1D array for Fortran compatibility by + * inserting each element of the 2D array into the 1D array. + * + * @tparam T Type of the elements in the array + * @tparam N Number of elements in each row of the array + * @param input 2D array to flatten + * @return Flattened 1D array + */ template - std::vector FlattenArray(const std::vector>& input) { + std::vector FlattenArray(const std::vector>& input) const { std::vector output; output.reserve(input.size() * N); for (const auto& arr : input) { @@ -857,11 +871,22 @@ class AeroDynInflowLibrary { return output; } - /// Method to validate a 2D array for the correct number of points and then flatten it to 1D + /** + * @brief Validates a 2D array for the correct number of points and then flattens it to 1D + * + * @details This function validates a 2D array for the correct number of points and then + * flattens it to 1D by inserting each element of the 2D array into the 1D array. + * + * @tparam T Type of the elements in the array + * @tparam N Number of elements in each row of the array + * @param array 2D array to validate and flatten + * @param expected_size Expected size of the 2D array + * @return Flattened 1D array + */ template std::vector ValidateAndFlattenArray( const std::vector>& array, size_t expected_size - ) { + ) const { std::string array_name; if constexpr (std::is_same_v && N == 3) { array_name = "position"; @@ -884,8 +909,39 @@ class AeroDynInflowLibrary { return FlattenArray(array); } - /// Method to join a vector of strings into a single string with a delimiter - std::string JoinStringArray(const std::vector& input, char delimiter) { + /** + * @brief Unflattens a 1D array into a 2D array + * + * @details This function unflattens a 1D array into a 2D array by inserting each element of + * the 1D array into the 2D array. + * + * @tparam T Type of the elements in the array + * @tparam N Number of elements in each row of the array + * @param input 1D array to unflatten + * @return Unflattened 2D array + */ + template + std::vector> UnflattenArray(const std::vector& input) const { + std::vector> output(input.size() / N); + for (size_t i = 0; i < output.size(); ++i) { + for (size_t j = 0; j < N; ++j) { + output[i][j] = input[i * N + j]; + } + } + return output; + } + + /** + * @brief Joins a vector of strings into a single string with a delimiter + * + * @details This function joins a vector of strings into a single string with a delimiter by + * inserting each element of the vector into the string. + * + * @param input Vector of strings to join + * @param delimiter Delimiter to insert between the strings + * @return Joined string + */ + std::string JoinStringArray(const std::vector& input, char delimiter) const { if (input.empty()) { return ""; } diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 15c11ca2..5e97d9a1 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -1,3 +1,5 @@ +#include + #include #include "test_utilities.hpp" @@ -81,7 +83,7 @@ TEST(AerodynInflowTest, SetPositionAndOrientation) { util::SetPositionAndOrientation(data, position, orientation); - ExpectArrayNear(position, {1.0f, 2.0f, 3.0f}); + ExpectArrayNear(position, {1.f, 2.f, 3.f}); ExpectArrayNear(orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.}); } @@ -202,6 +204,7 @@ TEST(AerodynInflowTest, StructuralMesh_Validate_ExpectNoThrow) { {1., 0., 0., 0., 1., 0., 0., 0., 1.}, {1., 0., 0., 0., 1., 0., 0., 0., 1.}, {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; + structural_mesh.mesh_point_to_blade_num = {1, 2, 3}; EXPECT_NO_THROW(structural_mesh.Validate()); } @@ -214,6 +217,7 @@ TEST(AerodynInflowTest, StructuralMesh_Validate_InvalidMeshPositionSize) { {1., 0., 0., 0., 1., 0., 0., 0., 1.}, {1., 0., 0., 0., 1., 0., 0., 0., 1.}, {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; + structural_mesh.mesh_point_to_blade_num = {1, 2, 3}; EXPECT_THROW(structural_mesh.Validate(), std::invalid_argument); } @@ -226,18 +230,19 @@ TEST(AerodynInflowTest, StructuralMesh_Validate_InvalidMeshOrientationSize) { {1., 0., 0., 0., 1., 0., 0., 0., 1.}, {1., 0., 0., 0., 1., 0., 0., 0., 1.} // Only 2 orientations }; - + structural_mesh.mesh_point_to_blade_num = {1, 2, 3}; EXPECT_THROW(structural_mesh.Validate(), std::invalid_argument); } -TEST(AerodynInflowTest, StructuralMesh_Validate_MismatchedSizes) { +TEST(AerodynInflowTest, StructuralMesh_Validate_MismatchedBladeNumbers) { util::StructuralMesh structural_mesh; structural_mesh.n_mesh_points = 3; structural_mesh.initial_mesh_position = {{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {2.f, 2.f, 2.f}}; structural_mesh.initial_mesh_orientation = { {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.} // Only 2 orientations - }; + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, + {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; + structural_mesh.mesh_point_to_blade_num = {1, 2}; // Only 2 blade numbers EXPECT_THROW(structural_mesh.Validate(), std::invalid_argument); } @@ -421,29 +426,24 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { } TEST(AerodynInflowTest, AeroDynInflowLibrary_PreInitialize) { - // Load the shared library const std::string path = GetSharedLibraryPath(); util::AeroDynInflowLibrary aerodyn_inflow_library(path); - // Check initial error handling state EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); - // Call PreInitialize aerodyn_inflow_library.PreInitialize(); - // Check error handling state after PreInitialize EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); } TEST(AerodynInflowTest, AeroDynInflowLibrary_FlattenArray) { - // Load the shared library const std::string path = GetSharedLibraryPath(); util::AeroDynInflowLibrary aerodyn_inflow_library(path); - std::vector> input = {{1.0f, 2.0f, 3.0f}, {4.0f, 5.0f, 6.0f}}; - std::vector expected = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; + std::vector> input = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; + std::vector expected = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; auto result = aerodyn_inflow_library.FlattenArray(input); @@ -461,9 +461,9 @@ class AerodynInflowValidateAndFlattenArrayTest : public ::testing::Test { }; TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidPositionArray) { - std::vector> position_array = {{1.0f, 2.0f, 3.0f}, {4.0f, 5.0f, 6.0f}}; + std::vector> position_array = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; size_t expected_size = 2; - std::vector expected = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; + std::vector expected = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; auto result = aerodyn_inflow_library.ValidateAndFlattenArray(position_array, expected_size); ASSERT_EQ(result, expected); @@ -471,11 +471,10 @@ TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidPositionArray) { TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidOrientationArray) { std::vector> orientation_array = { - {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}, - {10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0}}; + {1., 2., 3., 4., 5., 6., 7., 8., 9.}, {10., 11., 12., 13., 14., 15., 16., 17., 18.}}; size_t expected_size = 2; - std::vector expected = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, - 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0}; + std::vector expected = {1., 2., 3., 4., 5., 6., 7., 8., 9., + 10., 11., 12., 13., 14., 15., 16., 17., 18.}; auto result = aerodyn_inflow_library.ValidateAndFlattenArray(orientation_array, expected_size); ASSERT_EQ(result, expected); @@ -483,17 +482,16 @@ TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidOrientationArray) { TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidVelocityAccelerationArray) { std::vector> velocity_array = { - {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}, {7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f}}; + {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}, {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; size_t expected_size = 2; - std::vector expected = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, - 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f}; + std::vector expected = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f}; auto result = aerodyn_inflow_library.ValidateAndFlattenArray(velocity_array, expected_size); ASSERT_EQ(result, expected); } TEST_F(AerodynInflowValidateAndFlattenArrayTest, InvalidArraySize) { - std::vector> position_array = {{1.0f, 2.0f, 3.0f}, {4.0f, 5.0f, 6.0f}}; + std::vector> position_array = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; size_t expected_size = 3; // Incorrect size EXPECT_THROW( @@ -511,6 +509,21 @@ TEST_F(AerodynInflowValidateAndFlattenArrayTest, UnknownArrayType) { ASSERT_EQ(result, expected); } +TEST(AerodynInflowTest, UnflattenArray) { + const std::string path = GetSharedLibraryPath(); + util::AeroDynInflowLibrary aerodyn_inflow_library(path); + + std::vector input = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; + std::vector> expected = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; + + auto result = aerodyn_inflow_library.UnflattenArray(input); + + ASSERT_EQ(result.size(), expected.size()); + for (size_t i = 0; i < expected.size(); ++i) { + EXPECT_EQ(result[i], expected[i]); + } +} + class AerodynInflowJoinStringArrayTest : public ::testing::Test { protected: util::AeroDynInflowLibrary aerodyn_inflow_library; @@ -548,65 +561,6 @@ TEST_F(AerodynInflowJoinStringArrayTest, StringsContainingDelimiter) { EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, ';'), expected); } -// Write test based on py_ad_driver.py to complete a full loop of initialization, simulation, and -// cleanup -TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { - // Set up simulation parameters - util::SimulationControls sim_controls; - sim_controls.time_step = 0.0125; - sim_controls.max_time = 10.0; - - // Set up environmental conditions - util::EnvironmentalConditions env_conditions; - env_conditions.gravity = 9.80665f; - env_conditions.atm_pressure = 103500.0f; - - // Set up fluid properties - util::FluidProperties fluid_props; - fluid_props.density = 1.225f; - fluid_props.kinematic_viscosity = 1.464E-05f; - fluid_props.sound_speed = 335.0f; - fluid_props.vapor_pressure = 1700.0f; - - // Set up turbine settings - util::TurbineSettings turbine_settings; - turbine_settings.n_turbines = 1; - turbine_settings.n_blades = 3; - turbine_settings.initial_hub_position = {0.0f, 0.0f, 90.0f}; - turbine_settings.initial_hub_orientation = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}; - turbine_settings.initial_nacelle_position = {0.0f, 0.0f, 90.0f}; - turbine_settings.initial_nacelle_orientation = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}; - std::vector> root_positions = { - {0.0f, 0.0f, 90.0f}, {0.0f, 0.0f, 90.0f}, {0.0f, 0.0f, 90.0f}}; - turbine_settings.initial_root_position = root_positions; - std::vector> root_orientations( - 3, {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0} - ); - turbine_settings.initial_root_orientation = root_orientations; - - // Set up structural mesh - // Assuming 5 mesh points per blade - /* std::vector> mesh_positions(15); - std::vector> mesh_orientations( - 15, {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0} - ); - std::vector mesh_point_to_blade_num(15); - - for (int i = 0; i < 15; ++i) { - mesh_positions[static_cast(i)] = {0.0f, 0.0f, 90.0f + i * 2.0f}; - mesh_point_to_blade_num[static_cast(i)] = i / 5 + 1; - } - - util::StructuralMesh structural_mesh(mesh_positions, mesh_point_to_blade_num, 15); - - // Load the shared library - const std::string path = GetSharedLibraryPath(); - util::AeroDynInflowLibrary aerodyn_inflow_library( - path, util::ErrorHandling{}, fluid_props, env_conditions, turbine_settings, - util::StructuralMesh{}, sim_controls, util::VTKSettings{} - ); */ -} - #endif } // namespace openturbine::tests \ No newline at end of file From fef9bd5b1feb783534b13ca4ad596709cfb993d0 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 1 Oct 2024 10:54:59 -0600 Subject: [PATCH 55/87] Refactor to take the helper function out of the AeroDynInflowLibrary class since they don't need to be part of it --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 218 +++++++++--------- .../regression/test_aerodyn_inflow.cpp | 161 ++++++------- 2 files changed, 178 insertions(+), 201 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 2299d28a..6f80285d 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -431,6 +431,111 @@ struct VTKSettings { float vtk_hub_radius{1.5f}; //< Hub radius for VTK rendering }; +/** + * @brief Flattens a 2D array into a 1D array for Fortran compatibility + * + * @details This function flattens a 2D array into a 1D array for Fortran compatibility by + * inserting each element of the 2D array into the 1D array. + * + * @tparam T Type of the elements in the array + * @tparam N Number of elements in each row of the array + * @param input 2D array to flatten + * @return Flattened 1D array + */ +template +std::vector FlattenArray(const std::vector>& input) { + std::vector output; + output.reserve(input.size() * N); + for (const auto& arr : input) { + output.insert(output.end(), arr.begin(), arr.end()); + } + return output; +} + +/** + * @brief Validates a 2D array for the correct number of points and then flattens it to 1D + * + * @details This function validates a 2D array for the correct number of points and then + * flattens it to 1D by inserting each element of the 2D array into the 1D array. + * + * @tparam T Type of the elements in the array + * @tparam N Number of elements in each row of the array + * @param array 2D array to validate and flatten + * @param expected_size Expected size of the 2D array + * @return Flattened 1D array + */ +template +std::vector ValidateAndFlattenArray( + const std::vector>& array, size_t expected_size +) { + std::string array_name; + if constexpr (std::is_same_v && N == 3) { + array_name = "position"; + } else if constexpr (std::is_same_v && N == 9) { + array_name = "orientation"; + } else if constexpr (std::is_same_v && N == 6) { + array_name = "velocity/acceleration"; + } else { + array_name = "unknown"; + } + + if (array.size() != expected_size) { + throw std::runtime_error( + "The number of mesh points in the " + array_name + + " array changed from the initial value of " + std::to_string(expected_size) + " to " + + std::to_string(array.size()) + ". This is not permitted during the simulation." + ); + } + return FlattenArray(array); +} + +/** + * @brief Unflattens a 1D array into a 2D array + * + * @details This function unflattens a 1D array into a 2D array by inserting each element of + * the 1D array into the 2D array. + * + * @tparam T Type of the elements in the array + * @tparam N Number of elements in each row of the array + * @param input 1D array to unflatten + * @return Unflattened 2D array + */ +template +std::vector> UnflattenArray(const std::vector& input) { + std::vector> output(input.size() / N); + for (size_t i = 0; i < output.size(); ++i) { + for (size_t j = 0; j < N; ++j) { + output[i][j] = input[i * N + j]; + } + } + return output; +} + +/** + * @brief Joins a vector of strings into a single string with a delimiter + * + * @details This function joins a vector of strings into a single string with a delimiter by + * inserting each element of the vector into the string. + * + * @param input Vector of strings to join + * @param delimiter Delimiter to insert between the strings + * @return Joined string + */ +std::string JoinStringArray(const std::vector& input, char delimiter) { + if (input.empty()) { + return ""; + } + + std::ostringstream result; + std::copy( + input.begin(), input.end() - 1, + std::ostream_iterator(result, std::string(1, delimiter).c_str()) + ); + result << input.back(); + + return result.str(); +} + /** * @brief Wrapper class for the AeroDynInflow (ADI) shared library * @@ -605,12 +710,11 @@ class AeroDynInflowLibrary { ); // Primary input files will be passed as a single string joined by C_NULL_CHAR i.e. '\0' - std::string aerodyn_input_string = this->JoinStringArray(aerodyn_input_string_array, '\0'); + std::string aerodyn_input_string = JoinStringArray(aerodyn_input_string_array, '\0'); aerodyn_input_string = aerodyn_input_string + '\0'; int aerodyn_input_string_length = static_cast(aerodyn_input_string.size()); - std::string inflowwind_input_string = - this->JoinStringArray(inflowwind_input_string_array, '\0'); + std::string inflowwind_input_string = JoinStringArray(inflowwind_input_string_array, '\0'); inflowwind_input_string = inflowwind_input_string + '\0'; int inflowwind_input_string_length = static_cast(inflowwind_input_string.size()); @@ -690,7 +794,7 @@ class AeroDynInflowLibrary { ); // Flatten arrays to pass to the Fortran routine - auto flatten_and_validate = [this](const auto& motion, int n_pts) { + auto flatten_and_validate = [](const auto& motion, int n_pts) { return std::make_tuple( ValidateAndFlattenArray(motion.position, static_cast(n_pts)), ValidateAndFlattenArray(motion.orientation, static_cast(n_pts)), @@ -850,112 +954,6 @@ class AeroDynInflowLibrary { error_handling_.CheckError(); } - /** - * @brief Flattens a 2D array into a 1D array for Fortran compatibility - * - * @details This function flattens a 2D array into a 1D array for Fortran compatibility by - * inserting each element of the 2D array into the 1D array. - * - * @tparam T Type of the elements in the array - * @tparam N Number of elements in each row of the array - * @param input 2D array to flatten - * @return Flattened 1D array - */ - template - std::vector FlattenArray(const std::vector>& input) const { - std::vector output; - output.reserve(input.size() * N); - for (const auto& arr : input) { - output.insert(output.end(), arr.begin(), arr.end()); - } - return output; - } - - /** - * @brief Validates a 2D array for the correct number of points and then flattens it to 1D - * - * @details This function validates a 2D array for the correct number of points and then - * flattens it to 1D by inserting each element of the 2D array into the 1D array. - * - * @tparam T Type of the elements in the array - * @tparam N Number of elements in each row of the array - * @param array 2D array to validate and flatten - * @param expected_size Expected size of the 2D array - * @return Flattened 1D array - */ - template - std::vector ValidateAndFlattenArray( - const std::vector>& array, size_t expected_size - ) const { - std::string array_name; - if constexpr (std::is_same_v && N == 3) { - array_name = "position"; - } else if constexpr (std::is_same_v && N == 9) { - array_name = "orientation"; - } else if constexpr (std::is_same_v && N == 6) { - array_name = "velocity/acceleration"; - } else { - array_name = "unknown"; - } - - if (array.size() != expected_size) { - throw std::runtime_error( - "The number of mesh points in the " + array_name + - " array changed from the initial value of " + std::to_string(expected_size) + - " to " + std::to_string(array.size()) + - ". This is not permitted during the simulation." - ); - } - return FlattenArray(array); - } - - /** - * @brief Unflattens a 1D array into a 2D array - * - * @details This function unflattens a 1D array into a 2D array by inserting each element of - * the 1D array into the 2D array. - * - * @tparam T Type of the elements in the array - * @tparam N Number of elements in each row of the array - * @param input 1D array to unflatten - * @return Unflattened 2D array - */ - template - std::vector> UnflattenArray(const std::vector& input) const { - std::vector> output(input.size() / N); - for (size_t i = 0; i < output.size(); ++i) { - for (size_t j = 0; j < N; ++j) { - output[i][j] = input[i * N + j]; - } - } - return output; - } - - /** - * @brief Joins a vector of strings into a single string with a delimiter - * - * @details This function joins a vector of strings into a single string with a delimiter by - * inserting each element of the vector into the string. - * - * @param input Vector of strings to join - * @param delimiter Delimiter to insert between the strings - * @return Joined string - */ - std::string JoinStringArray(const std::vector& input, char delimiter) const { - if (input.empty()) { - return ""; - } - - std::ostringstream result; - std::copy( - input.begin(), input.end() - 1, - std::ostream_iterator(result, std::string(1, delimiter).c_str()) - ); - result << input.back(); - - return result.str(); - } - private: bool is_initialized_{false}; //< Flag to check if the library is initialized util::dylib lib_{ diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 5e97d9a1..44ae7808 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -391,61 +391,11 @@ TEST(AerodynInflowTest, VTKSettings_Set) { EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.0f); } -/// Helper function to get the shared library path -std::string GetSharedLibraryPath() { - const std::filesystem::path project_root = FindProjectRoot(); - const std::filesystem::path full_path = - project_root / "build/tests/unit_tests/libaerodyn_inflow_c_binding"; - -#ifdef __APPLE__ - return full_path.string() + ".dylib"; -#elif __linux__ - return full_path.string() + ".so"; -#else // Windows - return full_path.string() + ".dll"; -#endif -} - -TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { - // Load the shared library - const std::string path = GetSharedLibraryPath(); - util::AeroDynInflowLibrary aerodyn_inflow_library(path); - - // Check initial error handling state - EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); - EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); - - // Check default values for other important members - EXPECT_EQ(aerodyn_inflow_library.GetFluidProperties().density, 1.225f); - EXPECT_EQ(aerodyn_inflow_library.GetEnvironmentalConditions().gravity, 9.80665f); - EXPECT_EQ(aerodyn_inflow_library.GetTurbineSettings().n_turbines, 1); - EXPECT_EQ(aerodyn_inflow_library.GetSimulationControls().debug_level, 0); - EXPECT_EQ(aerodyn_inflow_library.GetSimulationControls().transpose_DCM, 1); - EXPECT_EQ(aerodyn_inflow_library.GetStructuralMesh().n_mesh_points, 1); - EXPECT_EQ(aerodyn_inflow_library.GetVTKSettings().write_vtk, 0); -} - -TEST(AerodynInflowTest, AeroDynInflowLibrary_PreInitialize) { - const std::string path = GetSharedLibraryPath(); - util::AeroDynInflowLibrary aerodyn_inflow_library(path); - - EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); - EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); - - aerodyn_inflow_library.PreInitialize(); - - EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); - EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); -} - -TEST(AerodynInflowTest, AeroDynInflowLibrary_FlattenArray) { - const std::string path = GetSharedLibraryPath(); - util::AeroDynInflowLibrary aerodyn_inflow_library(path); - +TEST(AerodynInflowTest, FlattenArray) { std::vector> input = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; std::vector expected = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; - auto result = aerodyn_inflow_library.FlattenArray(input); + auto result = util::FlattenArray(input); ASSERT_EQ(result.size(), expected.size()); for (size_t i = 0; i < expected.size(); ++i) { @@ -453,70 +403,59 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FlattenArray) { } } -class AerodynInflowValidateAndFlattenArrayTest : public ::testing::Test { -protected: - util::AeroDynInflowLibrary aerodyn_inflow_library; - - AerodynInflowValidateAndFlattenArrayTest() : aerodyn_inflow_library(GetSharedLibraryPath()) {} -}; - -TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidPositionArray) { +TEST(AerodynInflowTest, ValidateAndFlattenArray_ValidPositionArray) { std::vector> position_array = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; size_t expected_size = 2; std::vector expected = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; - auto result = aerodyn_inflow_library.ValidateAndFlattenArray(position_array, expected_size); + auto result = util::ValidateAndFlattenArray(position_array, expected_size); ASSERT_EQ(result, expected); } -TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidOrientationArray) { +TEST(AerodynInflowTest, ValidateAndFlattenArray_ValidOrientationArray) { std::vector> orientation_array = { {1., 2., 3., 4., 5., 6., 7., 8., 9.}, {10., 11., 12., 13., 14., 15., 16., 17., 18.}}; size_t expected_size = 2; std::vector expected = {1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18.}; - auto result = aerodyn_inflow_library.ValidateAndFlattenArray(orientation_array, expected_size); + auto result = util::ValidateAndFlattenArray(orientation_array, expected_size); ASSERT_EQ(result, expected); } -TEST_F(AerodynInflowValidateAndFlattenArrayTest, ValidVelocityAccelerationArray) { +TEST(AerodynInflowTest, ValidateAndFlattenArray_ValidVelocityAccelerationArray) { std::vector> velocity_array = { {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}, {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; size_t expected_size = 2; std::vector expected = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f}; - auto result = aerodyn_inflow_library.ValidateAndFlattenArray(velocity_array, expected_size); + auto result = util::ValidateAndFlattenArray(velocity_array, expected_size); ASSERT_EQ(result, expected); } -TEST_F(AerodynInflowValidateAndFlattenArrayTest, InvalidArraySize) { +TEST(AerodynInflowTest, ValidateAndFlattenArray_InvalidArraySize) { std::vector> position_array = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; size_t expected_size = 3; // Incorrect size EXPECT_THROW( - { aerodyn_inflow_library.ValidateAndFlattenArray(position_array, expected_size); }, - std::runtime_error + { util::ValidateAndFlattenArray(position_array, expected_size); }, std::runtime_error ); } -TEST_F(AerodynInflowValidateAndFlattenArrayTest, UnknownArrayType) { +TEST(AerodynInflowTest, ValidateAndFlattenArray_UnknownArrayType) { std::vector> unknown_array = {{1, 2}, {3, 4}}; size_t expected_size = 2; - auto result = aerodyn_inflow_library.ValidateAndFlattenArray(unknown_array, expected_size); + auto result = util::ValidateAndFlattenArray(unknown_array, expected_size); std::vector expected = {1, 2, 3, 4}; ASSERT_EQ(result, expected); } TEST(AerodynInflowTest, UnflattenArray) { - const std::string path = GetSharedLibraryPath(); - util::AeroDynInflowLibrary aerodyn_inflow_library(path); - std::vector input = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; std::vector> expected = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; - auto result = aerodyn_inflow_library.UnflattenArray(input); + auto result = util::UnflattenArray(input); ASSERT_EQ(result.size(), expected.size()); for (size_t i = 0; i < expected.size(); ++i) { @@ -524,41 +463,81 @@ TEST(AerodynInflowTest, UnflattenArray) { } } -class AerodynInflowJoinStringArrayTest : public ::testing::Test { -protected: - util::AeroDynInflowLibrary aerodyn_inflow_library; - - AerodynInflowJoinStringArrayTest() : aerodyn_inflow_library(GetSharedLibraryPath()) {} -}; - -TEST_F(AerodynInflowJoinStringArrayTest, NormalCase) { +TEST(AerodynInflowTest, JoinStringArray_NormalCase) { std::vector input = {"apple", "banana", "cherry"}; std::string expected = "apple,banana,cherry"; - EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, ','), expected); + EXPECT_EQ(util::JoinStringArray(input, ','), expected); } -TEST_F(AerodynInflowJoinStringArrayTest, EmptyInput) { +TEST(AerodynInflowTest, JoinStringArray_EmptyInput) { std::vector input = {}; std::string expected = ""; - EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, ','), expected); + EXPECT_EQ(util::JoinStringArray(input, ','), expected); } -TEST_F(AerodynInflowJoinStringArrayTest, SingleElement) { +TEST(AerodynInflowTest, JoinStringArray_SingleElement) { std::vector input = {"solo"}; std::string expected = "solo"; - EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, ','), expected); + EXPECT_EQ(util::JoinStringArray(input, ','), expected); } -TEST_F(AerodynInflowJoinStringArrayTest, DifferentDelimiter) { +TEST(AerodynInflowTest, JoinStringArray_DifferentDelimiter) { std::vector input = {"one", "two", "three"}; std::string expected = "one|two|three"; - EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, '|'), expected); + EXPECT_EQ(util::JoinStringArray(input, '|'), expected); } -TEST_F(AerodynInflowJoinStringArrayTest, StringsContainingDelimiter) { +TEST(AerodynInflowTest, JoinStringArray_StringsContainingDelimiter) { std::vector input = {"com,ma", "semi;colon", "pipe|symbol"}; std::string expected = "com,ma;semi;colon;pipe|symbol"; - EXPECT_EQ(aerodyn_inflow_library.JoinStringArray(input, ';'), expected); + EXPECT_EQ(util::JoinStringArray(input, ';'), expected); +} + +/// Helper function to get the shared library path +std::string GetSharedLibraryPath() { + const std::filesystem::path project_root = FindProjectRoot(); + const std::filesystem::path full_path = + project_root / "build/tests/unit_tests/libaerodyn_inflow_c_binding"; + +#ifdef __APPLE__ + return full_path.string() + ".dylib"; +#elif __linux__ + return full_path.string() + ".so"; +#else // Windows + return full_path.string() + ".dll"; +#endif +} + +TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { + // Load the shared library + const std::string path = GetSharedLibraryPath(); + util::AeroDynInflowLibrary aerodyn_inflow_library(path); + + // Check initial error handling state + EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); + EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); + + // Check default values for other important members + EXPECT_EQ(aerodyn_inflow_library.GetFluidProperties().density, 1.225f); + EXPECT_EQ(aerodyn_inflow_library.GetEnvironmentalConditions().gravity, 9.80665f); + EXPECT_EQ(aerodyn_inflow_library.GetTurbineSettings().n_turbines, 1); + EXPECT_EQ(aerodyn_inflow_library.GetSimulationControls().debug_level, 0); + EXPECT_EQ(aerodyn_inflow_library.GetSimulationControls().transpose_DCM, 1); + EXPECT_EQ(aerodyn_inflow_library.GetStructuralMesh().n_mesh_points, 1); + EXPECT_EQ(aerodyn_inflow_library.GetVTKSettings().write_vtk, 0); +} + +TEST(AerodynInflowTest, AeroDynInflowLibrary_PreInitialize) { + const std::string path = GetSharedLibraryPath(); + util::AeroDynInflowLibrary aerodyn_inflow_library(path); + + EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); + EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); + + aerodyn_inflow_library.PreInitialize(); + + EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); + EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); } #endif From 63f4229ecc7d2d71cf77f2a6c70a9451079fdecc Mon Sep 17 00:00:00 2001 From: dcdemen Date: Tue, 24 Sep 2024 12:12:39 -0600 Subject: [PATCH 56/87] Add unit tests for UpdateDynamicPrediction --- tests/unit_tests/state/CMakeLists.txt | 1 + .../state/test_update_dynamic_prediction.cpp | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 tests/unit_tests/state/test_update_dynamic_prediction.cpp diff --git a/tests/unit_tests/state/CMakeLists.txt b/tests/unit_tests/state/CMakeLists.txt index 4c4e9e1d..4491fff8 100644 --- a/tests/unit_tests/state/CMakeLists.txt +++ b/tests/unit_tests/state/CMakeLists.txt @@ -5,4 +5,5 @@ target_sources( test_calculate_next_state.cpp test_calculate_displacement.cpp test_update_algorithmic_acceleration.cpp + test_update_dynamic_prediction.cpp ) diff --git a/tests/unit_tests/state/test_update_dynamic_prediction.cpp b/tests/unit_tests/state/test_update_dynamic_prediction.cpp new file mode 100644 index 00000000..786e6f17 --- /dev/null +++ b/tests/unit_tests/state/test_update_dynamic_prediction.cpp @@ -0,0 +1,56 @@ +#include +#include + +#include "src/state/update_dynamic_prediction.hpp" + +namespace openturbine::tests { + +inline void CompareWithExpected( + const Kokkos::View::host_mirror_type& result, + const Kokkos::View& expected +) { + for (auto i = 0U; i < result.extent(0); ++i) { + for (auto j = 0U; j < result.extent(1); ++j) { + EXPECT_NEAR(result(i, j), expected(i, j), 1.e-14); + } + } +} + +TEST(UpdateDynamicPrediction, OneNode) { + constexpr auto h = .5; + constexpr auto beta_prime = 2.; + constexpr auto gamma_prime = 3.; + + constexpr auto x_delta_host_data = std::array{1., 2., 3., 4., 5., 6.}; + const auto x_delta_host = Kokkos::View::const_type(x_delta_host_data.data()); + const auto x_delta = Kokkos::View("x_delta"); + const auto x_delta_mirror = Kokkos::create_mirror(x_delta); + Kokkos::deep_copy(x_delta_mirror, x_delta_host); + Kokkos::deep_copy(x_delta, x_delta_mirror); + + const auto q_delta = Kokkos::View("q_delta"); + const auto v = Kokkos::View("v"); + const auto vd = Kokkos::View("vd"); + + Kokkos::parallel_for("UpdateDynamicPrediction", 1, UpdateDynamicPrediction{h, beta_prime, gamma_prime, x_delta, q_delta, v, vd}); + + constexpr auto q_delta_exact_data = std::array{2., 4., 6., 8., 10., 12.}; + const auto q_delta_exact = Kokkos::View::const_type(q_delta_exact_data.data()); + const auto q_delta_mirror = Kokkos::create_mirror(q_delta); + Kokkos::deep_copy(q_delta_mirror, q_delta); + CompareWithExpected(q_delta_mirror, q_delta_exact); + + constexpr auto v_exact_data = std::array{3., 6., 9., 12., 15., 18.}; + const auto v_exact = Kokkos::View::const_type(v_exact_data.data()); + const auto v_mirror = Kokkos::create_mirror(v); + Kokkos::deep_copy(v_mirror, v); + CompareWithExpected(v_mirror, v_exact); + + constexpr auto vd_exact_data = std::array{2., 4., 6., 8., 10., 12.}; + const auto vd_exact = Kokkos::View::const_type(vd_exact_data.data()); + const auto vd_mirror = Kokkos::create_mirror(vd); + Kokkos::deep_copy(vd_mirror, vd); + CompareWithExpected(vd_mirror, vd_exact); +} + +} From e0b677111af4087c2eb5e1c0d9c18a1d283c8d4e Mon Sep 17 00:00:00 2001 From: dcdemen Date: Tue, 1 Oct 2024 13:06:20 -0600 Subject: [PATCH 57/87] Added unit test for UpdateGlobalPosition --- tests/unit_tests/state/CMakeLists.txt | 1 + .../state/test_update_global_position.cpp | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 tests/unit_tests/state/test_update_global_position.cpp diff --git a/tests/unit_tests/state/CMakeLists.txt b/tests/unit_tests/state/CMakeLists.txt index 4491fff8..d2d35107 100644 --- a/tests/unit_tests/state/CMakeLists.txt +++ b/tests/unit_tests/state/CMakeLists.txt @@ -6,4 +6,5 @@ target_sources( test_calculate_displacement.cpp test_update_algorithmic_acceleration.cpp test_update_dynamic_prediction.cpp + test_update_global_position.cpp ) diff --git a/tests/unit_tests/state/test_update_global_position.cpp b/tests/unit_tests/state/test_update_global_position.cpp new file mode 100644 index 00000000..e96616c0 --- /dev/null +++ b/tests/unit_tests/state/test_update_global_position.cpp @@ -0,0 +1,37 @@ + +#include +#include + +#include "src/state/update_global_position.hpp" + +namespace openturbine::tests { +TEST(UpdateGlobalPosition, OneNode) { + + const auto q = Kokkos::View("q"); + constexpr auto q_host_data = std::array{1., 2., 3., 4., 5., 6., 7.}; + const auto q_host = Kokkos::View::const_type(q_host_data.data()); + const auto q_mirror = Kokkos::create_mirror(q); + Kokkos::deep_copy(q_mirror, q_host); + Kokkos::deep_copy(q, q_mirror); + + const auto x0 = Kokkos::View("x0"); + constexpr auto x0_host_data = std::array{8., 9., 10., 11., 12., 13., 14.}; + const auto x0_host = Kokkos::View::const_type(x0_host_data.data()); + const auto x0_mirror = Kokkos::create_mirror(x0); + Kokkos::deep_copy(x0_mirror, x0_host); + Kokkos::deep_copy(x0, x0_mirror); + + const auto x = Kokkos::View("x"); + + Kokkos::parallel_for("UpdateGlobalPosition", 1, UpdateGlobalPosition{q, x0, x}); + + constexpr auto x_exact_data = std::array{9., 11., 13., -192., 96., 132., 126.}; + const auto x_exact = Kokkos::View::const_type(x_exact_data.data()); + const auto x_mirror = Kokkos::create_mirror(x); + Kokkos::deep_copy(x_mirror, x); + for (auto i = 0U; i < 7U; ++i) { + EXPECT_NEAR(x_mirror(0, i), x_exact(0, i), 1.e-14); + } +} + +} From 14d8f53dd6494146df93b71baf5c94f1247953e1 Mon Sep 17 00:00:00 2001 From: dcdemen Date: Tue, 1 Oct 2024 13:22:43 -0600 Subject: [PATCH 58/87] Add unit test for UpdateStaticPrediction --- tests/unit_tests/state/CMakeLists.txt | 1 + .../state/test_update_static_prediction.cpp | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 tests/unit_tests/state/test_update_static_prediction.cpp diff --git a/tests/unit_tests/state/CMakeLists.txt b/tests/unit_tests/state/CMakeLists.txt index d2d35107..8857dc23 100644 --- a/tests/unit_tests/state/CMakeLists.txt +++ b/tests/unit_tests/state/CMakeLists.txt @@ -7,4 +7,5 @@ target_sources( test_update_algorithmic_acceleration.cpp test_update_dynamic_prediction.cpp test_update_global_position.cpp + test_update_static_prediction.cpp ) diff --git a/tests/unit_tests/state/test_update_static_prediction.cpp b/tests/unit_tests/state/test_update_static_prediction.cpp new file mode 100644 index 00000000..76255982 --- /dev/null +++ b/tests/unit_tests/state/test_update_static_prediction.cpp @@ -0,0 +1,36 @@ + +#include +#include + +#include "src/state/update_static_prediction.hpp" + +namespace openturbine::tests { +TEST(UpdateStaticPrediction, TwoNodes) { + constexpr auto h = 2.; + constexpr auto beta_prime = 20.; + constexpr auto gamma_prime = 50.; + + const auto x_delta = Kokkos::View("x_delta"); + constexpr auto x_delta_host_data = std::array{2., 4., 6., 8., 10., 12., 14., 16., 18., 20., 22., 24.}; + const auto x_delta_host = Kokkos::View::const_type(x_delta_host_data.data()); + const auto x_delta_mirror = Kokkos::create_mirror(x_delta); + Kokkos::deep_copy(x_delta_mirror, x_delta_host); + Kokkos::deep_copy(x_delta, x_delta_mirror); + + const auto q_delta = Kokkos::View("q_delta"); + + Kokkos::parallel_for("UpdateStaticPrediction", 2, UpdateStaticPrediction{h, beta_prime, gamma_prime, x_delta, q_delta}); + + constexpr auto q_delta_exact_data = std::array{1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.}; + const auto q_delta_exact = Kokkos::View::const_type(q_delta_exact_data.data()); + const auto q_delta_mirror = Kokkos::create_mirror(q_delta); + Kokkos::deep_copy(q_delta_mirror, q_delta); + for (auto i = 0U; i < 6U; ++i) { + EXPECT_NEAR(q_delta_mirror(0, i), q_delta_exact(0, i), 1.e-14); + } + for (auto i = 0U; i < 6U; ++i) { + EXPECT_NEAR(q_delta_mirror(1, i), q_delta_exact(1, i), 1.e-14); + } +} + +} From 319375d4f81a00a70ca7177461a49f992c9660ec Mon Sep 17 00:00:00 2001 From: dcdemen Date: Tue, 1 Oct 2024 13:23:28 -0600 Subject: [PATCH 59/87] clang-format --- .../state/test_update_dynamic_prediction.cpp | 21 ++++++++++++------- .../state/test_update_global_position.cpp | 14 +++++++------ .../state/test_update_static_prediction.cpp | 21 ++++++++++++------- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/tests/unit_tests/state/test_update_dynamic_prediction.cpp b/tests/unit_tests/state/test_update_dynamic_prediction.cpp index 786e6f17..85dcba2f 100644 --- a/tests/unit_tests/state/test_update_dynamic_prediction.cpp +++ b/tests/unit_tests/state/test_update_dynamic_prediction.cpp @@ -22,7 +22,8 @@ TEST(UpdateDynamicPrediction, OneNode) { constexpr auto gamma_prime = 3.; constexpr auto x_delta_host_data = std::array{1., 2., 3., 4., 5., 6.}; - const auto x_delta_host = Kokkos::View::const_type(x_delta_host_data.data()); + const auto x_delta_host = + Kokkos::View::const_type(x_delta_host_data.data()); const auto x_delta = Kokkos::View("x_delta"); const auto x_delta_mirror = Kokkos::create_mirror(x_delta); Kokkos::deep_copy(x_delta_mirror, x_delta_host); @@ -32,25 +33,31 @@ TEST(UpdateDynamicPrediction, OneNode) { const auto v = Kokkos::View("v"); const auto vd = Kokkos::View("vd"); - Kokkos::parallel_for("UpdateDynamicPrediction", 1, UpdateDynamicPrediction{h, beta_prime, gamma_prime, x_delta, q_delta, v, vd}); + Kokkos::parallel_for( + "UpdateDynamicPrediction", 1, + UpdateDynamicPrediction{h, beta_prime, gamma_prime, x_delta, q_delta, v, vd} + ); constexpr auto q_delta_exact_data = std::array{2., 4., 6., 8., 10., 12.}; - const auto q_delta_exact = Kokkos::View::const_type(q_delta_exact_data.data()); + const auto q_delta_exact = + Kokkos::View::const_type(q_delta_exact_data.data()); const auto q_delta_mirror = Kokkos::create_mirror(q_delta); Kokkos::deep_copy(q_delta_mirror, q_delta); CompareWithExpected(q_delta_mirror, q_delta_exact); - + constexpr auto v_exact_data = std::array{3., 6., 9., 12., 15., 18.}; - const auto v_exact = Kokkos::View::const_type(v_exact_data.data()); + const auto v_exact = + Kokkos::View::const_type(v_exact_data.data()); const auto v_mirror = Kokkos::create_mirror(v); Kokkos::deep_copy(v_mirror, v); CompareWithExpected(v_mirror, v_exact); constexpr auto vd_exact_data = std::array{2., 4., 6., 8., 10., 12.}; - const auto vd_exact = Kokkos::View::const_type(vd_exact_data.data()); + const auto vd_exact = + Kokkos::View::const_type(vd_exact_data.data()); const auto vd_mirror = Kokkos::create_mirror(vd); Kokkos::deep_copy(vd_mirror, vd); CompareWithExpected(vd_mirror, vd_exact); } -} +} // namespace openturbine::tests diff --git a/tests/unit_tests/state/test_update_global_position.cpp b/tests/unit_tests/state/test_update_global_position.cpp index e96616c0..1b02e4a8 100644 --- a/tests/unit_tests/state/test_update_global_position.cpp +++ b/tests/unit_tests/state/test_update_global_position.cpp @@ -6,17 +6,18 @@ namespace openturbine::tests { TEST(UpdateGlobalPosition, OneNode) { - const auto q = Kokkos::View("q"); constexpr auto q_host_data = std::array{1., 2., 3., 4., 5., 6., 7.}; - const auto q_host = Kokkos::View::const_type(q_host_data.data()); + const auto q_host = + Kokkos::View::const_type(q_host_data.data()); const auto q_mirror = Kokkos::create_mirror(q); Kokkos::deep_copy(q_mirror, q_host); Kokkos::deep_copy(q, q_mirror); - + const auto x0 = Kokkos::View("x0"); constexpr auto x0_host_data = std::array{8., 9., 10., 11., 12., 13., 14.}; - const auto x0_host = Kokkos::View::const_type(x0_host_data.data()); + const auto x0_host = + Kokkos::View::const_type(x0_host_data.data()); const auto x0_mirror = Kokkos::create_mirror(x0); Kokkos::deep_copy(x0_mirror, x0_host); Kokkos::deep_copy(x0, x0_mirror); @@ -26,7 +27,8 @@ TEST(UpdateGlobalPosition, OneNode) { Kokkos::parallel_for("UpdateGlobalPosition", 1, UpdateGlobalPosition{q, x0, x}); constexpr auto x_exact_data = std::array{9., 11., 13., -192., 96., 132., 126.}; - const auto x_exact = Kokkos::View::const_type(x_exact_data.data()); + const auto x_exact = + Kokkos::View::const_type(x_exact_data.data()); const auto x_mirror = Kokkos::create_mirror(x); Kokkos::deep_copy(x_mirror, x); for (auto i = 0U; i < 7U; ++i) { @@ -34,4 +36,4 @@ TEST(UpdateGlobalPosition, OneNode) { } } -} +} // namespace openturbine::tests diff --git a/tests/unit_tests/state/test_update_static_prediction.cpp b/tests/unit_tests/state/test_update_static_prediction.cpp index 76255982..8ac6bc96 100644 --- a/tests/unit_tests/state/test_update_static_prediction.cpp +++ b/tests/unit_tests/state/test_update_static_prediction.cpp @@ -8,21 +8,28 @@ namespace openturbine::tests { TEST(UpdateStaticPrediction, TwoNodes) { constexpr auto h = 2.; constexpr auto beta_prime = 20.; - constexpr auto gamma_prime = 50.; + constexpr auto gamma_prime = 50.; const auto x_delta = Kokkos::View("x_delta"); - constexpr auto x_delta_host_data = std::array{2., 4., 6., 8., 10., 12., 14., 16., 18., 20., 22., 24.}; - const auto x_delta_host = Kokkos::View::const_type(x_delta_host_data.data()); + constexpr auto x_delta_host_data = + std::array{2., 4., 6., 8., 10., 12., 14., 16., 18., 20., 22., 24.}; + const auto x_delta_host = + Kokkos::View::const_type(x_delta_host_data.data()); const auto x_delta_mirror = Kokkos::create_mirror(x_delta); Kokkos::deep_copy(x_delta_mirror, x_delta_host); Kokkos::deep_copy(x_delta, x_delta_mirror); const auto q_delta = Kokkos::View("q_delta"); - Kokkos::parallel_for("UpdateStaticPrediction", 2, UpdateStaticPrediction{h, beta_prime, gamma_prime, x_delta, q_delta}); + Kokkos::parallel_for( + "UpdateStaticPrediction", 2, + UpdateStaticPrediction{h, beta_prime, gamma_prime, x_delta, q_delta} + ); - constexpr auto q_delta_exact_data = std::array{1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.}; - const auto q_delta_exact = Kokkos::View::const_type(q_delta_exact_data.data()); + constexpr auto q_delta_exact_data = + std::array{1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.}; + const auto q_delta_exact = + Kokkos::View::const_type(q_delta_exact_data.data()); const auto q_delta_mirror = Kokkos::create_mirror(q_delta); Kokkos::deep_copy(q_delta_mirror, q_delta); for (auto i = 0U; i < 6U; ++i) { @@ -33,4 +40,4 @@ TEST(UpdateStaticPrediction, TwoNodes) { } } -} +} // namespace openturbine::tests From 28f6260fc782eb9f5ad41ea5665fb827d64187ba Mon Sep 17 00:00:00 2001 From: dcdemen Date: Tue, 1 Oct 2024 14:20:06 -0600 Subject: [PATCH 60/87] CI is now reporting a new crop of formatting errors. This fixes them --- src/beams/create_beams.hpp | 9 +++-- .../interpolate_to_quadrature_points.hpp | 13 ++++--- .../calculate_constraint_force.hpp | 3 +- .../calculate_constraint_output.hpp | 15 +++++--- .../calculate_fixed_bc_constraint.hpp | 6 ++-- .../calculate_prescribed_bc_constraint.hpp | 12 ++++--- .../calculate_revolute_joint_constraint.hpp | 18 ++++++---- .../calculate_revolute_joint_force.hpp | 6 ++-- .../calculate_rigid_joint_constraint.hpp | 9 +++-- .../calculate_rotation_control_constraint.hpp | 9 +++-- src/constraints/constraint.hpp | 3 +- src/model/node.hpp | 3 +- src/solver/solver.hpp | 12 ++++--- src/state/calculate_displacement.hpp | 6 ++-- src/step/assemble_constraints_matrix.hpp | 3 +- src/step/assemble_constraints_residual.hpp | 6 ++-- src/step/assemble_inertia_matrix.hpp | 3 +- src/step/assemble_residual_vector.hpp | 3 +- src/step/assemble_system_matrix.hpp | 3 +- src/step/assemble_system_residual.hpp | 3 +- src/step/update_constraint_variables.hpp | 6 ++-- src/step/update_system_variables.hpp | 6 ++-- src/system/calculate_RR0.hpp | 3 +- .../calculate_quadrature_point_values.hpp | 9 +++-- src/system/calculate_strain.hpp | 13 ++++--- src/system/calculate_temporary_variables.hpp | 7 ++-- src/system/integrate_inertia_matrix.hpp | 7 ++-- src/system/integrate_residual_vector.hpp | 3 +- src/system/integrate_stiffness_matrix.hpp | 3 +- src/system/update_node_state.hpp | 3 +- .../scripts/windio_mapped_structs.hpp | 36 +++++++++---------- src/vendor/dylib/dylib.hpp | 13 +++---- .../beams/test_interpolate_QP_vector.cpp | 9 +++-- .../regression/iea15_rotor_data.hpp | 24 ++++++++----- tests/unit_tests/regression/test_beams.cpp | 4 +-- tests/unit_tests/regression/test_math.cpp | 8 ++--- .../regression/test_rotating_beam.cpp | 11 +++--- tests/unit_tests/regression/test_rotor.cpp | 3 +- tests/unit_tests/regression/test_solver.cpp | 9 +++-- ...ompute_number_of_non_zeros_constraints.cpp | 6 ++-- ...e_sparse_row_ptrs_col_inds_constraints.cpp | 12 ++++--- ...ate_sparse_row_ptrs_col_inds_transpose.cpp | 6 ++-- ...est_calculate_inertia_stiffness_matrix.cpp | 6 ++-- .../system/test_calculate_inertial_forces.cpp | 3 +- .../system/test_integrate_inertia_matrix.cpp | 24 ++++++++----- .../system/test_integrate_residual_vector.cpp | 32 +++++++++++------ .../test_integrate_stiffness_matrix.cpp | 12 ++++--- .../system/test_rotate_section_matrix.cpp | 16 +++++---- 48 files changed, 272 insertions(+), 167 deletions(-) diff --git a/src/beams/create_beams.hpp b/src/beams/create_beams.hpp index 5aadba35..0476aba4 100644 --- a/src/beams/create_beams.hpp +++ b/src/beams/create_beams.hpp @@ -91,14 +91,16 @@ inline Beams CreateBeams(const BeamsInput& beams_input) { "InterpolateQPPosition", beams.num_elems, InterpolateQPPosition{ beams.num_nodes_per_element, beams.num_qps_per_element, beams.shape_interp, - beams.node_x0, beams.qp_x0} + beams.node_x0, beams.qp_x0 + } ); Kokkos::parallel_for( "InterpolateQPRotation", beams.num_elems, InterpolateQPRotation{ beams.num_nodes_per_element, beams.num_qps_per_element, beams.shape_interp, - beams.node_x0, beams.qp_r0} + beams.node_x0, beams.qp_r0 + } ); Kokkos::parallel_for( @@ -120,7 +122,8 @@ inline Beams CreateBeams(const BeamsInput& beams_input) { beams.num_nodes_per_element, beams.num_qps_per_element, beams.shape_interp, beams.shape_deriv, beams.qp_jacobian, beams.node_u, beams.node_u_dot, beams.node_u_ddot, beams.qp_x0, beams.qp_r0, beams.qp_u, beams.qp_u_prime, beams.qp_r, beams.qp_r_prime, - beams.qp_u_dot, beams.qp_omega, beams.qp_u_ddot, beams.qp_omega_dot, beams.qp_x} + beams.qp_u_dot, beams.qp_omega, beams.qp_u_ddot, beams.qp_omega_dot, beams.qp_x + } ); return beams; } diff --git a/src/beams/interpolate_to_quadrature_points.hpp b/src/beams/interpolate_to_quadrature_points.hpp index 1302b753..ade23d87 100644 --- a/src/beams/interpolate_to_quadrature_points.hpp +++ b/src/beams/interpolate_to_quadrature_points.hpp @@ -55,27 +55,30 @@ struct InterpolateToQuadraturePoints { Kokkos::TeamThreadRange(member, num_qps), InterpolateQPVector{ i_elem, num_nodes, shape_interp, - Kokkos::subview(node_u_dot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(0, 3)), qp_u_dot} + Kokkos::subview(node_u_dot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(0, 3)), qp_u_dot + } ); Kokkos::parallel_for( Kokkos::TeamThreadRange(member, num_qps), InterpolateQPVector{ i_elem, num_nodes, shape_interp, - Kokkos::subview(node_u_dot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(3, 6)), qp_omega} + Kokkos::subview(node_u_dot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(3, 6)), qp_omega + } ); Kokkos::parallel_for( Kokkos::TeamThreadRange(member, num_qps), InterpolateQPVector{ i_elem, num_nodes, shape_interp, - Kokkos::subview(node_u_ddot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(0, 3)), - qp_u_ddot} + Kokkos::subview(node_u_ddot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(0, 3)), qp_u_ddot + } ); Kokkos::parallel_for( Kokkos::TeamThreadRange(member, num_qps), InterpolateQPVector{ i_elem, num_nodes, shape_interp, Kokkos::subview(node_u_ddot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(3, 6)), - qp_omega_dot} + qp_omega_dot + } ); Kokkos::parallel_for( Kokkos::TeamThreadRange(member, num_qps), diff --git a/src/constraints/calculate_constraint_force.hpp b/src/constraints/calculate_constraint_force.hpp index a438c530..8127f78f 100644 --- a/src/constraints/calculate_constraint_force.hpp +++ b/src/constraints/calculate_constraint_force.hpp @@ -23,7 +23,8 @@ struct CalculateConstraintForce { case ConstraintType::kRevoluteJoint: { // Applies the torque from a revolute joint constraint to the system residual CalculateRevoluteJointForce{ - target_node_index, axes, inputs, node_u, system_residual_terms}(i_constraint); + target_node_index, axes, inputs, node_u, system_residual_terms + }(i_constraint); } break; default: { // Do nothing diff --git a/src/constraints/calculate_constraint_output.hpp b/src/constraints/calculate_constraint_output.hpp index 607ee487..80972077 100644 --- a/src/constraints/calculate_constraint_output.hpp +++ b/src/constraints/calculate_constraint_output.hpp @@ -26,7 +26,8 @@ struct CalculateConstraintOutput { case ConstraintType::kRevoluteJoint: { // Axis of rotation unit vector const auto joint_axis0_data = Kokkos::Array{ - axes(i_constraint, 0, 0), axes(i_constraint, 0, 1), axes(i_constraint, 0, 2)}; + axes(i_constraint, 0, 0), axes(i_constraint, 0, 1), axes(i_constraint, 0, 2) + }; const auto joint_axis0 = View_3::const_type{joint_axis0_data.data()}; // Target node index @@ -34,12 +35,14 @@ struct CalculateConstraintOutput { // Target node initial rotation const auto R0_data = Kokkos::Array{ - node_u(i_node, 3), node_u(i_node, 4), node_u(i_node, 5), node_u(i_node, 6)}; + node_u(i_node, 3), node_u(i_node, 4), node_u(i_node, 5), node_u(i_node, 6) + }; const auto R0 = View_Quaternion::const_type{R0_data.data()}; // Target node rotational displacement const auto R_data = Kokkos::Array{ - node_u(i_node, 3), node_u(i_node, 4), node_u(i_node, 5), node_u(i_node, 6)}; + node_u(i_node, 3), node_u(i_node, 4), node_u(i_node, 5), node_u(i_node, 6) + }; const auto R = View_Quaternion::const_type{R_data.data()}; // Calculate current orientation @@ -54,12 +57,14 @@ struct CalculateConstraintOutput { // Target node rotational velocity vector auto omega_data = Kokkos::Array{ - node_udot(i_node, 3), node_udot(i_node, 4), node_udot(i_node, 5)}; + node_udot(i_node, 3), node_udot(i_node, 4), node_udot(i_node, 5) + }; auto omega = View_3{omega_data.data()}; // Target node rotational acceleration vector auto omega_dot_data = Kokkos::Array{ - node_uddot(i_node, 3), node_uddot(i_node, 4), node_uddot(i_node, 5)}; + node_uddot(i_node, 3), node_uddot(i_node, 4), node_uddot(i_node, 5) + }; auto omega_dot = View_3{omega_dot_data.data()}; // Calculate joint axis in current configuration diff --git a/src/constraints/calculate_fixed_bc_constraint.hpp b/src/constraints/calculate_fixed_bc_constraint.hpp index 2d0267f1..558d316d 100644 --- a/src/constraints/calculate_fixed_bc_constraint.hpp +++ b/src/constraints/calculate_fixed_bc_constraint.hpp @@ -23,7 +23,8 @@ struct CalculateFixedBCConstraint { // Initial difference between nodes const auto X0_data = Kokkos::Array{ - X0_(i_constraint, 0), X0_(i_constraint, 1), X0_(i_constraint, 2)}; + X0_(i_constraint, 0), X0_(i_constraint, 1), X0_(i_constraint, 2) + }; const auto X0 = View_3::const_type{X0_data.data()}; // Base node displacement @@ -34,7 +35,8 @@ struct CalculateFixedBCConstraint { // Target node displacement const auto R2_data = Kokkos::Array{ - node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6)}; + node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6) + }; const auto R2 = Kokkos::View::const_type{R2_data.data()}; const auto u2_data = Kokkos::Array{node_u(i_node2, 0), node_u(i_node2, 1), node_u(i_node2, 2)}; diff --git a/src/constraints/calculate_prescribed_bc_constraint.hpp b/src/constraints/calculate_prescribed_bc_constraint.hpp index b05aefca..33937143 100644 --- a/src/constraints/calculate_prescribed_bc_constraint.hpp +++ b/src/constraints/calculate_prescribed_bc_constraint.hpp @@ -23,23 +23,27 @@ struct CalculatePrescribedBCConstraint { // Initial difference between nodes const auto X0_data = Kokkos::Array{ - X0_(i_constraint, 0), X0_(i_constraint, 1), X0_(i_constraint, 2)}; + X0_(i_constraint, 0), X0_(i_constraint, 1), X0_(i_constraint, 2) + }; const auto X0 = View_3::const_type{X0_data.data()}; // Base node displacement const auto u1_data = Kokkos::Array{ constraint_inputs(i_constraint, 0), constraint_inputs(i_constraint, 1), - constraint_inputs(i_constraint, 2)}; + constraint_inputs(i_constraint, 2) + }; auto u1 = View_3::const_type{u1_data.data()}; const auto R1_data = Kokkos::Array{ constraint_inputs(i_constraint, 3), constraint_inputs(i_constraint, 4), - constraint_inputs(i_constraint, 5), constraint_inputs(i_constraint, 6)}; + constraint_inputs(i_constraint, 5), constraint_inputs(i_constraint, 6) + }; const auto R1 = Kokkos::View::const_type{R1_data.data()}; // Target node displacement const auto R2_data = Kokkos::Array{ - node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6)}; + node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6) + }; const auto R2 = Kokkos::View::const_type{R2_data.data()}; const auto u2_data = Kokkos::Array{node_u(i_node2, 0), node_u(i_node2, 1), node_u(i_node2, 2)}; diff --git a/src/constraints/calculate_revolute_joint_constraint.hpp b/src/constraints/calculate_revolute_joint_constraint.hpp index 7a4bf6c4..0ed89ff6 100644 --- a/src/constraints/calculate_revolute_joint_constraint.hpp +++ b/src/constraints/calculate_revolute_joint_constraint.hpp @@ -27,7 +27,8 @@ struct CalculateRevoluteJointConstraint { // Initial difference between nodes const auto X0_data = Kokkos::Array{ - X0_(i_constraint, 0), X0_(i_constraint, 1), X0_(i_constraint, 2)}; + X0_(i_constraint, 0), X0_(i_constraint, 1), X0_(i_constraint, 2) + }; const auto X0 = View_3::const_type{X0_data.data()}; // Base node displacement @@ -35,12 +36,14 @@ struct CalculateRevoluteJointConstraint { Kokkos::Array{node_u(i_node1, 0), node_u(i_node1, 1), node_u(i_node1, 2)}; const auto u1 = View_3::const_type{u1_data.data()}; const auto R1_data = Kokkos::Array{ - node_u(i_node1, 3), node_u(i_node1, 4), node_u(i_node1, 5), node_u(i_node1, 6)}; + node_u(i_node1, 3), node_u(i_node1, 4), node_u(i_node1, 5), node_u(i_node1, 6) + }; const auto R1 = Kokkos::View::const_type{R1_data.data()}; // Target node displacement const auto R2_data = Kokkos::Array{ - node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6)}; + node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6) + }; const auto R2 = Kokkos::View::const_type{R2_data.data()}; const auto u2_data = Kokkos::Array{node_u(i_node2, 0), node_u(i_node2, 1), node_u(i_node2, 2)}; @@ -55,13 +58,16 @@ struct CalculateRevoluteJointConstraint { // Revolute joint constraint data const auto x0_data = Kokkos::Array{ - axes(i_constraint, 0, 0), axes(i_constraint, 0, 1), axes(i_constraint, 0, 2)}; + axes(i_constraint, 0, 0), axes(i_constraint, 0, 1), axes(i_constraint, 0, 2) + }; const auto x0 = View_3::const_type{x0_data.data()}; const auto y0_data = Kokkos::Array{ - axes(i_constraint, 1, 0), axes(i_constraint, 1, 1), axes(i_constraint, 1, 2)}; + axes(i_constraint, 1, 0), axes(i_constraint, 1, 1), axes(i_constraint, 1, 2) + }; const auto y0 = View_3::const_type{y0_data.data()}; const auto z0_data = Kokkos::Array{ - axes(i_constraint, 2, 0), axes(i_constraint, 2, 1), axes(i_constraint, 2, 2)}; + axes(i_constraint, 2, 0), axes(i_constraint, 2, 1), axes(i_constraint, 2, 2) + }; const auto z0 = View_3::const_type{z0_data.data()}; auto x_data = Kokkos::Array{}; const auto x = View_3{x_data.data()}; diff --git a/src/constraints/calculate_revolute_joint_force.hpp b/src/constraints/calculate_revolute_joint_force.hpp index 704c269c..38b53787 100644 --- a/src/constraints/calculate_revolute_joint_force.hpp +++ b/src/constraints/calculate_revolute_joint_force.hpp @@ -23,7 +23,8 @@ struct CalculateRevoluteJointForce { // Initial difference between nodes const auto X0_data = Kokkos::Array{ - axes(i_constraint, 0, 0), axes(i_constraint, 0, 1), axes(i_constraint, 0, 2)}; + axes(i_constraint, 0, 0), axes(i_constraint, 0, 1), axes(i_constraint, 0, 2) + }; const auto X0 = View_3::const_type{X0_data.data()}; auto R2_X0_data = Kokkos::Array{}; @@ -31,7 +32,8 @@ struct CalculateRevoluteJointForce { // Target node displacement const auto R2_data = Kokkos::Array{ - node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6)}; + node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6) + }; const auto R2 = Kokkos::View::const_type{R2_data.data()}; //---------------------------------------------------------------------- diff --git a/src/constraints/calculate_rigid_joint_constraint.hpp b/src/constraints/calculate_rigid_joint_constraint.hpp index ba1730b9..1b2325a3 100644 --- a/src/constraints/calculate_rigid_joint_constraint.hpp +++ b/src/constraints/calculate_rigid_joint_constraint.hpp @@ -26,7 +26,8 @@ struct CalculateRigidJointConstraint { // Initial difference between nodes const auto X0_data = Kokkos::Array{ - X0_(i_constraint, 0), X0_(i_constraint, 1), X0_(i_constraint, 2)}; + X0_(i_constraint, 0), X0_(i_constraint, 1), X0_(i_constraint, 2) + }; const auto X0 = View_3::const_type{X0_data.data()}; // Base node displacement @@ -34,12 +35,14 @@ struct CalculateRigidJointConstraint { Kokkos::Array{node_u(i_node1, 0), node_u(i_node1, 1), node_u(i_node1, 2)}; const auto u1 = View_3::const_type{u1_data.data()}; const auto R1_data = Kokkos::Array{ - node_u(i_node1, 3), node_u(i_node1, 4), node_u(i_node1, 5), node_u(i_node1, 6)}; + node_u(i_node1, 3), node_u(i_node1, 4), node_u(i_node1, 5), node_u(i_node1, 6) + }; const auto R1 = Kokkos::View::const_type{R1_data.data()}; // Target node displacement const auto R2_data = Kokkos::Array{ - node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6)}; + node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6) + }; const auto R2 = Kokkos::View::const_type{R2_data.data()}; const auto u2_data = Kokkos::Array{node_u(i_node2, 0), node_u(i_node2, 1), node_u(i_node2, 2)}; diff --git a/src/constraints/calculate_rotation_control_constraint.hpp b/src/constraints/calculate_rotation_control_constraint.hpp index bd4bcb80..e3cf0e7a 100644 --- a/src/constraints/calculate_rotation_control_constraint.hpp +++ b/src/constraints/calculate_rotation_control_constraint.hpp @@ -27,20 +27,23 @@ struct CalculateRotationControlConstraint { // Initial difference between nodes const auto X0_data = Kokkos::Array{ - X0_(i_constraint, 0), X0_(i_constraint, 1), X0_(i_constraint, 2)}; + X0_(i_constraint, 0), X0_(i_constraint, 1), X0_(i_constraint, 2) + }; const auto X0 = View_3::const_type{X0_data.data()}; // Base node displacement const auto u1_data = Kokkos::Array{node_u(i_node1, 0), node_u(i_node1, 1), node_u(i_node1, 2)}; const auto R1_data = Kokkos::Array{ - node_u(i_node1, 3), node_u(i_node1, 4), node_u(i_node1, 5), node_u(i_node1, 6)}; + node_u(i_node1, 3), node_u(i_node1, 4), node_u(i_node1, 5), node_u(i_node1, 6) + }; const auto u1 = View_3::const_type{u1_data.data()}; const auto R1 = Kokkos::View::const_type{R1_data.data()}; // Target node displacement const auto R2_data = Kokkos::Array{ - node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6)}; + node_u(i_node2, 3), node_u(i_node2, 4), node_u(i_node2, 5), node_u(i_node2, 6) + }; const auto R2 = Kokkos::View::const_type{R2_data.data()}; const auto u2_data = Kokkos::Array{node_u(i_node2, 0), node_u(i_node2, 1), node_u(i_node2, 2)}; diff --git a/src/constraints/constraint.hpp b/src/constraints/constraint.hpp index cd424bc3..65c65d05 100644 --- a/src/constraints/constraint.hpp +++ b/src/constraints/constraint.hpp @@ -74,7 +74,8 @@ struct Constraint { v[2] * v[0] * k - v[1], v[2] * v[1] * k + v[0], v[2] * v[2] * k + c, - }}}; + }} + }; // Set orthogonal unit vectors from the rotation matrix x_axis = {R[0][0], R[1][0], R[2][0]}; diff --git a/src/model/node.hpp b/src/model/node.hpp index 550abe85..4fe4e00e 100644 --- a/src/model/node.hpp +++ b/src/model/node.hpp @@ -48,7 +48,8 @@ struct Node { void Rotate(const Array_3& axis, double angle) { auto q = Array_4{ cos(angle / 2.), sin(angle / 2.) * axis[0], sin(angle / 2.) * axis[1], - sin(angle / 2.) * axis[2]}; + sin(angle / 2.) * axis[2] + }; Rotate(q); } }; diff --git a/src/solver/solver.hpp b/src/solver/solver.hpp index f61236cc..a6a164e4 100644 --- a/src/solver/solver.hpp +++ b/src/solver/solver.hpp @@ -151,7 +151,8 @@ struct Solver { "PopulateSparseRowPtrsColInds_Constraints", 1, PopulateSparseRowPtrsColInds_Constraints{ constraint_type, constraint_base_node_index, constraint_target_node_index, - constraint_row_range, B_row_ptrs, B_col_ind} + constraint_row_range, B_row_ptrs, B_col_ind + } ); auto B_values = ValuesType("B values", B_num_non_zero); KokkosSparse::sort_crs_matrix(B_row_ptrs, B_col_ind, B_values); @@ -172,7 +173,8 @@ struct Solver { "PopulateSparseRowPtrsColInds_Transpose", 1, PopulateSparseRowPtrsColInds_Transpose{ B_num_rows, B_num_columns, B_row_ptrs, B_col_ind, col_count, tmp_row_ptrs, - B_t_row_ptrs, B_t_col_inds} + B_t_row_ptrs, B_t_col_inds + } ); B_t = CrsMatrixType( "B_t", static_cast(B_t_num_rows), static_cast(B_t_num_columns), @@ -203,7 +205,8 @@ struct Solver { Kokkos::parallel_for( "FillUnshiftedRowPtrs", num_dofs + 1, FillUnshiftedRowPtrs{ - num_system_dofs, system_matrix.graph.row_map, system_matrix_full_row_ptrs} + num_system_dofs, system_matrix.graph.row_map, system_matrix_full_row_ptrs + } ); system_matrix_full = CrsMatrixType( "system_matrix_full", static_cast(num_dofs), static_cast(num_dofs), @@ -230,7 +233,8 @@ struct Solver { Kokkos::parallel_for( "FillUnshiftedRowPtrs", num_dofs + 1, FillUnshiftedRowPtrs{ - num_system_dofs, B_t.graph.row_map, transpose_matrix_full_row_ptrs} + num_system_dofs, B_t.graph.row_map, transpose_matrix_full_row_ptrs + } ); auto transpose_matrix_full_indices = IndicesType("transpose_matrix_full_indices", B_t.nnz()); Kokkos::deep_copy(transpose_matrix_full_indices, static_cast(num_system_dofs)); diff --git a/src/state/calculate_displacement.hpp b/src/state/calculate_displacement.hpp index 5aec741b..f0cfa2ee 100644 --- a/src/state/calculate_displacement.hpp +++ b/src/state/calculate_displacement.hpp @@ -19,7 +19,8 @@ struct CalculateDisplacement { } auto delta_data = Kokkos::Array{ - h * q_delta(i_node, 3), h * q_delta(i_node, 4), h * q_delta(i_node, 5)}; + h * q_delta(i_node, 3), h * q_delta(i_node, 4), h * q_delta(i_node, 5) + }; auto delta = Kokkos::View>{delta_data.data()}; @@ -28,7 +29,8 @@ struct CalculateDisplacement { Kokkos::View>{quat_delta_data.data()}; RotationVectorToQuaternion(delta, quat_delta); auto quat_prev_data = Kokkos::Array{ - q_prev(i_node, 3), q_prev(i_node, 4), q_prev(i_node, 5), q_prev(i_node, 6)}; + q_prev(i_node, 3), q_prev(i_node, 4), q_prev(i_node, 5), q_prev(i_node, 6) + }; auto quat_prev = Kokkos::View>{quat_prev_data.data()}; auto quat_new_data = Kokkos::Array{}; diff --git a/src/step/assemble_constraints_matrix.hpp b/src/step/assemble_constraints_matrix.hpp index 93832da7..d0f71619 100644 --- a/src/step/assemble_constraints_matrix.hpp +++ b/src/step/assemble_constraints_matrix.hpp @@ -26,7 +26,8 @@ inline void AssembleConstraintsMatrix(Solver& solver, Constraints& constraints) CopyConstraintsToSparseMatrix{ constraints.row_range, constraints.base_node_col_range, constraints.target_node_col_range, solver.B, constraints.base_gradient_terms, - constraints.target_gradient_terms} + constraints.target_gradient_terms + } ); } diff --git a/src/step/assemble_constraints_residual.hpp b/src/step/assemble_constraints_residual.hpp index 0f8d4dd3..45191e60 100644 --- a/src/step/assemble_constraints_residual.hpp +++ b/src/step/assemble_constraints_residual.hpp @@ -20,7 +20,8 @@ inline void AssembleConstraintsResidual(Solver& solver, Constraints& constraints Kokkos::parallel_for( "ContributeConstraintsSystemResidualToVector", constraints.num, ContributeConstraintsSystemResidualToVector{ - constraints.target_node_index, solver.R, constraints.system_residual_terms} + constraints.target_node_index, solver.R, constraints.system_residual_terms + } ); auto R = Solver::ValuesType( @@ -44,7 +45,8 @@ inline void AssembleConstraintsResidual(Solver& solver, Constraints& constraints CopyConstraintsResidualToVector{ constraints.row_range, Kokkos::subview(solver.R, Kokkos::make_pair(solver.num_system_dofs, solver.num_dofs)), - constraints.residual_terms} + constraints.residual_terms + } ); } diff --git a/src/step/assemble_inertia_matrix.hpp b/src/step/assemble_inertia_matrix.hpp index 0e268cc1..0c65eee8 100644 --- a/src/step/assemble_inertia_matrix.hpp +++ b/src/step/assemble_inertia_matrix.hpp @@ -20,7 +20,8 @@ inline void AssembleInertiaMatrix(const Beams& beams, double beta_prime, double IntegrateInertiaMatrix{ beams.num_nodes_per_element, beams.num_qps_per_element, beams.qp_weight, beams.qp_jacobian, beams.shape_interp, beams.qp_Muu, beams.qp_Guu, beta_prime, - gamma_prime, beams.inertia_matrix_terms} + gamma_prime, beams.inertia_matrix_terms + } ); } diff --git a/src/step/assemble_residual_vector.hpp b/src/step/assemble_residual_vector.hpp index 4cf1172f..5327ca24 100644 --- a/src/step/assemble_residual_vector.hpp +++ b/src/step/assemble_residual_vector.hpp @@ -25,7 +25,8 @@ inline void AssembleResidualVector(const Beams& beams) { IntegrateResidualVector{ beams.num_nodes_per_element, beams.num_qps_per_element, beams.qp_weight, beams.qp_jacobian, beams.shape_interp, beams.shape_deriv, beams.node_FX, beams.qp_Fc, - beams.qp_Fd, beams.qp_Fi, beams.qp_Fg, beams.residual_vector_terms} + beams.qp_Fd, beams.qp_Fi, beams.qp_Fg, beams.residual_vector_terms + } ); } diff --git a/src/step/assemble_system_matrix.hpp b/src/step/assemble_system_matrix.hpp index 6c6f5405..dcfa7869 100644 --- a/src/step/assemble_system_matrix.hpp +++ b/src/step/assemble_system_matrix.hpp @@ -30,7 +30,8 @@ inline void AssembleSystemMatrix(Solver& solver, Beams& beams) { Kokkos::parallel_for( "ContributeElementsToSparseMatrix", sparse_matrix_policy, ContributeElementsToSparseMatrix{ - solver.K, beams.stiffness_matrix_terms} + solver.K, beams.stiffness_matrix_terms + } ); Kokkos::fence(); diff --git a/src/step/assemble_system_residual.hpp b/src/step/assemble_system_residual.hpp index b08045ee..7edb572d 100644 --- a/src/step/assemble_system_residual.hpp +++ b/src/step/assemble_system_residual.hpp @@ -20,7 +20,8 @@ inline void AssembleSystemResidual(Solver& solver, Beams& beams) { "ContributeElementsToVector", vector_policy, ContributeElementsToVector{ beams.num_nodes_per_element, beams.node_state_indices, beams.residual_vector_terms, - solver.R} + solver.R + } ); } diff --git a/src/step/update_constraint_variables.hpp b/src/step/update_constraint_variables.hpp index ab582ae1..8bb1e653 100644 --- a/src/step/update_constraint_variables.hpp +++ b/src/step/update_constraint_variables.hpp @@ -23,7 +23,8 @@ inline void UpdateConstraintVariables(State& state, Constraints& constraints) { "CalculateConstraintForce", constraints.num, CalculateConstraintForce{ constraints.type, constraints.target_node_index, constraints.axes, constraints.input, - state.q, constraints.system_residual_terms} + state.q, constraints.system_residual_terms + } ); Kokkos::parallel_for( @@ -31,7 +32,8 @@ inline void UpdateConstraintVariables(State& state, Constraints& constraints) { CalculateConstraintResidualGradient{ constraints.type, constraints.base_node_index, constraints.target_node_index, constraints.X0, constraints.axes, constraints.input, state.q, constraints.residual_terms, - constraints.base_gradient_terms, constraints.target_gradient_terms} + constraints.base_gradient_terms, constraints.target_gradient_terms + } ); } diff --git a/src/step/update_system_variables.hpp b/src/step/update_system_variables.hpp index 36150ac1..cdf0763a 100644 --- a/src/step/update_system_variables.hpp +++ b/src/step/update_system_variables.hpp @@ -39,7 +39,8 @@ inline void UpdateSystemVariables(StepParameters& parameters, const Beams& beams beams.num_nodes_per_element, beams.num_qps_per_element, beams.shape_interp, beams.shape_deriv, beams.qp_jacobian, beams.node_u, beams.node_u_dot, beams.node_u_ddot, beams.qp_x0, beams.qp_r0, beams.qp_u, beams.qp_u_prime, beams.qp_r, beams.qp_r_prime, - beams.qp_u_dot, beams.qp_omega, beams.qp_u_ddot, beams.qp_omega_dot, beams.qp_x} + beams.qp_u_dot, beams.qp_omega, beams.qp_u_ddot, beams.qp_omega_dot, beams.qp_x + } ); Kokkos::parallel_for( @@ -80,7 +81,8 @@ inline void UpdateSystemVariables(StepParameters& parameters, const Beams& beams beams.qp_Puu, beams.qp_Quu, beams.qp_Guu, - beams.qp_Kuu} + beams.qp_Kuu + } ); AssembleResidualVector(beams); diff --git a/src/system/calculate_RR0.hpp b/src/system/calculate_RR0.hpp index cc9d7f34..058df25a 100644 --- a/src/system/calculate_RR0.hpp +++ b/src/system/calculate_RR0.hpp @@ -14,7 +14,8 @@ struct CalculateRR0 { KOKKOS_FUNCTION void operator()(const int i_qp) const { auto RR0_quaternion_data = Kokkos::Array{ qp_x_(i_elem, i_qp, 3), qp_x_(i_elem, i_qp, 4), qp_x_(i_elem, i_qp, 5), - qp_x_(i_elem, i_qp, 6)}; + qp_x_(i_elem, i_qp, 6) + }; auto RR0_quaternion = Kokkos::View::const_type(RR0_quaternion_data.data()); auto RR0_data = Kokkos::Array{}; auto RR0 = View_3x3(RR0_data.data()); diff --git a/src/system/calculate_quadrature_point_values.hpp b/src/system/calculate_quadrature_point_values.hpp index b566818a..237d5988 100644 --- a/src/system/calculate_quadrature_point_values.hpp +++ b/src/system/calculate_quadrature_point_values.hpp @@ -99,7 +99,8 @@ struct CalculateQuadraturePointValues { Kokkos::TeamThreadRange(member, num_qps), CalculateInertialForces{ i_elem, qp_Muu_, qp_u_ddot_, qp_omega_, qp_omega_dot_, qp_eta_tilde_, - qp_omega_tilde_, qp_omega_dot_tilde_, qp_rho_, qp_eta_, qp_FI_} + qp_omega_tilde_, qp_omega_dot_tilde_, qp_rho_, qp_eta_, qp_FI_ + } ); member.team_barrier(); @@ -131,14 +132,16 @@ struct CalculateQuadraturePointValues { Kokkos::parallel_for( Kokkos::TeamThreadRange(member, num_qps), CalculateGyroscopicMatrix{ - i_elem, qp_Muu_, qp_omega_, qp_omega_tilde_, qp_rho_, qp_eta_, qp_Guu_} + i_elem, qp_Muu_, qp_omega_, qp_omega_tilde_, qp_rho_, qp_eta_, qp_Guu_ + } ); Kokkos::parallel_for( Kokkos::TeamThreadRange(member, num_qps), CalculateInertiaStiffnessMatrix{ i_elem, qp_Muu_, qp_u_ddot_, qp_omega_, qp_omega_dot_, qp_omega_tilde_, - qp_omega_dot_tilde_, qp_rho_, qp_eta_, qp_Kuu_} + qp_omega_dot_tilde_, qp_rho_, qp_eta_, qp_Kuu_ + } ); } }; diff --git a/src/system/calculate_strain.hpp b/src/system/calculate_strain.hpp index 1d5b6bec..923adb03 100644 --- a/src/system/calculate_strain.hpp +++ b/src/system/calculate_strain.hpp @@ -23,15 +23,17 @@ struct CalculateStrain { void operator()(const int i_qp) const { auto x0_prime_data = Kokkos::Array{ qp_x0_prime_(i_elem, i_qp, 0), qp_x0_prime_(i_elem, i_qp, 1), - qp_x0_prime_(i_elem, i_qp, 2)}; + qp_x0_prime_(i_elem, i_qp, 2) + }; auto x0_prime = Kokkos::View(x0_prime_data.data()); auto u_prime_data = Kokkos::Array{ - qp_u_prime_(i_elem, i_qp, 0), qp_u_prime_(i_elem, i_qp, 1), - qp_u_prime_(i_elem, i_qp, 2)}; + qp_u_prime_(i_elem, i_qp, 0), qp_u_prime_(i_elem, i_qp, 1), qp_u_prime_(i_elem, i_qp, 2) + }; auto u_prime = Kokkos::View(u_prime_data.data()); auto R_data = Kokkos::Array{ qp_r_(i_elem, i_qp, 0), qp_r_(i_elem, i_qp, 1), qp_r_(i_elem, i_qp, 2), - qp_r_(i_elem, i_qp, 3)}; + qp_r_(i_elem, i_qp, 3) + }; auto R = Kokkos::View(R_data.data()); auto R_x0_prime_data = Kokkos::Array{}; @@ -46,7 +48,8 @@ struct CalculateStrain { QuaternionDerivative(R, E); auto R_prime_data = Kokkos::Array{ qp_r_prime_(i_elem, i_qp, 0), qp_r_prime_(i_elem, i_qp, 1), qp_r_prime_(i_elem, i_qp, 2), - qp_r_prime_(i_elem, i_qp, 3)}; + qp_r_prime_(i_elem, i_qp, 3) + }; auto R_prime = Kokkos::View(R_prime_data.data()); auto e2_data = Kokkos::Array{}; auto e2 = Kokkos::View{e2_data.data()}; diff --git a/src/system/calculate_temporary_variables.hpp b/src/system/calculate_temporary_variables.hpp index 04109d79..fde710cc 100644 --- a/src/system/calculate_temporary_variables.hpp +++ b/src/system/calculate_temporary_variables.hpp @@ -17,11 +17,12 @@ struct CalculateTemporaryVariables { void operator()(int i_qp) const { auto x0pup_data = Kokkos::Array{ qp_x0_prime_(i_elem, i_qp, 0), qp_x0_prime_(i_elem, i_qp, 1), - qp_x0_prime_(i_elem, i_qp, 2)}; + qp_x0_prime_(i_elem, i_qp, 2) + }; auto x0pup = View_3{x0pup_data.data()}; auto u_prime_data = Kokkos::Array{ - qp_u_prime_(i_elem, i_qp, 0), qp_u_prime_(i_elem, i_qp, 1), - qp_u_prime_(i_elem, i_qp, 2)}; + qp_u_prime_(i_elem, i_qp, 0), qp_u_prime_(i_elem, i_qp, 1), qp_u_prime_(i_elem, i_qp, 2) + }; auto u_prime = View_3{u_prime_data.data()}; KokkosBlas::serial_axpy(1., u_prime, x0pup); auto tmp_data = Kokkos::Array{}; diff --git a/src/system/integrate_inertia_matrix.hpp b/src/system/integrate_inertia_matrix.hpp index 960bfdd5..9c089347 100644 --- a/src/system/integrate_inertia_matrix.hpp +++ b/src/system/integrate_inertia_matrix.hpp @@ -117,9 +117,10 @@ struct IntegrateInertiaMatrix { member.team_barrier(); const auto node_range = Kokkos::TeamThreadRange(member, num_nodes * simd_nodes); - const auto element_integrator = IntegrateInertiaMatrixElement{ - i_elem, num_nodes, num_qps, qp_weight, qp_jacobian, shape_interp, - qp_Muu, qp_Guu, beta_prime_, gamma_prime_, gbl_M_}; + const auto element_integrator = + IntegrateInertiaMatrixElement{i_elem, num_nodes, num_qps, qp_weight, + qp_jacobian, shape_interp, qp_Muu, qp_Guu, + beta_prime_, gamma_prime_, gbl_M_}; Kokkos::parallel_for(node_range, element_integrator); } }; diff --git a/src/system/integrate_residual_vector.hpp b/src/system/integrate_residual_vector.hpp index 6a337e06..cd60557e 100644 --- a/src/system/integrate_residual_vector.hpp +++ b/src/system/integrate_residual_vector.hpp @@ -92,7 +92,8 @@ struct IntegrateResidualVector { const auto node_range = Kokkos::TeamThreadRange(member, num_nodes); const auto element_integrator = IntegrateResidualVectorElement{ i_elem, num_qps, qp_weight, qp_jacobian, shape_interp, shape_deriv, - node_FX, qp_Fc, qp_Fd, qp_Fi, qp_Fg, residual_vector_terms_}; + node_FX, qp_Fc, qp_Fd, qp_Fi, qp_Fg, residual_vector_terms_ + }; Kokkos::parallel_for(node_range, element_integrator); } }; diff --git a/src/system/integrate_stiffness_matrix.hpp b/src/system/integrate_stiffness_matrix.hpp index 4ae8f0a6..99f9757b 100644 --- a/src/system/integrate_stiffness_matrix.hpp +++ b/src/system/integrate_stiffness_matrix.hpp @@ -138,7 +138,8 @@ struct IntegrateStiffnessMatrix { const auto node_range = Kokkos::TeamThreadRange(member, num_nodes * simd_nodes); const auto element_integrator = IntegrateStiffnessMatrixElement{ i_elem, num_nodes, num_qps, qp_weight, qp_jacobian, shape_interp, shape_deriv, - qp_Kuu, qp_Puu, qp_Cuu, qp_Ouu, qp_Quu, gbl_M_}; + qp_Kuu, qp_Puu, qp_Cuu, qp_Ouu, qp_Quu, gbl_M_ + }; Kokkos::parallel_for(node_range, element_integrator); } }; diff --git a/src/system/update_node_state.hpp b/src/system/update_node_state.hpp index e78da2cc..6991b7a8 100644 --- a/src/system/update_node_state.hpp +++ b/src/system/update_node_state.hpp @@ -47,7 +47,8 @@ struct UpdateNodeState { Kokkos::parallel_for( Kokkos::TeamThreadRange(member, num_nodes), UpdateNodeStateElement{ - i_elem, node_state_indices, node_u, node_u_dot, node_u_ddot, Q, V, A} + i_elem, node_state_indices, node_u, node_u_dot, node_u_ddot, Q, V, A + } ); } }; diff --git a/src/utilities/scripts/windio_mapped_structs.hpp b/src/utilities/scripts/windio_mapped_structs.hpp index 47629e91..c719b7f5 100644 --- a/src/utilities/scripts/windio_mapped_structs.hpp +++ b/src/utilities/scripts/windio_mapped_structs.hpp @@ -1131,9 +1131,9 @@ struct Drivetrain { // simple generator scaling double generator_mass_user{}; // User input override of generator mass, only used when using // simple generator mass scaling - GeneratorRpmEfficiencyUser - generator_rpm_efficiency_user{}; // User input override of generator rpm-efficiency values, - // with rpm as grid input and eff as values input + GeneratorRpmEfficiencyUser generator_rpm_efficiency_user{ + }; // User input override of generator rpm-efficiency values, + // with rpm as grid input and eff as values input double gear_ratio{}; // Gear ratio of the drivetrain. Set it to 1 for direct drive machines. double gearbox_length_user{}; // User input override of gearbox length along shaft, only used // when using gearbox_mass_user is > 0 @@ -1608,10 +1608,10 @@ struct OuterShape_1 { std::vector side_lengths2; // Polygon side lengths at joint1 std::vector angles; // Polygon angles with the ordering such that angle[i] is between // side_length[i] and side_length[i+1] - double - rotation{}; // Angle between principle axes of the cross-section and the member coordinate - // system. Essentially the rotation of the member if both joints were placed - // on the global x-y axis with the first side length along the z-axis + double rotation{ + }; // Angle between principle axes of the cross-section and the member coordinate + // system. Essentially the rotation of the member if both joints were placed + // on the global x-y axis with the first side length along the z-axis void parse(const YAML::Node& node) { shape = node["shape"].as(""); @@ -1916,9 +1916,9 @@ struct LineTypes { // having the same displacement per unit length std::string type; // Type of material for property lookup double mass_density{}; // mass per unit length (in air) - double - stiffness{}; // axial line stiffness, product of elasticity modulus and cross-sectional area - double cost{}; // cost per unit length + double stiffness{ + }; // axial line stiffness, product of elasticity modulus and cross-sectional area + double cost{}; // cost per unit length double breaking_load{}; // line break tension double damping{}; // internal damping (BA) double transverse_added_mass{}; // transverse added mass coefficient (with respect to line @@ -2171,9 +2171,9 @@ struct Materials { // https://www.nrel.gov/docs/fy19osti/73585.pdf to define the manufacturing // process behind the laminate. 0 - coating, 1 - sandwich filler , 2 - shell // skin, 3 - shear webs, 4 - spar caps, 5 - TE reinf. - double - waste{}; // Fraction of material that ends up wasted during manufacturing. This quantity is - // used in the NREL blade cost model https://www.nrel.gov/docs/fy19osti/73585.pdf + double waste{ + }; // Fraction of material that ends up wasted during manufacturing. This quantity is + // used in the NREL blade cost model https://www.nrel.gov/docs/fy19osti/73585.pdf double roll_mass{}; // Mass of a fabric roll. This quantity is used in the NREL blade cost model // https://www.nrel.gov/docs/fy19osti/73585.pdf double gic{}; // Mode 1 critical energy-release rate. It is used by NuMAD from Sandia National @@ -2408,11 +2408,11 @@ struct Costs { // cost/rating ratio double crane_cost{}; // crane cost if present double electricity_price{}; // Electricity price used to compute value in beyond lcoe metrics - double - reserve_margin_price{}; // Reserve margin price used to compute value in beyond lcoe metrics - double capacity_credit{}; // Capacity credit used to compute value in beyond lcoe metrics - double - benchmark_price{}; // Benchmark price used to nondimensionalize value in beyond lcoe metrics + double reserve_margin_price{ + }; // Reserve margin price used to compute value in beyond lcoe metrics + double capacity_credit{}; // Capacity credit used to compute value in beyond lcoe metrics + double benchmark_price{ + }; // Benchmark price used to nondimensionalize value in beyond lcoe metrics void parse(const YAML::Node& node) { wake_loss_factor = node["wake_loss_factor"].as(0.); diff --git a/src/vendor/dylib/dylib.hpp b/src/vendor/dylib/dylib.hpp index be99f3e2..6fef016b 100644 --- a/src/vendor/dylib/dylib.hpp +++ b/src/vendor/dylib/dylib.hpp @@ -175,22 +175,19 @@ class dylib { #ifdef DYLIB_CPP17 explicit dylib(const std::filesystem::path& lib_path) - : dylib("", lib_path.string().c_str(), no_filename_decorations) { - } + : dylib("", lib_path.string().c_str(), no_filename_decorations) {} dylib( const std::filesystem::path& dir_path, const std::string& lib_name, bool decorations = add_filename_decorations ) - : dylib(dir_path.string().c_str(), lib_name.c_str(), decorations) { - } + : dylib(dir_path.string().c_str(), lib_name.c_str(), decorations) {} dylib( const std::filesystem::path& dir_path, const char* lib_name, bool decorations = add_filename_decorations ) - : dylib(dir_path.string().c_str(), lib_name, decorations) { - } + : dylib(dir_path.string().c_str(), lib_name, decorations) {} #endif ///@} @@ -307,9 +304,7 @@ class dylib { /** * @return the dynamic library handle */ - native_handle_type native_handle() noexcept { - return m_handle; - } + native_handle_type native_handle() noexcept { return m_handle; } protected: native_handle_type m_handle{nullptr}; diff --git a/tests/unit_tests/beams/test_interpolate_QP_vector.cpp b/tests/unit_tests/beams/test_interpolate_QP_vector.cpp index 28a442ca..9e0a0593 100644 --- a/tests/unit_tests/beams/test_interpolate_QP_vector.cpp +++ b/tests/unit_tests/beams/test_interpolate_QP_vector.cpp @@ -30,7 +30,8 @@ TEST(InterpolateQPVectorTests, OneNodeOneQP) { num_qp, InterpolateQPVector{ 0U, num_nodes, shape_interp, - Kokkos::subview(node_u_dot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(0, 3)), qp_u_dot} + Kokkos::subview(node_u_dot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(0, 3)), qp_u_dot + } ); auto qp_u_dot_mirror = Kokkos::create_mirror(qp_u_dot); Kokkos::deep_copy(qp_u_dot_mirror, qp_u_dot); @@ -50,7 +51,8 @@ TEST(InterpolateQPVectorTests, OneNodeTwoQP) { num_qp, InterpolateQPVector{ 0, num_nodes, shape_interp, - Kokkos::subview(node_u_dot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(0, 3)), qp_u_dot} + Kokkos::subview(node_u_dot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(0, 3)), qp_u_dot + } ); auto qp_u_dot_mirror = Kokkos::create_mirror(qp_u_dot); Kokkos::deep_copy(qp_u_dot_mirror, qp_u_dot); @@ -89,7 +91,8 @@ TEST(InterpolateQPVectorTests, TwoNodeTwoQP) { num_qp, InterpolateQPVector{ 0U, num_nodes, shape_interp, - Kokkos::subview(node_u_dot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(0, 3)), qp_u_dot} + Kokkos::subview(node_u_dot, Kokkos::ALL, Kokkos::ALL, Kokkos::pair(0, 3)), qp_u_dot + } ); auto qp_u_dot_mirror = Kokkos::create_mirror(qp_u_dot); Kokkos::deep_copy(qp_u_dot_mirror, qp_u_dot); diff --git a/tests/unit_tests/regression/iea15_rotor_data.hpp b/tests/unit_tests/regression/iea15_rotor_data.hpp index 80d29af4..f718ffa6 100644 --- a/tests/unit_tests/regression/iea15_rotor_data.hpp +++ b/tests/unit_tests/regression/iea15_rotor_data.hpp @@ -9,7 +9,8 @@ namespace openturbine::tests { static constexpr auto node_xi = std::array{ -1., -0.93400143040805916, -0.78448347366314441, -0.56523532699620493, -0.29575813558693936, 0., 0.29575813558693936, 0.56523532699620493, 0.78448347366314441, 0.93400143040805916, - 1.}; + 1. +}; // Node Coordinates static constexpr auto node_coords = std::array{ @@ -29,23 +30,30 @@ static constexpr auto node_coords = std::array{ // Node Rotation static constexpr auto node_rotation = std::array{ std::array{ - 0.9907227443578874, -0.13566457224703346, 0.007899216411874178, 0.0010816788266006607}, + 0.9907227443578874, -0.13566457224703346, 0.007899216411874178, 0.0010816788266006607 + }, std::array{ - 0.9909252982620628, -0.13426519082440538, 0.006260373215503113, 0.0008482478001981521}, + 0.9909252982620628, -0.13426519082440538, 0.006260373215503113, 0.0008482478001981521 + }, std::array{0.9935167796154206, -0.11359784336864302, 0.00443636138538857, 0.0005072496973620513}, std::array{0.9974116582281851, -0.0718560788024431, 0.002579425796743258, 0.0001858284107537662}, std::array{ - 0.9993668285851437, -0.035436934270507724, -0.0031863532939427376, -0.00011298613182902174}, + 0.9993668285851437, -0.035436934270507724, -0.0031863532939427376, -0.00011298613182902174 + }, std::array{ - 0.9997694376789568, -0.01571896564353078, -0.014626439008272178, -0.00022996551364081713}, + 0.9997694376789568, -0.01571896564353078, -0.014626439008272178, -0.00022996551364081713 + }, std::array{ - 0.9996244263618369, -0.00011737820053802088, -0.027404241145705933, -3.21786905958282e-06}, + 0.9996244263618369, -0.00011737820053802088, -0.027404241145705933, -3.21786905958282e-06 + }, std::array{ - 0.9992258769980531, 0.014354416057417973, -0.03662404497855903, 0.0005261240640677012}, + 0.9992258769980531, 0.014354416057417973, -0.03662404497855903, 0.0005261240640677012 + }, std::array{0.998912306432686, 0.018760692766230495, -0.04268018194796855, 0.000801581655943506}, std::array{0.9987446969974336, 0.014486925484536069, -0.0479445051145804, 0.0006954414627441799}, std::array{ - 0.9986289781304243, 0.010827457815091479, -0.051211540599741034, 0.000555252058204468}, + 0.9986289781304243, 0.010827457815091479, -0.051211540599741034, 0.000555252058204468 + }, }; // Element quadrature diff --git a/tests/unit_tests/regression/test_beams.cpp b/tests/unit_tests/regression/test_beams.cpp index 1ecede0c..9acfcacb 100644 --- a/tests/unit_tests/regression/test_beams.cpp +++ b/tests/unit_tests/regression/test_beams.cpp @@ -68,8 +68,8 @@ inline auto SetUpBeams() { -0.01428571428571428, -0.01428571428571428} ); model.AddNode( - {5, 1, -1, 0.9210746582719719, -0.07193653093139739, 0.20507529985516368, - 0.32309554437664584}, + {5, 1, -1, 0.9210746582719719, -0.07193653093139739, 0.20507529985516368, 0.32309554437664584 + }, {0.1, 0, 0.12, 0.9987502603949663, 0.04997916927067825, 0, 0}, {0.1, 0, 0.12, 0.1, 0, 0.12}, {0.1, 0.1, 0.22000000000000003, 0.1, 0, 0} ); diff --git a/tests/unit_tests/regression/test_math.cpp b/tests/unit_tests/regression/test_math.cpp index 08f13c90..9d92a227 100644 --- a/tests/unit_tests/regression/test_math.cpp +++ b/tests/unit_tests/regression/test_math.cpp @@ -238,9 +238,7 @@ TEST(QuaternionTest, QuaternionToRotationVector_2) { void TestVecTilde(const Kokkos::View& v, const std::vector>& exact) { auto m = Kokkos::View("m"); - Kokkos::parallel_for( - "VecTilde", 1, KOKKOS_LAMBDA(int) { VecTilde(v, m); } - ); + Kokkos::parallel_for("VecTilde", 1, KOKKOS_LAMBDA(int) { VecTilde(v, m); }); expect_kokkos_view_2D_equal(m, exact); } @@ -314,9 +312,7 @@ TEST(VectorTest, VectorTest_UnitVector_Set3_Test) { inline void test_AX_Matrix() { auto A = Create2DView<3, 3>({{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}}); auto out = Create2DView<3, 3>({{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}); - Kokkos::parallel_for( - 1, KOKKOS_LAMBDA(int) { AX_Matrix(A, out); } - ); + Kokkos::parallel_for(1, KOKKOS_LAMBDA(int) { AX_Matrix(A, out); }); auto tmp = kokkos_view_2D_to_vector(out); expect_kokkos_view_2D_equal( out, diff --git a/tests/unit_tests/regression/test_rotating_beam.cpp b/tests/unit_tests/regression/test_rotating_beam.cpp index 369c81e6..199c355b 100644 --- a/tests/unit_tests/regression/test_rotating_beam.cpp +++ b/tests/unit_tests/regression/test_rotating_beam.cpp @@ -85,7 +85,8 @@ constexpr auto stiffness_matrix_unity = std::array{ // Node locations (GLL quadrature) const auto node_s = std::vector{ - 0., 0.11747233803526763, 0.35738424175967748, 0.64261575824032247, 0.88252766196473242, 1.}; + 0., 0.11747233803526763, 0.35738424175967748, 0.64261575824032247, 0.88252766196473242, 1. +}; // Element quadrature const auto quadrature = BeamQuadrature{ @@ -224,8 +225,8 @@ inline void CreateTwoBeamSolverWithSameBeamsAndStep() { const auto v = CrossProduct(omega, pos); return BeamNode( s, *model.AddNode( - {pos[0], pos[1], pos[2], q_root[0], q_root[1], q_root[2], - q_root[3]}, // position + {pos[0], pos[1], pos[2], q_root[0], q_root[1], q_root[2], q_root[3] + }, // position {0., 0., 0., 1., 0., 0., 0.}, // displacement {v[0], v[1], v[2], omega[0], omega[1], omega[2]} // velocity ) @@ -330,8 +331,8 @@ TEST(RotatingBeamTest, ThreeBladeRotor) { auto v = CrossProduct(omega, pos); return BeamNode( s, *model.AddNode( - {pos[0], pos[1], pos[2], q_root[0], q_root[1], q_root[2], - q_root[3]}, // position + {pos[0], pos[1], pos[2], q_root[0], q_root[1], q_root[2], q_root[3] + }, // position {0., 0., 0., 1., 0., 0., 0.}, // displacement {v[0], v[1], v[2], omega[0], omega[1], omega[2]} // velocity ) diff --git a/tests/unit_tests/regression/test_rotor.cpp b/tests/unit_tests/regression/test_rotor.cpp index c4fb5169..6517eee1 100644 --- a/tests/unit_tests/regression/test_rotor.cpp +++ b/tests/unit_tests/regression/test_rotor.cpp @@ -455,7 +455,8 @@ TEST(RotorTest, IEA15RotorController) { // Pitch control variable auto blade_pitch_command = std::array{ &controller.io.pitch_blade1_command, &controller.io.pitch_blade2_command, - &controller.io.pitch_blade3_command}; + &controller.io.pitch_blade3_command + }; // Define hub node and associated constraints auto hub_node = model.AddNode({0., 0., 0., 1., 0., 0., 0.}); diff --git a/tests/unit_tests/regression/test_solver.cpp b/tests/unit_tests/regression/test_solver.cpp index e616afc1..eaf63738 100644 --- a/tests/unit_tests/regression/test_solver.cpp +++ b/tests/unit_tests/regression/test_solver.cpp @@ -51,7 +51,8 @@ inline void SetUpSolverAndAssemble() { // Node locations (GLL quadrature) constexpr auto node_s = std::array{ - 0., 0.11747233803526763, 0.35738424175967748, 0.64261575824032247, 0.88252766196473242, 1.}; + 0., 0.11747233803526763, 0.35738424175967748, 0.64261575824032247, 0.88252766196473242, 1. + }; // Build vector of nodes (straight along x axis, no rotation) // Calculate displacement, velocity, acceleration assuming a @@ -287,7 +288,8 @@ inline void SetupAndTakeNoSteps() { // Node locations (GLL quadrature) constexpr auto node_s = std::array{ - 0., 0.11747233803526763, 0.35738424175967748, 0.64261575824032247, 0.88252766196473242, 1.}; + 0., 0.11747233803526763, 0.35738424175967748, 0.64261575824032247, 0.88252766196473242, 1. + }; // Build vector of nodes (straight along x axis, no rotation) // Calculate displacement, velocity, acceleration assuming a @@ -494,7 +496,8 @@ inline auto SetupAndTakeTwoSteps() { // Node locations (GLL quadrature) constexpr auto node_s = std::array{ - 0., 0.11747233803526763, 0.35738424175967748, 0.64261575824032247, 0.88252766196473242, 1.}; + 0., 0.11747233803526763, 0.35738424175967748, 0.64261575824032247, 0.88252766196473242, 1. + }; // Build vector of nodes (straight along x axis, no rotation) // Calculate displacement, velocity, acceleration assuming a diff --git a/tests/unit_tests/solver/test_compute_number_of_non_zeros_constraints.cpp b/tests/unit_tests/solver/test_compute_number_of_non_zeros_constraints.cpp index d2e325b8..df2615b8 100644 --- a/tests/unit_tests/solver/test_compute_number_of_non_zeros_constraints.cpp +++ b/tests/unit_tests/solver/test_compute_number_of_non_zeros_constraints.cpp @@ -9,11 +9,13 @@ TEST(ComputeNumberOfNonZeros_Constraints, OneOfEach) { constexpr auto num_constraints = 5U; constexpr auto type_host_data = std::array{ ConstraintType::kFixedBC, ConstraintType::kPrescribedBC, ConstraintType::kRigidJoint, - ConstraintType::kRevoluteJoint, ConstraintType::kRotationControl}; + ConstraintType::kRevoluteJoint, ConstraintType::kRotationControl + }; constexpr auto row_range_host_data = std::array{ Kokkos::pair{0U, 6U}, Kokkos::pair{6U, 12U}, Kokkos::pair{12U, 18U}, Kokkos::pair{18U, 24U}, - Kokkos::pair{24U, 30U}}; + Kokkos::pair{24U, 30U} + }; const auto type_host = Kokkos::View(type_host_data.data() diff --git a/tests/unit_tests/solver/test_populate_sparse_row_ptrs_col_inds_constraints.cpp b/tests/unit_tests/solver/test_populate_sparse_row_ptrs_col_inds_constraints.cpp index 1c504188..fdae9a53 100644 --- a/tests/unit_tests/solver/test_populate_sparse_row_ptrs_col_inds_constraints.cpp +++ b/tests/unit_tests/solver/test_populate_sparse_row_ptrs_col_inds_constraints.cpp @@ -11,11 +11,13 @@ TEST(PopulateSparseRowPtrsColInds_Constraints, OneOfEach) { constexpr auto num_non_zero = 288U; constexpr auto type_host_data = std::array{ ConstraintType::kFixedBC, ConstraintType::kPrescribedBC, ConstraintType::kRigidJoint, - ConstraintType::kRevoluteJoint, ConstraintType::kRotationControl}; + ConstraintType::kRevoluteJoint, ConstraintType::kRotationControl + }; constexpr auto row_range_host_data = std::array{ Kokkos::pair{0U, 6U}, Kokkos::pair{6U, 12U}, Kokkos::pair{12U, 18U}, Kokkos::pair{18U, 23U}, - Kokkos::pair{23U, 29U}}; + Kokkos::pair{23U, 29U} + }; constexpr auto base_node_index_host_data = std::array{1U, 3U, 5U, 7U, 9U}; constexpr auto target_node_index_host_data = @@ -53,12 +55,14 @@ TEST(PopulateSparseRowPtrsColInds_Constraints, OneOfEach) { Kokkos::parallel_for( "PopulateSparseRowPtrsColInds_Constraints", 1, PopulateSparseRowPtrsColInds_Constraints, Kokkos::View>{ - type, base_node_index, target_node_index, row_range, B_row_ptrs, B_col_inds} + type, base_node_index, target_node_index, row_range, B_row_ptrs, B_col_inds + } ); constexpr auto B_row_ptrs_exact_data = std::array{ 0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 84, 96, - 108, 120, 132, 144, 156, 168, 180, 192, 204, 216, 228, 240, 252, 264, 276}; + 108, 120, 132, 144, 156, 168, 180, 192, 204, 216, 228, 240, 252, 264, 276 + }; const auto B_row_ptrs_exact = Kokkos::View( B_row_ptrs_exact_data.data() diff --git a/tests/unit_tests/solver/test_populate_sparse_row_ptrs_col_inds_transpose.cpp b/tests/unit_tests/solver/test_populate_sparse_row_ptrs_col_inds_transpose.cpp index a7aa9c50..8b20308a 100644 --- a/tests/unit_tests/solver/test_populate_sparse_row_ptrs_col_inds_transpose.cpp +++ b/tests/unit_tests/solver/test_populate_sparse_row_ptrs_col_inds_transpose.cpp @@ -32,7 +32,8 @@ TEST(PopulateSparseRowPtrsColInds_Transpose, DenseSquare) { Kokkos::parallel_for( "PopulateSparseRowPtrsColInds_Transpose", 1, PopulateSparseRowPtrsColInds_Transpose, Kokkos::View>{ - rows, cols, row_ptrs, col_inds, col_count, temp_row_ptr, row_ptrs_trans, col_inds_trans} + rows, cols, row_ptrs, col_inds, col_count, temp_row_ptr, row_ptrs_trans, col_inds_trans + } ); constexpr auto row_ptrs_trans_exact_data = row_ptrs_host_data; @@ -81,7 +82,8 @@ TEST(PopulateSparseRowPtrsColInds_Transpose, DenseRectangle) { Kokkos::parallel_for( "PopulateSparseRowPtrsColInds_Transpose", 1, PopulateSparseRowPtrsColInds_Transpose, Kokkos::View>{ - rows, cols, row_ptrs, col_inds, col_count, temp_row_ptr, row_ptrs_trans, col_inds_trans} + rows, cols, row_ptrs, col_inds, col_count, temp_row_ptr, row_ptrs_trans, col_inds_trans + } ); constexpr auto row_ptrs_trans_exact_data = std::array{0U, 1U, 2U, 3U, 4U, 5U}; diff --git a/tests/unit_tests/system/test_calculate_inertia_stiffness_matrix.cpp b/tests/unit_tests/system/test_calculate_inertia_stiffness_matrix.cpp index df27277f..4f99061a 100644 --- a/tests/unit_tests/system/test_calculate_inertia_stiffness_matrix.cpp +++ b/tests/unit_tests/system/test_calculate_inertia_stiffness_matrix.cpp @@ -84,13 +84,15 @@ TEST(CalculateInertiaStiffnessMatrixTests, OneNode) { Kokkos::parallel_for( "CalculateInertiaStiffnessMatrix", 1, CalculateInertiaStiffnessMatrix{ - 0, Muu, u_ddot, omega, omega_dot, omega_tilde, omega_dot_tilde, rho, eta, Kuu} + 0, Muu, u_ddot, omega, omega_dot, omega_tilde, omega_dot_tilde, rho, eta, Kuu + } ); constexpr auto Kuu_exact_data = std::array{ 0., 0., 0., 3396., -6792., 3396., 0., 0., 0., 3609., -7218., 3609., 0., 0., 0., 3822., -7644., 3822., 0., 0., 0., 1407766., 1481559., 1465326., - 0., 0., 0., 1496300., 1558384., 1576048., 0., 0., 0., 1604122., 1652877., 1652190.}; + 0., 0., 0., 1496300., 1558384., 1576048., 0., 0., 0., 1604122., 1652877., 1652190. + }; const auto Kuu_exact = Kokkos::View(Kuu_exact_data.data()); diff --git a/tests/unit_tests/system/test_calculate_inertial_forces.cpp b/tests/unit_tests/system/test_calculate_inertial_forces.cpp index 33bfe8f4..48544d0f 100644 --- a/tests/unit_tests/system/test_calculate_inertial_forces.cpp +++ b/tests/unit_tests/system/test_calculate_inertial_forces.cpp @@ -70,7 +70,8 @@ TEST(CalculateInertialForcesTests, OneNode) { Kokkos::parallel_for( "CalculateInertialForces", 1, CalculateInertialForces{ - 0, Muu, u_ddot, omega, omega_dot, eta_tilde, omega_tilde, omega_dot_tilde, rho, eta, FI} + 0, Muu, u_ddot, omega, omega_dot, eta_tilde, omega_tilde, omega_dot_tilde, rho, eta, FI + } ); constexpr auto omega_tilde_exact_data = std::array{0., -42., 41., 42., 0., -40., -41., 40., 0.}; diff --git a/tests/unit_tests/system/test_integrate_inertia_matrix.cpp b/tests/unit_tests/system/test_integrate_inertia_matrix.cpp index 2f56806b..51260bb1 100644 --- a/tests/unit_tests/system/test_integrate_inertia_matrix.cpp +++ b/tests/unit_tests/system/test_integrate_inertia_matrix.cpp @@ -34,7 +34,8 @@ inline void IntegrateInertiaMatrix_TestOneElementOneNodeOneQP_Muu() { const auto integrator = IntegrateInertiaMatrixElement{ 0U, number_of_nodes, number_of_qps, qp_weights, qp_jacobian, shape_interp, qp_Muu, qp_Guu, 1., 0., - gbl_M}; + gbl_M + }; Kokkos::parallel_for(policy, integrator); @@ -80,7 +81,8 @@ void IntegrateInertiaMatrix_TestOneElementOneNodeOneQP_Guu() { const auto integrator = IntegrateInertiaMatrixElement{ 0U, number_of_nodes, number_of_qps, qp_weights, qp_jacobian, shape_interp, qp_Muu, qp_Guu, 0., 1., - gbl_M}; + gbl_M + }; Kokkos::parallel_for(policy, integrator); constexpr auto exact_M_data = @@ -124,7 +126,8 @@ void IntegrateInertiaMatrix_TestTwoElementsOneNodeOneQP() { const auto integrator = IntegrateInertiaMatrixElement{ 0U, number_of_nodes, number_of_qps, qp_weights, qp_jacobian, shape_interp, qp_Muu, qp_Guu, 1., 0., - gbl_M}; + gbl_M + }; Kokkos::parallel_for(policy, integrator); } @@ -139,7 +142,8 @@ void IntegrateInertiaMatrix_TestTwoElementsOneNodeOneQP() { const auto integrator = IntegrateInertiaMatrixElement{ 1U, number_of_nodes, number_of_qps, qp_weights, qp_jacobian, shape_interp, qp_Muu, qp_Guu, 1., 0., - gbl_M}; + gbl_M + }; Kokkos::parallel_for(policy, integrator); } @@ -188,7 +192,8 @@ void IntegrateInertiaMatrix_TestOneElementTwoNodesOneQP() { const auto integrator = IntegrateInertiaMatrixElement{ 0U, number_of_nodes, number_of_qps, qp_weights, qp_jacobian, shape_interp, qp_Muu, qp_Guu, 1., 0., - gbl_M}; + gbl_M + }; Kokkos::parallel_for(policy, integrator); constexpr auto exact_M_data = std::array{ @@ -203,7 +208,8 @@ void IntegrateInertiaMatrix_TestOneElementTwoNodesOneQP() { 802., 804., 806., 808., 810., 812., 1002., 1004., 1006., 1008., 1010., 1012., 4., 8., 12., 16., 20., 24., 404., 408., 412., 416., 420., 424., 804., 808., 812., 816., 820., 824., 1204., 1208., 1212., 1216., 1220., 1224., - 1604., 1608., 1612., 1616., 1620., 1624., 2004., 2008., 2012., 2016., 2020., 2024.}; + 1604., 1608., 1612., 1616., 1620., 1624., 2004., 2008., 2012., 2016., 2020., 2024. + }; const auto exact_M = Kokkos::View(exact_M_data.data()); @@ -245,7 +251,8 @@ void IntegrateInertiaMatrix_TestOneElementOneNodeTwoQPs() { const auto integrator = IntegrateInertiaMatrixElement{ 0U, number_of_nodes, number_of_qps, qp_weights, qp_jacobian, shape_interp, qp_Muu, qp_Guu, 1., 0., - gbl_M}; + gbl_M + }; Kokkos::parallel_for(policy, integrator); constexpr auto exact_M_data = @@ -287,7 +294,8 @@ void IntegrateInertiaMatrix_TestOneElementOneNodeOneQP_WithMultiplicationFactor( const auto policy = Kokkos::RangePolicy(0, number_of_nodes * number_of_simd_nodes); const auto integrator = IntegrateInertiaMatrixElement{ 0U, number_of_nodes, number_of_qps, qp_weights, qp_jacobian, shape_interp, - qp_Muu, qp_Guu, multiplication_factor, 0., gbl_M}; + qp_Muu, qp_Guu, multiplication_factor, 0., gbl_M + }; Kokkos::parallel_for(policy, integrator); constexpr auto exact_M_data = diff --git a/tests/unit_tests/system/test_integrate_residual_vector.cpp b/tests/unit_tests/system/test_integrate_residual_vector.cpp index 5f4f2ce6..4e8301f5 100644 --- a/tests/unit_tests/system/test_integrate_residual_vector.cpp +++ b/tests/unit_tests/system/test_integrate_residual_vector.cpp @@ -37,7 +37,8 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fc) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms} + qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + } ); constexpr auto resid_exact_data = @@ -79,7 +80,8 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fd) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms} + qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + } ); constexpr auto resid_exact_data = @@ -121,7 +123,8 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fi) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms} + qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + } ); constexpr auto resid_exact_data = @@ -163,7 +166,8 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fg) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms} + qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + } ); constexpr auto resid_exact_data = @@ -204,7 +208,8 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_FX) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms} + qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + } ); constexpr auto resid_exact_data = @@ -247,18 +252,21 @@ TEST(IntegrateResidualVector, TwoElementsOneNodeOneQP) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc_1, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms} + qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + } ); Kokkos::parallel_for( "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 1U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc_2, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms} + qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + } ); - constexpr auto resid_exact_data = std::array{ - 6., 12., 18., 24., 30., 36., 12., 24., 36., 48., 60., 72.}; + constexpr auto resid_exact_data = + std::array{6., 12., 18., 24., 30., 36., + 12., 24., 36., 48., 60., 72.}; const auto resid_exact = Kokkos::View(resid_exact_data.data() ); @@ -296,7 +304,8 @@ TEST(IntegrateResidualVector, OneElementOneNodeTwoQPs) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms} + qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + } ); constexpr auto resid_exact_data = @@ -338,7 +347,8 @@ TEST(IntegrateResidualVector, OneElementTwoNodesOneQP) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms} + qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + } ); constexpr auto resid_exact_data = diff --git a/tests/unit_tests/system/test_integrate_stiffness_matrix.cpp b/tests/unit_tests/system/test_integrate_stiffness_matrix.cpp index 8faf8cbc..cccaacf6 100644 --- a/tests/unit_tests/system/test_integrate_stiffness_matrix.cpp +++ b/tests/unit_tests/system/test_integrate_stiffness_matrix.cpp @@ -355,7 +355,8 @@ void TestIntegrateStiffnessMatrix_1Element2Nodes1QP_Puu() { 802., 804., 806., 808., 810., 812., 1002., 1004., 1006., 1008., 1010., 1012., 8., 16., 24., 32., 40., 48., 808., 816., 824., 832., 840., 848., 1608., 1616., 1624., 1632., 1640., 1648., 2408., 2416., 2424., 2432., 2440., 2448., - 3208., 3216., 3224., 3232., 3240., 3248., 4008., 4016., 4024., 4032., 4040., 4048.}; + 3208., 3216., 3224., 3232., 3240., 3248., 4008., 4016., 4024., 4032., 4040., 4048. + }; TestIntegrateStiffnessMatrix_1Element2Nodes1QP( qp_Kuu, qp_Puu, qp_Cuu, qp_Ouu, qp_Quu, exact_M_data @@ -392,7 +393,8 @@ void TestIntegrateStiffnessMatrix_1Element2Nodes1QP_Quu() { 802., 804., 806., 808., 810., 812., 1002., 1004., 1006., 1008., 1010., 1012., 4., 8., 12., 16., 20., 24., 404., 408., 412., 416., 420., 424., 804., 808., 812., 816., 820., 824., 1204., 1208., 1212., 1216., 1220., 1224., - 1604., 1608., 1612., 1616., 1620., 1624., 2004., 2008., 2012., 2016., 2020., 2024.}; + 1604., 1608., 1612., 1616., 1620., 1624., 2004., 2008., 2012., 2016., 2020., 2024. + }; TestIntegrateStiffnessMatrix_1Element2Nodes1QP( qp_Kuu, qp_Puu, qp_Cuu, qp_Ouu, qp_Quu, exact_M_data @@ -429,7 +431,8 @@ void TestIntegrateStiffnessMatrix_1Element2Nodes1QP_Cuu() { 1604., 1608., 1612., 1616., 1620., 1624., 2004., 2008., 2012., 2016., 2020., 2024., 16., 32., 48., 64., 80., 96., 1616., 1632., 1648., 1664., 1680., 1696., 3216., 3232., 3248., 3264., 3280., 3296., 4816., 4832., 4848., 4864., 4880., 4896., - 6416., 6432., 6448., 6464., 6480., 6496., 8016., 8032., 8048., 8064., 8080., 8096.}; + 6416., 6432., 6448., 6464., 6480., 6496., 8016., 8032., 8048., 8064., 8080., 8096. + }; TestIntegrateStiffnessMatrix_1Element2Nodes1QP( qp_Kuu, qp_Puu, qp_Cuu, qp_Ouu, qp_Quu, exact_M_data @@ -466,7 +469,8 @@ void TestIntegrateStiffnessMatrix_1Element2Nodes1QP_Ouu() { 1604., 1608., 1612., 1616., 1620., 1624., 2004., 2008., 2012., 2016., 2020., 2024., 8., 16., 24., 32., 40., 48., 808., 816., 824., 832., 840., 848., 1608., 1616., 1624., 1632., 1640., 1648., 2408., 2416., 2424., 2432., 2440., 2448., - 3208., 3216., 3224., 3232., 3240., 3248., 4008., 4016., 4024., 4032., 4040., 4048.}; + 3208., 3216., 3224., 3232., 3240., 3248., 4008., 4016., 4024., 4032., 4040., 4048. + }; TestIntegrateStiffnessMatrix_1Element2Nodes1QP( qp_Kuu, qp_Puu, qp_Cuu, qp_Ouu, qp_Quu, exact_M_data diff --git a/tests/unit_tests/system/test_rotate_section_matrix.cpp b/tests/unit_tests/system/test_rotate_section_matrix.cpp index cfdf2f0d..bcaa1987 100644 --- a/tests/unit_tests/system/test_rotate_section_matrix.cpp +++ b/tests/unit_tests/system/test_rotate_section_matrix.cpp @@ -18,9 +18,10 @@ TEST(RotateSectionMatrixTests, OneNode) { Kokkos::deep_copy(rr0, rr0_mirror); const auto Cstar = Kokkos::View("Cstar"); - constexpr auto Cstar_data = std::array{ - 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., - 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36.}; + constexpr auto Cstar_data = + std::array{1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., + 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., + 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36.}; const auto Cstar_host = Kokkos::View(Cstar_data.data()); const auto Cstar_mirror = Kokkos::create_mirror(Cstar); @@ -31,10 +32,11 @@ TEST(RotateSectionMatrixTests, OneNode) { Kokkos::parallel_for("RotateSectionMatrix", 1, RotateSectionMatrix{0, rr0, Cstar, Cuu}); - constexpr auto Cuu_exact_data = std::array{ - 372., 912., 1452., 480., 1182., 1884., 822., 2010., 3198., 1092., 2685., 4278., - 1272., 3108., 4944., 1704., 4188., 6672., 1020., 2532., 4044., 1128., 2802., 4476., - 2442., 6060., 9678., 2712., 6735., 10758., 3864., 9588., 15312., 4296., 10668., 17040.}; + constexpr auto Cuu_exact_data = + std::array{372., 912., 1452., 480., 1182., 1884., 822., 2010., 3198., + 1092., 2685., 4278., 1272., 3108., 4944., 1704., 4188., 6672., + 1020., 2532., 4044., 1128., 2802., 4476., 2442., 6060., 9678., + 2712., 6735., 10758., 3864., 9588., 15312., 4296., 10668., 17040.}; const auto Cuu_exact = Kokkos::View(Cuu_exact_data.data()); From 5f7322f5a5ffc6c1fbefa3945f0399b8844546c0 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 1 Oct 2024 16:07:05 -0600 Subject: [PATCH 61/87] Add unit test for Initialize wrapper --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 37 ++++++----- .../regression/test_aerodyn_inflow.cpp | 66 +++++++++++++++++++ 2 files changed, 85 insertions(+), 18 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 6f80285d..5d0c5282 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -407,8 +407,8 @@ struct SimulationControls { int debug_level{0}; //< Debug level (0-4) // Outputs - int output_format{0}; //< File format for writing outputs - float output_time_step{0.}; //< Timestep for outputs to file + int output_format{0}; //< File format for writing outputs + double output_time_step{0.}; //< Timestep for outputs to file std::array output_root_name{ "Output_ADIlib_default" //< Root name for output files }; @@ -619,6 +619,7 @@ class AeroDynInflowLibrary { error_handling_.error_message.data() // output: Error message ); + std::cout << "ADI_C_PreInit completed" << std::endl; error_handling_.CheckError(); } @@ -685,6 +686,7 @@ class AeroDynInflowLibrary { error_handling_.error_message.data() // output: Error message buffer ); + std::cout << "ADI_C_SetupRotor completed" << std::endl; error_handling_.CheckError(); // is_initialized_ = true; } @@ -702,34 +704,32 @@ class AeroDynInflowLibrary { std::vector aerodyn_input_string_array, std::vector inflowwind_input_string_array ) { - auto ADI_C_Init = - lib_ - .get_function< - void(int*, const char*, int*, int*, const char*, int*, char*, float*, float*, float*, float*, float*, float*, float*, float*, int*, double*, double*, int*, int*, int*, float*, float*, int*, float*, int*, char*, char*, int*, char*)>( - "ADI_C_Init" - ); + auto ADI_C_Init = lib_.get_function< + void(int*, char**, int*, int*, char**, int*, char*, float*, float*, float*, float*, float*, float*, float*, float*, int*, double*, double*, int*, int*, int*, float*, float*, int*, double*, int*, char*, char*, int*, char*)>( + "ADI_C_Init" + ); + + // Convert bool -> int to pass to the Fortran routine + int aerodyn_input_passed_int = sim_controls_.aerodyn_input_passed ? 1 : 0; + int inflowwind_input_passed_int = sim_controls_.inflowwind_input_passed ? 1 : 0; + int store_HH_wind_speed_int = sim_controls_.store_HH_wind_speed ? 1 : 0; + int write_vtk_int = vtk_settings_.write_vtk ? 1 : 0; // Primary input files will be passed as a single string joined by C_NULL_CHAR i.e. '\0' std::string aerodyn_input_string = JoinStringArray(aerodyn_input_string_array, '\0'); - aerodyn_input_string = aerodyn_input_string + '\0'; int aerodyn_input_string_length = static_cast(aerodyn_input_string.size()); + char* aerodyn_input_cstring = aerodyn_input_string.data(); std::string inflowwind_input_string = JoinStringArray(inflowwind_input_string_array, '\0'); - inflowwind_input_string = inflowwind_input_string + '\0'; int inflowwind_input_string_length = static_cast(inflowwind_input_string.size()); - - // Convert bool -> int to pass to the Fortran routine - int aerodyn_input_passed_int = sim_controls_.aerodyn_input_passed ? 1 : 0; - int inflowwind_input_passed_int = sim_controls_.inflowwind_input_passed ? 1 : 0; - int store_HH_wind_speed_int = sim_controls_.store_HH_wind_speed ? 1 : 0; - int write_vtk_int = vtk_settings_.write_vtk ? 1 : 0; + char* inflowwind_input_cstring = inflowwind_input_string.data(); ADI_C_Init( &aerodyn_input_passed_int, // input: AD input file is passed? - aerodyn_input_string.data(), // input: AD input file as string + &aerodyn_input_cstring, // input: AD input file as string array &aerodyn_input_string_length, // input: AD input file string length &inflowwind_input_passed_int, // input: IfW input file is passed? - inflowwind_input_string.data(), // input: IfW input file as string + &inflowwind_input_cstring, // input: IfW input file as string array &inflowwind_input_string_length, // input: IfW input file string length sim_controls_.output_root_name.data(), // input: rootname for ADI file writing &env_conditions_.gravity, // input: gravity @@ -757,6 +757,7 @@ class AeroDynInflowLibrary { error_handling_.error_message.data() // output: error message buffer ); + std::cout << "ADI_C_Init completed" << std::endl; error_handling_.CheckError(); } diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 44ae7808..8fb35d8e 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -493,6 +493,14 @@ TEST(AerodynInflowTest, JoinStringArray_StringsContainingDelimiter) { EXPECT_EQ(util::JoinStringArray(input, ';'), expected); } +// Add a test with null characters in the input strings +TEST(AerodynInflowTest, JoinStringArray_NullCharacters) { + std::vector input = {"one", "two", "three"}; + std::string expected = "one\0two\0three"; + util::JoinStringArray(input, '\0'); + // EXPECT_EQ(util::JoinStringArray(input, '\0'), expected); +} + /// Helper function to get the shared library path std::string GetSharedLibraryPath() { const std::filesystem::path project_root = FindProjectRoot(); @@ -540,6 +548,64 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_PreInitialize) { EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); } +TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { + // Set up simulation parameters + util::SimulationControls sim_controls{ + .aerodyn_input_passed = false, + .inflowwind_input_passed = false, + .time_step = 0.0125, + .max_time = 10.0, + .interpolation_order = 2, + .store_HH_wind_speed = false, + .transpose_DCM = 1, + .debug_level = 4, + .output_format = 0, + .output_time_step = 0.1}; + + // Set up environmental conditions and fluid properties + util::EnvironmentalConditions env_conditions{ + .gravity = 9.80665f, .atm_pressure = 103500.0f, .water_depth = 0.0f, .msl_offset = 0.0f}; + + util::FluidProperties fluid_props{ + .density = 1.225f, + .kinematic_viscosity = 1.464E-05f, + .sound_speed = 335.0f, + .vapor_pressure = 1700.0f}; + + // Set up turbine settings + std::array hub_data = {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}; + std::array nacelle_data = {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}; + std::vector> root_data(3, {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}); + + util::TurbineSettings turbine_settings(hub_data, nacelle_data, root_data, 1, 3); + + // Set up VTK settings + util::VTKSettings vtk_settings{}; + + // Load the shared library and initialize AeroDynInflowLibrary + const std::string path = GetSharedLibraryPath(); + util::AeroDynInflowLibrary aerodyn_inflow_library( + path, util::ErrorHandling{}, fluid_props, env_conditions, turbine_settings, + util::StructuralMesh{}, sim_controls, vtk_settings + ); + + // Pre-initialize and setup rotor + EXPECT_NO_THROW(aerodyn_inflow_library.PreInitialize()); + EXPECT_NO_THROW(aerodyn_inflow_library.SetupRotor(1, true, {0.0f, 0.0f, 0.0f})); + + // Initialize with input files + const std::filesystem::path project_root = FindProjectRoot(); + std::filesystem::path input_path = project_root / + "build/external/OpenFAST_ADI/src/OpenFAST_ADI/reg_tests/" + "r-test/modules/aerodyn/py_ad_5MW_OC4Semi_WSt_WavesWN/"; + std::vector adiAD_input_string_array = {(input_path / "ad_primary.dat").string()}; + std::vector adiIfW_input_string_array = {(input_path / "ifw_primary.dat").string()}; + + EXPECT_NO_THROW( + aerodyn_inflow_library.Initialize(adiAD_input_string_array, adiIfW_input_string_array) + ); +} + #endif } // namespace openturbine::tests \ No newline at end of file From 5523593fd561528633dd51ecbb3171d4fd206f03 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 1 Oct 2024 17:03:01 -0600 Subject: [PATCH 62/87] Add a regression test performing end-to-end simulation for the ADI wrapper --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 5 +- tests/unit_tests/regression/CMakeLists.txt | 1 + .../regression/test_aerodyn_inflow.cpp | 80 +++++++++++++++---- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 5d0c5282..79b719d2 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -619,7 +619,6 @@ class AeroDynInflowLibrary { error_handling_.error_message.data() // output: Error message ); - std::cout << "ADI_C_PreInit completed" << std::endl; error_handling_.CheckError(); } @@ -686,9 +685,7 @@ class AeroDynInflowLibrary { error_handling_.error_message.data() // output: Error message buffer ); - std::cout << "ADI_C_SetupRotor completed" << std::endl; error_handling_.CheckError(); - // is_initialized_ = true; } /** @@ -757,8 +754,8 @@ class AeroDynInflowLibrary { error_handling_.error_message.data() // output: error message buffer ); - std::cout << "ADI_C_Init completed" << std::endl; error_handling_.CheckError(); + is_initialized_ = true; } /** diff --git a/tests/unit_tests/regression/CMakeLists.txt b/tests/unit_tests/regression/CMakeLists.txt index fdbaac59..79355acf 100644 --- a/tests/unit_tests/regression/CMakeLists.txt +++ b/tests/unit_tests/regression/CMakeLists.txt @@ -11,4 +11,5 @@ target_sources( test_utilities.cpp test_controller.cpp test_yaml_parser.cpp + test_aerodyn_inflow.cpp ) diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/regression/test_aerodyn_inflow.cpp index 8fb35d8e..d4fc37b7 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/regression/test_aerodyn_inflow.cpp @@ -535,19 +535,6 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { EXPECT_EQ(aerodyn_inflow_library.GetVTKSettings().write_vtk, 0); } -TEST(AerodynInflowTest, AeroDynInflowLibrary_PreInitialize) { - const std::string path = GetSharedLibraryPath(); - util::AeroDynInflowLibrary aerodyn_inflow_library(path); - - EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); - EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); - - aerodyn_inflow_library.PreInitialize(); - - EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); - EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); -} - TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { // Set up simulation parameters util::SimulationControls sim_controls{ @@ -558,7 +545,7 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { .interpolation_order = 2, .store_HH_wind_speed = false, .transpose_DCM = 1, - .debug_level = 4, + .debug_level = 1, .output_format = 0, .output_time_step = 0.1}; @@ -604,6 +591,71 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { EXPECT_NO_THROW( aerodyn_inflow_library.Initialize(adiAD_input_string_array, adiIfW_input_string_array) ); + + // Set up motion data for hub, nacelle, root, and mesh + util::MeshMotionData hub_motion( + {{0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}}, {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}}, + {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}} + ); + util::MeshMotionData nacelle_motion( + {{0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}}, {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}}, + {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}} + ); + util::MeshMotionData root_motion( + {{0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}}, + {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}}, + {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}}, + 3 + ); + + // Get the number of mesh points from the structural mesh + size_t n_mesh_points = 1; + + // Create mesh motion data with the correct number of mesh points + std::vector> mesh_positions( + n_mesh_points, {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0} + ); + std::vector> mesh_velocities( + n_mesh_points, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f} + ); + std::vector> mesh_accelerations( + n_mesh_points, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f} + ); + + util::MeshMotionData mesh_motion( + mesh_positions, mesh_velocities, mesh_accelerations, n_mesh_points + ); + + // Set up rotor motion + EXPECT_NO_THROW(aerodyn_inflow_library.SetupRotorMotion( + 1, hub_motion, nacelle_motion, root_motion, mesh_motion + )); + + // Simulate for a few time steps + double current_time = 0.0; + double next_time = sim_controls.time_step; + std::vector output_channel_values; + std::vector> mesh_force_moment(1); // Assuming 1 mesh point + + for (int i = 0; i < 10; ++i) { + EXPECT_NO_THROW(aerodyn_inflow_library.UpdateStates(current_time, next_time)); + EXPECT_NO_THROW( + aerodyn_inflow_library.CalculateOutputChannels(next_time, output_channel_values) + ); + EXPECT_NO_THROW(aerodyn_inflow_library.GetRotorAerodynamicLoads(1, mesh_force_moment)); + + current_time = next_time; + next_time += sim_controls.time_step; + } + + // End simulation + EXPECT_NO_THROW(aerodyn_inflow_library.Finalize()); } #endif From 6ae5a0ea574870f359322839886c36ad7e0aa2b3 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Wed, 2 Oct 2024 11:52:30 -0600 Subject: [PATCH 63/87] Make slight adjustments to how OpenFAST_ADI is built - build type follows openturbine build type - trailing spaces removed --- cmake/Dependencies.cmake | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 0816646d..4742ff67 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -31,27 +31,29 @@ function(openturbine_setup_dependencies) message(STATUS "Building OpenFAST AerodynInflow (ADI) library") include(ExternalProject) ExternalProject_Add(OpenFAST_ADI - PREFIX ${CMAKE_BINARY_DIR}/external/OpenFAST_ADI + PREFIX ${CMAKE_BINARY_DIR}/external GIT_REPOSITORY https://github.com/OpenFAST/openfast.git GIT_TAG dev # Use the "dev" branch GIT_SHALLOW TRUE # Clone only the latest commit - GIT_SUBMODULES_RECURSE OFF # Avoid unnecessary submodule cloning + GIT_SUBMODULES "" # Skip downloading r-test CMAKE_ARGS -DBUILD_TESTING=OFF # Disable testing + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} # Use the same build type as the main project BUILD_IN_SOURCE OFF # Build in a separate directory for cleaner output BINARY_DIR ${CMAKE_BINARY_DIR}/OpenFAST_ADI_build # Build only the aerodyn_inflow_c_binding target and do it sequentially (avoid parallel build) BUILD_COMMAND ${CMAKE_COMMAND} --build . --target aerodyn_inflow_c_binding -- -j 1 INSTALL_COMMAND + # Copy the built library to the tests directory ${CMAKE_COMMAND} -E copy - ${CMAKE_BINARY_DIR}/OpenFAST_ADI_build/modules/aerodyn/libaerodyn_inflow_c_binding${CMAKE_SHARED_LIBRARY_SUFFIX} - ${CMAKE_BINARY_DIR}/tests/unit_tests/ + ${CMAKE_BINARY_DIR}/OpenFAST_ADI_build/modules/aerodyn/${CMAKE_SHARED_LIBRARY_PREFIX}aerodyn_inflow_c_binding${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_BINARY_DIR}/tests/unit_tests/aerodyn_inflow_c_binding.dll ) endif() # Conditionally add external project to build the ROSCO Controller library if(OpenTurbine_BUILD_ROSCO_CONTROLLER) - if (NOT ROSCO_BUILD_TAG) + if (NOT ROSCO_BUILD_TAG) set(ROSCO_BUILD_TAG "v2.9.4") endif() message(STATUS "Building ROSCO Controller library") From bab9fe9dabb5fc9c03c6a990c034bc4f8653b423 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Wed, 2 Oct 2024 13:58:28 -0600 Subject: [PATCH 64/87] Transfer unit test files from regression->external directory + add all required input files for the ADI library --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 20 +- tests/unit_tests/CMakeLists.txt | 1 + .../5MW_Baseline/Airfoils/Cylinder1.dat | 59 +++ .../Airfoils/Cylinder1_coords.txt | 407 ++++++++++++++++++ .../5MW_Baseline/Airfoils/Cylinder2.dat | 60 +++ .../Airfoils/Cylinder2_coords.txt | 407 ++++++++++++++++++ .../5MW_Baseline/Airfoils/DU21_A17.dat | 196 +++++++++ .../5MW_Baseline/Airfoils/DU21_A17_coords.txt | 407 ++++++++++++++++++ .../5MW_Baseline/Airfoils/DU25_A17.dat | 194 +++++++++ .../5MW_Baseline/Airfoils/DU25_A17_coords.txt | 407 ++++++++++++++++++ .../5MW_Baseline/Airfoils/DU30_A17.dat | 198 +++++++++ .../5MW_Baseline/Airfoils/DU30_A17_coords.txt | 407 ++++++++++++++++++ .../5MW_Baseline/Airfoils/DU35_A17.dat | 189 ++++++++ .../5MW_Baseline/Airfoils/DU35_A17_coords.txt | 407 ++++++++++++++++++ .../5MW_Baseline/Airfoils/DU40_A17.dat | 190 ++++++++ .../5MW_Baseline/Airfoils/DU40_A17_coords.txt | 407 ++++++++++++++++++ .../5MW_Baseline/Airfoils/NACA64_A17.dat | 181 ++++++++ .../Airfoils/NACA64_A17_coords.txt | 407 ++++++++++++++++++ .../NRELOffshrBsline5MW_AeroDyn_blade.dat | 28 ++ tests/unit_tests/external/CMakeLists.txt | 15 +- tests/unit_tests/external/ad_primary.dat | 185 ++++++++ tests/unit_tests/external/ifw_primary.dat | 71 +++ .../test_aerodyn_inflow.cpp | 20 +- tests/unit_tests/regression/CMakeLists.txt | 1 - 24 files changed, 4831 insertions(+), 33 deletions(-) create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder1.dat create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder1_coords.txt create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder2.dat create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder2_coords.txt create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/DU21_A17.dat create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/DU21_A17_coords.txt create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/DU25_A17.dat create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/DU25_A17_coords.txt create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/DU30_A17.dat create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/DU30_A17_coords.txt create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/DU35_A17.dat create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/DU35_A17_coords.txt create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/DU40_A17.dat create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/DU40_A17_coords.txt create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/NACA64_A17.dat create mode 100644 tests/unit_tests/external/5MW_Baseline/Airfoils/NACA64_A17_coords.txt create mode 100644 tests/unit_tests/external/5MW_Baseline/NRELOffshrBsline5MW_AeroDyn_blade.dat create mode 100644 tests/unit_tests/external/ad_primary.dat create mode 100644 tests/unit_tests/external/ifw_primary.dat rename tests/unit_tests/{regression => external}/test_aerodyn_inflow.cpp (97%) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 79b719d2..eb6ce0d4 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -561,23 +561,20 @@ class AeroDynInflowLibrary { public: /// Constructor to initialize AeroDyn Inflow library with default settings and optional path AeroDynInflowLibrary( - std::string shared_lib_path = "", ErrorHandling eh = ErrorHandling{}, - FluidProperties fp = FluidProperties{}, + std::string shared_lib_path = "aerodyn_inflow_c_binding.dll", + ErrorHandling eh = ErrorHandling{}, FluidProperties fp = FluidProperties{}, EnvironmentalConditions ec = EnvironmentalConditions{}, TurbineSettings ts = TurbineSettings{}, StructuralMesh sm = StructuralMesh{}, SimulationControls sc = SimulationControls{}, VTKSettings vtk = VTKSettings{} ) - : error_handling_(std::move(eh)), + : lib_{shared_lib_path, util::dylib::no_filename_decorations}, + error_handling_(std::move(eh)), air_(std::move(fp)), env_conditions_(std::move(ec)), turbine_settings_(std::move(ts)), structural_mesh_(std::move(sm)), sim_controls_(std::move(sc)), - vtk_settings_(std::move(vtk)) { - if (!shared_lib_path.empty()) { - lib_ = util::dylib(shared_lib_path, util::dylib::no_filename_decorations); - } - } + vtk_settings_(std::move(vtk)) {} /// Destructor to take care of Fortran-side cleanup if the library is initialized ~AeroDynInflowLibrary() noexcept { @@ -953,11 +950,8 @@ class AeroDynInflowLibrary { } private: - bool is_initialized_{false}; //< Flag to check if the library is initialized - util::dylib lib_{ - "libaerodyn_inflow_c_binding.dylib", - util::dylib::no_filename_decorations //< Dynamic library object for AeroDyn Inflow - }; + bool is_initialized_{false}; //< Flag to check if the library is initialized + util::dylib lib_; //< Dynamic library object for AeroDyn Inflow ErrorHandling error_handling_; //< Error handling settings FluidProperties air_; //< Properties of the working fluid (air) EnvironmentalConditions env_conditions_; //< Environmental conditions diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 454ffd2c..986e66dc 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -3,6 +3,7 @@ add_executable(openturbine_unit_tests) # Add subdirectories for additional components add_subdirectory(beams) +add_subdirectory(external) add_subdirectory(model) add_subdirectory(regression) add_subdirectory(solver) diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder1.dat b/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder1.dat new file mode 100644 index 00000000..d9e59b23 --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder1.dat @@ -0,0 +1,59 @@ +! ------------ AirfoilInfo v1.01.x Input File ---------------------------------- +! Round root section with a Cd of 0.50 +! Made by Jason Jonkman +! note that this file uses Marshall Buhl's new input file processing; start all comment lines with ! +! ------------------------------------------------------------------------------ +"DEFAULT" InterpOrd ! Interpolation order to use for quasi-steady table lookup {1=linear; 3=cubic spline; "default"} [default=1] + 1 NonDimArea ! The non-dimensional area of the airfoil (area/chord^2) (set to 1.0 if unsure or unneeded) +@"Cylinder1_coords.txt" NumCoords ! The number of coordinates in the airfoil shape file. Set to zero if coordinates not included. +"unused" BL_file ! The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. + 1 NumTabs ! Number of airfoil tables in this file. +! ------------------------------------------------------------------------------ +! data for table 1 +! ------------------------------------------------------------------------------ + 0.75 Re ! Reynolds number in millions + 0 UserProp ! User property (control) setting +True InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +!........................................ + 0 alpha0 ! 0-lift angle of attack, depends on airfoil. + 0 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) + 0 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] + 0 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It can be calculated from the static value of Cn at either the break in the pitching moment or the loss of chord force at the onset of stall. It is close to the condition of maximum lift of the airfoil at low Mach numbers. + 0 Cn2 ! As Cn1 for negative AOAs. + 0.19 St_sh ! Strouhal's shedding frequency constant. [default = 0.19] + 0.5 Cd0 ! 2D drag coefficient value at 0-lift. + 0 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does not include a column for Cm, this needs to be set to 0.0] + 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] + 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] + 0.2 x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] +"DEFAULT" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] +"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] +!........................................ +! Table of aerodynamics coefficients + 3 NumAlf ! Number of data lines in the following table +! Alpha Cl Cd Cm +! (deg) (-) (-) (-) + -180.00 0.000 0.5000 0.0 + 0.00 0.000 0.5000 0.0 + 180.00 0.000 0.5000 0.0 +! ------------------------------------------------------------------------------ + diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder1_coords.txt b/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder1_coords.txt new file mode 100644 index 00000000..30663b70 --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder1_coords.txt @@ -0,0 +1,407 @@ + 400 NumCoords ! The number of coordinates in the airfoil shape file (including an extra coordinate for airfoil reference). Set to zero if coordinates not included. +! ......... x-y coordinates are next if NumCoords > 0 ............. +! x-y coordinate of airfoil reference +! x/c y/c +0.5 0 +! coordinates of airfoil shape +! cylinder (interpolated to 399 points) +! x/c y/c +1.000000 0.000000 +0.996600 0.058210 +0.993140 0.082541 +0.989610 0.101400 +0.986010 0.117449 +0.982350 0.131676 +0.978630 0.144614 +0.974840 0.156611 +0.970980 0.167863 +0.967060 0.178480 +0.963070 0.188590 +0.959020 0.198244 +0.954900 0.207523 +0.950720 0.216452 +0.946470 0.225088 +0.942160 0.233441 +0.937780 0.241555 +0.933330 0.249450 +0.928820 0.257125 +0.924250 0.264598 +0.919610 0.271896 +0.914900 0.279030 +0.910130 0.285995 +0.905290 0.292814 +0.900390 0.299479 +0.895420 0.306011 +0.890390 0.312403 +0.885290 0.318672 +0.880130 0.324809 +0.874900 0.330832 +0.869610 0.336732 +0.864250 0.342523 +0.858820 0.348207 +0.853330 0.353777 +0.847780 0.359234 +0.842160 0.364591 +0.836470 0.369849 +0.830720 0.374999 +0.824900 0.380053 +0.819020 0.385002 +0.813070 0.389855 +0.807060 0.394606 +0.800980 0.399263 +0.794840 0.403818 +0.788630 0.408280 +0.782350 0.412648 +0.776010 0.416915 +0.769610 0.421082 +0.763140 0.425156 +0.756600 0.429135 +0.750000 0.433013 +0.743330 0.436796 +0.736670 0.440440 +0.730000 0.443959 +0.723330 0.447352 +0.716670 0.450615 +0.710000 0.453762 +0.703330 0.456790 +0.696670 0.459697 +0.690000 0.462493 +0.683330 0.465178 +0.676670 0.467747 +0.670000 0.470213 +0.663330 0.472571 +0.656670 0.474821 +0.650000 0.476970 +0.643330 0.479016 +0.636670 0.480959 +0.630000 0.482804 +0.623330 0.484551 +0.616670 0.486198 +0.610000 0.487750 +0.603330 0.489206 +0.596670 0.490566 +0.590000 0.491833 +0.583330 0.493007 +0.576670 0.494087 +0.570000 0.495076 +0.563330 0.495973 +0.556670 0.496778 +0.550000 0.497494 +0.543330 0.498119 +0.536670 0.498653 +0.529999 0.499099 +0.523330 0.499455 +0.516670 0.499722 +0.510000 0.499900 +0.503330 0.499989 +0.496670 0.499989 +0.490000 0.499900 +0.483330 0.499722 +0.476670 0.499455 +0.470001 0.499099 +0.463330 0.498653 +0.456670 0.498119 +0.450000 0.497494 +0.443330 0.496778 +0.436670 0.495973 +0.430000 0.495076 +0.423330 0.494087 +0.416670 0.493007 +0.410000 0.491833 +0.403330 0.490566 +0.396670 0.489206 +0.390000 0.487750 +0.383330 0.486198 +0.376670 0.484551 +0.370000 0.482804 +0.363330 0.480959 +0.356670 0.479016 +0.350000 0.476970 +0.343330 0.474821 +0.336670 0.472571 +0.330000 0.470213 +0.323330 0.467747 +0.316670 0.465178 +0.310000 0.462493 +0.303330 0.459697 +0.296670 0.456790 +0.290000 0.453762 +0.283330 0.450615 +0.276671 0.447352 +0.270000 0.443959 +0.263330 0.440440 +0.256670 0.436796 +0.250000 0.433013 +0.243420 0.429146 +0.236930 0.425199 +0.230530 0.421172 +0.224210 0.417061 +0.217980 0.412874 +0.211840 0.408612 +0.205790 0.404278 +0.199820 0.399865 +0.193950 0.395390 +0.188160 0.390840 +0.182450 0.386215 +0.176840 0.381533 +0.171310 0.376780 +0.165870 0.371964 +0.160520 0.367088 +0.155260 0.362152 +0.150080 0.357150 +0.144990 0.352091 +0.139990 0.346977 +0.135080 0.341809 +0.130260 0.336589 +0.125520 0.331308 +0.120870 0.325976 +0.116310 0.320596 +0.111830 0.315157 +0.107450 0.309685 +0.103150 0.304155 +0.098930 0.298568 +0.094810 0.292952 +0.090770 0.287282 +0.086830 0.281586 +0.082970 0.275837 +0.079191 0.270037 +0.075510 0.264212 +0.071910 0.258339 +0.068400 0.252431 +0.064980 0.246491 +0.061640 0.240501 +0.058400 0.234498 +0.055240 0.228448 +0.052170 0.222370 +0.049180 0.216244 +0.046290 0.210112 +0.043480 0.203935 +0.040760 0.197734 +0.038120 0.191486 +0.035580 0.185241 +0.033120 0.178950 +0.030750 0.172640 +0.028470 0.166311 +0.026270 0.159937 +0.024170 0.153577 +0.022150 0.147171 +0.020220 0.140752 +0.018370 0.134285 +0.016620 0.127843 +0.014950 0.121353 +0.013370 0.114853 +0.011870 0.108301 +0.010470 0.101786 +0.009150 0.095217 +0.007920 0.088641 +0.006780 0.082061 +0.005720 0.075414 +0.004760 0.068828 +0.003880 0.062169 +0.003090 0.055502 +0.002380 0.048727 +0.001770 0.042034 +0.001240 0.035192 +0.000800 0.028273 +0.000440 0.020972 +0.000180 0.013415 +0.000000 0.000000 +0.000180 -0.013415 +0.000440 -0.020972 +0.000800 -0.028273 +0.001240 -0.035192 +0.001770 -0.042034 +0.002380 -0.048727 +0.003090 -0.055502 +0.003880 -0.062169 +0.004760 -0.068828 +0.005720 -0.075414 +0.006780 -0.082061 +0.007920 -0.088641 +0.009150 -0.095217 +0.010470 -0.101786 +0.011870 -0.108301 +0.013370 -0.114853 +0.014950 -0.121353 +0.016620 -0.127843 +0.018370 -0.134285 +0.020220 -0.140752 +0.022150 -0.147171 +0.024170 -0.153577 +0.026270 -0.159937 +0.028470 -0.166311 +0.030750 -0.172640 +0.033120 -0.178950 +0.035580 -0.185241 +0.038120 -0.191486 +0.040760 -0.197734 +0.043480 -0.203935 +0.046290 -0.210112 +0.049180 -0.216244 +0.052170 -0.222370 +0.055240 -0.228448 +0.058400 -0.234498 +0.061640 -0.240501 +0.064980 -0.246491 +0.068400 -0.252431 +0.071910 -0.258339 +0.075510 -0.264212 +0.079190 -0.270035 +0.082970 -0.275837 +0.086830 -0.281586 +0.090770 -0.287282 +0.094810 -0.292952 +0.098930 -0.298568 +0.103150 -0.304155 +0.107450 -0.309685 +0.111830 -0.315157 +0.116310 -0.320596 +0.120870 -0.325976 +0.125520 -0.331308 +0.130260 -0.336589 +0.135080 -0.341809 +0.139990 -0.346977 +0.144990 -0.352091 +0.150080 -0.357150 +0.155260 -0.362152 +0.160520 -0.367088 +0.165870 -0.371964 +0.171310 -0.376780 +0.176840 -0.381533 +0.182450 -0.386215 +0.188160 -0.390840 +0.193950 -0.395390 +0.199820 -0.399865 +0.205790 -0.404278 +0.211840 -0.408612 +0.217980 -0.412874 +0.224210 -0.417061 +0.230530 -0.421172 +0.236930 -0.425199 +0.243420 -0.429146 +0.250000 -0.433013 +0.256670 -0.436796 +0.263330 -0.440440 +0.270000 -0.443959 +0.276670 -0.447352 +0.283330 -0.450615 +0.290000 -0.453762 +0.296670 -0.456790 +0.303330 -0.459697 +0.310000 -0.462493 +0.316670 -0.465178 +0.323330 -0.467747 +0.330000 -0.470213 +0.336670 -0.472571 +0.343330 -0.474821 +0.350000 -0.476970 +0.356670 -0.479016 +0.363330 -0.480959 +0.370000 -0.482804 +0.376670 -0.484551 +0.383330 -0.486198 +0.390000 -0.487750 +0.396670 -0.489206 +0.403330 -0.490566 +0.410000 -0.491833 +0.416670 -0.493007 +0.423330 -0.494087 +0.430000 -0.495076 +0.436670 -0.495973 +0.443330 -0.496778 +0.450000 -0.497494 +0.456670 -0.498119 +0.463330 -0.498653 +0.470000 -0.499099 +0.476670 -0.499455 +0.483330 -0.499722 +0.490000 -0.499900 +0.496670 -0.499989 +0.503330 -0.499989 +0.510000 -0.499900 +0.516670 -0.499722 +0.523330 -0.499455 +0.530000 -0.499099 +0.536670 -0.498653 +0.543330 -0.498119 +0.550000 -0.497494 +0.556670 -0.496778 +0.563330 -0.495973 +0.570000 -0.495076 +0.576670 -0.494087 +0.583330 -0.493007 +0.590000 -0.491833 +0.596669 -0.490566 +0.603330 -0.489206 +0.610000 -0.487750 +0.616670 -0.486198 +0.623330 -0.484551 +0.630000 -0.482804 +0.636670 -0.480959 +0.643330 -0.479016 +0.650000 -0.476970 +0.656670 -0.474821 +0.663330 -0.472571 +0.670000 -0.470213 +0.676670 -0.467747 +0.683330 -0.465178 +0.690000 -0.462493 +0.696670 -0.459697 +0.703330 -0.456790 +0.710000 -0.453762 +0.716670 -0.450615 +0.723330 -0.447352 +0.730000 -0.443959 +0.736670 -0.440440 +0.743330 -0.436796 +0.750000 -0.433013 +0.756600 -0.429135 +0.763140 -0.425156 +0.769610 -0.421082 +0.776010 -0.416915 +0.782350 -0.412648 +0.788630 -0.408280 +0.794840 -0.403818 +0.800980 -0.399263 +0.807060 -0.394606 +0.813070 -0.389855 +0.819020 -0.385002 +0.824900 -0.380053 +0.830720 -0.374999 +0.836470 -0.369849 +0.842160 -0.364591 +0.847780 -0.359234 +0.853330 -0.353777 +0.858820 -0.348207 +0.864250 -0.342523 +0.869610 -0.336732 +0.874900 -0.330832 +0.880130 -0.324809 +0.885290 -0.318672 +0.890390 -0.312403 +0.895420 -0.306011 +0.900390 -0.299479 +0.905290 -0.292814 +0.910130 -0.285995 +0.914900 -0.279030 +0.919610 -0.271896 +0.924250 -0.264598 +0.928820 -0.257125 +0.933330 -0.249450 +0.937780 -0.241555 +0.942160 -0.233441 +0.946470 -0.225088 +0.950720 -0.216452 +0.954900 -0.207523 +0.959020 -0.198244 +0.963070 -0.188590 +0.967060 -0.178480 +0.970980 -0.167863 +0.974840 -0.156611 +0.978630 -0.144614 +0.982350 -0.131676 +0.986010 -0.117449 +0.989610 -0.101400 +0.993140 -0.082541 +0.996600 -0.058210 +1.000000 0.000000 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder2.dat b/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder2.dat new file mode 100644 index 00000000..f6e6a9fe --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder2.dat @@ -0,0 +1,60 @@ +! ------------ AirfoilInfo v1.01.x Input File ---------------------------------- +! Round root section with a Cd of 0.35 +! Made by Jason Jonkman +! note that this file uses Marshall Buhl's new input file processing; start all comment lines with ! +! ------------------------------------------------------------------------------ +"DEFAULT" InterpOrd ! Interpolation order to use for quasi-steady table lookup {1=linear; 3=cubic spline; "default"} [default=1] + 1 NonDimArea ! The non-dimensional area of the airfoil (area/chord^2) (set to 1.0 if unsure or unneeded) +@"Cylinder2_coords.txt" NumCoords ! The number of coordinates in the airfoil shape file. Set to zero if coordinates not included. +! ......... x-y coordinates are next if NumCoords > 0 ............. +"unused" BL_file ! The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. + 1 NumTabs ! Number of airfoil tables in this file. +! ------------------------------------------------------------------------------ +! data for table 1 +! ------------------------------------------------------------------------------ + 0.75 Re ! Reynolds number in millions + 0 UserProp ! User property (control) setting +True InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +!........................................ + 0 alpha0 ! 0-lift angle of attack, depends on airfoil. + 0 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) + 0 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] + 0 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It can be calculated from the static value of Cn at either the break in the pitching moment or the loss of chord force at the onset of stall. It is close to the condition of maximum lift of the airfoil at low Mach numbers. + 0 Cn2 ! As Cn1 for negative AOAs. + 0.19 St_sh ! Strouhal's shedding frequency constant. [default = 0.19] + 0.35 Cd0 ! 2D drag coefficient value at 0-lift. + 0 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does not include a column for Cm, this needs to be set to 0.0] + 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] + 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] + 0.2 x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] +"DEFAULT" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] +"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] +!........................................ +! Table of aerodynamics coefficients + 3 NumAlf ! Number of data lines in the following table +! Alpha Cl Cd Cm +! (deg) (-) (-) (-) + -180.00 0.000 0.3500 0.0 + 0.00 0.000 0.3500 0.0 + 180.00 0.000 0.3500 0.0 +! ------------------------------------------------------------------------------ + diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder2_coords.txt b/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder2_coords.txt new file mode 100644 index 00000000..ba5741b6 --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/Cylinder2_coords.txt @@ -0,0 +1,407 @@ + 400 NumCoords ! The number of coordinates in the airfoil shape file (including an extra coordinate for airfoil reference). Set to zero if coordinates not included. +! ......... x-y coordinates are next if NumCoords > 0 ............. +! x-y coordinate of airfoil reference +! x/c y/c +0.41667 0 +! coordinates of airfoil shape +! cylinder (interpolated to 399 points) +! x/c y/c +1.000000 0.000000 +0.996600 0.058210 +0.993140 0.082541 +0.989610 0.101400 +0.986010 0.117449 +0.982350 0.131676 +0.978630 0.144614 +0.974840 0.156611 +0.970980 0.167863 +0.967060 0.178480 +0.963070 0.188590 +0.959020 0.198244 +0.954900 0.207523 +0.950720 0.216452 +0.946470 0.225088 +0.942160 0.233441 +0.937780 0.241555 +0.933330 0.249450 +0.928820 0.257125 +0.924250 0.264598 +0.919610 0.271896 +0.914900 0.279030 +0.910130 0.285995 +0.905290 0.292814 +0.900390 0.299479 +0.895420 0.306011 +0.890390 0.312403 +0.885290 0.318672 +0.880130 0.324809 +0.874900 0.330832 +0.869610 0.336732 +0.864250 0.342523 +0.858820 0.348207 +0.853330 0.353777 +0.847780 0.359234 +0.842160 0.364591 +0.836470 0.369849 +0.830720 0.374999 +0.824900 0.380053 +0.819020 0.385002 +0.813070 0.389855 +0.807060 0.394606 +0.800980 0.399263 +0.794840 0.403818 +0.788630 0.408280 +0.782350 0.412648 +0.776010 0.416915 +0.769610 0.421082 +0.763140 0.425156 +0.756600 0.429135 +0.750000 0.433013 +0.743330 0.436796 +0.736670 0.440440 +0.730000 0.443959 +0.723330 0.447352 +0.716670 0.450615 +0.710000 0.453762 +0.703330 0.456790 +0.696670 0.459697 +0.690000 0.462493 +0.683330 0.465178 +0.676670 0.467747 +0.670000 0.470213 +0.663330 0.472571 +0.656670 0.474821 +0.650000 0.476970 +0.643330 0.479016 +0.636670 0.480959 +0.630000 0.482804 +0.623330 0.484551 +0.616670 0.486198 +0.610000 0.487750 +0.603330 0.489206 +0.596670 0.490566 +0.590000 0.491833 +0.583330 0.493007 +0.576670 0.494087 +0.570000 0.495076 +0.563330 0.495973 +0.556670 0.496778 +0.550000 0.497494 +0.543330 0.498119 +0.536670 0.498653 +0.529999 0.499099 +0.523330 0.499455 +0.516670 0.499722 +0.510000 0.499900 +0.503330 0.499989 +0.496670 0.499989 +0.490000 0.499900 +0.483330 0.499722 +0.476670 0.499455 +0.470001 0.499099 +0.463330 0.498653 +0.456670 0.498119 +0.450000 0.497494 +0.443330 0.496778 +0.436670 0.495973 +0.430000 0.495076 +0.423330 0.494087 +0.416670 0.493007 +0.410000 0.491833 +0.403330 0.490566 +0.396670 0.489206 +0.390000 0.487750 +0.383330 0.486198 +0.376670 0.484551 +0.370000 0.482804 +0.363330 0.480959 +0.356670 0.479016 +0.350000 0.476970 +0.343330 0.474821 +0.336670 0.472571 +0.330000 0.470213 +0.323330 0.467747 +0.316670 0.465178 +0.310000 0.462493 +0.303330 0.459697 +0.296670 0.456790 +0.290000 0.453762 +0.283330 0.450615 +0.276671 0.447352 +0.270000 0.443959 +0.263330 0.440440 +0.256670 0.436796 +0.250000 0.433013 +0.243420 0.429146 +0.236930 0.425199 +0.230530 0.421172 +0.224210 0.417061 +0.217980 0.412874 +0.211840 0.408612 +0.205790 0.404278 +0.199820 0.399865 +0.193950 0.395390 +0.188160 0.390840 +0.182450 0.386215 +0.176840 0.381533 +0.171310 0.376780 +0.165870 0.371964 +0.160520 0.367088 +0.155260 0.362152 +0.150080 0.357150 +0.144990 0.352091 +0.139990 0.346977 +0.135080 0.341809 +0.130260 0.336589 +0.125520 0.331308 +0.120870 0.325976 +0.116310 0.320596 +0.111830 0.315157 +0.107450 0.309685 +0.103150 0.304155 +0.098930 0.298568 +0.094810 0.292952 +0.090770 0.287282 +0.086830 0.281586 +0.082970 0.275837 +0.079191 0.270037 +0.075510 0.264212 +0.071910 0.258339 +0.068400 0.252431 +0.064980 0.246491 +0.061640 0.240501 +0.058400 0.234498 +0.055240 0.228448 +0.052170 0.222370 +0.049180 0.216244 +0.046290 0.210112 +0.043480 0.203935 +0.040760 0.197734 +0.038120 0.191486 +0.035580 0.185241 +0.033120 0.178950 +0.030750 0.172640 +0.028470 0.166311 +0.026270 0.159937 +0.024170 0.153577 +0.022150 0.147171 +0.020220 0.140752 +0.018370 0.134285 +0.016620 0.127843 +0.014950 0.121353 +0.013370 0.114853 +0.011870 0.108301 +0.010470 0.101786 +0.009150 0.095217 +0.007920 0.088641 +0.006780 0.082061 +0.005720 0.075414 +0.004760 0.068828 +0.003880 0.062169 +0.003090 0.055502 +0.002380 0.048727 +0.001770 0.042034 +0.001240 0.035192 +0.000800 0.028273 +0.000440 0.020972 +0.000180 0.013415 +0.000000 0.000000 +0.000180 -0.013415 +0.000440 -0.020972 +0.000800 -0.028273 +0.001240 -0.035192 +0.001770 -0.042034 +0.002380 -0.048727 +0.003090 -0.055502 +0.003880 -0.062169 +0.004760 -0.068828 +0.005720 -0.075414 +0.006780 -0.082061 +0.007920 -0.088641 +0.009150 -0.095217 +0.010470 -0.101786 +0.011870 -0.108301 +0.013370 -0.114853 +0.014950 -0.121353 +0.016620 -0.127843 +0.018370 -0.134285 +0.020220 -0.140752 +0.022150 -0.147171 +0.024170 -0.153577 +0.026270 -0.159937 +0.028470 -0.166311 +0.030750 -0.172640 +0.033120 -0.178950 +0.035580 -0.185241 +0.038120 -0.191486 +0.040760 -0.197734 +0.043480 -0.203935 +0.046290 -0.210112 +0.049180 -0.216244 +0.052170 -0.222370 +0.055240 -0.228448 +0.058400 -0.234498 +0.061640 -0.240501 +0.064980 -0.246491 +0.068400 -0.252431 +0.071910 -0.258339 +0.075510 -0.264212 +0.079190 -0.270035 +0.082970 -0.275837 +0.086830 -0.281586 +0.090770 -0.287282 +0.094810 -0.292952 +0.098930 -0.298568 +0.103150 -0.304155 +0.107450 -0.309685 +0.111830 -0.315157 +0.116310 -0.320596 +0.120870 -0.325976 +0.125520 -0.331308 +0.130260 -0.336589 +0.135080 -0.341809 +0.139990 -0.346977 +0.144990 -0.352091 +0.150080 -0.357150 +0.155260 -0.362152 +0.160520 -0.367088 +0.165870 -0.371964 +0.171310 -0.376780 +0.176840 -0.381533 +0.182450 -0.386215 +0.188160 -0.390840 +0.193950 -0.395390 +0.199820 -0.399865 +0.205790 -0.404278 +0.211840 -0.408612 +0.217980 -0.412874 +0.224210 -0.417061 +0.230530 -0.421172 +0.236930 -0.425199 +0.243420 -0.429146 +0.250000 -0.433013 +0.256670 -0.436796 +0.263330 -0.440440 +0.270000 -0.443959 +0.276670 -0.447352 +0.283330 -0.450615 +0.290000 -0.453762 +0.296670 -0.456790 +0.303330 -0.459697 +0.310000 -0.462493 +0.316670 -0.465178 +0.323330 -0.467747 +0.330000 -0.470213 +0.336670 -0.472571 +0.343330 -0.474821 +0.350000 -0.476970 +0.356670 -0.479016 +0.363330 -0.480959 +0.370000 -0.482804 +0.376670 -0.484551 +0.383330 -0.486198 +0.390000 -0.487750 +0.396670 -0.489206 +0.403330 -0.490566 +0.410000 -0.491833 +0.416670 -0.493007 +0.423330 -0.494087 +0.430000 -0.495076 +0.436670 -0.495973 +0.443330 -0.496778 +0.450000 -0.497494 +0.456670 -0.498119 +0.463330 -0.498653 +0.470000 -0.499099 +0.476670 -0.499455 +0.483330 -0.499722 +0.490000 -0.499900 +0.496670 -0.499989 +0.503330 -0.499989 +0.510000 -0.499900 +0.516670 -0.499722 +0.523330 -0.499455 +0.530000 -0.499099 +0.536670 -0.498653 +0.543330 -0.498119 +0.550000 -0.497494 +0.556670 -0.496778 +0.563330 -0.495973 +0.570000 -0.495076 +0.576670 -0.494087 +0.583330 -0.493007 +0.590000 -0.491833 +0.596669 -0.490566 +0.603330 -0.489206 +0.610000 -0.487750 +0.616670 -0.486198 +0.623330 -0.484551 +0.630000 -0.482804 +0.636670 -0.480959 +0.643330 -0.479016 +0.650000 -0.476970 +0.656670 -0.474821 +0.663330 -0.472571 +0.670000 -0.470213 +0.676670 -0.467747 +0.683330 -0.465178 +0.690000 -0.462493 +0.696670 -0.459697 +0.703330 -0.456790 +0.710000 -0.453762 +0.716670 -0.450615 +0.723330 -0.447352 +0.730000 -0.443959 +0.736670 -0.440440 +0.743330 -0.436796 +0.750000 -0.433013 +0.756600 -0.429135 +0.763140 -0.425156 +0.769610 -0.421082 +0.776010 -0.416915 +0.782350 -0.412648 +0.788630 -0.408280 +0.794840 -0.403818 +0.800980 -0.399263 +0.807060 -0.394606 +0.813070 -0.389855 +0.819020 -0.385002 +0.824900 -0.380053 +0.830720 -0.374999 +0.836470 -0.369849 +0.842160 -0.364591 +0.847780 -0.359234 +0.853330 -0.353777 +0.858820 -0.348207 +0.864250 -0.342523 +0.869610 -0.336732 +0.874900 -0.330832 +0.880130 -0.324809 +0.885290 -0.318672 +0.890390 -0.312403 +0.895420 -0.306011 +0.900390 -0.299479 +0.905290 -0.292814 +0.910130 -0.285995 +0.914900 -0.279030 +0.919610 -0.271896 +0.924250 -0.264598 +0.928820 -0.257125 +0.933330 -0.249450 +0.937780 -0.241555 +0.942160 -0.233441 +0.946470 -0.225088 +0.950720 -0.216452 +0.954900 -0.207523 +0.959020 -0.198244 +0.963070 -0.188590 +0.967060 -0.178480 +0.970980 -0.167863 +0.974840 -0.156611 +0.978630 -0.144614 +0.982350 -0.131676 +0.986010 -0.117449 +0.989610 -0.101400 +0.993140 -0.082541 +0.996600 -0.058210 +1.000000 0.000000 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/DU21_A17.dat b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU21_A17.dat new file mode 100644 index 00000000..9fb97f0d --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU21_A17.dat @@ -0,0 +1,196 @@ +! ------------ AirfoilInfo v1.01.x Input File ---------------------------------- +! DU21 airfoil with an aspect ratio of 17. Original -180 to 180deg Cl, Cd, and Cm versus AOA data taken from Appendix A of DOWEC document 10046_009.pdf (numerical values obtained from Koert Lindenburg of ECN). +! Cl and Cd values corrected for rotational stall delay and Cd values corrected using the Viterna method for 0 to 90deg AOA by Jason Jonkman using AirfoilPrep_v2p0.xls. +! note that this file uses Marshall Buhl's new input file processing; start all comment lines with ! +! ------------------------------------------------------------------------------ +"DEFAULT" InterpOrd ! Interpolation order to use for quasi-steady table lookup {1=linear; 3=cubic spline; "default"} [default=1] + 1 NonDimArea ! The non-dimensional area of the airfoil (area/chord^2) (set to 1.0 if unsure or unneeded) +@"DU21_A17_coords.txt" NumCoords ! The number of coordinates in the airfoil shape file. Set to zero if coordinates not included. +"unused" BL_file ! The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. + 1 NumTabs ! Number of airfoil tables in this file. +! ------------------------------------------------------------------------------ +! data for table 1 +! ------------------------------------------------------------------------------ + 0.75 Re ! Reynolds number in millions + 0 UserProp ! User property (control) setting +True InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +!........................................ + -4.2 alpha0 ! 0-lift angle of attack, depends on airfoil. + 8 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) + -8 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] + 0 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] + 1.4144 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It can be calculated from the static value of Cn at either the break in the pitching moment or the loss of chord force at the onset of stall. It is close to the condition of maximum lift of the airfoil at low Mach numbers. + -0.5324 Cn2 ! As Cn1 for negative AOAs. + 0.19 St_sh ! Strouhal's shedding frequency constant. [default = 0.19] + 0.006 Cd0 ! 2D drag coefficient value at 0-lift. + -0.121 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does not include a column for Cm, this needs to be set to 0.0] + 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] + 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] + 0.2 x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] +"DEFAULT" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] +"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] +!........................................ +! Table of aerodynamics coefficients + 142 NumAlf ! Number of data lines in the following table +! Alpha Cl Cd Cm +! (deg) (-) (-) (-) + -180.00 0.000 0.0185 0.0000 + -175.00 0.394 0.0332 0.1978 + -170.00 0.788 0.0945 0.3963 + -160.00 0.670 0.2809 0.2738 + -155.00 0.749 0.3932 0.3118 + -150.00 0.797 0.5112 0.3413 + -145.00 0.818 0.6309 0.3636 + -140.00 0.813 0.7485 0.3799 + -135.00 0.786 0.8612 0.3911 + -130.00 0.739 0.9665 0.3980 + -125.00 0.675 1.0625 0.4012 + -120.00 0.596 1.1476 0.4014 + -115.00 0.505 1.2206 0.3990 + -110.00 0.403 1.2805 0.3943 + -105.00 0.294 1.3265 0.3878 + -100.00 0.179 1.3582 0.3796 + -95.00 0.060 1.3752 0.3700 + -90.00 -0.060 1.3774 0.3591 + -85.00 -0.179 1.3648 0.3471 + -80.00 -0.295 1.3376 0.3340 + -75.00 -0.407 1.2962 0.3199 + -70.00 -0.512 1.2409 0.3049 + -65.00 -0.608 1.1725 0.2890 + -60.00 -0.693 1.0919 0.2722 + -55.00 -0.764 1.0002 0.2545 + -50.00 -0.820 0.8990 0.2359 + -45.00 -0.857 0.7900 0.2163 + -40.00 -0.875 0.6754 0.1958 + -35.00 -0.869 0.5579 0.1744 + -30.00 -0.838 0.4405 0.1520 + -25.00 -0.791 0.3256 0.1262 + -24.00 -0.794 0.3013 0.1170 + -23.00 -0.805 0.2762 0.1059 + -22.00 -0.821 0.2506 0.0931 + -21.00 -0.843 0.2246 0.0788 + -20.00 -0.869 0.1983 0.0631 + -19.00 -0.899 0.1720 0.0464 + -18.00 -0.931 0.1457 0.0286 + -17.00 -0.964 0.1197 0.0102 + -16.00 -0.999 0.0940 -0.0088 + -15.00 -1.033 0.0689 -0.0281 + -14.50 -1.050 0.0567 -0.0378 + -12.01 -0.953 0.0271 -0.0349 + -11.00 -0.900 0.0303 -0.0361 + -9.98 -0.827 0.0287 -0.0464 + -8.12 -0.536 0.0124 -0.0821 + -7.62 -0.467 0.0109 -0.0924 + -7.11 -0.393 0.0092 -0.1015 + -6.60 -0.323 0.0083 -0.1073 + -6.50 -0.311 0.0089 -0.1083 + -6.00 -0.245 0.0082 -0.1112 + -5.50 -0.178 0.0074 -0.1146 + -5.00 -0.113 0.0069 -0.1172 + -4.50 -0.048 0.0065 -0.1194 + -4.00 0.016 0.0063 -0.1213 + -3.50 0.080 0.0061 -0.1232 + -3.00 0.145 0.0058 -0.1252 + -2.50 0.208 0.0057 -0.1268 + -2.00 0.270 0.0057 -0.1282 + -1.50 0.333 0.0057 -0.1297 + -1.00 0.396 0.0057 -0.1310 + -0.50 0.458 0.0057 -0.1324 + 0.00 0.521 0.0057 -0.1337 + 0.50 0.583 0.0057 -0.1350 + 1.00 0.645 0.0058 -0.1363 + 1.50 0.706 0.0058 -0.1374 + 2.00 0.768 0.0059 -0.1385 + 2.50 0.828 0.0061 -0.1395 + 3.00 0.888 0.0063 -0.1403 + 3.50 0.948 0.0066 -0.1406 + 4.00 0.996 0.0071 -0.1398 + 4.50 1.046 0.0079 -0.1390 + 5.00 1.095 0.0090 -0.1378 + 5.50 1.145 0.0103 -0.1369 + 6.00 1.192 0.0113 -0.1353 + 6.50 1.239 0.0122 -0.1338 + 7.00 1.283 0.0131 -0.1317 + 7.50 1.324 0.0139 -0.1291 + 8.00 1.358 0.0147 -0.1249 + 8.50 1.385 0.0158 -0.1213 + 9.00 1.403 0.0181 -0.1177 + 9.50 1.401 0.0211 -0.1142 + 10.00 1.358 0.0255 -0.1103 + 10.50 1.313 0.0301 -0.1066 + 11.00 1.287 0.0347 -0.1032 + 11.50 1.274 0.0401 -0.1002 + 12.00 1.272 0.0468 -0.0971 + 12.50 1.273 0.0545 -0.0940 + 13.00 1.273 0.0633 -0.0909 + 13.50 1.273 0.0722 -0.0883 + 14.00 1.272 0.0806 -0.0865 + 14.50 1.273 0.0900 -0.0854 + 15.00 1.275 0.0987 -0.0849 + 15.50 1.281 0.1075 -0.0847 + 16.00 1.284 0.1170 -0.0850 + 16.50 1.296 0.1270 -0.0858 + 17.00 1.306 0.1368 -0.0869 + 17.50 1.308 0.1464 -0.0883 + 18.00 1.308 0.1562 -0.0901 + 18.50 1.308 0.1664 -0.0922 + 19.00 1.308 0.1770 -0.0949 + 19.50 1.307 0.1878 -0.0980 + 20.00 1.311 0.1987 -0.1017 + 20.50 1.325 0.2100 -0.1059 + 21.00 1.324 0.2214 -0.1105 + 22.00 1.277 0.2499 -0.1172 + 23.00 1.229 0.2786 -0.1239 + 24.00 1.182 0.3077 -0.1305 + 25.00 1.136 0.3371 -0.1370 + 26.00 1.093 0.3664 -0.1433 + 28.00 1.017 0.4246 -0.1556 + 30.00 0.962 0.4813 -0.1671 + 32.00 0.937 0.5356 -0.1778 + 35.00 0.947 0.6127 -0.1923 + 40.00 0.950 0.7396 -0.2154 + 45.00 0.928 0.8623 -0.2374 + 50.00 0.884 0.9781 -0.2583 + 55.00 0.821 1.0846 -0.2782 + 60.00 0.740 1.1796 -0.2971 + 65.00 0.646 1.2617 -0.3149 + 70.00 0.540 1.3297 -0.3318 + 75.00 0.425 1.3827 -0.3476 + 80.00 0.304 1.4202 -0.3625 + 85.00 0.179 1.4423 -0.3763 + 90.00 0.053 1.4512 -0.3890 + 95.00 -0.073 1.4480 -0.4004 + 100.00 -0.198 1.4294 -0.4105 + 105.00 -0.319 1.3954 -0.4191 + 110.00 -0.434 1.3464 -0.4260 + 115.00 -0.541 1.2829 -0.4308 + 120.00 -0.637 1.2057 -0.4333 + 125.00 -0.720 1.1157 -0.4330 + 130.00 -0.787 1.0144 -0.4294 + 135.00 -0.836 0.9033 -0.4219 + 140.00 -0.864 0.7845 -0.4098 + 145.00 -0.869 0.6605 -0.3922 + 150.00 -0.847 0.5346 -0.3682 + 155.00 -0.795 0.4103 -0.3364 + 160.00 -0.711 0.2922 -0.2954 + 170.00 -0.788 0.0969 -0.3966 + 175.00 -0.394 0.0334 -0.1978 + 180.00 0.000 0.0185 0.0000 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/DU21_A17_coords.txt b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU21_A17_coords.txt new file mode 100644 index 00000000..72e36bf8 --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU21_A17_coords.txt @@ -0,0 +1,407 @@ + 400 NumCoords ! The number of coordinates in the airfoil shape file (including an extra coordinate for airfoil reference). Set to zero if coordinates not included. +! ......... x-y coordinates are next if NumCoords > 0 ............. +! x-y coordinate of airfoil reference +! x/c y/c +0.25 0 +! coordinates of airfoil shape; data from TU Delft as posted here: https://wind.nrel.gov/forum/wind/viewtopic.php?f=2&t=440 +! DU 93-W-210.lm +! x/c y/c +1.00000 0.00194 +0.99660 0.00304 +0.99314 0.00411 +0.98961 0.00516 +0.98601 0.00618 +0.98235 0.00720 +0.97863 0.00823 +0.97484 0.00927 +0.97098 0.01033 +0.96706 0.01139 +0.96307 0.01246 +0.95902 0.01354 +0.95490 0.01463 +0.95072 0.01573 +0.94647 0.01684 +0.94216 0.01797 +0.93778 0.01911 +0.93333 0.02026 +0.92882 0.02142 +0.92425 0.02260 +0.91961 0.02379 +0.91490 0.02499 +0.91013 0.02621 +0.90529 0.02744 +0.90039 0.02869 +0.89542 0.02995 +0.89039 0.03122 +0.88529 0.03250 +0.88013 0.03379 +0.87490 0.03510 +0.86961 0.03643 +0.86425 0.03776 +0.85882 0.03912 +0.85333 0.04048 +0.84778 0.04186 +0.84216 0.04326 +0.83647 0.04466 +0.83072 0.04608 +0.82490 0.04752 +0.81902 0.04896 +0.81307 0.05041 +0.80706 0.05188 +0.80098 0.05336 +0.79484 0.05484 +0.78863 0.05634 +0.78235 0.05784 +0.77601 0.05936 +0.76961 0.06088 +0.76314 0.06242 +0.75660 0.06396 +0.75000 0.06550 +0.74333 0.06706 +0.73667 0.06860 +0.73000 0.07014 +0.72333 0.07167 +0.71667 0.07320 +0.71000 0.07472 +0.70333 0.07624 +0.69667 0.07774 +0.69000 0.07924 +0.68333 0.08072 +0.67667 0.08220 +0.67000 0.08366 +0.66333 0.08511 +0.65667 0.08655 +0.65000 0.08798 +0.64333 0.08940 +0.63667 0.09080 +0.63000 0.09220 +0.62333 0.09357 +0.61667 0.09492 +0.61000 0.09626 +0.60333 0.09759 +0.59667 0.09889 +0.59000 0.10017 +0.58333 0.10143 +0.57667 0.10267 +0.57000 0.10389 +0.56333 0.10509 +0.55667 0.10627 +0.55000 0.10742 +0.54333 0.10854 +0.53667 0.10964 +0.53000 0.11071 +0.52333 0.11175 +0.51667 0.11277 +0.51000 0.11375 +0.50333 0.11470 +0.49667 0.11562 +0.49000 0.11650 +0.48333 0.11734 +0.47667 0.11813 +0.47000 0.11889 +0.46333 0.11960 +0.45667 0.12027 +0.45000 0.12089 +0.44333 0.12147 +0.43667 0.12200 +0.43000 0.12249 +0.42333 0.12292 +0.41667 0.12331 +0.41000 0.12365 +0.40333 0.12394 +0.39667 0.12418 +0.39000 0.12436 +0.38333 0.12449 +0.37667 0.12457 +0.37000 0.12459 +0.36333 0.12455 +0.35667 0.12446 +0.35000 0.12431 +0.34333 0.12411 +0.33667 0.12385 +0.33000 0.12353 +0.32333 0.12317 +0.31667 0.12275 +0.31000 0.12228 +0.30333 0.12175 +0.29667 0.12117 +0.29000 0.12053 +0.28333 0.11984 +0.27667 0.11910 +0.27000 0.11829 +0.26333 0.11744 +0.25667 0.11653 +0.25000 0.11557 +0.24342 0.11456 +0.23693 0.11351 +0.23053 0.11243 +0.22421 0.11130 +0.21798 0.11015 +0.21184 0.10896 +0.20579 0.10773 +0.19982 0.10648 +0.19395 0.10520 +0.18816 0.10389 +0.18245 0.10255 +0.17684 0.10119 +0.17131 0.09980 +0.16587 0.09839 +0.16052 0.09696 +0.15526 0.09550 +0.15008 0.09403 +0.14499 0.09254 +0.13999 0.09103 +0.13508 0.08950 +0.13026 0.08796 +0.12552 0.08641 +0.12087 0.08483 +0.11631 0.08325 +0.11183 0.08165 +0.10745 0.08004 +0.10315 0.07843 +0.09893 0.07679 +0.09481 0.07516 +0.09077 0.07351 +0.08683 0.07186 +0.08297 0.07021 +0.07919 0.06854 +0.07551 0.06687 +0.07191 0.06520 +0.06840 0.06353 +0.06498 0.06185 +0.06164 0.06017 +0.05840 0.05850 +0.05524 0.05682 +0.05217 0.05515 +0.04918 0.05348 +0.04629 0.05181 +0.04348 0.05015 +0.04076 0.04849 +0.03812 0.04683 +0.03558 0.04519 +0.03312 0.04356 +0.03075 0.04194 +0.02847 0.04033 +0.02627 0.03874 +0.02417 0.03716 +0.02215 0.03560 +0.02022 0.03404 +0.01837 0.03249 +0.01662 0.03094 +0.01495 0.02940 +0.01337 0.02785 +0.01187 0.02630 +0.01047 0.02476 +0.00915 0.02322 +0.00792 0.02169 +0.00678 0.02017 +0.00572 0.01864 +0.00476 0.01713 +0.00388 0.01562 +0.00309 0.01410 +0.00238 0.01253 +0.00177 0.01094 +0.00124 0.00923 +0.00080 0.00740 +0.00044 0.00537 +0.00018 0.00333 +0.00000 0.00000 +0.00018 -0.00292 +0.00044 -0.00443 +0.00080 -0.00589 +0.00124 -0.00727 +0.00177 -0.00864 +0.00238 -0.00998 +0.00309 -0.01134 +0.00388 -0.01266 +0.00476 -0.01397 +0.00572 -0.01526 +0.00678 -0.01656 +0.00792 -0.01785 +0.00915 -0.01914 +0.01047 -0.02044 +0.01187 -0.02174 +0.01337 -0.02306 +0.01495 -0.02438 +0.01662 -0.02571 +0.01837 -0.02705 +0.02022 -0.02841 +0.02215 -0.02976 +0.02417 -0.03112 +0.02627 -0.03248 +0.02847 -0.03384 +0.03075 -0.03520 +0.03312 -0.03655 +0.03558 -0.03789 +0.03812 -0.03923 +0.04076 -0.04056 +0.04348 -0.04188 +0.04629 -0.04319 +0.04918 -0.04449 +0.05217 -0.04579 +0.05524 -0.04708 +0.05840 -0.04836 +0.06164 -0.04963 +0.06498 -0.05089 +0.06840 -0.05215 +0.07191 -0.05340 +0.07551 -0.05464 +0.07919 -0.05587 +0.08297 -0.05709 +0.08683 -0.05831 +0.09077 -0.05951 +0.09481 -0.06071 +0.09893 -0.06189 +0.10315 -0.06306 +0.10745 -0.06422 +0.11183 -0.06536 +0.11631 -0.06648 +0.12087 -0.06759 +0.12552 -0.06868 +0.13026 -0.06975 +0.13508 -0.07079 +0.13999 -0.07182 +0.14499 -0.07282 +0.15008 -0.07380 +0.15526 -0.07476 +0.16052 -0.07568 +0.16587 -0.07658 +0.17131 -0.07746 +0.17684 -0.07830 +0.18245 -0.07911 +0.18816 -0.07989 +0.19395 -0.08063 +0.19982 -0.08134 +0.20579 -0.08201 +0.21184 -0.08264 +0.21798 -0.08324 +0.22421 -0.08380 +0.23053 -0.08432 +0.23693 -0.08479 +0.24342 -0.08523 +0.25000 -0.08561 +0.25667 -0.08595 +0.26333 -0.08624 +0.27000 -0.08648 +0.27667 -0.08667 +0.28333 -0.08680 +0.29000 -0.08689 +0.29667 -0.08693 +0.30333 -0.08692 +0.31000 -0.08686 +0.31667 -0.08676 +0.32333 -0.08660 +0.33000 -0.08640 +0.33667 -0.08615 +0.34333 -0.08586 +0.35000 -0.08553 +0.35667 -0.08515 +0.36333 -0.08473 +0.37000 -0.08427 +0.37667 -0.08376 +0.38333 -0.08322 +0.39000 -0.08263 +0.39667 -0.08200 +0.40333 -0.08134 +0.41000 -0.08062 +0.41667 -0.07987 +0.42333 -0.07908 +0.43000 -0.07824 +0.43667 -0.07736 +0.44333 -0.07644 +0.45000 -0.07548 +0.45667 -0.07448 +0.46333 -0.07343 +0.47000 -0.07235 +0.47667 -0.07122 +0.48333 -0.07006 +0.49000 -0.06886 +0.49667 -0.06763 +0.50333 -0.06636 +0.51000 -0.06506 +0.51667 -0.06373 +0.52333 -0.06237 +0.53000 -0.06097 +0.53667 -0.05955 +0.54333 -0.05810 +0.55000 -0.05663 +0.55667 -0.05513 +0.56333 -0.05361 +0.57000 -0.05207 +0.57667 -0.05050 +0.58333 -0.04892 +0.59000 -0.04732 +0.59667 -0.04571 +0.60333 -0.04408 +0.61000 -0.04243 +0.61667 -0.04078 +0.62333 -0.03911 +0.63000 -0.03744 +0.63667 -0.03576 +0.64333 -0.03409 +0.65000 -0.03241 +0.65667 -0.03073 +0.66333 -0.02906 +0.67000 -0.02740 +0.67667 -0.02574 +0.68333 -0.02411 +0.69000 -0.02248 +0.69667 -0.02088 +0.70333 -0.01930 +0.71000 -0.01774 +0.71667 -0.01620 +0.72333 -0.01470 +0.73000 -0.01323 +0.73667 -0.01179 +0.74333 -0.01039 +0.75000 -0.00903 +0.75660 -0.00772 +0.76314 -0.00647 +0.76961 -0.00528 +0.77601 -0.00416 +0.78235 -0.00308 +0.78863 -0.00207 +0.79484 -0.00112 +0.80098 -0.00023 +0.80706 0.00060 +0.81307 0.00136 +0.81902 0.00207 +0.82490 0.00273 +0.83072 0.00333 +0.83647 0.00387 +0.84216 0.00435 +0.84778 0.00479 +0.85333 0.00517 +0.85882 0.00550 +0.86425 0.00578 +0.86961 0.00601 +0.87490 0.00620 +0.88013 0.00633 +0.88529 0.00643 +0.89039 0.00648 +0.89542 0.00649 +0.90039 0.00646 +0.90529 0.00639 +0.91013 0.00628 +0.91490 0.00613 +0.91961 0.00595 +0.92425 0.00574 +0.92882 0.00550 +0.93333 0.00523 +0.93778 0.00494 +0.94216 0.00462 +0.94647 0.00428 +0.95072 0.00392 +0.95490 0.00355 +0.95902 0.00315 +0.96307 0.00275 +0.96706 0.00233 +0.97098 0.00189 +0.97484 0.00145 +0.97863 0.00099 +0.98235 0.00053 +0.98601 0.00005 +0.98961 -0.00044 +0.99314 -0.00094 +0.99660 -0.00143 +1.00000 -0.00194 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/DU25_A17.dat b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU25_A17.dat new file mode 100644 index 00000000..60730d0a --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU25_A17.dat @@ -0,0 +1,194 @@ +! ------------ AirfoilInfo v1.01.x Input File ---------------------------------- +! DU25 airfoil with an aspect ratio of 17. Original -180 to 180deg Cl, Cd, and Cm versus AOA data taken from Appendix A of DOWEC document 10046_009.pdf (numerical values obtained from Koert Lindenburg of ECN). +! Cl and Cd values corrected for rotational stall delay and Cd values corrected using the Viterna method for 0 to 90deg AOA by Jason Jonkman using AirfoilPrep_v2p0.xls. +! note that this file uses Marshall Buhl's new input file processing; start all comment lines with ! +! ------------------------------------------------------------------------------ +"DEFAULT" InterpOrd ! Interpolation order to use for quasi-steady table lookup {1=linear; 3=cubic spline; "default"} [default=1] + 1 NonDimArea ! The non-dimensional area of the airfoil (area/chord^2) (set to 1.0 if unsure or unneeded) +@"DU25_A17_coords.txt" NumCoords ! The number of coordinates in the airfoil shape file. Set to zero if coordinates not included. +"unused" BL_file ! The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. + 1 NumTabs ! Number of airfoil tables in this file. +! ------------------------------------------------------------------------------ +! data for table 1 +! ------------------------------------------------------------------------------ + 0.75 Re ! Reynolds number in millions + 0 UserProp ! User property (control) setting +True InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +!........................................ + -3.2 alpha0 ! 0-lift angle of attack, depends on airfoil. + 8.5 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) + -8.5 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] + 0 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] + 1.4336 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It can be calculated from the static value of Cn at either the break in the pitching moment or the loss of chord force at the onset of stall. It is close to the condition of maximum lift of the airfoil at low Mach numbers. + -0.6873 Cn2 ! As Cn1 for negative AOAs. + 0.19 St_sh ! Strouhal's shedding frequency constant. [default = 0.19] + 0.006 Cd0 ! 2D drag coefficient value at 0-lift. + -0.12 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does not include a column for Cm, this needs to be set to 0.0] + 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] + 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] + 0.2 x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] +"DEFAULT" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] +"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] +!........................................ +! Table of aerodynamics coefficients + 140 NumAlf ! Number of data lines in the following table +! Alpha Cl Cd Cm +! (deg) (-) (-) (-) + -180.00 0.000 0.0202 0.0000 + -175.00 0.368 0.0324 0.1845 + -170.00 0.735 0.0943 0.3701 + -160.00 0.695 0.2848 0.2679 + -155.00 0.777 0.4001 0.3046 + -150.00 0.828 0.5215 0.3329 + -145.00 0.850 0.6447 0.3540 + -140.00 0.846 0.7660 0.3693 + -135.00 0.818 0.8823 0.3794 + -130.00 0.771 0.9911 0.3854 + -125.00 0.705 1.0905 0.3878 + -120.00 0.624 1.1787 0.3872 + -115.00 0.530 1.2545 0.3841 + -110.00 0.426 1.3168 0.3788 + -105.00 0.314 1.3650 0.3716 + -100.00 0.195 1.3984 0.3629 + -95.00 0.073 1.4169 0.3529 + -90.00 -0.050 1.4201 0.3416 + -85.00 -0.173 1.4081 0.3292 + -80.00 -0.294 1.3811 0.3159 + -75.00 -0.409 1.3394 0.3017 + -70.00 -0.518 1.2833 0.2866 + -65.00 -0.617 1.2138 0.2707 + -60.00 -0.706 1.1315 0.2539 + -55.00 -0.780 1.0378 0.2364 + -50.00 -0.839 0.9341 0.2181 + -45.00 -0.879 0.8221 0.1991 + -40.00 -0.898 0.7042 0.1792 + -35.00 -0.893 0.5829 0.1587 + -30.00 -0.862 0.4616 0.1374 + -25.00 -0.803 0.3441 0.1154 + -24.00 -0.792 0.3209 0.1101 + -23.00 -0.789 0.2972 0.1031 + -22.00 -0.792 0.2730 0.0947 + -21.00 -0.801 0.2485 0.0849 + -20.00 -0.815 0.2237 0.0739 + -19.00 -0.833 0.1990 0.0618 + -18.00 -0.854 0.1743 0.0488 + -17.00 -0.879 0.1498 0.0351 + -16.00 -0.905 0.1256 0.0208 + -15.00 -0.932 0.1020 0.0060 + -14.00 -0.959 0.0789 -0.0091 + -13.00 -0.985 0.0567 -0.0243 + -12.01 -0.953 0.0271 -0.0349 + -11.00 -0.900 0.0303 -0.0361 + -9.98 -0.827 0.0287 -0.0464 + -8.98 -0.753 0.0271 -0.0534 + -8.47 -0.691 0.0264 -0.0650 + -7.45 -0.555 0.0114 -0.0782 + -6.42 -0.413 0.0094 -0.0904 + -5.40 -0.271 0.0086 -0.1006 + -5.00 -0.220 0.0073 -0.1107 + -4.50 -0.152 0.0071 -0.1135 + -4.00 -0.084 0.0070 -0.1162 + -3.50 -0.018 0.0069 -0.1186 + -3.00 0.049 0.0068 -0.1209 + -2.50 0.115 0.0068 -0.1231 + -2.00 0.181 0.0068 -0.1252 + -1.50 0.247 0.0067 -0.1272 + -1.00 0.312 0.0067 -0.1293 + -0.50 0.377 0.0067 -0.1311 + 0.00 0.444 0.0065 -0.1330 + 0.50 0.508 0.0065 -0.1347 + 1.00 0.573 0.0066 -0.1364 + 1.50 0.636 0.0067 -0.1380 + 2.00 0.701 0.0068 -0.1396 + 2.50 0.765 0.0069 -0.1411 + 3.00 0.827 0.0070 -0.1424 + 3.50 0.890 0.0071 -0.1437 + 4.00 0.952 0.0073 -0.1448 + 4.50 1.013 0.0076 -0.1456 + 5.00 1.062 0.0079 -0.1445 + 6.00 1.161 0.0099 -0.1419 + 6.50 1.208 0.0117 -0.1403 + 7.00 1.254 0.0132 -0.1382 + 7.50 1.301 0.0143 -0.1362 + 8.00 1.336 0.0153 -0.1320 + 8.50 1.369 0.0165 -0.1276 + 9.00 1.400 0.0181 -0.1234 + 9.50 1.428 0.0211 -0.1193 + 10.00 1.442 0.0262 -0.1152 + 10.50 1.427 0.0336 -0.1115 + 11.00 1.374 0.0420 -0.1081 + 11.50 1.316 0.0515 -0.1052 + 12.00 1.277 0.0601 -0.1026 + 12.50 1.250 0.0693 -0.1000 + 13.00 1.246 0.0785 -0.0980 + 13.50 1.247 0.0888 -0.0969 + 14.00 1.256 0.1000 -0.0968 + 14.50 1.260 0.1108 -0.0973 + 15.00 1.271 0.1219 -0.0981 + 15.50 1.281 0.1325 -0.0992 + 16.00 1.289 0.1433 -0.1006 + 16.50 1.294 0.1541 -0.1023 + 17.00 1.304 0.1649 -0.1042 + 17.50 1.309 0.1754 -0.1064 + 18.00 1.315 0.1845 -0.1082 + 18.50 1.320 0.1953 -0.1110 + 19.00 1.330 0.2061 -0.1143 + 19.50 1.343 0.2170 -0.1179 + 20.00 1.354 0.2280 -0.1219 + 20.50 1.359 0.2390 -0.1261 + 21.00 1.360 0.2536 -0.1303 + 22.00 1.325 0.2814 -0.1375 + 23.00 1.288 0.3098 -0.1446 + 24.00 1.251 0.3386 -0.1515 + 25.00 1.215 0.3678 -0.1584 + 26.00 1.181 0.3972 -0.1651 + 28.00 1.120 0.4563 -0.1781 + 30.00 1.076 0.5149 -0.1904 + 32.00 1.056 0.5720 -0.2017 + 35.00 1.066 0.6548 -0.2173 + 40.00 1.064 0.7901 -0.2418 + 45.00 1.035 0.9190 -0.2650 + 50.00 0.980 1.0378 -0.2867 + 55.00 0.904 1.1434 -0.3072 + 60.00 0.810 1.2333 -0.3265 + 65.00 0.702 1.3055 -0.3446 + 70.00 0.582 1.3587 -0.3616 + 75.00 0.456 1.3922 -0.3775 + 80.00 0.326 1.4063 -0.3921 + 85.00 0.197 1.4042 -0.4057 + 90.00 0.072 1.3985 -0.4180 + 95.00 -0.050 1.3973 -0.4289 + 100.00 -0.170 1.3810 -0.4385 + 105.00 -0.287 1.3498 -0.4464 + 110.00 -0.399 1.3041 -0.4524 + 115.00 -0.502 1.2442 -0.4563 + 120.00 -0.596 1.1709 -0.4577 + 125.00 -0.677 1.0852 -0.4563 + 130.00 -0.743 0.9883 -0.4514 + 135.00 -0.792 0.8818 -0.4425 + 140.00 -0.821 0.7676 -0.4288 + 145.00 -0.826 0.6481 -0.4095 + 150.00 -0.806 0.5264 -0.3836 + 155.00 -0.758 0.4060 -0.3497 + 160.00 -0.679 0.2912 -0.3065 + 170.00 -0.735 0.0995 -0.3706 + 175.00 -0.368 0.0356 -0.1846 + 180.00 0.000 0.0202 0.0000 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/DU25_A17_coords.txt b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU25_A17_coords.txt new file mode 100644 index 00000000..8e481fb8 --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU25_A17_coords.txt @@ -0,0 +1,407 @@ + 400 NumCoords ! The number of coordinates in the airfoil shape file (including an extra coordinate for airfoil reference). Set to zero if coordinates not included. +! ......... x-y coordinates are next if NumCoords > 0 ............. +! x-y coordinate of airfoil reference +! x/c y/c +0.25 0 +! coordinates of airfoil shape; data from TU Delft as posted here: https://wind.nrel.gov/forum/wind/viewtopic.php?f=2&t=440 +! DU 91-W2-250.lm +! x/c y/c +1.00000 0.00213 +0.99660 0.00314 +0.99314 0.00414 +0.98961 0.00515 +0.98601 0.00617 +0.98235 0.00720 +0.97863 0.00822 +0.97484 0.00926 +0.97098 0.01030 +0.96706 0.01135 +0.96307 0.01240 +0.95902 0.01347 +0.95490 0.01454 +0.95072 0.01562 +0.94647 0.01672 +0.94216 0.01783 +0.93778 0.01895 +0.93333 0.02009 +0.92882 0.02125 +0.92425 0.02241 +0.91961 0.02359 +0.91490 0.02479 +0.91013 0.02600 +0.90529 0.02722 +0.90039 0.02846 +0.89542 0.02972 +0.89039 0.03099 +0.88529 0.03227 +0.88013 0.03357 +0.87490 0.03489 +0.86961 0.03622 +0.86425 0.03756 +0.85882 0.03892 +0.85333 0.04029 +0.84778 0.04167 +0.84216 0.04306 +0.83647 0.04447 +0.83072 0.04589 +0.82490 0.04732 +0.81902 0.04877 +0.81307 0.05023 +0.80706 0.05170 +0.80098 0.05319 +0.79484 0.05468 +0.78863 0.05619 +0.78235 0.05771 +0.77601 0.05925 +0.76961 0.06078 +0.76314 0.06233 +0.75660 0.06388 +0.75000 0.06544 +0.74333 0.06701 +0.73667 0.06857 +0.73000 0.07013 +0.72333 0.07168 +0.71667 0.07322 +0.71000 0.07475 +0.70333 0.07628 +0.69667 0.07779 +0.69000 0.07930 +0.68333 0.08080 +0.67667 0.08228 +0.67000 0.08375 +0.66333 0.08522 +0.65667 0.08667 +0.65000 0.08810 +0.64333 0.08953 +0.63667 0.09093 +0.63000 0.09233 +0.62333 0.09371 +0.61667 0.09507 +0.61000 0.09642 +0.60333 0.09775 +0.59667 0.09906 +0.59000 0.10035 +0.58333 0.10163 +0.57667 0.10289 +0.57000 0.10413 +0.56333 0.10535 +0.55667 0.10655 +0.55000 0.10773 +0.54333 0.10888 +0.53667 0.11001 +0.53000 0.11112 +0.52333 0.11221 +0.51667 0.11327 +0.51000 0.11430 +0.50333 0.11530 +0.49667 0.11628 +0.49000 0.11723 +0.48333 0.11815 +0.47667 0.11904 +0.47000 0.11991 +0.46333 0.12074 +0.45667 0.12154 +0.45000 0.12230 +0.44333 0.12303 +0.43667 0.12372 +0.43000 0.12436 +0.42333 0.12497 +0.41667 0.12554 +0.41000 0.12606 +0.40333 0.12655 +0.39667 0.12699 +0.39000 0.12738 +0.38333 0.12773 +0.37667 0.12803 +0.37000 0.12828 +0.36333 0.12846 +0.35667 0.12858 +0.35000 0.12864 +0.34333 0.12862 +0.33667 0.12853 +0.33000 0.12837 +0.32333 0.12815 +0.31667 0.12785 +0.31000 0.12749 +0.30333 0.12705 +0.29667 0.12656 +0.29000 0.12600 +0.28333 0.12538 +0.27667 0.12469 +0.27000 0.12394 +0.26333 0.12312 +0.25667 0.12224 +0.25000 0.12130 +0.24342 0.12030 +0.23693 0.11926 +0.23053 0.11817 +0.22421 0.11704 +0.21798 0.11586 +0.21184 0.11465 +0.20579 0.11340 +0.19982 0.11210 +0.19395 0.11078 +0.18816 0.10942 +0.18245 0.10803 +0.17684 0.10660 +0.17131 0.10515 +0.16587 0.10367 +0.16052 0.10217 +0.15526 0.10064 +0.15008 0.09909 +0.14499 0.09751 +0.13999 0.09591 +0.13508 0.09430 +0.13026 0.09266 +0.12552 0.09101 +0.12087 0.08934 +0.11631 0.08765 +0.11183 0.08595 +0.10745 0.08424 +0.10315 0.08251 +0.09893 0.08076 +0.09481 0.07902 +0.09077 0.07725 +0.08683 0.07549 +0.08297 0.07371 +0.07919 0.07192 +0.07551 0.07013 +0.07191 0.06834 +0.06840 0.06654 +0.06498 0.06474 +0.06164 0.06293 +0.05840 0.06113 +0.05524 0.05933 +0.05217 0.05753 +0.04918 0.05573 +0.04629 0.05393 +0.04348 0.05214 +0.04076 0.05035 +0.03812 0.04856 +0.03558 0.04679 +0.03312 0.04502 +0.03075 0.04326 +0.02847 0.04151 +0.02627 0.03976 +0.02417 0.03804 +0.02215 0.03632 +0.02022 0.03462 +0.01837 0.03293 +0.01662 0.03126 +0.01495 0.02961 +0.01337 0.02797 +0.01187 0.02635 +0.01047 0.02475 +0.00915 0.02317 +0.00792 0.02161 +0.00678 0.02007 +0.00572 0.01855 +0.00476 0.01706 +0.00388 0.01557 +0.00309 0.01409 +0.00238 0.01261 +0.00177 0.01110 +0.00124 0.00945 +0.00080 0.00766 +0.00044 0.00568 +0.00018 0.00363 +0.00000 0.00000 +0.00018 -0.00360 +0.00044 -0.00572 +0.00080 -0.00778 +0.00124 -0.00974 +0.00177 -0.01169 +0.00238 -0.01359 +0.00309 -0.01549 +0.00388 -0.01736 +0.00476 -0.01921 +0.00572 -0.02104 +0.00678 -0.02288 +0.00792 -0.02470 +0.00915 -0.02652 +0.01047 -0.02835 +0.01187 -0.03019 +0.01337 -0.03205 +0.01495 -0.03392 +0.01662 -0.03581 +0.01837 -0.03770 +0.02022 -0.03961 +0.02215 -0.04153 +0.02417 -0.04345 +0.02627 -0.04537 +0.02847 -0.04730 +0.03075 -0.04921 +0.03312 -0.05112 +0.03558 -0.05302 +0.03812 -0.05491 +0.04076 -0.05679 +0.04348 -0.05865 +0.04629 -0.06051 +0.04918 -0.06234 +0.05217 -0.06418 +0.05524 -0.06600 +0.05840 -0.06780 +0.06164 -0.06960 +0.06498 -0.07139 +0.06840 -0.07316 +0.07191 -0.07492 +0.07551 -0.07668 +0.07919 -0.07842 +0.08297 -0.08015 +0.08683 -0.08186 +0.09077 -0.08356 +0.09481 -0.08525 +0.09893 -0.08692 +0.10315 -0.08858 +0.10745 -0.09022 +0.11183 -0.09182 +0.11631 -0.09342 +0.12087 -0.09498 +0.12552 -0.09652 +0.13026 -0.09803 +0.13508 -0.09951 +0.13999 -0.10095 +0.14499 -0.10237 +0.15008 -0.10376 +0.15526 -0.10511 +0.16052 -0.10642 +0.16587 -0.10769 +0.17131 -0.10892 +0.17684 -0.11011 +0.18245 -0.11126 +0.18816 -0.11236 +0.19395 -0.11341 +0.19982 -0.11441 +0.20579 -0.11536 +0.21184 -0.11626 +0.21798 -0.11710 +0.22421 -0.11789 +0.23053 -0.11862 +0.23693 -0.11929 +0.24342 -0.11990 +0.25000 -0.12045 +0.25667 -0.12094 +0.26333 -0.12135 +0.27000 -0.12168 +0.27667 -0.12195 +0.28333 -0.12214 +0.29000 -0.12227 +0.29667 -0.12232 +0.30333 -0.12231 +0.31000 -0.12222 +0.31667 -0.12207 +0.32333 -0.12186 +0.33000 -0.12157 +0.33667 -0.12123 +0.34333 -0.12082 +0.35000 -0.12034 +0.35667 -0.11981 +0.36333 -0.11922 +0.37000 -0.11857 +0.37667 -0.11785 +0.38333 -0.11708 +0.39000 -0.11625 +0.39667 -0.11537 +0.40333 -0.11442 +0.41000 -0.11342 +0.41667 -0.11236 +0.42333 -0.11124 +0.43000 -0.11006 +0.43667 -0.10881 +0.44333 -0.10752 +0.45000 -0.10615 +0.45667 -0.10474 +0.46333 -0.10326 +0.47000 -0.10173 +0.47667 -0.10015 +0.48333 -0.09851 +0.49000 -0.09682 +0.49667 -0.09508 +0.50333 -0.09329 +0.51000 -0.09145 +0.51667 -0.08956 +0.52333 -0.08764 +0.53000 -0.08567 +0.53667 -0.08366 +0.54333 -0.08162 +0.55000 -0.07953 +0.55667 -0.07742 +0.56333 -0.07527 +0.57000 -0.07309 +0.57667 -0.07088 +0.58333 -0.06865 +0.59000 -0.06639 +0.59667 -0.06410 +0.60333 -0.06180 +0.61000 -0.05948 +0.61667 -0.05713 +0.62333 -0.05478 +0.63000 -0.05242 +0.63667 -0.05005 +0.64333 -0.04767 +0.65000 -0.04530 +0.65667 -0.04293 +0.66333 -0.04057 +0.67000 -0.03822 +0.67667 -0.03588 +0.68333 -0.03357 +0.69000 -0.03127 +0.69667 -0.02900 +0.70333 -0.02676 +0.71000 -0.02456 +0.71667 -0.02238 +0.72333 -0.02026 +0.73000 -0.01817 +0.73667 -0.01614 +0.74333 -0.01416 +0.75000 -0.01223 +0.75660 -0.01038 +0.76314 -0.00861 +0.76961 -0.00692 +0.77601 -0.00532 +0.78235 -0.00381 +0.78863 -0.00238 +0.79484 -0.00103 +0.80098 0.00023 +0.80706 0.00141 +0.81307 0.00250 +0.81902 0.00351 +0.82490 0.00444 +0.83072 0.00529 +0.83647 0.00606 +0.84216 0.00675 +0.84778 0.00737 +0.85333 0.00791 +0.85882 0.00839 +0.86425 0.00879 +0.86961 0.00913 +0.87490 0.00940 +0.88013 0.00961 +0.88529 0.00975 +0.89039 0.00983 +0.89542 0.00985 +0.90039 0.00982 +0.90529 0.00972 +0.91013 0.00957 +0.91490 0.00936 +0.91961 0.00910 +0.92425 0.00880 +0.92882 0.00844 +0.93333 0.00804 +0.93778 0.00760 +0.94216 0.00712 +0.94647 0.00660 +0.95072 0.00606 +0.95490 0.00549 +0.95902 0.00490 +0.96307 0.00429 +0.96706 0.00367 +0.97098 0.00303 +0.97484 0.00238 +0.97863 0.00173 +0.98235 0.00108 +0.98601 0.00043 +0.98961 -0.00022 +0.99314 -0.00086 +0.99660 -0.00149 +1.00000 -0.00213 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/DU30_A17.dat b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU30_A17.dat new file mode 100644 index 00000000..438015cb --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU30_A17.dat @@ -0,0 +1,198 @@ +! ------------ AirfoilInfo v1.01.x Input File ---------------------------------- +! DU30 airfoil with an aspect ratio of 17. Original -180 to 180deg Cl, Cd, and Cm versus AOA data taken from Appendix A of DOWEC document 10046_009.pdf (numerical values obtained from Koert Lindenburg of ECN). +! Cl and Cd values corrected for rotational stall delay and Cd values corrected using the Viterna method for 0 to 90deg AOA by Jason Jonkman using AirfoilPrep_v2p0.xls. +! note that this file uses Marshall Buhl's new input file processing; start all comment lines with ! +! ------------------------------------------------------------------------------ +"DEFAULT" InterpOrd ! Interpolation order to use for quasi-steady table lookup {1=linear; 3=cubic spline; "default"} [default=1] + 1 NonDimArea ! The non-dimensional area of the airfoil (area/chord^2) (set to 1.0 if unsure or unneeded) +@"DU30_A17_coords.txt" NumCoords ! The number of coordinates in the airfoil shape file. Set to zero if coordinates not included. +"unused" BL_file ! The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. + 1 NumTabs ! Number of airfoil tables in this file. +! ------------------------------------------------------------------------------ +! data for table 1 +! ------------------------------------------------------------------------------ + 0.75 Re ! Reynolds number in millions + 0 UserProp ! User property (control) setting +True InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +!........................................ + -2.2 alpha0 ! 0-lift angle of attack, depends on airfoil. + 9 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) + -9 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] + 0 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] + 1.449 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It can be calculated from the static value of Cn at either the break in the pitching moment or the loss of chord force at the onset of stall. It is close to the condition of maximum lift of the airfoil at low Mach numbers. + -0.6138 Cn2 ! As Cn1 for negative AOAs. + 0.19 St_sh ! Strouhal's shedding frequency constant. [default = 0.19] + 0.008 Cd0 ! 2D drag coefficient value at 0-lift. + -0.09 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does not include a column for Cm, this needs to be set to 0.0] + 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] + 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] + 0.2 x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] +"DEFAULT" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] +"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] +!........................................ +! Table of aerodynamics coefficients + 143 NumAlf ! Number of data lines in the following table +! Alpha Cl Cd Cm +! (deg) (-) (-) (-) + -180.00 0.000 0.0267 0.0000 + -175.00 0.274 0.0370 0.1379 + -170.00 0.547 0.0968 0.2778 + -160.00 0.685 0.2876 0.2740 + -155.00 0.766 0.4025 0.3118 + -150.00 0.816 0.5232 0.3411 + -145.00 0.836 0.6454 0.3631 + -140.00 0.832 0.7656 0.3791 + -135.00 0.804 0.8807 0.3899 + -130.00 0.756 0.9882 0.3965 + -125.00 0.690 1.0861 0.3994 + -120.00 0.609 1.1730 0.3992 + -115.00 0.515 1.2474 0.3964 + -110.00 0.411 1.3084 0.3915 + -105.00 0.300 1.3552 0.3846 + -100.00 0.182 1.3875 0.3761 + -95.00 0.061 1.4048 0.3663 + -90.00 -0.061 1.4070 0.3551 + -85.00 -0.183 1.3941 0.3428 + -80.00 -0.302 1.3664 0.3295 + -75.00 -0.416 1.3240 0.3153 + -70.00 -0.523 1.2676 0.3001 + -65.00 -0.622 1.1978 0.2841 + -60.00 -0.708 1.1156 0.2672 + -55.00 -0.781 1.0220 0.2494 + -50.00 -0.838 0.9187 0.2308 + -45.00 -0.877 0.8074 0.2113 + -40.00 -0.895 0.6904 0.1909 + -35.00 -0.889 0.5703 0.1696 + -30.00 -0.858 0.4503 0.1475 + -25.00 -0.832 0.3357 0.1224 + -24.00 -0.852 0.3147 0.1156 + -23.00 -0.882 0.2946 0.1081 + -22.00 -0.919 0.2752 0.1000 + -21.00 -0.963 0.2566 0.0914 + -20.00 -1.013 0.2388 0.0823 + -19.00 -1.067 0.2218 0.0728 + -18.00 -1.125 0.2056 0.0631 + -17.00 -1.185 0.1901 0.0531 + -16.00 -1.245 0.1754 0.0430 + -15.25 -1.290 0.1649 0.0353 + -14.24 -1.229 0.1461 0.0240 + -13.24 -1.148 0.1263 0.0100 + -12.22 -1.052 0.1051 -0.0090 + -11.22 -0.965 0.0886 -0.0230 + -10.19 -0.867 0.0740 -0.0336 + -9.70 -0.822 0.0684 -0.0375 + -9.18 -0.769 0.0605 -0.0440 + -8.18 -0.756 0.0270 -0.0578 + -7.19 -0.690 0.0180 -0.0590 + -6.65 -0.616 0.0166 -0.0633 + -6.13 -0.542 0.0152 -0.0674 + -6.00 -0.525 0.0117 -0.0732 + -5.50 -0.451 0.0105 -0.0766 + -5.00 -0.382 0.0097 -0.0797 + -4.50 -0.314 0.0092 -0.0825 + -4.00 -0.251 0.0091 -0.0853 + -3.50 -0.189 0.0089 -0.0884 + -3.00 -0.120 0.0089 -0.0914 + -2.50 -0.051 0.0088 -0.0942 + -2.00 0.017 0.0088 -0.0969 + -1.50 0.085 0.0088 -0.0994 + -1.00 0.152 0.0088 -0.1018 + -0.50 0.219 0.0088 -0.1041 + 0.00 0.288 0.0087 -0.1062 + 0.50 0.354 0.0087 -0.1086 + 1.00 0.421 0.0088 -0.1107 + 1.50 0.487 0.0089 -0.1129 + 2.00 0.554 0.0090 -0.1149 + 2.50 0.619 0.0091 -0.1168 + 3.00 0.685 0.0092 -0.1185 + 3.50 0.749 0.0093 -0.1201 + 4.00 0.815 0.0095 -0.1218 + 4.50 0.879 0.0096 -0.1233 + 5.00 0.944 0.0097 -0.1248 + 5.50 1.008 0.0099 -0.1260 + 6.00 1.072 0.0101 -0.1270 + 6.50 1.135 0.0103 -0.1280 + 7.00 1.197 0.0107 -0.1287 + 7.50 1.256 0.0112 -0.1289 + 8.00 1.305 0.0125 -0.1270 + 9.00 1.390 0.0155 -0.1207 + 9.50 1.424 0.0171 -0.1158 + 10.00 1.458 0.0192 -0.1116 + 10.50 1.488 0.0219 -0.1073 + 11.00 1.512 0.0255 -0.1029 + 11.50 1.533 0.0307 -0.0983 + 12.00 1.549 0.0370 -0.0949 + 12.50 1.558 0.0452 -0.0921 + 13.00 1.470 0.0630 -0.0899 + 13.50 1.398 0.0784 -0.0885 + 14.00 1.354 0.0931 -0.0885 + 14.50 1.336 0.1081 -0.0902 + 15.00 1.333 0.1239 -0.0928 + 15.50 1.326 0.1415 -0.0963 + 16.00 1.329 0.1592 -0.1006 + 16.50 1.326 0.1743 -0.1042 + 17.00 1.321 0.1903 -0.1084 + 17.50 1.331 0.2044 -0.1125 + 18.00 1.333 0.2186 -0.1169 + 18.50 1.340 0.2324 -0.1215 + 19.00 1.362 0.2455 -0.1263 + 19.50 1.382 0.2584 -0.1313 + 20.00 1.398 0.2689 -0.1352 + 20.50 1.426 0.2814 -0.1406 + 21.00 1.437 0.2943 -0.1462 + 22.00 1.418 0.3246 -0.1516 + 23.00 1.397 0.3557 -0.1570 + 24.00 1.376 0.3875 -0.1623 + 25.00 1.354 0.4198 -0.1676 + 26.00 1.332 0.4524 -0.1728 + 28.00 1.293 0.5183 -0.1832 + 30.00 1.265 0.5843 -0.1935 + 32.00 1.253 0.6492 -0.2039 + 35.00 1.264 0.7438 -0.2193 + 40.00 1.258 0.8970 -0.2440 + 45.00 1.217 1.0402 -0.2672 + 50.00 1.146 1.1686 -0.2891 + 55.00 1.049 1.2779 -0.3097 + 60.00 0.932 1.3647 -0.3290 + 65.00 0.799 1.4267 -0.3471 + 70.00 0.657 1.4621 -0.3641 + 75.00 0.509 1.4708 -0.3799 + 80.00 0.362 1.4544 -0.3946 + 85.00 0.221 1.4196 -0.4081 + 90.00 0.092 1.3938 -0.4204 + 95.00 -0.030 1.3943 -0.4313 + 100.00 -0.150 1.3798 -0.4408 + 105.00 -0.267 1.3504 -0.4486 + 110.00 -0.379 1.3063 -0.4546 + 115.00 -0.483 1.2481 -0.4584 + 120.00 -0.578 1.1763 -0.4597 + 125.00 -0.660 1.0919 -0.4582 + 130.00 -0.727 0.9962 -0.4532 + 135.00 -0.777 0.8906 -0.4441 + 140.00 -0.807 0.7771 -0.4303 + 145.00 -0.815 0.6581 -0.4109 + 150.00 -0.797 0.5364 -0.3848 + 155.00 -0.750 0.4157 -0.3508 + 160.00 -0.673 0.3000 -0.3074 + 170.00 -0.547 0.1051 -0.2786 + 175.00 -0.274 0.0388 -0.1380 + 180.00 0.000 0.0267 0.0000 + diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/DU30_A17_coords.txt b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU30_A17_coords.txt new file mode 100644 index 00000000..722bf83a --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU30_A17_coords.txt @@ -0,0 +1,407 @@ + 400 NumCoords ! The number of coordinates in the airfoil shape file (including an extra coordinate for airfoil reference). Set to zero if coordinates not included. +! ......... x-y coordinates are next if NumCoords > 0 ............. +! x-y coordinate of airfoil reference +! x/c y/c +0.25 0 +! coordinates of airfoil shape; data from TU Delft as posted here: https://wind.nrel.gov/forum/wind/viewtopic.php?f=2&t=440 +! DU 97-W-300.lm +! x/c y/c +1.00000 0.00246 +0.99660 0.00340 +0.99314 0.00437 +0.98961 0.00536 +0.98601 0.00638 +0.98235 0.00740 +0.97863 0.00843 +0.97484 0.00947 +0.97098 0.01051 +0.96706 0.01157 +0.96307 0.01264 +0.95902 0.01371 +0.95490 0.01479 +0.95072 0.01588 +0.94647 0.01698 +0.94216 0.01810 +0.93778 0.01922 +0.93333 0.02036 +0.92882 0.02151 +0.92425 0.02268 +0.91961 0.02386 +0.91490 0.02505 +0.91013 0.02626 +0.90529 0.02749 +0.90039 0.02873 +0.89542 0.02999 +0.89039 0.03126 +0.88529 0.03255 +0.88013 0.03385 +0.87490 0.03516 +0.86961 0.03649 +0.86425 0.03784 +0.85882 0.03921 +0.85333 0.04058 +0.84778 0.04197 +0.84216 0.04337 +0.83647 0.04479 +0.83072 0.04622 +0.82490 0.04767 +0.81902 0.04913 +0.81307 0.05060 +0.80706 0.05208 +0.80098 0.05357 +0.79484 0.05508 +0.78863 0.05659 +0.78235 0.05812 +0.77601 0.05965 +0.76961 0.06120 +0.76314 0.06275 +0.75660 0.06432 +0.75000 0.06589 +0.74333 0.06747 +0.73667 0.06904 +0.73000 0.07061 +0.72333 0.07217 +0.71667 0.07371 +0.71000 0.07525 +0.70333 0.07678 +0.69667 0.07830 +0.69000 0.07981 +0.68333 0.08131 +0.67667 0.08280 +0.67000 0.08428 +0.66333 0.08575 +0.65667 0.08720 +0.65000 0.08864 +0.64333 0.09008 +0.63667 0.09149 +0.63000 0.09290 +0.62333 0.09429 +0.61667 0.09566 +0.61000 0.09703 +0.60333 0.09837 +0.59667 0.09970 +0.59000 0.10102 +0.58333 0.10232 +0.57667 0.10360 +0.57000 0.10486 +0.56333 0.10611 +0.55667 0.10734 +0.55000 0.10855 +0.54333 0.10975 +0.53667 0.11092 +0.53000 0.11207 +0.52333 0.11321 +0.51667 0.11432 +0.51000 0.11541 +0.50333 0.11648 +0.49667 0.11752 +0.49000 0.11854 +0.48333 0.11954 +0.47667 0.12051 +0.47000 0.12146 +0.46333 0.12239 +0.45667 0.12328 +0.45000 0.12415 +0.44333 0.12500 +0.43667 0.12581 +0.43000 0.12659 +0.42333 0.12734 +0.41667 0.12806 +0.41000 0.12875 +0.40333 0.12940 +0.39667 0.13001 +0.39000 0.13059 +0.38333 0.13113 +0.37667 0.13164 +0.37000 0.13210 +0.36333 0.13252 +0.35667 0.13290 +0.35000 0.13323 +0.34333 0.13352 +0.33667 0.13377 +0.33000 0.13396 +0.32333 0.13411 +0.31667 0.13420 +0.31000 0.13424 +0.30333 0.13422 +0.29667 0.13415 +0.29000 0.13401 +0.28333 0.13381 +0.27667 0.13355 +0.27000 0.13321 +0.26333 0.13280 +0.25667 0.13231 +0.25000 0.13174 +0.24342 0.13109 +0.23693 0.13036 +0.23053 0.12955 +0.22421 0.12866 +0.21798 0.12771 +0.21184 0.12668 +0.20579 0.12559 +0.19982 0.12444 +0.19395 0.12324 +0.18816 0.12197 +0.18245 0.12066 +0.17684 0.11930 +0.17131 0.11789 +0.16587 0.11644 +0.16052 0.11495 +0.15526 0.11342 +0.15008 0.11185 +0.14499 0.11024 +0.13999 0.10860 +0.13508 0.10693 +0.13026 0.10522 +0.12552 0.10348 +0.12087 0.10172 +0.11631 0.09993 +0.11183 0.09811 +0.10745 0.09627 +0.10315 0.09441 +0.09893 0.09252 +0.09481 0.09061 +0.09077 0.08869 +0.08683 0.08675 +0.08297 0.08478 +0.07919 0.08280 +0.07551 0.08082 +0.07191 0.07881 +0.06840 0.07680 +0.06498 0.07477 +0.06164 0.07274 +0.05840 0.07070 +0.05524 0.06865 +0.05217 0.06660 +0.04918 0.06454 +0.04629 0.06248 +0.04348 0.06042 +0.04076 0.05835 +0.03812 0.05629 +0.03558 0.05423 +0.03312 0.05217 +0.03075 0.05012 +0.02847 0.04808 +0.02627 0.04604 +0.02417 0.04402 +0.02215 0.04200 +0.02022 0.03999 +0.01837 0.03799 +0.01662 0.03602 +0.01495 0.03405 +0.01337 0.03211 +0.01187 0.03017 +0.01047 0.02827 +0.00915 0.02637 +0.00792 0.02450 +0.00678 0.02266 +0.00572 0.02083 +0.00476 0.01904 +0.00388 0.01725 +0.00309 0.01548 +0.00238 0.01370 +0.00177 0.01194 +0.00124 0.01010 +0.00080 0.00820 +0.00044 0.00612 +0.00018 0.00390 +0.00000 0.00000 +0.00018 -0.00382 +0.00044 -0.00601 +0.00080 -0.00815 +0.00124 -0.01017 +0.00177 -0.01216 +0.00238 -0.01412 +0.00309 -0.01611 +0.00388 -0.01811 +0.00476 -0.02014 +0.00572 -0.02217 +0.00678 -0.02423 +0.00792 -0.02630 +0.00915 -0.02840 +0.01047 -0.03053 +0.01187 -0.03267 +0.01337 -0.03485 +0.01495 -0.03705 +0.01662 -0.03929 +0.01837 -0.04154 +0.02022 -0.04385 +0.02215 -0.04617 +0.02417 -0.04852 +0.02627 -0.05088 +0.02847 -0.05328 +0.03075 -0.05569 +0.03312 -0.05813 +0.03558 -0.06060 +0.03812 -0.06308 +0.04076 -0.06559 +0.04348 -0.06811 +0.04629 -0.07064 +0.04918 -0.07318 +0.05217 -0.07574 +0.05524 -0.07831 +0.05840 -0.08088 +0.06164 -0.08345 +0.06498 -0.08604 +0.06840 -0.08862 +0.07191 -0.09121 +0.07551 -0.09379 +0.07919 -0.09637 +0.08297 -0.09895 +0.08683 -0.10152 +0.09077 -0.10408 +0.09481 -0.10665 +0.09893 -0.10919 +0.10315 -0.11173 +0.10745 -0.11425 +0.11183 -0.11675 +0.11631 -0.11923 +0.12087 -0.12169 +0.12552 -0.12412 +0.13026 -0.12652 +0.13508 -0.12888 +0.13999 -0.13121 +0.14499 -0.13350 +0.15008 -0.13576 +0.15526 -0.13797 +0.16052 -0.14014 +0.16587 -0.14225 +0.17131 -0.14432 +0.17684 -0.14633 +0.18245 -0.14828 +0.18816 -0.15017 +0.19395 -0.15198 +0.19982 -0.15371 +0.20579 -0.15537 +0.21184 -0.15693 +0.21798 -0.15840 +0.22421 -0.15976 +0.23053 -0.16101 +0.23693 -0.16214 +0.24342 -0.16314 +0.25000 -0.16399 +0.25667 -0.16470 +0.26333 -0.16526 +0.27000 -0.16567 +0.27667 -0.16592 +0.28333 -0.16602 +0.29000 -0.16598 +0.29667 -0.16580 +0.30333 -0.16548 +0.31000 -0.16503 +0.31667 -0.16445 +0.32333 -0.16374 +0.33000 -0.16291 +0.33667 -0.16198 +0.34333 -0.16094 +0.35000 -0.15980 +0.35667 -0.15855 +0.36333 -0.15722 +0.37000 -0.15580 +0.37667 -0.15429 +0.38333 -0.15270 +0.39000 -0.15104 +0.39667 -0.14931 +0.40333 -0.14752 +0.41000 -0.14566 +0.41667 -0.14374 +0.42333 -0.14178 +0.43000 -0.13977 +0.43667 -0.13772 +0.44333 -0.13562 +0.45000 -0.13348 +0.45667 -0.13130 +0.46333 -0.12908 +0.47000 -0.12683 +0.47667 -0.12456 +0.48333 -0.12225 +0.49000 -0.11992 +0.49667 -0.11757 +0.50333 -0.11520 +0.51000 -0.11281 +0.51667 -0.11040 +0.52333 -0.10799 +0.53000 -0.10555 +0.53667 -0.10310 +0.54333 -0.10065 +0.55000 -0.09818 +0.55667 -0.09570 +0.56333 -0.09321 +0.57000 -0.09071 +0.57667 -0.08821 +0.58333 -0.08571 +0.59000 -0.08320 +0.59667 -0.08070 +0.60333 -0.07819 +0.61000 -0.07569 +0.61667 -0.07319 +0.62333 -0.07070 +0.63000 -0.06820 +0.63667 -0.06572 +0.64333 -0.06325 +0.65000 -0.06079 +0.65667 -0.05834 +0.66333 -0.05590 +0.67000 -0.05347 +0.67667 -0.05106 +0.68333 -0.04867 +0.69000 -0.04629 +0.69667 -0.04394 +0.70333 -0.04161 +0.71000 -0.03931 +0.71667 -0.03703 +0.72333 -0.03478 +0.73000 -0.03256 +0.73667 -0.03037 +0.74333 -0.02822 +0.75000 -0.02610 +0.75660 -0.02405 +0.76314 -0.02205 +0.76961 -0.02013 +0.77601 -0.01827 +0.78235 -0.01647 +0.78863 -0.01474 +0.79484 -0.01309 +0.80098 -0.01150 +0.80706 -0.00998 +0.81307 -0.00854 +0.81902 -0.00717 +0.82490 -0.00586 +0.83072 -0.00462 +0.83647 -0.00346 +0.84216 -0.00236 +0.84778 -0.00133 +0.85333 -0.00037 +0.85882 0.00052 +0.86425 0.00134 +0.86961 0.00210 +0.87490 0.00278 +0.88013 0.00340 +0.88529 0.00395 +0.89039 0.00444 +0.89542 0.00487 +0.90039 0.00524 +0.90529 0.00555 +0.91013 0.00580 +0.91490 0.00600 +0.91961 0.00614 +0.92425 0.00622 +0.92882 0.00625 +0.93333 0.00623 +0.93778 0.00615 +0.94216 0.00602 +0.94647 0.00583 +0.95072 0.00558 +0.95490 0.00528 +0.95902 0.00493 +0.96307 0.00452 +0.96706 0.00405 +0.97098 0.00352 +0.97484 0.00294 +0.97863 0.00231 +0.98235 0.00163 +0.98601 0.00090 +0.98961 0.00012 +0.99314 -0.00071 +0.99660 -0.00158 +1.00000 -0.00246 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/DU35_A17.dat b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU35_A17.dat new file mode 100644 index 00000000..48e2b5f3 --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU35_A17.dat @@ -0,0 +1,189 @@ +! ------------ AirfoilInfo v1.01.x Input File ---------------------------------- +! DU35 airfoil with an aspect ratio of 17. Original -180 to 180deg Cl, Cd, and Cm versus AOA data taken from Appendix A of DOWEC document 10046_009.pdf (numerical values obtained from Koert Lindenburg of ECN). +! Cl and Cd values corrected for rotational stall delay and Cd values corrected using the Viterna method for 0 to 90deg AOA by Jason Jonkman using AirfoilPrep_v2p0.xls. +! note that this file uses Marshall Buhl's new input file processing; start all comment lines with ! +! ------------------------------------------------------------------------------ +"DEFAULT" InterpOrd ! Interpolation order to use for quasi-steady table lookup {1=linear; 3=cubic spline; "default"} [default=1] + 1 NonDimArea ! The non-dimensional area of the airfoil (area/chord^2) (set to 1.0 if unsure or unneeded) +@"DU35_A17_coords.txt" NumCoords ! The number of coordinates in the airfoil shape file. Set to zero if coordinates not included. +"unused" BL_file ! The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. + 1 NumTabs ! Number of airfoil tables in this file. +! ------------------------------------------------------------------------------ +! data for table 1 +! ------------------------------------------------------------------------------ + 0.75 Re ! Reynolds number in millions + 0 UserProp ! User property (control) setting +True InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +!........................................ + -1.2 alpha0 ! 0-lift angle of attack, depends on airfoil. + 11.5 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) + -11.5 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] + 0 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] + 1.6717 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It can be calculated from the static value of Cn at either the break in the pitching moment or the loss of chord force at the onset of stall. It is close to the condition of maximum lift of the airfoil at low Mach numbers. + -0.3075 Cn2 ! As Cn1 for negative AOAs. + 0.19 St_sh ! Strouhal's shedding frequency constant. [default = 0.19] + 0.012 Cd0 ! 2D drag coefficient value at 0-lift. + -0.07 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does not include a column for Cm, this needs to be set to 0.0] + 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] + 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] + 0.2 x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] +"DEFAULT" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] +"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] +!........................................ +! Table of aerodynamics coefficients + 135 NumAlf ! Number of data lines in the following table +! Alpha Cl Cd Cm +! (deg) (-) (-) (-) + -180.00 0.000 0.0407 0.0000 + -175.00 0.223 0.0507 0.0937 + -170.00 0.405 0.1055 0.1702 + -160.00 0.658 0.2982 0.2819 + -155.00 0.733 0.4121 0.3213 + -150.00 0.778 0.5308 0.3520 + -145.00 0.795 0.6503 0.3754 + -140.00 0.787 0.7672 0.3926 + -135.00 0.757 0.8785 0.4046 + -130.00 0.708 0.9819 0.4121 + -125.00 0.641 1.0756 0.4160 + -120.00 0.560 1.1580 0.4167 + -115.00 0.467 1.2280 0.4146 + -110.00 0.365 1.2847 0.4104 + -105.00 0.255 1.3274 0.4041 + -100.00 0.139 1.3557 0.3961 + -95.00 0.021 1.3692 0.3867 + -90.00 -0.098 1.3680 0.3759 + -85.00 -0.216 1.3521 0.3639 + -80.00 -0.331 1.3218 0.3508 + -75.00 -0.441 1.2773 0.3367 + -70.00 -0.544 1.2193 0.3216 + -65.00 -0.638 1.1486 0.3054 + -60.00 -0.720 1.0660 0.2884 + -55.00 -0.788 0.9728 0.2703 + -50.00 -0.840 0.8705 0.2512 + -45.00 -0.875 0.7611 0.2311 + -40.00 -0.889 0.6466 0.2099 + -35.00 -0.880 0.5299 0.1876 + -30.00 -0.846 0.4141 0.1641 + -25.00 -0.784 0.3030 0.1396 + -24.00 -0.768 0.2817 0.1345 + -23.00 -0.751 0.2608 0.1294 + -22.00 -0.733 0.2404 0.1243 + -21.00 -0.714 0.2205 0.1191 + -20.00 -0.693 0.2011 0.1139 + -19.00 -0.671 0.1822 0.1086 + -18.00 -0.648 0.1640 0.1032 + -17.00 -0.624 0.1465 0.0975 + -16.00 -0.601 0.1300 0.0898 + -15.00 -0.579 0.1145 0.0799 + -14.00 -0.559 0.1000 0.0682 + -13.00 -0.539 0.0867 0.0547 + -12.00 -0.519 0.0744 0.0397 + -11.00 -0.499 0.0633 0.0234 + -10.00 -0.480 0.0534 0.0060 + -5.54 -0.385 0.0245 -0.0800 + -5.04 -0.359 0.0225 -0.0800 + -4.54 -0.360 0.0196 -0.0800 + -4.04 -0.355 0.0174 -0.0800 + -3.54 -0.307 0.0162 -0.0800 + -3.04 -0.246 0.0144 -0.0800 + -3.00 -0.240 0.0240 -0.0623 + -2.50 -0.163 0.0188 -0.0674 + -2.00 -0.091 0.0160 -0.0712 + -1.50 -0.019 0.0137 -0.0746 + -1.00 0.052 0.0118 -0.0778 + -0.50 0.121 0.0104 -0.0806 + 0.00 0.196 0.0094 -0.0831 + 0.50 0.265 0.0096 -0.0863 + 1.00 0.335 0.0098 -0.0895 + 1.50 0.404 0.0099 -0.0924 + 2.00 0.472 0.0100 -0.0949 + 2.50 0.540 0.0102 -0.0973 + 3.00 0.608 0.0103 -0.0996 + 3.50 0.674 0.0104 -0.1016 + 4.00 0.742 0.0105 -0.1037 + 4.50 0.809 0.0107 -0.1057 + 5.00 0.875 0.0108 -0.1076 + 5.50 0.941 0.0109 -0.1094 + 6.00 1.007 0.0110 -0.1109 + 6.50 1.071 0.0113 -0.1118 + 7.00 1.134 0.0115 -0.1127 + 7.50 1.198 0.0117 -0.1138 + 8.00 1.260 0.0120 -0.1144 + 8.50 1.318 0.0126 -0.1137 + 9.00 1.368 0.0133 -0.1112 + 9.50 1.422 0.0143 -0.1100 + 10.00 1.475 0.0156 -0.1086 + 10.50 1.523 0.0174 -0.1064 + 11.00 1.570 0.0194 -0.1044 + 11.50 1.609 0.0227 -0.1013 + 12.00 1.642 0.0269 -0.0980 + 12.50 1.675 0.0319 -0.0953 + 13.00 1.700 0.0398 -0.0925 + 13.50 1.717 0.0488 -0.0896 + 14.00 1.712 0.0614 -0.0864 + 14.50 1.703 0.0786 -0.0840 + 15.50 1.671 0.1173 -0.0830 + 16.00 1.649 0.1377 -0.0848 + 16.50 1.621 0.1600 -0.0880 + 17.00 1.598 0.1814 -0.0926 + 17.50 1.571 0.2042 -0.0984 + 18.00 1.549 0.2316 -0.1052 + 19.00 1.544 0.2719 -0.1158 + 19.50 1.549 0.2906 -0.1213 + 20.00 1.565 0.3085 -0.1248 + 21.00 1.565 0.3447 -0.1317 + 22.00 1.563 0.3820 -0.1385 + 23.00 1.558 0.4203 -0.1452 + 24.00 1.552 0.4593 -0.1518 + 25.00 1.546 0.4988 -0.1583 + 26.00 1.539 0.5387 -0.1647 + 28.00 1.527 0.6187 -0.1770 + 30.00 1.522 0.6978 -0.1886 + 32.00 1.529 0.7747 -0.1994 + 35.00 1.544 0.8869 -0.2148 + 40.00 1.529 1.0671 -0.2392 + 45.00 1.471 1.2319 -0.2622 + 50.00 1.376 1.3747 -0.2839 + 55.00 1.249 1.4899 -0.3043 + 60.00 1.097 1.5728 -0.3236 + 65.00 0.928 1.6202 -0.3417 + 70.00 0.750 1.6302 -0.3586 + 75.00 0.570 1.6031 -0.3745 + 80.00 0.396 1.5423 -0.3892 + 85.00 0.237 1.4598 -0.4028 + 90.00 0.101 1.4041 -0.4151 + 95.00 -0.022 1.4053 -0.4261 + 100.00 -0.143 1.3914 -0.4357 + 105.00 -0.261 1.3625 -0.4437 + 110.00 -0.374 1.3188 -0.4498 + 115.00 -0.480 1.2608 -0.4538 + 120.00 -0.575 1.1891 -0.4553 + 125.00 -0.659 1.1046 -0.4540 + 130.00 -0.727 1.0086 -0.4492 + 135.00 -0.778 0.9025 -0.4405 + 140.00 -0.809 0.7883 -0.4270 + 145.00 -0.818 0.6684 -0.4078 + 150.00 -0.800 0.5457 -0.3821 + 155.00 -0.754 0.4236 -0.3484 + 160.00 -0.677 0.3066 -0.3054 + 170.00 -0.417 0.1085 -0.1842 + 175.00 -0.229 0.0510 -0.1013 + 180.00 0.000 0.0407 0.0000 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/DU35_A17_coords.txt b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU35_A17_coords.txt new file mode 100644 index 00000000..5d3f84e8 --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU35_A17_coords.txt @@ -0,0 +1,407 @@ + 400 NumCoords ! The number of coordinates in the airfoil shape file (including an extra coordinate for airfoil reference). Set to zero if coordinates not included. +! ......... x-y coordinates are next if NumCoords > 0 ............. +! x-y coordinate of airfoil reference +! x/c y/c +0.25 0 +! coordinates of airfoil shape; data from TU Delft as posted here: https://wind.nrel.gov/forum/wind/viewtopic.php?f=2&t=440 +! Adjusted DU 35 +! x/c y/c +1.00000 0.00283 +0.99660 0.00378 +0.99314 0.00477 +0.98961 0.00578 +0.98601 0.00683 +0.98235 0.00789 +0.97863 0.00897 +0.97484 0.01006 +0.97098 0.01116 +0.96706 0.01228 +0.96307 0.01342 +0.95902 0.01456 +0.95490 0.01571 +0.95072 0.01688 +0.94647 0.01806 +0.94216 0.01926 +0.93778 0.02046 +0.93333 0.02169 +0.92882 0.02292 +0.92425 0.02419 +0.91961 0.02547 +0.91490 0.02675 +0.91013 0.02807 +0.90529 0.02941 +0.90039 0.03077 +0.89542 0.03214 +0.89039 0.03353 +0.88529 0.03495 +0.88013 0.03638 +0.87490 0.03782 +0.86961 0.03929 +0.86425 0.04079 +0.85882 0.04230 +0.85333 0.04383 +0.84778 0.04538 +0.84216 0.04694 +0.83647 0.04853 +0.83072 0.05013 +0.82490 0.05176 +0.81902 0.05340 +0.81307 0.05506 +0.80706 0.05673 +0.80098 0.05842 +0.79484 0.06013 +0.78863 0.06184 +0.78235 0.06358 +0.77601 0.06531 +0.76961 0.06708 +0.76314 0.06885 +0.75660 0.07064 +0.75000 0.07244 +0.74333 0.07426 +0.73667 0.07606 +0.73000 0.07786 +0.72333 0.07966 +0.71667 0.08144 +0.71000 0.08322 +0.70333 0.08498 +0.69667 0.08674 +0.69000 0.08849 +0.68333 0.09022 +0.67667 0.09196 +0.67000 0.09368 +0.66333 0.09539 +0.65667 0.09708 +0.65000 0.09876 +0.64333 0.10044 +0.63667 0.10210 +0.63000 0.10375 +0.62333 0.10538 +0.61667 0.10699 +0.61000 0.10859 +0.60333 0.11017 +0.59667 0.11174 +0.59000 0.11330 +0.58333 0.11483 +0.57667 0.11634 +0.57000 0.11784 +0.56333 0.11931 +0.55667 0.12077 +0.55000 0.12220 +0.54333 0.12363 +0.53667 0.12502 +0.53000 0.12639 +0.52333 0.12775 +0.51667 0.12907 +0.51000 0.13037 +0.50333 0.13165 +0.49667 0.13289 +0.49000 0.13411 +0.48333 0.13531 +0.47667 0.13648 +0.47000 0.13761 +0.46333 0.13873 +0.45667 0.13980 +0.45000 0.14086 +0.44333 0.14189 +0.43667 0.14288 +0.43000 0.14384 +0.42333 0.14477 +0.41667 0.14566 +0.41000 0.14653 +0.40333 0.14736 +0.39667 0.14814 +0.39000 0.14889 +0.38333 0.14960 +0.37667 0.15028 +0.37000 0.15091 +0.36333 0.15151 +0.35667 0.15207 +0.35000 0.15258 +0.34333 0.15306 +0.33667 0.15351 +0.33000 0.15391 +0.32333 0.15426 +0.31667 0.15457 +0.31000 0.15482 +0.30333 0.15502 +0.29667 0.15516 +0.29000 0.15524 +0.28333 0.15525 +0.27667 0.15520 +0.27000 0.15507 +0.26333 0.15486 +0.25667 0.15457 +0.25000 0.15419 +0.24342 0.15372 +0.23693 0.15316 +0.23053 0.15250 +0.22421 0.15175 +0.21798 0.15094 +0.21184 0.15002 +0.20579 0.14904 +0.19982 0.14798 +0.19395 0.14686 +0.18816 0.14566 +0.18245 0.14439 +0.17684 0.14308 +0.17131 0.14169 +0.16587 0.14025 +0.16052 0.13875 +0.15526 0.13721 +0.15008 0.13561 +0.14499 0.13396 +0.13999 0.13226 +0.13508 0.13052 +0.13026 0.12873 +0.12552 0.12689 +0.12087 0.12502 +0.11631 0.12311 +0.11183 0.12115 +0.10745 0.11917 +0.10315 0.11715 +0.09893 0.11509 +0.09481 0.11299 +0.09077 0.11087 +0.08683 0.10871 +0.08297 0.10652 +0.07919 0.10430 +0.07551 0.10207 +0.07191 0.09979 +0.06840 0.09750 +0.06498 0.09517 +0.06164 0.09284 +0.05840 0.09048 +0.05524 0.08809 +0.05217 0.08569 +0.04918 0.08327 +0.04629 0.08084 +0.04348 0.07839 +0.04076 0.07592 +0.03812 0.07345 +0.03558 0.07096 +0.03312 0.06847 +0.03075 0.06597 +0.02847 0.06348 +0.02627 0.06097 +0.02417 0.05847 +0.02215 0.05596 +0.02022 0.05344 +0.01837 0.05091 +0.01662 0.04841 +0.01495 0.04589 +0.01337 0.04339 +0.01187 0.04088 +0.01047 0.03840 +0.00915 0.03591 +0.00792 0.03343 +0.00678 0.03096 +0.00572 0.02845 +0.00476 0.02592 +0.00388 0.02329 +0.00309 0.02056 +0.00238 0.01774 +0.00177 0.01503 +0.00124 0.01240 +0.00080 0.00990 +0.00044 0.00733 +0.00018 0.00465 +0.00000 0.00000 +0.00018 -0.00461 +0.00044 -0.00726 +0.00080 -0.00990 +0.00124 -0.01246 +0.00177 -0.01509 +0.00238 -0.01776 +0.00309 -0.02049 +0.00388 -0.02317 +0.00476 -0.02585 +0.00572 -0.02848 +0.00678 -0.03112 +0.00792 -0.03376 +0.00915 -0.03642 +0.01047 -0.03911 +0.01187 -0.04178 +0.01337 -0.04450 +0.01495 -0.04721 +0.01662 -0.04995 +0.01837 -0.05269 +0.02022 -0.05547 +0.02215 -0.05825 +0.02417 -0.06105 +0.02627 -0.06386 +0.02847 -0.06670 +0.03075 -0.06955 +0.03312 -0.07244 +0.03558 -0.07536 +0.03812 -0.07828 +0.04076 -0.08125 +0.04348 -0.08422 +0.04629 -0.08720 +0.04918 -0.09020 +0.05217 -0.09321 +0.05524 -0.09622 +0.05840 -0.09925 +0.06164 -0.10225 +0.06498 -0.10528 +0.06840 -0.10829 +0.07191 -0.11131 +0.07551 -0.11431 +0.07919 -0.11730 +0.08297 -0.12028 +0.08683 -0.12325 +0.09077 -0.12619 +0.09481 -0.12914 +0.09893 -0.13205 +0.10315 -0.13494 +0.10745 -0.13780 +0.11183 -0.14065 +0.11631 -0.14345 +0.12087 -0.14624 +0.12552 -0.14898 +0.13026 -0.15169 +0.13508 -0.15435 +0.13999 -0.15697 +0.14499 -0.15954 +0.15008 -0.16207 +0.15526 -0.16454 +0.16052 -0.16696 +0.16587 -0.16932 +0.17131 -0.17163 +0.17684 -0.17387 +0.18245 -0.17603 +0.18816 -0.17814 +0.19395 -0.18015 +0.19982 -0.18207 +0.20579 -0.18391 +0.21184 -0.18564 +0.21798 -0.18727 +0.22421 -0.18877 +0.23053 -0.19015 +0.23693 -0.19140 +0.24342 -0.19250 +0.25000 -0.19343 +0.25667 -0.19420 +0.26333 -0.19481 +0.27000 -0.19525 +0.27667 -0.19552 +0.28333 -0.19562 +0.29000 -0.19556 +0.29667 -0.19535 +0.30333 -0.19498 +0.31000 -0.19448 +0.31667 -0.19383 +0.32333 -0.19303 +0.33000 -0.19211 +0.33667 -0.19107 +0.34333 -0.18991 +0.35000 -0.18864 +0.35667 -0.18725 +0.36333 -0.18577 +0.37000 -0.18418 +0.37667 -0.18251 +0.38333 -0.18074 +0.39000 -0.17889 +0.39667 -0.17695 +0.40333 -0.17496 +0.41000 -0.17288 +0.41667 -0.17074 +0.42333 -0.16855 +0.43000 -0.16631 +0.43667 -0.16402 +0.44333 -0.16167 +0.45000 -0.15929 +0.45667 -0.15684 +0.46333 -0.15436 +0.47000 -0.15185 +0.47667 -0.14930 +0.48333 -0.14671 +0.49000 -0.14410 +0.49667 -0.14147 +0.50333 -0.13881 +0.51000 -0.13613 +0.51667 -0.13342 +0.52333 -0.13071 +0.53000 -0.12797 +0.53667 -0.12522 +0.54333 -0.12247 +0.55000 -0.11970 +0.55667 -0.11692 +0.56333 -0.11413 +0.57000 -0.11133 +0.57667 -0.10853 +0.58333 -0.10573 +0.59000 -0.10293 +0.59667 -0.10014 +0.60333 -0.09734 +0.61000 -0.09456 +0.61667 -0.09178 +0.62333 -0.08901 +0.63000 -0.08624 +0.63667 -0.08348 +0.64333 -0.08074 +0.65000 -0.07801 +0.65667 -0.07529 +0.66333 -0.07257 +0.67000 -0.06987 +0.67667 -0.06719 +0.68333 -0.06452 +0.69000 -0.06186 +0.69667 -0.05922 +0.70333 -0.05661 +0.71000 -0.05401 +0.71667 -0.05144 +0.72333 -0.04889 +0.73000 -0.04637 +0.73667 -0.04386 +0.74333 -0.04140 +0.75000 -0.03896 +0.75660 -0.03658 +0.76314 -0.03425 +0.76961 -0.03199 +0.77601 -0.02979 +0.78235 -0.02765 +0.78863 -0.02557 +0.79484 -0.02357 +0.80098 -0.02162 +0.80706 -0.01974 +0.81307 -0.01794 +0.81902 -0.01621 +0.82490 -0.01454 +0.83072 -0.01294 +0.83647 -0.01142 +0.84216 -0.00996 +0.84778 -0.00858 +0.85333 -0.00727 +0.85882 -0.00604 +0.86425 -0.00487 +0.86961 -0.00377 +0.87490 -0.00276 +0.88013 -0.00182 +0.88529 -0.00095 +0.89039 -0.00014 +0.89542 0.00061 +0.90039 0.00128 +0.90529 0.00189 +0.91013 0.00243 +0.91490 0.00293 +0.91961 0.00335 +0.92425 0.00370 +0.92882 0.00401 +0.93333 0.00425 +0.93778 0.00441 +0.94216 0.00452 +0.94647 0.00455 +0.95072 0.00451 +0.95490 0.00439 +0.95902 0.00420 +0.96307 0.00394 +0.96706 0.00358 +0.97098 0.00315 +0.97484 0.00264 +0.97863 0.00206 +0.98235 0.00141 +0.98601 0.00069 +0.98961 -0.00011 +0.99314 -0.00097 +0.99660 -0.00190 +1.00000 -0.00283 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/DU40_A17.dat b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU40_A17.dat new file mode 100644 index 00000000..54a6d4ac --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU40_A17.dat @@ -0,0 +1,190 @@ +! ------------ AirfoilInfo v1.01.x Input File ---------------------------------- +! DU40 airfoil with an aspect ratio of 17. Original -180 to 180deg Cl, Cd, and Cm versus AOA data taken from Appendix A of DOWEC document 10046_009.pdf (numerical values obtained from Koert Lindenburg of ECN). +! Cl and Cd values corrected for rotational stall delay and Cd values corrected using the Viterna method for 0 to 90deg AOA by Jason Jonkman using AirfoilPrep_v2p0.xls. +! note that this file uses Marshall Buhl's new input file processing; start all comment lines with ! +! ------------------------------------------------------------------------------ +"DEFAULT" InterpOrd ! Interpolation order to use for quasi-steady table lookup {1=linear; 3=cubic spline; "default"} [default=1] + 1 NonDimArea ! The non-dimensional area of the airfoil (area/chord^2) (set to 1.0 if unsure or unneeded) +@"DU40_A17_coords.txt" NumCoords ! The number of coordinates in the airfoil shape file. Set to zero if coordinates not included. +"unused" BL_file ! The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. + 1 NumTabs ! Number of airfoil tables in this file. +! ------------------------------------------------------------------------------ +! data for table 1 +! ------------------------------------------------------------------------------ + 0.75 Re ! Reynolds number in millions + 0 UserProp ! User property (control) setting +True InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +!........................................ + -3.2 alpha0 ! 0-lift angle of attack, depends on airfoil. + 9 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) + -9 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] + 0 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] + 1.3519 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It can be calculated from the static value of Cn at either the break in the pitching moment or the loss of chord force at the onset of stall. It is close to the condition of maximum lift of the airfoil at low Mach numbers. + -0.3226 Cn2 ! As Cn1 for negative AOAs. + 0.19 St_sh ! Strouhal's shedding frequency constant. [default = 0.19] + 0.03 Cd0 ! 2D drag coefficient value at 0-lift. + -0.05 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does not include a column for Cm, this needs to be set to 0.0] + 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] + 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] + 0.2 x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] +"DEFAULT" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] +"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] +!........................................ +! Table of aerodynamics coefficients + 136 NumAlf ! Number of data lines in the following table +! Alpha Cl Cd Cm +! (deg) (-) (-) (-) + -180.00 0.000 0.0602 0.0000 + -175.00 0.218 0.0699 0.0934 + -170.00 0.397 0.1107 0.1697 + -160.00 0.642 0.3045 0.2813 + -155.00 0.715 0.4179 0.3208 + -150.00 0.757 0.5355 0.3516 + -145.00 0.772 0.6535 0.3752 + -140.00 0.762 0.7685 0.3926 + -135.00 0.731 0.8777 0.4048 + -130.00 0.680 0.9788 0.4126 + -125.00 0.613 1.0700 0.4166 + -120.00 0.532 1.1499 0.4176 + -115.00 0.439 1.2174 0.4158 + -110.00 0.337 1.2716 0.4117 + -105.00 0.228 1.3118 0.4057 + -100.00 0.114 1.3378 0.3979 + -95.00 -0.002 1.3492 0.3887 + -90.00 -0.120 1.3460 0.3781 + -85.00 -0.236 1.3283 0.3663 + -80.00 -0.349 1.2964 0.3534 + -75.00 -0.456 1.2507 0.3394 + -70.00 -0.557 1.1918 0.3244 + -65.00 -0.647 1.1204 0.3084 + -60.00 -0.727 1.0376 0.2914 + -55.00 -0.792 0.9446 0.2733 + -50.00 -0.842 0.8429 0.2543 + -45.00 -0.874 0.7345 0.2342 + -40.00 -0.886 0.6215 0.2129 + -35.00 -0.875 0.5067 0.1906 + -30.00 -0.839 0.3932 0.1670 + -25.00 -0.777 0.2849 0.1422 + -24.00 -0.761 0.2642 0.1371 + -23.00 -0.744 0.2440 0.1320 + -22.00 -0.725 0.2242 0.1268 + -21.00 -0.706 0.2049 0.1215 + -20.00 -0.685 0.1861 0.1162 + -19.00 -0.662 0.1687 0.1097 + -18.00 -0.635 0.1533 0.1012 + -17.00 -0.605 0.1398 0.0907 + -16.00 -0.571 0.1281 0.0784 + -15.00 -0.534 0.1183 0.0646 + -14.00 -0.494 0.1101 0.0494 + -13.00 -0.452 0.1036 0.0330 + -12.00 -0.407 0.0986 0.0156 + -11.00 -0.360 0.0951 -0.0026 + -10.00 -0.311 0.0931 -0.0213 + -8.00 -0.208 0.0930 -0.0600 + -6.00 -0.111 0.0689 -0.0500 + -5.50 -0.090 0.0614 -0.0516 + -5.00 -0.072 0.0547 -0.0532 + -4.50 -0.065 0.0480 -0.0538 + -4.00 -0.054 0.0411 -0.0544 + -3.50 -0.017 0.0349 -0.0554 + -3.00 0.003 0.0299 -0.0558 + -2.50 0.014 0.0255 -0.0555 + -2.00 0.009 0.0198 -0.0534 + -1.50 0.004 0.0164 -0.0442 + -1.00 0.036 0.0147 -0.0469 + -0.50 0.073 0.0137 -0.0522 + 0.00 0.137 0.0113 -0.0573 + 0.50 0.213 0.0114 -0.0644 + 1.00 0.292 0.0118 -0.0718 + 1.50 0.369 0.0122 -0.0783 + 2.00 0.444 0.0124 -0.0835 + 2.50 0.514 0.0124 -0.0866 + 3.00 0.580 0.0123 -0.0887 + 3.50 0.645 0.0120 -0.0900 + 4.00 0.710 0.0119 -0.0914 + 4.50 0.776 0.0122 -0.0933 + 5.00 0.841 0.0125 -0.0947 + 5.50 0.904 0.0129 -0.0957 + 6.00 0.967 0.0135 -0.0967 + 6.50 1.027 0.0144 -0.0973 + 7.00 1.084 0.0158 -0.0972 + 7.50 1.140 0.0174 -0.0972 + 8.00 1.193 0.0198 -0.0968 + 8.50 1.242 0.0231 -0.0958 + 9.00 1.287 0.0275 -0.0948 + 9.50 1.333 0.0323 -0.0942 + 10.00 1.368 0.0393 -0.0926 + 10.50 1.400 0.0475 -0.0908 + 11.00 1.425 0.0580 -0.0890 + 11.50 1.449 0.0691 -0.0877 + 12.00 1.473 0.0816 -0.0870 + 12.50 1.494 0.0973 -0.0870 + 13.00 1.513 0.1129 -0.0876 + 13.50 1.538 0.1288 -0.0886 + 14.50 1.587 0.1650 -0.0917 + 15.00 1.614 0.1845 -0.0939 + 15.50 1.631 0.2052 -0.0966 + 16.00 1.649 0.2250 -0.0996 + 16.50 1.666 0.2467 -0.1031 + 17.00 1.681 0.2684 -0.1069 + 17.50 1.699 0.2900 -0.1110 + 18.00 1.719 0.3121 -0.1157 + 19.00 1.751 0.3554 -0.1242 + 19.50 1.767 0.3783 -0.1291 + 20.50 1.798 0.4212 -0.1384 + 21.00 1.810 0.4415 -0.1416 + 22.00 1.830 0.4830 -0.1479 + 23.00 1.847 0.5257 -0.1542 + 24.00 1.861 0.5694 -0.1603 + 25.00 1.872 0.6141 -0.1664 + 26.00 1.881 0.6593 -0.1724 + 28.00 1.894 0.7513 -0.1841 + 30.00 1.904 0.8441 -0.1954 + 32.00 1.915 0.9364 -0.2063 + 35.00 1.929 1.0722 -0.2220 + 40.00 1.903 1.2873 -0.2468 + 45.00 1.820 1.4796 -0.2701 + 50.00 1.690 1.6401 -0.2921 + 55.00 1.522 1.7609 -0.3127 + 60.00 1.323 1.8360 -0.3321 + 65.00 1.106 1.8614 -0.3502 + 70.00 0.880 1.8347 -0.3672 + 75.00 0.658 1.7567 -0.3830 + 80.00 0.449 1.6334 -0.3977 + 85.00 0.267 1.4847 -0.4112 + 90.00 0.124 1.3879 -0.4234 + 95.00 0.002 1.3912 -0.4343 + 100.00 -0.118 1.3795 -0.4437 + 105.00 -0.235 1.3528 -0.4514 + 110.00 -0.348 1.3114 -0.4573 + 115.00 -0.453 1.2557 -0.4610 + 120.00 -0.549 1.1864 -0.4623 + 125.00 -0.633 1.1041 -0.4606 + 130.00 -0.702 1.0102 -0.4554 + 135.00 -0.754 0.9060 -0.4462 + 140.00 -0.787 0.7935 -0.4323 + 145.00 -0.797 0.6750 -0.4127 + 150.00 -0.782 0.5532 -0.3863 + 155.00 -0.739 0.4318 -0.3521 + 160.00 -0.664 0.3147 -0.3085 + 170.00 -0.410 0.1144 -0.1858 + 175.00 -0.226 0.0702 -0.1022 + 180.00 0.000 0.0602 0.0000 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/DU40_A17_coords.txt b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU40_A17_coords.txt new file mode 100644 index 00000000..18d11b7b --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/DU40_A17_coords.txt @@ -0,0 +1,407 @@ + 400 NumCoords ! The number of coordinates in the airfoil shape file (including an extra coordinate for airfoil reference). Set to zero if coordinates not included. +! ......... x-y coordinates are next if NumCoords > 0 ............. +! x-y coordinate of airfoil reference +! x/c y/c +0.33087 0 +! coordinates of airfoil shape; data from TU Delft as posted here: https://wind.nrel.gov/forum/wind/viewtopic.php?f=2&t=440 +! Adjusted DU4050 +! x/c y/c +1.00000 0.00347 +0.99660 0.00455 +0.99314 0.00565 +0.98961 0.00678 +0.98601 0.00795 +0.98235 0.00914 +0.97863 0.01035 +0.97484 0.01158 +0.97098 0.01283 +0.96706 0.01410 +0.96307 0.01539 +0.95902 0.01670 +0.95490 0.01803 +0.95072 0.01937 +0.94647 0.02074 +0.94216 0.02212 +0.93778 0.02352 +0.93333 0.02495 +0.92882 0.02639 +0.92425 0.02786 +0.91961 0.02936 +0.91490 0.03087 +0.91013 0.03242 +0.90529 0.03400 +0.90039 0.03560 +0.89542 0.03723 +0.89039 0.03887 +0.88529 0.04056 +0.88013 0.04226 +0.87490 0.04399 +0.86961 0.04575 +0.86425 0.04754 +0.85882 0.04935 +0.85333 0.05119 +0.84778 0.05305 +0.84216 0.05493 +0.83647 0.05685 +0.83072 0.05878 +0.82490 0.06076 +0.81902 0.06275 +0.81307 0.06476 +0.80706 0.06680 +0.80098 0.06886 +0.79484 0.07094 +0.78863 0.07304 +0.78235 0.07517 +0.77601 0.07730 +0.76961 0.07947 +0.76314 0.08165 +0.75660 0.08386 +0.75000 0.08608 +0.74333 0.08832 +0.73667 0.09056 +0.73000 0.09279 +0.72333 0.09502 +0.71667 0.09724 +0.71000 0.09946 +0.70333 0.10166 +0.69667 0.10386 +0.69000 0.10605 +0.68333 0.10822 +0.67667 0.11040 +0.67000 0.11256 +0.66333 0.11471 +0.65667 0.11685 +0.65000 0.11897 +0.64333 0.12109 +0.63667 0.12318 +0.63000 0.12527 +0.62333 0.12733 +0.61667 0.12938 +0.61000 0.13142 +0.60333 0.13343 +0.59667 0.13543 +0.59000 0.13742 +0.58333 0.13938 +0.57667 0.14131 +0.57000 0.14323 +0.56333 0.14512 +0.55667 0.14698 +0.55000 0.14882 +0.54333 0.15064 +0.53667 0.15242 +0.53000 0.15417 +0.52333 0.15591 +0.51667 0.15759 +0.51000 0.15925 +0.50333 0.16088 +0.49667 0.16246 +0.49000 0.16401 +0.48333 0.16553 +0.47667 0.16701 +0.47000 0.16844 +0.46333 0.16985 +0.45667 0.17120 +0.45000 0.17253 +0.44333 0.17381 +0.43667 0.17504 +0.43000 0.17624 +0.42333 0.17739 +0.41667 0.17849 +0.41000 0.17957 +0.40333 0.18058 +0.39667 0.18154 +0.39000 0.18246 +0.38333 0.18333 +0.37667 0.18415 +0.37000 0.18491 +0.36333 0.18563 +0.35667 0.18629 +0.35000 0.18690 +0.34333 0.18746 +0.33667 0.18797 +0.33000 0.18842 +0.32333 0.18881 +0.31667 0.18914 +0.31000 0.18940 +0.30333 0.18961 +0.29667 0.18973 +0.29000 0.18979 +0.28333 0.18977 +0.27667 0.18968 +0.27000 0.18950 +0.26333 0.18924 +0.25667 0.18890 +0.25000 0.18845 +0.24342 0.18791 +0.23693 0.18729 +0.23053 0.18657 +0.22421 0.18575 +0.21798 0.18487 +0.21184 0.18388 +0.20579 0.18282 +0.19982 0.18167 +0.19395 0.18046 +0.18816 0.17916 +0.18245 0.17778 +0.17684 0.17634 +0.17131 0.17482 +0.16587 0.17323 +0.16052 0.17158 +0.15526 0.16987 +0.15008 0.16809 +0.14499 0.16625 +0.13999 0.16436 +0.13508 0.16240 +0.13026 0.16038 +0.12552 0.15831 +0.12087 0.15619 +0.11631 0.15402 +0.11183 0.15179 +0.10745 0.14953 +0.10315 0.14722 +0.09893 0.14485 +0.09481 0.14243 +0.09077 0.13998 +0.08683 0.13748 +0.08297 0.13493 +0.07919 0.13234 +0.07551 0.12972 +0.07191 0.12705 +0.06840 0.12435 +0.06498 0.12161 +0.06164 0.11884 +0.05840 0.11603 +0.05524 0.11319 +0.05217 0.11031 +0.04918 0.10740 +0.04629 0.10447 +0.04348 0.10150 +0.04076 0.09851 +0.03812 0.09549 +0.03558 0.09246 +0.03312 0.08940 +0.03075 0.08632 +0.02847 0.08324 +0.02627 0.08013 +0.02417 0.07701 +0.02215 0.07387 +0.02022 0.07070 +0.01837 0.06751 +0.01662 0.06433 +0.01495 0.06111 +0.01337 0.05790 +0.01187 0.05468 +0.01047 0.05148 +0.00915 0.04826 +0.00792 0.04505 +0.00678 0.04181 +0.00572 0.03847 +0.00476 0.03502 +0.00388 0.03133 +0.00309 0.02736 +0.00238 0.02318 +0.00177 0.01920 +0.00124 0.01552 +0.00080 0.01217 +0.00044 0.00892 +0.00018 0.00563 +0.00000 0.00000 +0.00018 -0.00567 +0.00044 -0.00905 +0.00080 -0.01247 +0.00124 -0.01591 +0.00177 -0.01956 +0.00238 -0.02333 +0.00309 -0.02716 +0.00388 -0.03085 +0.00476 -0.03442 +0.00572 -0.03783 +0.00678 -0.04120 +0.00792 -0.04453 +0.00915 -0.04785 +0.01047 -0.05117 +0.01187 -0.05447 +0.01337 -0.05781 +0.01495 -0.06113 +0.01662 -0.06446 +0.01837 -0.06775 +0.02022 -0.07107 +0.02215 -0.07437 +0.02417 -0.07766 +0.02627 -0.08092 +0.02847 -0.08421 +0.03075 -0.08748 +0.03312 -0.09076 +0.03558 -0.09406 +0.03812 -0.09733 +0.04076 -0.10064 +0.04348 -0.10392 +0.04629 -0.10720 +0.04918 -0.11047 +0.05217 -0.11374 +0.05524 -0.11698 +0.05840 -0.12023 +0.06164 -0.12344 +0.06498 -0.12665 +0.06840 -0.12982 +0.07191 -0.13299 +0.07551 -0.13612 +0.07919 -0.13922 +0.08297 -0.14230 +0.08683 -0.14535 +0.09077 -0.14835 +0.09481 -0.15135 +0.09893 -0.15429 +0.10315 -0.15720 +0.10745 -0.16006 +0.11183 -0.16289 +0.11631 -0.16567 +0.12087 -0.16842 +0.12552 -0.17111 +0.13026 -0.17376 +0.13508 -0.17635 +0.13999 -0.17890 +0.14499 -0.18137 +0.15008 -0.18380 +0.15526 -0.18616 +0.16052 -0.18847 +0.16587 -0.19070 +0.17131 -0.19287 +0.17684 -0.19496 +0.18245 -0.19698 +0.18816 -0.19894 +0.19395 -0.20080 +0.19982 -0.20257 +0.20579 -0.20425 +0.21184 -0.20584 +0.21798 -0.20733 +0.22421 -0.20870 +0.23053 -0.20996 +0.23693 -0.21110 +0.24342 -0.21210 +0.25000 -0.21297 +0.25667 -0.21370 +0.26333 -0.21429 +0.27000 -0.21472 +0.27667 -0.21501 +0.28333 -0.21515 +0.29000 -0.21516 +0.29667 -0.21502 +0.30333 -0.21476 +0.31000 -0.21437 +0.31667 -0.21384 +0.32333 -0.21320 +0.33000 -0.21243 +0.33667 -0.21155 +0.34333 -0.21057 +0.35000 -0.20948 +0.35667 -0.20827 +0.36333 -0.20697 +0.37000 -0.20556 +0.37667 -0.20407 +0.38333 -0.20248 +0.39000 -0.20081 +0.39667 -0.19904 +0.40333 -0.19720 +0.41000 -0.19527 +0.41667 -0.19327 +0.42333 -0.19119 +0.43000 -0.18905 +0.43667 -0.18683 +0.44333 -0.18454 +0.45000 -0.18219 +0.45667 -0.17976 +0.46333 -0.17727 +0.47000 -0.17473 +0.47667 -0.17212 +0.48333 -0.16945 +0.49000 -0.16673 +0.49667 -0.16397 +0.50333 -0.16115 +0.51000 -0.15828 +0.51667 -0.15537 +0.52333 -0.15242 +0.53000 -0.14942 +0.53667 -0.14639 +0.54333 -0.14333 +0.55000 -0.14024 +0.55667 -0.13713 +0.56333 -0.13399 +0.57000 -0.13083 +0.57667 -0.12765 +0.58333 -0.12446 +0.59000 -0.12125 +0.59667 -0.11804 +0.60333 -0.11482 +0.61000 -0.11160 +0.61667 -0.10838 +0.62333 -0.10515 +0.63000 -0.10192 +0.63667 -0.09870 +0.64333 -0.09549 +0.65000 -0.09228 +0.65667 -0.08909 +0.66333 -0.08590 +0.67000 -0.08274 +0.67667 -0.07958 +0.68333 -0.07645 +0.69000 -0.07333 +0.69667 -0.07024 +0.70333 -0.06717 +0.71000 -0.06413 +0.71667 -0.06112 +0.72333 -0.05814 +0.73000 -0.05519 +0.73667 -0.05228 +0.74333 -0.04941 +0.75000 -0.04658 +0.75660 -0.04382 +0.76314 -0.04114 +0.76961 -0.03853 +0.77601 -0.03600 +0.78235 -0.03354 +0.78863 -0.03116 +0.79484 -0.02887 +0.80098 -0.02665 +0.80706 -0.02452 +0.81307 -0.02247 +0.81902 -0.02050 +0.82490 -0.01862 +0.83072 -0.01681 +0.83647 -0.01510 +0.84216 -0.01346 +0.84778 -0.01191 +0.85333 -0.01045 +0.85882 -0.00907 +0.86425 -0.00776 +0.86961 -0.00653 +0.87490 -0.00539 +0.88013 -0.00434 +0.88529 -0.00335 +0.89039 -0.00245 +0.89542 -0.00160 +0.90039 -0.00085 +0.90529 -0.00015 +0.91013 0.00046 +0.91490 0.00103 +0.91961 0.00151 +0.92425 0.00193 +0.92882 0.00229 +0.93333 0.00258 +0.93778 0.00279 +0.94216 0.00295 +0.94647 0.00303 +0.95072 0.00303 +0.95490 0.00296 +0.95902 0.00282 +0.96307 0.00261 +0.96706 0.00232 +0.97098 0.00194 +0.97484 0.00149 +0.97863 0.00098 +0.98235 0.00040 +0.98601 -0.00025 +0.98961 -0.00097 +0.99314 -0.00176 +0.99660 -0.00261 +1.00000 -0.00347 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/NACA64_A17.dat b/tests/unit_tests/external/5MW_Baseline/Airfoils/NACA64_A17.dat new file mode 100644 index 00000000..b9dca301 --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/NACA64_A17.dat @@ -0,0 +1,181 @@ +! ------------ AirfoilInfo v1.01.x Input File ---------------------------------- +! NACA64 airfoil with an aspect ratio of 17. Original -180 to 180deg Cl, Cd, and Cm versus AOA data taken from Appendix A of DOWEC document 10046_009.pdf (numerical values obtained from Koert Lindenburg of ECN). +! Cl and Cd values corrected for rotational stall delay and Cd values corrected using the Viterna method for 0 to 90deg AOA by Jason Jonkman using AirfoilPrep_v2p0.xls. +! note that this file uses Marshall Buhl's new input file processing; start all comment lines with ! +! ------------------------------------------------------------------------------ +"DEFAULT" InterpOrd ! Interpolation order to use for quasi-steady table lookup {1=linear; 3=cubic spline; "default"} [default=1] + 1 NonDimArea ! The non-dimensional area of the airfoil (area/chord^2) (set to 1.0 if unsure or unneeded) +@"NACA64_A17_coords.txt" NumCoords ! The number of coordinates in the airfoil shape file. Set to zero if coordinates not included. +"unused" BL_file ! The file name including the boundary layer characteristics of the profile. Ignored if the aeroacoustic module is not called. + 1 NumTabs ! Number of airfoil tables in this file. +! ------------------------------------------------------------------------------ +! data for table 1 +! ------------------------------------------------------------------------------ + 0.75 Re ! Reynolds number in millions + 0 UserProp ! User property (control) setting +True InclUAdata ! Is unsteady aerodynamics data included in this table? If TRUE, then include 30 UA coefficients below this line +!........................................ + -4.432 alpha0 ! 0-lift angle of attack, depends on airfoil. + 9 alpha1 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA>alpha0. (deg) + -9 alpha2 ! Angle of attack at f=0.7, (approximately the stall angle) for AOA1] + 0 S2 ! Constant in the f curve best-fit for AOA> alpha1; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S3 ! Constant in the f curve best-fit for alpha2<=AOA< alpha0; by definition it depends on the airfoil. [ignored if UAMod<>1] + 0 S4 ! Constant in the f curve best-fit for AOA< alpha2; by definition it depends on the airfoil. [ignored if UAMod<>1] + 1.4073 Cn1 ! Critical value of C0n at leading edge separation. It should be extracted from airfoil data at a given Mach and Reynolds number. It can be calculated from the static value of Cn at either the break in the pitching moment or the loss of chord force at the onset of stall. It is close to the condition of maximum lift of the airfoil at low Mach numbers. + -0.7945 Cn2 ! As Cn1 for negative AOAs. + 0.19 St_sh ! Strouhal's shedding frequency constant. [default = 0.19] + 0.0065 Cd0 ! 2D drag coefficient value at 0-lift. + -0.088 Cm0 ! 2D pitching moment coefficient about 1/4-chord location, at 0-lift, positive if nose up. [If the aerodynamics coefficients table does not include a column for Cm, this needs to be set to 0.0] + 0 k0 ! Constant in the \hat(x)_cp curve best-fit; = (\hat(x)_AC-0.25). [ignored if UAMod<>1] + 0 k1 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k2 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k3 ! Constant in the \hat(x)_cp curve best-fit. [ignored if UAMod<>1] + 0 k1_hat ! Constant in the expression of Cc due to leading edge vortex effects. [ignored if UAMod<>1] +"Default" x_cp_bar ! Constant in the expression of \hat(x)_cp^v. [ignored if UAMod<>1, default = 0.2] +"Default" UACutout ! Angle of attack above which unsteady aerodynamics are disabled (deg). [Specifying the string "Default" sets UACutout to 45 degrees] +"DEFAULT" filtCutOff ! Reduced frequency cut-off for low-pass filtering the AoA input to UA, as well as the 1st and 2nd derivatives (-) [default = 0.5] +!........................................ +! Table of aerodynamics coefficients + 127 NumAlf ! Number of data lines in the following table +! Alpha Cl Cd Cm +! (deg) (-) (-) (-) + -180.00 0.000 0.0198 0.0000 + -175.00 0.374 0.0341 0.1880 + -170.00 0.749 0.0955 0.3770 + -160.00 0.659 0.2807 0.2747 + -155.00 0.736 0.3919 0.3130 + -150.00 0.783 0.5086 0.3428 + -145.00 0.803 0.6267 0.3654 + -140.00 0.798 0.7427 0.3820 + -135.00 0.771 0.8537 0.3935 + -130.00 0.724 0.9574 0.4007 + -125.00 0.660 1.0519 0.4042 + -120.00 0.581 1.1355 0.4047 + -115.00 0.491 1.2070 0.4025 + -110.00 0.390 1.2656 0.3981 + -105.00 0.282 1.3104 0.3918 + -100.00 0.169 1.3410 0.3838 + -95.00 0.052 1.3572 0.3743 + -90.00 -0.067 1.3587 0.3636 + -85.00 -0.184 1.3456 0.3517 + -80.00 -0.299 1.3181 0.3388 + -75.00 -0.409 1.2765 0.3248 + -70.00 -0.512 1.2212 0.3099 + -65.00 -0.606 1.1532 0.2940 + -60.00 -0.689 1.0731 0.2772 + -55.00 -0.759 0.9822 0.2595 + -50.00 -0.814 0.8820 0.2409 + -45.00 -0.850 0.7742 0.2212 + -40.00 -0.866 0.6610 0.2006 + -35.00 -0.860 0.5451 0.1789 + -30.00 -0.829 0.4295 0.1563 + -25.00 -0.853 0.3071 0.1156 + -24.00 -0.870 0.2814 0.1040 + -23.00 -0.890 0.2556 0.0916 + -22.00 -0.911 0.2297 0.0785 + -21.00 -0.934 0.2040 0.0649 + -20.00 -0.958 0.1785 0.0508 + -19.00 -0.982 0.1534 0.0364 + -18.00 -1.005 0.1288 0.0218 + -17.00 -1.082 0.1037 0.0129 + -16.00 -1.113 0.0786 -0.0028 + -15.00 -1.105 0.0535 -0.0251 + -14.00 -1.078 0.0283 -0.0419 + -13.50 -1.053 0.0158 -0.0521 + -13.00 -1.015 0.0151 -0.0610 + -12.00 -0.904 0.0134 -0.0707 + -11.00 -0.807 0.0121 -0.0722 + -10.00 -0.711 0.0111 -0.0734 + -9.00 -0.595 0.0099 -0.0772 + -8.00 -0.478 0.0091 -0.0807 + -7.00 -0.375 0.0086 -0.0825 + -6.00 -0.264 0.0082 -0.0832 + -5.00 -0.151 0.0079 -0.0841 + -4.00 -0.017 0.0072 -0.0869 + -3.00 0.088 0.0064 -0.0912 + -2.00 0.213 0.0054 -0.0946 + -1.00 0.328 0.0052 -0.0971 + 0.00 0.442 0.0052 -0.1014 + 1.00 0.556 0.0052 -0.1076 + 2.00 0.670 0.0053 -0.1126 + 3.00 0.784 0.0053 -0.1157 + 4.00 0.898 0.0054 -0.1199 + 5.00 1.011 0.0058 -0.1240 + 6.00 1.103 0.0091 -0.1234 + 7.00 1.181 0.0113 -0.1184 + 8.00 1.257 0.0124 -0.1163 + 8.50 1.293 0.0130 -0.1163 + 9.00 1.326 0.0136 -0.1160 + 9.50 1.356 0.0143 -0.1154 + 10.00 1.382 0.0150 -0.1149 + 10.50 1.400 0.0267 -0.1145 + 11.00 1.415 0.0383 -0.1143 + 11.50 1.425 0.0498 -0.1147 + 12.00 1.434 0.0613 -0.1158 + 12.50 1.443 0.0727 -0.1165 + 13.00 1.451 0.0841 -0.1153 + 13.50 1.453 0.0954 -0.1131 + 14.00 1.448 0.1065 -0.1112 + 14.50 1.444 0.1176 -0.1101 + 15.00 1.445 0.1287 -0.1103 + 15.50 1.447 0.1398 -0.1109 + 16.00 1.448 0.1509 -0.1114 + 16.50 1.444 0.1619 -0.1111 + 17.00 1.438 0.1728 -0.1097 + 17.50 1.439 0.1837 -0.1079 + 18.00 1.448 0.1947 -0.1080 + 18.50 1.452 0.2057 -0.1090 + 19.00 1.448 0.2165 -0.1086 + 19.50 1.438 0.2272 -0.1077 + 20.00 1.428 0.2379 -0.1099 + 21.00 1.401 0.2590 -0.1169 + 22.00 1.359 0.2799 -0.1190 + 23.00 1.300 0.3004 -0.1235 + 24.00 1.220 0.3204 -0.1393 + 25.00 1.168 0.3377 -0.1440 + 26.00 1.116 0.3554 -0.1486 + 28.00 1.015 0.3916 -0.1577 + 30.00 0.926 0.4294 -0.1668 + 32.00 0.855 0.4690 -0.1759 + 35.00 0.800 0.5324 -0.1897 + 40.00 0.804 0.6452 -0.2126 + 45.00 0.793 0.7573 -0.2344 + 50.00 0.763 0.8664 -0.2553 + 55.00 0.717 0.9708 -0.2751 + 60.00 0.656 1.0693 -0.2939 + 65.00 0.582 1.1606 -0.3117 + 70.00 0.495 1.2438 -0.3285 + 75.00 0.398 1.3178 -0.3444 + 80.00 0.291 1.3809 -0.3593 + 85.00 0.176 1.4304 -0.3731 + 90.00 0.053 1.4565 -0.3858 + 95.00 -0.074 1.4533 -0.3973 + 100.00 -0.199 1.4345 -0.4075 + 105.00 -0.321 1.4004 -0.4162 + 110.00 -0.436 1.3512 -0.4231 + 115.00 -0.543 1.2874 -0.4280 + 120.00 -0.640 1.2099 -0.4306 + 125.00 -0.723 1.1196 -0.4304 + 130.00 -0.790 1.0179 -0.4270 + 135.00 -0.840 0.9064 -0.4196 + 140.00 -0.868 0.7871 -0.4077 + 145.00 -0.872 0.6627 -0.3903 + 150.00 -0.850 0.5363 -0.3665 + 155.00 -0.798 0.4116 -0.3349 + 160.00 -0.714 0.2931 -0.2942 + 170.00 -0.749 0.0971 -0.3771 + 175.00 -0.374 0.0334 -0.1879 + 180.00 0.000 0.0198 0.0000 diff --git a/tests/unit_tests/external/5MW_Baseline/Airfoils/NACA64_A17_coords.txt b/tests/unit_tests/external/5MW_Baseline/Airfoils/NACA64_A17_coords.txt new file mode 100644 index 00000000..b2b49e07 --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/Airfoils/NACA64_A17_coords.txt @@ -0,0 +1,407 @@ + 400 NumCoords ! The number of coordinates in the airfoil shape file (including an extra coordinate for airfoil reference). Set to zero if coordinates not included. +! ......... x-y coordinates are next if NumCoords > 0 ............. +! x-y coordinate of airfoil reference +! x/c y/c +0.25 0 +! coordinates of airfoil shape; data from TU Delft as posted here: https://wind.nrel.gov/forum/wind/viewtopic.php?f=2&t=440 +! NACA 64-618 (interpolated to 399 points) +! x/c y/c +1.000000 0.000000 +0.990000 0.003385 +0.980000 0.006126 +0.975000 0.007447 +0.970000 0.008767 +0.965000 0.010062 +0.960000 0.011357 +0.955000 0.012639 +0.950000 0.013921 +0.945000 0.015200 +0.940000 0.016478 +0.935000 0.017757 +0.930000 0.019036 +0.925000 0.020317 +0.920000 0.021598 +0.915000 0.022881 +0.910000 0.024163 +0.905000 0.025448 +0.900000 0.026733 +0.887500 0.029951 +0.875000 0.033169 +0.862500 0.036386 +0.850000 0.039603 +0.837500 0.042804 +0.825000 0.046004 +0.812500 0.049171 +0.800000 0.052337 +0.787500 0.055452 +0.775000 0.058566 +0.762500 0.061611 +0.750000 0.064656 +0.737500 0.067615 +0.725000 0.070573 +0.712500 0.073429 +0.700000 0.076285 +0.687500 0.079029 +0.675000 0.081773 +0.662500 0.084393 +0.650000 0.087012 +0.637500 0.089490 +0.625000 0.091967 +0.612500 0.094283 +0.600000 0.096599 +0.587500 0.098743 +0.575000 0.100887 +0.562500 0.102843 +0.550000 0.104799 +0.537500 0.106549 +0.525000 0.108299 +0.512500 0.109830 +0.500000 0.111360 +0.487500 0.112649 +0.475000 0.113937 +0.462500 0.114964 +0.450000 0.115990 +0.445000 0.116320 +0.440000 0.116650 +0.435000 0.116931 +0.430000 0.117211 +0.425000 0.117439 +0.420000 0.117667 +0.415000 0.117835 +0.410000 0.118003 +0.405000 0.118104 +0.400000 0.118204 +0.395000 0.118231 +0.390000 0.118258 +0.385000 0.118213 +0.380000 0.118168 +0.375000 0.118057 +0.370000 0.117946 +0.365000 0.117777 +0.360000 0.117607 +0.355000 0.117383 +0.350000 0.117159 +0.345000 0.116881 +0.340000 0.116603 +0.335000 0.116273 +0.330000 0.115942 +0.325000 0.115562 +0.320000 0.115181 +0.315000 0.114750 +0.310000 0.114319 +0.305000 0.113838 +0.300000 0.113356 +0.295000 0.112824 +0.290000 0.112292 +0.285000 0.111710 +0.280000 0.111127 +0.275000 0.110495 +0.270000 0.109863 +0.265000 0.109180 +0.260000 0.108497 +0.255000 0.107762 +0.250000 0.107027 +0.245000 0.106241 +0.240000 0.105454 +0.235000 0.104614 +0.230000 0.103774 +0.225000 0.102880 +0.220000 0.101985 +0.215000 0.101035 +0.210000 0.100084 +0.205000 0.099076 +0.200000 0.098068 +0.195000 0.097001 +0.190000 0.095934 +0.185000 0.094805 +0.180000 0.093676 +0.175000 0.092484 +0.170000 0.091291 +0.165000 0.090032 +0.160000 0.088772 +0.155000 0.087441 +0.150000 0.086110 +0.145000 0.084704 +0.140000 0.083298 +0.135000 0.081814 +0.130000 0.080329 +0.125000 0.078759 +0.120000 0.077188 +0.115000 0.075525 +0.110000 0.073862 +0.105000 0.072098 +0.100000 0.070334 +0.097500 0.069412 +0.095000 0.068489 +0.092500 0.067537 +0.090000 0.066584 +0.087500 0.065601 +0.085000 0.064617 +0.082500 0.063600 +0.080000 0.062583 +0.077500 0.061531 +0.075000 0.060478 +0.072500 0.059388 +0.070000 0.058297 +0.067500 0.057165 +0.065000 0.056032 +0.062500 0.054854 +0.060000 0.053676 +0.057500 0.052447 +0.055000 0.051218 +0.052500 0.049933 +0.050000 0.048647 +0.047500 0.047299 +0.045000 0.045950 +0.042500 0.044530 +0.040000 0.043110 +0.037500 0.041606 +0.035000 0.040102 +0.032500 0.038501 +0.030000 0.036899 +0.027500 0.035177 +0.025000 0.033454 +0.022500 0.031574 +0.020000 0.029694 +0.018750 0.028680 +0.017500 0.027666 +0.016250 0.026589 +0.015000 0.025511 +0.013750 0.024354 +0.012500 0.023197 +0.011250 0.021936 +0.010000 0.020674 +0.009500 0.020131 +0.009000 0.019587 +0.008500 0.019017 +0.008000 0.018447 +0.007500 0.017844 +0.007000 0.017241 +0.006500 0.016598 +0.006000 0.015955 +0.005500 0.015260 +0.005000 0.014565 +0.004500 0.013801 +0.004000 0.013037 +0.003500 0.012167 +0.003000 0.011296 +0.002500 0.010262 +0.002000 0.009227 +0.001875 0.008930 +0.001750 0.008633 +0.001625 0.008315 +0.001500 0.007997 +0.001375 0.007655 +0.001250 0.007312 +0.001125 0.006934 +0.001000 0.006555 +0.000875 0.006125 +0.000750 0.005695 +0.000625 0.005184 +0.000500 0.004672 +0.000400 0.004190 +0.000350 0.003913 +0.000300 0.003636 +0.000200 0.002970 +0.000100 0.002104 +0.000050 0.001052 +0.000000 0.000000 +0.000050 -0.001046 +0.000100 -0.002092 +0.000200 -0.002954 +0.000300 -0.003613 +0.000350 -0.003891 +0.000400 -0.004169 +0.000500 -0.004658 +0.000625 -0.005178 +0.000750 -0.005698 +0.000875 -0.006135 +0.001000 -0.006572 +0.001125 -0.006956 +0.001250 -0.007340 +0.001375 -0.007684 +0.001500 -0.008027 +0.001625 -0.008341 +0.001750 -0.008654 +0.001875 -0.008943 +0.002000 -0.009231 +0.002500 -0.010204 +0.003000 -0.011176 +0.003500 -0.011953 +0.004000 -0.012729 +0.004500 -0.013380 +0.005000 -0.014030 +0.005500 -0.014595 +0.006000 -0.015160 +0.006500 -0.015667 +0.007000 -0.016174 +0.007500 -0.016636 +0.008000 -0.017098 +0.008500 -0.017526 +0.009000 -0.017953 +0.009500 -0.018352 +0.010000 -0.018750 +0.011250 -0.019644 +0.012500 -0.020537 +0.013750 -0.021322 +0.015000 -0.022107 +0.016250 -0.022812 +0.017500 -0.023517 +0.018750 -0.024160 +0.020000 -0.024803 +0.022500 -0.025948 +0.025000 -0.027092 +0.027500 -0.028097 +0.030000 -0.029102 +0.032500 -0.030003 +0.035000 -0.030904 +0.037500 -0.031725 +0.040000 -0.032546 +0.042500 -0.033304 +0.045000 -0.034061 +0.047500 -0.034767 +0.050000 -0.035472 +0.052500 -0.036132 +0.055000 -0.036792 +0.057500 -0.037414 +0.060000 -0.038035 +0.062500 -0.038622 +0.065000 -0.039209 +0.067500 -0.039766 +0.070000 -0.040322 +0.072500 -0.040852 +0.075000 -0.041381 +0.077500 -0.041885 +0.080000 -0.042389 +0.082500 -0.042870 +0.085000 -0.043350 +0.087500 -0.043809 +0.090000 -0.044268 +0.092500 -0.044707 +0.095000 -0.045145 +0.097500 -0.045566 +0.100000 -0.045987 +0.105000 -0.046782 +0.110000 -0.047576 +0.115000 -0.048313 +0.120000 -0.049050 +0.125000 -0.049734 +0.130000 -0.050417 +0.135000 -0.051053 +0.140000 -0.051688 +0.145000 -0.052278 +0.150000 -0.052868 +0.155000 -0.053418 +0.160000 -0.053967 +0.165000 -0.054478 +0.170000 -0.054988 +0.175000 -0.055461 +0.180000 -0.055934 +0.185000 -0.056373 +0.190000 -0.056811 +0.195000 -0.057216 +0.200000 -0.057621 +0.205000 -0.057993 +0.210000 -0.058365 +0.215000 -0.058705 +0.220000 -0.059045 +0.225000 -0.059355 +0.230000 -0.059664 +0.235000 -0.059944 +0.240000 -0.060224 +0.245000 -0.060474 +0.250000 -0.060723 +0.255000 -0.060943 +0.260000 -0.061163 +0.265000 -0.061354 +0.270000 -0.061545 +0.275000 -0.061708 +0.280000 -0.061871 +0.285000 -0.062004 +0.290000 -0.062137 +0.295000 -0.062240 +0.300000 -0.062343 +0.305000 -0.062417 +0.310000 -0.062490 +0.315000 -0.062534 +0.320000 -0.062577 +0.325000 -0.062590 +0.330000 -0.062602 +0.335000 -0.062583 +0.340000 -0.062563 +0.345000 -0.062512 +0.350000 -0.062460 +0.355000 -0.062374 +0.360000 -0.062287 +0.365000 -0.062164 +0.370000 -0.062040 +0.375000 -0.061878 +0.380000 -0.061716 +0.385000 -0.061509 +0.390000 -0.061301 +0.395000 -0.061040 +0.400000 -0.060778 +0.405000 -0.060458 +0.410000 -0.060138 +0.415000 -0.059763 +0.420000 -0.059388 +0.425000 -0.058966 +0.430000 -0.058544 +0.435000 -0.058083 +0.440000 -0.057622 +0.445000 -0.057127 +0.450000 -0.056632 +0.462500 -0.055265 +0.475000 -0.053897 +0.487500 -0.052374 +0.500000 -0.050850 +0.512500 -0.049195 +0.525000 -0.047539 +0.537500 -0.045777 +0.550000 -0.044014 +0.562500 -0.042165 +0.575000 -0.040316 +0.587500 -0.038401 +0.600000 -0.036486 +0.612500 -0.034526 +0.625000 -0.032565 +0.637500 -0.030575 +0.650000 -0.028585 +0.662500 -0.026594 +0.675000 -0.024603 +0.687500 -0.022632 +0.700000 -0.020660 +0.712500 -0.018728 +0.725000 -0.016795 +0.737500 -0.014922 +0.750000 -0.013048 +0.762500 -0.011260 +0.775000 -0.009472 +0.787500 -0.007797 +0.800000 -0.006122 +0.812500 -0.004594 +0.825000 -0.003065 +0.837500 -0.001721 +0.850000 -0.000376 +0.862500 0.000742 +0.875000 0.001859 +0.887500 0.002698 +0.900000 0.003536 +0.905000 0.003780 +0.910000 0.004023 +0.915000 0.004205 +0.920000 0.004387 +0.925000 0.004504 +0.930000 0.004620 +0.935000 0.004661 +0.940000 0.004702 +0.945000 0.004658 +0.950000 0.004614 +0.955000 0.004476 +0.960000 0.004338 +0.965000 0.004084 +0.970000 0.003829 +0.975000 0.003436 +0.980000 0.003042 +0.990000 0.001910 +1.000000 0.000000 diff --git a/tests/unit_tests/external/5MW_Baseline/NRELOffshrBsline5MW_AeroDyn_blade.dat b/tests/unit_tests/external/5MW_Baseline/NRELOffshrBsline5MW_AeroDyn_blade.dat new file mode 100644 index 00000000..afef3475 --- /dev/null +++ b/tests/unit_tests/external/5MW_Baseline/NRELOffshrBsline5MW_AeroDyn_blade.dat @@ -0,0 +1,28 @@ +------- AERODYN v15.00.* BLADE DEFINITION INPUT FILE ------------------------------------- +NREL 5.0 MW offshore baseline aerodynamic blade input properties; note that we need to add the aerodynamic center to this file +====== Blade Properties ================================================================= + 19 NumBlNds - Number of blade nodes used in the analysis (-) + BlSpn BlCrvAC BlSwpAC BlCrvAng BlTwist BlChord BlAFID BlCb BlCenBn BlCenBt + (m) (m) (m) (deg) (deg) (m) (-) (-) (m) (m) +0.0000000E+00 0.0000000E+00 0.0000000E+00 0.0000000E+00 1.3308000E+01 3.5420000E+00 1 0.0 0.0 0.0 +1.3667000E+00 -8.1531745E-04 -3.4468858E-03 0.0000000E+00 1.3308000E+01 3.5420000E+00 1 0.0 0.0 0.0 +4.1000000E+00 -2.4839790E-02 -1.0501421E-01 0.0000000E+00 1.3308000E+01 3.8540000E+00 1 0.0 0.0 0.0 +6.8333000E+00 -5.9469375E-02 -2.5141635E-01 0.0000000E+00 1.3308000E+01 4.1670000E+00 2 0.0 0.0 0.0 +1.0250000E+01 -1.0909141E-01 -4.6120149E-01 0.0000000E+00 1.3308000E+01 4.5570000E+00 3 0.0 0.0 0.0 +1.4350000E+01 -1.1573354E-01 -5.6986665E-01 0.0000000E+00 1.1480000E+01 4.6520000E+00 4 0.0 0.0 0.0 +1.8450000E+01 -9.8316709E-02 -5.4850833E-01 0.0000000E+00 1.0162000E+01 4.4580000E+00 4 0.0 0.0 0.0 +2.2550000E+01 -8.3186967E-02 -5.2457001E-01 0.0000000E+00 9.0110000E+00 4.2490000E+00 5 0.0 0.0 0.0 +2.6650000E+01 -6.7933232E-02 -4.9624675E-01 0.0000000E+00 7.7950000E+00 4.0070000E+00 6 0.0 0.0 0.0 +3.0750000E+01 -5.3393159E-02 -4.6544755E-01 0.0000000E+00 6.5440000E+00 3.7480000E+00 6 0.0 0.0 0.0 +3.4850000E+01 -4.0899260E-02 -4.3583519E-01 0.0000000E+00 5.3610000E+00 3.5020000E+00 7 0.0 0.0 0.0 +3.8950000E+01 -2.9722933E-02 -4.0591323E-01 0.0000000E+00 4.1880000E+00 3.2560000E+00 7 0.0 0.0 0.0 +4.3050000E+01 -2.0511081E-02 -3.7569051E-01 0.0000000E+00 3.1250000E+00 3.0100000E+00 8 0.0 0.0 0.0 +4.7150000E+01 -1.3980013E-02 -3.4521705E-01 0.0000000E+00 2.3190000E+00 2.7640000E+00 8 0.0 0.0 0.0 +5.1250000E+01 -8.3819737E-03 -3.1463837E-01 0.0000000E+00 1.5260000E+00 2.5180000E+00 8 0.0 0.0 0.0 +5.4666700E+01 -4.3546914E-03 -2.8909220E-01 0.0000000E+00 8.6300000E-01 2.3130000E+00 8 0.0 0.0 0.0 +5.7400000E+01 -1.6838383E-03 -2.6074456E-01 0.0000000E+00 3.7000000E-01 2.0860000E+00 8 0.0 0.0 0.0 +6.0133300E+01 -3.2815226E-04 -1.7737470E-01 0.0000000E+00 1.0600000E-01 1.4190000E+00 8 0.0 0.0 0.0 +6.1499900E+01 -3.2815226E-04 -1.7737470E-01 0.0000000E+00 1.0600000E-01 1.4190000E+00 8 0.0 0.0 0.0 + +!bjj: because of precision in the BD-AD coupling, 61.5m didn't work, so I changed it to 61.4999m +6.1500000E+01 -3.2815226E-04 -1.7737470E-01 0.0000000E+00 1.0600000E-01 1.4190000E+00 8 0.0 0.0 0.0 diff --git a/tests/unit_tests/external/CMakeLists.txt b/tests/unit_tests/external/CMakeLists.txt index 83e815b6..902d37ce 100644 --- a/tests/unit_tests/external/CMakeLists.txt +++ b/tests/unit_tests/external/CMakeLists.txt @@ -1,5 +1,6 @@ -# Specify the ROSCO controller test source files for the unit test executable -if (OpenTurbine_BUILD_ROSCO_CONTROLLER) +# Test ROSCO controller +if (OpenTurbine_BUILD_ROSCO_CONTROLLER AND + (NOT OpenTurbine_ENABLE_SANITIZER_LEAK)) target_sources( openturbine_unit_tests PRIVATE @@ -7,4 +8,12 @@ if (OpenTurbine_BUILD_ROSCO_CONTROLLER) ) endif() - +# Test AeroDyn Inflow +if (OpenTurbine_BUILD_OPENFAST_ADI AND + (NOT OpenTurbine_ENABLE_SANITIZER_LEAK)) + target_sources( + openturbine_unit_tests + PRIVATE + test_aerodyn_inflow.cpp + ) +endif() diff --git a/tests/unit_tests/external/ad_primary.dat b/tests/unit_tests/external/ad_primary.dat new file mode 100644 index 00000000..79c80a8c --- /dev/null +++ b/tests/unit_tests/external/ad_primary.dat @@ -0,0 +1,185 @@ +------- AERODYN INPUT FILE -------------------------------------------------------------------------- +NREL 5.0 MW offshore baseline aerodynamic input properties. +====== General Options ============================================================================ +False Echo - Echo the input to ".AD.ech"? (flag) +"default" DTAero - Time interval for aerodynamic calculations {or "default"} (s) +1 Wake_Mod - Wake/induction model (switch) {0=none, 1=BEMT, 3=OLAF} [Wake_Mod cannot be 2 or 3 when linearizing] +0 TwrPotent - Type tower influence on wind based on potential flow around the tower (switch) {0=none, 1=baseline potential flow, 2=potential flow with Bak correction} +0 TwrShadow - Calculate tower influence on wind based on downstream tower shadow (switch) {0=none, 1=Powles model, 2=Eames model} +False TwrAero - Calculate tower aerodynamic loads? (flag) +False CavitCheck - Perform cavitation check? (flag) [UA_Mod must be 0 when CavitCheck=true] +False Buoyancy - Include buoyancy effects? (flag) +False NacelleDrag - Include Nacelle Drag effects? (flag) +False CompAA - Flag to compute AeroAcoustics calculation [used only when Wake_Mod = 1 or 2] +"unused" AA_InputFile - AeroAcoustics input file [used only when CompAA=true] +====== Environmental Conditions =================================================================== +"default" AirDens - Air density (kg/m^3) +"default" KinVisc - Kinematic viscosity of working fluid (m^2/s) +"default" SpdSound - Speed of sound in working fluid (m/s) +"default" Patm - Atmospheric pressure (Pa) [used only when CavitCheck=True] +"default" Pvap - Vapour pressure of working fluid (Pa) [used only when CavitCheck=True] +====== Blade-Element/Momentum Theory Options ====================================================== [unused when Wake_Mod=0 or 3, except for BEM_Mod] +1 BEM_Mod - BEM model {1=legacy NoSweepPitchTwist, 2=polar} (switch) [used for all Wake_Mod to determine output coordinate system] +--- Skew correction +0 Skew_Mod - Skew model {0=No skew model, -1=Remove non-normal component for linearization, 1=skew model active} +False SkewMomCorr - Turn the skew momentum correction on or off [used only when Skew_Mod=1] +default SkewRedistr_Mod - Type of skewed-wake correction model (switch) {0=no redistribution, 1=Glauert/Pitt/Peters, default=1} [used only when Skew_Mod=1] +"default" SkewRedistrFactor - Constant used in Pitt/Peters skewed wake model {or "default" is 15/32*pi} (-) [used only when Skew_Mod=1 and SkewRedistr_Mod=1] +--- BEM algorithm +True TipLoss - Use the Prandtl tip-loss model? (flag) [unused when Wake_Mod=0 or 3] +True HubLoss - Use the Prandtl hub-loss model? (flag) [unused when Wake_Mod=0 or 3] +True TanInd - Include tangential induction in BEMT calculations? (flag) [unused when Wake_Mod=0 or 3] +False AIDrag - Include the drag term in the axial-induction calculation? (flag) [unused when Wake_Mod=0 or 3] +False TIDrag - Include the drag term in the tangential-induction calculation? (flag) [unused when Wake_Mod=0,3 or TanInd=FALSE] +"Default" IndToler - Convergence tolerance for BEMT nonlinear solve residual equation {or "default"} (-) [unused when Wake_Mod=0 or 3] +100 MaxIter - Maximum number of iteration steps (-) [unused when Wake_Mod=0] +--- Shear correction +False SectAvg - Use sector averaging (flag) +1 SectAvgWeighting - Weighting function for sector average {1=Uniform, default=1} within a sector centered on the blade (switch) [used only when SectAvg=True] +default SectAvgNPoints - Number of points per sectors (-) {default=5} [used only when SectAvg=True] +default SectAvgPsiBwd - Backward azimuth relative to blade where the sector starts (<=0) {default=-60} (deg) [used only when SectAvg=True] +default SectAvgPsiFwd - Forward azimuth relative to blade where the sector ends (>=0) {default=60} (deg) [used only when SectAvg=True] +--- Dynamic wake/inflow +0 DBEMT_Mod - Type of dynamic BEMT (DBEMT) model {0=No Dynamic Wake, -1=Frozen Wake for linearization, 1:constant tau1, 2=time-dependent tau1, 3=constant tau1 with continuous formulation} (-) +4 tau1_const - Time constant for DBEMT (s) [used only when DBEMT_Mod=1 or 3] +====== OLAF -- cOnvecting LAgrangian Filaments (Free Vortex Wake) Theory Options ================== [used only when Wake_Mod=3] +"unused" OLAFInputFileName - Input file for OLAF [used only when Wake_Mod=3] +====== Unsteady Airfoil Aerodynamics Options ==================================================== +False AoA34 - Sample the angle of attack (AoA) at the 3/4 chord or the AC point {default=True} [always used] +0 UA_Mod - Unsteady Aero Model Switch (switch) {0=Quasi-steady (no UA), 2=B-L Gonzalez, 3=B-L Minnema/Pierce, 4=B-L HGM 4-states, 5=B-L HGM+vortex 5 states, 6=Oye, 7=Boeing-Vertol} +True FLookup - Flag to indicate whether a lookup for f' will be calculated (TRUE) or whether best-fit exponential equations will be used (FALSE); if FALSE S1-S4 must be provided in airfoil input files (flag) [used only when AFAeroMod=2] +3 IntegrationMethod - Switch to indicate which integration method UA uses (1=RK4, 2=AB4, 3=ABM4, 4=BDF2) +0 UAStartRad - Starting radius for dynamic stall (fraction of rotor radius [0.0,1.0]) [used only when UA_Mod>0; if line is missing UAStartRad=0] +1 UAEndRad - Ending radius for dynamic stall (fraction of rotor radius [0.0,1.0]) [used only when UA_Mod>0; if line is missing UAEndRad=1] +====== Airfoil Information ========================================================================= +1 AFTabMod - Interpolation method for multiple airfoil tables {1=1D interpolation on AoA (first table only); 2=2D interpolation on AoA and Re; 3=2D interpolation on AoA and UserProp} (-) +1 InCol_Alfa - The column in the airfoil tables that contains the angle of attack (-) +2 InCol_Cl - The column in the airfoil tables that contains the lift coefficient (-) +3 InCol_Cd - The column in the airfoil tables that contains the drag coefficient (-) +4 InCol_Cm - The column in the airfoil tables that contains the pitching-moment coefficient; use zero if there is no Cm column (-) +0 InCol_Cpmin - The column in the airfoil tables that contains the Cpmin coefficient; use zero if there is no Cpmin column (-) +8 NumAFfiles - Number of airfoil files used (-) +"5MW_Baseline/Airfoils/Cylinder1.dat" AFNames - Airfoil file names (NumAFfiles lines) (quoted strings) +"5MW_Baseline/Airfoils/Cylinder2.dat" +"5MW_Baseline/Airfoils/DU40_A17.dat" +"5MW_Baseline/Airfoils/DU35_A17.dat" +"5MW_Baseline/Airfoils/DU30_A17.dat" +"5MW_Baseline/Airfoils/DU25_A17.dat" +"5MW_Baseline/Airfoils/DU21_A17.dat" +"5MW_Baseline/Airfoils/NACA64_A17.dat" +====== Rotor/Blade Properties ===================================================================== +True UseBlCm - Include aerodynamic pitching moment in calculations? (flag) +"5MW_Baseline/NRELOffshrBsline5MW_AeroDyn_blade.dat" ADBlFile(1) - Name of file containing distributed aerodynamic properties for Blade #1 (-) +"5MW_Baseline/NRELOffshrBsline5MW_AeroDyn_blade.dat" ADBlFile(2) - Name of file containing distributed aerodynamic properties for Blade #2 (-) [unused if NumBl < 2] +"5MW_Baseline/NRELOffshrBsline5MW_AeroDyn_blade.dat" ADBlFile(3) - Name of file containing distributed aerodynamic properties for Blade #3 (-) [unused if NumBl < 3] +====== Hub Properties ============================================================================== [used only when Buoyancy=True] +0.0 VolHub - Hub volume (m^3) +0.0 HubCenBx - Hub center of buoyancy x direction offset (m) +====== Nacelle Properties ========================================================================== [used only when Buoyancy=True or NacelleDrag=True] +0.0 VolNac - Nacelle volume (m^3) +0.0, 0.0, 0.0 NacCenB - Position of nacelle center of buoyancy from yaw bearing in nacelle coordinates (m) +0, 0, 0 NacArea - Projected area of the nacelle in X, Y, Z in the nacelle coordinate system (m^2) +0, 0, 0 NacCd - Drag coefficient for the nacelle areas defined above (-) +0, 0, 0 NacDragAC - Position of aerodynamic center of nacelle drag in nacelle coordinates (m) +====== Tail Fin Aerodynamics ======================================================================= +False TFinAero - Calculate tail fin aerodynamics model (flag) +"unused" TFinFile - Input file for tail fin aerodynamics [used only when TFinAero=True] +====== Tower Influence and Aerodynamics ============================================================ [used only when TwrPotent/=0, TwrShadow/=0, TwrAero=True, or Buoyancy=True] +11 NumTwrNds - Number of tower nodes used in the analysis (-) [used only when TwrPotent/=0, TwrShadow/=0, TwrAero=True, or Buoyancy=True] +TwrElev TwrDiam TwrCd TwrTI TwrCb ! TwrTI used only when TwrShadow=2; TwrCb used only when Buoyancy=True +(m) (m) (-) (-) (-) +1.0000000E+01 6.5000000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +1.7760000E+01 6.2400000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +2.5520000E+01 5.9700000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +3.3280000E+01 5.7100000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +4.1040000E+01 5.4500000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +4.8800000E+01 5.1800000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +5.6560000E+01 4.9200000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +6.4320000E+01 4.6600000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +7.2080000E+01 4.4000000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +7.9840000E+01 4.1300000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +8.7600000E+01 3.8700000E+00 1.0000000E+00 1.0000000E-01 0.0000000E+00 +====== Outputs ==================================================================================== +True SumPrint - Generate a summary file listing input options and interpolated properties to ".AD.sum"? (flag) +3 NBlOuts - Number of blade node outputs [0 - 9] (-) +1, 9, 19 BlOutNd - Blade nodes whose values will be output (-) +0 NTwOuts - Number of tower node outputs [0 - 9] (-) +1, 2, 6 TwOutNd - Tower nodes whose values will be output (-) + OutList - The next line(s) contains a list of output parameters. See OutListParameters.xlsx for a listing of available output channels, (-) +"RtAeroCp" +"RtAeroCt" +"RtAeroCq" +"RtTSR" +"RtSpeed, RtSkew, B1Azimuth" +"B1N1VDisx, B1N1VDisy, B1N1VDisz" ! disturbed wind velocity at Blade 1, Node 1 +"B1N2VDisx, B1N2VDisy, B1N2VDisz" ! disturbed wind velocity at Blade 1, Node 2 +"B1N3VDisx, B1N3VDisy, B1N3VDisz" ! disturbed wind velocity at Blade 1, Node 3 +"B1N1STVx, B1N1STVy, B1N1STVz" ! structural translational velocity at Blade 1, Node 1 +"B1N2STVx, B1N2STVy, B1N2STVz" ! structural translational velocity at Blade 1, Node 2 +"B1N3STVx, B1N3STVy, B1N3STVz" ! structural translational velocity at Blade 1, Node 2 +"B1N1AxInd, B1N2AxInd, B1N3AxInd" +"B1N1Alpha, B1N2Alpha, B1N3Alpha" +"B1N1Theta, B1N2Theta, B1N3Theta" +END of input file (the word "END" must appear in the first 3 columns of this last OutList line) + + +#### The following was for testing, but bloats the regression test case too much +#---------------------- NODE OUTPUTS -------------------------------------------- +#3 BldNd_BladesOut - Number of blades to output all node information at. Up to number of blades on turbine. (-) +#ALL BldNd_BlOutNd - Specify a portion of the nodes to output. {"ALL", "Tip", "Root", or a list of node numbers} (-) + # OutList - The next line(s) contains a list of output parameters. See OutListParameters.xlsx, AeroDyn_Nodes tab for a listing of available output channels, (-) +#"VUndx" - x-component of undisturbed wind velocity at each node +#"VUndy" - y-component of undisturbed wind velocity at each node +#"VUndz" - z-component of undisturbed wind velocity at each node +#"VDisx" - x-component of disturbed wind velocity at each node +#"VDisy" - y-component of disturbed wind velocity at each node +#"VDisz" - z-component of disturbed wind velocity at each node +#"STVx" - x-component of structural translational velocity at each node +#"STVy" - y-component of structural translational velocity at each node +#"STVz" - z-component of structural translational velocity at each node +#"VRel" - Relvative wind speed at each node +#"DynP" - Dynamic pressure at each node +#"Re" - Reynolds number (in millions) at each node +#"M" - Mach number at each node +#"Vindx" - Axial induced wind velocity at each node +#"Vindy" - Tangential induced wind velocity at each node +#"AxInd" - Axial induction factor at each node +#"TnInd" - Tangential induction factor at each node +#"Alpha" - Angle of attack at each node +#"Theta" - Pitch+Twist angle at each node +#"Phi" - Inflow angle at each node +#"Curve" - Curvature angle at each node +#"Cl" - Lift force coefficient at each node +#"Cd" - Drag force coefficient at each node +#"Cm" - Pitching moment coefficient at each node +#"Cx" - Normal force (to plane) coefficient at each node +#"Cy" - Tangential force (to plane) coefficient at each node +#"Cn" - Normal force (to chord) coefficient at each node +#"Ct" - Tangential force (to chord) coefficient at each node +#"Fl" - Lift force per unit length at each node +#"Fd" - Drag force per unit length at each node +#"Mm" - Pitching moment per unit length at each node +#"Fx" - Normal force (to plane) per unit length at each node +#"Fy" - Tangential force (to plane) per unit length at each node +#"Fn" - Normal force (to chord) per unit length at each node +#"Ft" - Tangential force (to chord) per unit length at each node +#"Clrnc" - Tower clearance at each node (based on the absolute distance to the nearest point in the tower from blade node B#N# minus the local tower radius, in the deflected configuration); please note that this clearance is only approximate because the calculation assumes that the blade is a line with no volume (however, the calculation does use the local tower radius); when blade node B#N# is above the tower top (or below the tower base), the absolute distance to the tower top (or base) minus the local tower radius, in the deflected configuration, is output +#"Vx" - Local axial velocity +#"Vy" - Local tangential velocity +#"GeomPhi" - Geometric phi? If phi was solved using normal BEMT equations, GeomPhi = 1; otherwise, if it was solved geometrically, GeomPhi = 0. +#"Chi" - Skew angle (used in skewed wake correction) -- not available for OLAF +#"UA_Flag" - Flag indicating if UA is turned on for this node. -- not available for OLAF +#"CpMin" - Pressure coefficient +#"SgCav" - Cavitation number +#"SigCr" - Critical cavitation number +#"Gam" - Gamma -- circulation on blade +#"Cl_Static" - Static portion of lift force coefficient at each node, without unsteady effects -- not available for BEMT/DBEMT +#"Cd_Static" - Static portion of drag force coefficient at each node, without unsteady effects -- not available for BEMT/DBEMT +#"Cm_Static" - Static portion of pitching moment coefficient at each node, without unsteady effects -- not available for BEMT/DBEMT +#"Uin" - Axial induced velocity in rotating hub coordinates. Axial aligned with hub axis. rotor plane polar hub rotating coordinates +#"Uit" - Tangential induced velocity in rotating hub coordinates. Tangential to the rotation plane. Perpendicular to blade aziumth. rotor plane polar hub rotating coordinates +#"Uir" - Radial induced velocity in rotating hub coordinates. Radial outwards in rotation plane. Aligned with blade azimuth. rotor plane polar hub rotating coordinates +#END of input file (the word "END" must appear in the first 3 columns of this last OutList line) +#--------------------------------------------------------------------------------------- +END of input file (the word "END" must appear in the first 3 columns of this last OutList line) +--------------------------------------------------------------------------------------- diff --git a/tests/unit_tests/external/ifw_primary.dat b/tests/unit_tests/external/ifw_primary.dat new file mode 100644 index 00000000..3ab9b34d --- /dev/null +++ b/tests/unit_tests/external/ifw_primary.dat @@ -0,0 +1,71 @@ +------- InflowWind INPUT FILE ------------------------------------------------------------------------- +Steady 8 m/s winds with no shear for FAST CertTests #20 and #25 +--------------------------------------------------------------------------------------------------------------- +False Echo - Echo input data to .ech (flag) + 1 WindType - switch for wind file type (1=steady; 2=uniform; 3=binary TurbSim FF; 4=binary Bladed-style FF; 5=HAWC format; 6=User defined; 7=native Bladed FF) + 0 PropagationDir - Direction of wind propagation (meteorological rotation from aligned with X (positive rotates towards -Y) -- degrees) (not used for native Bladed format WindType=7) + 0 VFlowAng - Upflow angle (degrees) (not used for native Bladed format WindType=7) + False VelInterpCubic - Use cubic interpolation for velocity in time (false=linear, true=cubic) [Used with WindType=2,3,4,5,7] + 1 NWindVel - Number of points to output the wind velocity (0 to 9) + 0 WindVxiList - List of coordinates in the inertial X direction (m) + 0 WindVyiList - List of coordinates in the inertial Y direction (m) + 90 WindVziList - List of coordinates in the inertial Z direction (m) +================== Parameters for Steady Wind Conditions [used only for WindType = 1] ========================= + 8 HWindSpeed - Horizontal wind speed (m/s) + 90 RefHt - Reference height for horizontal wind speed (m) + 0 PLExp - Power law exponent (-) +================== Parameters for Uniform wind file [used only for WindType = 2] ============================ +"unused" Filename_Uni - Filename of time series data for uniform wind field. (-) + 90 RefHt_Uni - Reference height for horizontal wind speed (m) + 125.88 RefLength - Reference length for linear horizontal and vertical sheer (-) +================== Parameters for Binary TurbSim Full-Field files [used only for WindType = 3] ============== +"unused" FileName_BTS - Name of the Full field wind file to use (.bts) +================== Parameters for Binary Bladed-style Full-Field files [used only for WindType = 4 or WindType = 7] ========= +"unused" FileNameRoot - WindType=4: Rootname of the full-field wind file to use (.wnd, .sum); WindType=7: name of the intermediate file with wind scaling values +False TowerFile - Have tower file (.twr) (flag) ignored when WindType = 7 +================== Parameters for HAWC-format binary files [Only used with WindType = 5] ===================== +"wasp\Output\basic_5u.bin" FileName_u - name of the file containing the u-component fluctuating wind (.bin) +"wasp\Output\basic_5v.bin" FileName_v - name of the file containing the v-component fluctuating wind (.bin) +"wasp\Output\basic_5w.bin" FileName_w - name of the file containing the w-component fluctuating wind (.bin) + 64 nx - number of grids in the x direction (in the 3 files above) (-) + 32 ny - number of grids in the y direction (in the 3 files above) (-) + 32 nz - number of grids in the z direction (in the 3 files above) (-) + 16 dx - distance (in meters) between points in the x direction (m) + 3 dy - distance (in meters) between points in the y direction (m) + 3 dz - distance (in meters) between points in the z direction (m) + 90 RefHt_Hawc - reference height; the height (in meters) of the vertical center of the grid (m) + ------------- Scaling parameters for turbulence --------------------------------------------------------- + 1 ScaleMethod - Turbulence scaling method [0 = none, 1 = direct scaling, 2 = calculate scaling factor based on a desired standard deviation] + 1 SFx - Turbulence scaling factor for the x direction (-) [ScaleMethod=1] + 1 SFy - Turbulence scaling factor for the y direction (-) [ScaleMethod=1] + 1 SFz - Turbulence scaling factor for the z direction (-) [ScaleMethod=1] + 12 SigmaFx - Turbulence standard deviation to calculate scaling from in x direction (m/s) [ScaleMethod=2] + 8 SigmaFy - Turbulence standard deviation to calculate scaling from in y direction (m/s) [ScaleMethod=2] + 2 SigmaFz - Turbulence standard deviation to calculate scaling from in z direction (m/s) [ScaleMethod=2] + ------------- Mean wind profile parameters (added to HAWC-format files) --------------------------------- + 5 URef - Mean u-component wind speed at the reference height (m/s) + 2 WindProfile - Wind profile type (0=constant;1=logarithmic,2=power law) + 0 PLExp_Hawc - Power law exponent (-) (used for PL wind profile type only) + 0.03 Z0 - Surface roughness length (m) (used for LG wind profile type only) + 0 XOffset - Initial offset in +x direction (shift of wind box) +================== LIDAR Parameters =========================================================================== + 0 SensorType - Switch for lidar configuration (0 = None, 1 = Single Point Beam(s), 2 = Continuous, 3 = Pulsed) + 0 NumPulseGate - Number of lidar measurement gates (used when SensorType = 3) + 30 PulseSpacing - Distance between range gates (m) (used when SensorType = 3) + 0 NumBeam - Number of lidar measurement beams (0-5)(used when SensorType = 1) + -200 FocalDistanceX - Focal distance co-ordinates of the lidar beam in the x direction (relative to hub height) (only first coordinate used for SensorType 2 and 3) (m) + 0 FocalDistanceY - Focal distance co-ordinates of the lidar beam in the y direction (relative to hub height) (only first coordinate used for SensorType 2 and 3) (m) + 0 FocalDistanceZ - Focal distance co-ordinates of the lidar beam in the z direction (relative to hub height) (only first coordinate used for SensorType 2 and 3) (m) +0.0 0.0 0.0 RotorApexOffsetPos - Offset of the lidar from hub height (m) + 17 URefLid - Reference average wind speed for the lidar[m/s] + 0.25 MeasurementInterval - Time between each measurement [s] + False LidRadialVel - TRUE => return radial component, FALSE => return 'x' direction estimate + 1 ConsiderHubMotion - Flag whether to consider the hub motion's impact on Lidar measurements +====================== OUTPUT ================================================== +False SumPrint - Print summary data to .IfW.sum (flag) + OutList - The next line(s) contains a list of output parameters. See OutListParameters.xlsx for a listing of available output channels, (-) +"Wind1VelX" X-direction wind velocity at point WindList(1) +"Wind1VelY" Y-direction wind velocity at point WindList(1) +"Wind1VelZ" Z-direction wind velocity at point WindList(1) +END of input file (the word "END" must appear in the first 3 columns of this last OutList line) +--------------------------------------------------------------------------------------- diff --git a/tests/unit_tests/regression/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp similarity index 97% rename from tests/unit_tests/regression/test_aerodyn_inflow.cpp rename to tests/unit_tests/external/test_aerodyn_inflow.cpp index d4fc37b7..7a46bf01 100644 --- a/tests/unit_tests/regression/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -1,11 +1,8 @@ -#include #include -#include "test_utilities.hpp" - #include "src/utilities/aerodynamics/aerodyn_inflow.hpp" -#include "src/vendor/dylib/dylib.hpp" +#include "tests/unit_tests/regression/test_utilities.hpp" namespace openturbine::tests { @@ -505,15 +502,8 @@ TEST(AerodynInflowTest, JoinStringArray_NullCharacters) { std::string GetSharedLibraryPath() { const std::filesystem::path project_root = FindProjectRoot(); const std::filesystem::path full_path = - project_root / "build/tests/unit_tests/libaerodyn_inflow_c_binding"; - -#ifdef __APPLE__ - return full_path.string() + ".dylib"; -#elif __linux__ - return full_path.string() + ".so"; -#else // Windows - return full_path.string() + ".dll"; -#endif + project_root / "build/tests/unit_tests/aerodyn_inflow_c_binding.dll"; + return full_path.string(); } TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { @@ -582,9 +572,7 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { // Initialize with input files const std::filesystem::path project_root = FindProjectRoot(); - std::filesystem::path input_path = project_root / - "build/external/OpenFAST_ADI/src/OpenFAST_ADI/reg_tests/" - "r-test/modules/aerodyn/py_ad_5MW_OC4Semi_WSt_WavesWN/"; + std::filesystem::path input_path = project_root / "tests/unit_tests/external/"; std::vector adiAD_input_string_array = {(input_path / "ad_primary.dat").string()}; std::vector adiIfW_input_string_array = {(input_path / "ifw_primary.dat").string()}; diff --git a/tests/unit_tests/regression/CMakeLists.txt b/tests/unit_tests/regression/CMakeLists.txt index 79355acf..fdbaac59 100644 --- a/tests/unit_tests/regression/CMakeLists.txt +++ b/tests/unit_tests/regression/CMakeLists.txt @@ -11,5 +11,4 @@ target_sources( test_utilities.cpp test_controller.cpp test_yaml_parser.cpp - test_aerodyn_inflow.cpp ) From 289aca59756fa7f9b345c16dd8a7315c392ef55a Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Wed, 2 Oct 2024 18:40:32 -0600 Subject: [PATCH 65/87] Refactor MeshMotionData -> MeshData to include loads vector at each node of the mesh --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 57 +++++---- .../external/test_aerodyn_inflow.cpp | 118 ++++++++---------- 2 files changed, 89 insertions(+), 86 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index eb6ce0d4..3193e74c 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -261,34 +261,49 @@ struct StructuralMesh { }; /** - * @brief Struct to hold the motion data of any structural mesh component + * @brief Struct to hold the motion + loads data of any structural mesh component * * @details This struct holds the motion data (i.e. position, orientation, - * velocity, and acceleration) of the structural mesh, which can be the hub, nacelle, root, or - * mesh points/nodes. + * velocity, and acceleration) and loads of the structural mesh, which can be + * the hub, nacelle, root, or mesh points/nodes */ -struct MeshMotionData { +struct MeshData { + size_t n_mesh_points; //< Number of mesh points (nodes) std::vector> position; //< N x 3 array [x, y, z] std::vector> orientation; //< N x 9 array [r11, r12, ..., r33] std::vector> velocity; //< N x 6 array [u, v, w, p, q, r] std::vector> acceleration; //< N x 6 array [u_dot, v_dot, w_dot, p_dot, q_dot, r_dot] - - /// Default constructor - MeshMotionData() = default; - - /// Constructor to initialize all data based on provided inputs - MeshMotionData( - const std::vector>& mesh_data, - const std::vector>& mesh_velocities, - const std::vector>& mesh_accelerations, size_t n_mesh_points = 1 + std::vector> loads; //< N x 6 array [Fx, Fy, Fz, Mx, My, Mz] + + /// Constructor to initialize all mesh data to zero based on provided number of nodes + MeshData(size_t n_nodes) + : n_mesh_points(n_nodes), + position(std::vector>(n_nodes, {0.f, 0.f, 0.f})), + orientation( + std::vector>(n_nodes, {0., 0., 0., 0., 0., 0., 0., 0., 0.}) + ), + velocity(std::vector>(n_nodes, {0.f, 0.f, 0.f, 0.f, 0.f, 0.f})), + acceleration(std::vector>(n_nodes, {0.f, 0.f, 0.f, 0.f, 0.f, 0.f})), + loads(std::vector>(n_nodes, {0.f, 0.f, 0.f, 0.f, 0.f, 0.f})) {} + + /// Constructor to initialize all mesh data based on provided inputs + MeshData( + size_t n_mesh_points, const std::vector>& mesh_data, + const std::vector>& velocities, + const std::vector>& accelerations, + const std::vector>& loads ) - : position(n_mesh_points), - orientation(n_mesh_points), - velocity(std::move(mesh_velocities)), - acceleration(std::move(mesh_accelerations)) { - if (mesh_data.size() != n_mesh_points || mesh_velocities.size() != n_mesh_points || - mesh_accelerations.size() != n_mesh_points) { + : n_mesh_points(n_mesh_points), + position(std::vector>(n_mesh_points, {0.f, 0.f, 0.f})), + orientation( + std::vector>(n_mesh_points, {0., 0., 0., 0., 0., 0., 0., 0., 0.}) + ), + velocity(std::move(velocities)), + acceleration(std::move(accelerations)), + loads(std::move(loads)) { + if (mesh_data.size() != n_mesh_points || velocities.size() != n_mesh_points || + accelerations.size() != n_mesh_points || loads.size() != n_mesh_points) { throw std::invalid_argument("Input vector sizes must match n_mesh_points"); } @@ -768,8 +783,8 @@ class AeroDynInflowLibrary { * @param mesh_motion Motion data for the mesh points */ void SetupRotorMotion( - int turbine_number, MeshMotionData hub_motion, MeshMotionData nacelle_motion, - MeshMotionData root_motion, MeshMotionData mesh_motion + int turbine_number, MeshData hub_motion, MeshData nacelle_motion, MeshData root_motion, + MeshData mesh_motion ) { auto ADI_C_SetRotorMotion = lib_.get_function< void(int*, float*, double*, float*, float*, float*, double*, float*, float*, float*, double*, float*, float*, int*, float*, double*, float*, float*, int*, char*)>( diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index 7a46bf01..695d2a7a 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -244,51 +244,73 @@ TEST(AerodynInflowTest, StructuralMesh_Validate_MismatchedBladeNumbers) { EXPECT_THROW(structural_mesh.Validate(), std::invalid_argument); } -TEST(AerodynInflowTest, MeshMotionData_Default) { - util::MeshMotionData mesh_motion_data; - EXPECT_EQ(mesh_motion_data.position.size(), 0); - EXPECT_EQ(mesh_motion_data.orientation.size(), 0); - EXPECT_EQ(mesh_motion_data.velocity.size(), 0); - EXPECT_EQ(mesh_motion_data.acceleration.size(), 0); -} - -TEST(AerodynInflowTest, MeshMotionData_Set) { - std::vector> mesh_data = {{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; - std::vector> mesh_velocities = {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; - std::vector> mesh_accelerations = {{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; - util::MeshMotionData mesh_motion_data(mesh_data, mesh_velocities, mesh_accelerations, 1); - +TEST(AerodynInflowTest, MeshData_Constructor_NumberOfNodes) { + util::MeshData mesh_motion_data{1}; + EXPECT_EQ(mesh_motion_data.n_mesh_points, 1); EXPECT_EQ(mesh_motion_data.position.size(), 1); EXPECT_EQ(mesh_motion_data.orientation.size(), 1); EXPECT_EQ(mesh_motion_data.velocity.size(), 1); EXPECT_EQ(mesh_motion_data.acceleration.size(), 1); + EXPECT_EQ(mesh_motion_data.loads.size(), 1); +} + +TEST(AerodynInflowTest, MeshData_Constructor_Data) { + size_t n_mesh_points{1}; + std::vector> mesh_data{{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; + std::vector> mesh_velocities{{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; + std::vector> mesh_accelerations{{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + std::vector> mesh_loads = {{13.f, 14.f, 15.f, 16.f, 17.f, 18.f}}; + util::MeshData mesh_motion_data( + n_mesh_points, mesh_data, mesh_velocities, mesh_accelerations, mesh_loads + ); + + EXPECT_EQ(mesh_motion_data.n_mesh_points, n_mesh_points); + EXPECT_EQ(mesh_motion_data.position.size(), n_mesh_points); + EXPECT_EQ(mesh_motion_data.orientation.size(), n_mesh_points); + EXPECT_EQ(mesh_motion_data.velocity.size(), n_mesh_points); + EXPECT_EQ(mesh_motion_data.acceleration.size(), n_mesh_points); + EXPECT_EQ(mesh_motion_data.loads.size(), n_mesh_points); ExpectArrayNear(mesh_motion_data.position[0], {1.f, 2.f, 3.f}); ExpectArrayNear(mesh_motion_data.orientation[0], {1., 0., 0., 0., 0., -1., 0., 1., 0.}); ExpectArrayNear(mesh_motion_data.velocity[0], {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}); ExpectArrayNear(mesh_motion_data.acceleration[0], {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}); + ExpectArrayNear(mesh_motion_data.loads[0], {13.f, 14.f, 15.f, 16.f, 17.f, 18.f}); } -TEST(AerodynInflowTest, MeshMotionData_CheckArraySize_NoThrow) { - std::vector> mesh_data = {{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; - std::vector> mesh_velocities = {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; - std::vector> mesh_accelerations = {{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; - util::MeshMotionData mesh_motion_data(mesh_data, mesh_velocities, mesh_accelerations, 1); +TEST(AerodynInflowTest, MeshData_CheckArraySize_NoThrow) { + size_t n_mesh_points{1}; + std::vector> mesh_data{{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; + std::vector> mesh_velocities{{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; + std::vector> mesh_accelerations{{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + std::vector> mesh_loads{{13.f, 14.f, 15.f, 16.f, 17.f, 18.f}}; + util::MeshData mesh_motion_data( + n_mesh_points, mesh_data, mesh_velocities, mesh_accelerations, mesh_loads + ); - mesh_motion_data.CheckArraySize(mesh_motion_data.position, 1, 3, "position", "mesh motion data"); mesh_motion_data.CheckArraySize( - mesh_motion_data.orientation, 1, 9, "orientation", "mesh motion data" + mesh_motion_data.position, n_mesh_points, 3, "position", "mesh motion data" + ); + mesh_motion_data.CheckArraySize( + mesh_motion_data.orientation, n_mesh_points, 9, "orientation", "mesh motion data" ); mesh_motion_data.CheckArraySize(mesh_motion_data.velocity, 1, 6, "velocity", "mesh motion data"); mesh_motion_data.CheckArraySize( - mesh_motion_data.acceleration, 1, 6, "acceleration", "mesh motion data" + mesh_motion_data.acceleration, n_mesh_points, 6, "acceleration", "mesh motion data" + ); + mesh_motion_data.CheckArraySize( + mesh_motion_data.loads, n_mesh_points, 6, "loads", "mesh motion data" ); } -TEST(AerodynInflowTest, MeshMotionData_CheckArraySize_ExpectThrow) { - std::vector> mesh_data = {{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; - std::vector> mesh_velocities = {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; - std::vector> mesh_accelerations = {{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; - util::MeshMotionData mesh_motion_data(mesh_data, mesh_velocities, mesh_accelerations, 1); +TEST(AerodynInflowTest, MeshData_CheckArraySize_ExpectThrow) { + size_t n_mesh_points{1}; + std::vector> mesh_data{{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; + std::vector> mesh_velocities{{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; + std::vector> mesh_accelerations{{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + std::vector> mesh_loads{{13.f, 14.f, 15.f, 16.f, 17.f, 18.f}}; + util::MeshData mesh_motion_data( + n_mesh_points, mesh_data, mesh_velocities, mesh_accelerations, mesh_loads + ); EXPECT_THROW( mesh_motion_data.CheckArraySize( @@ -581,44 +603,10 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { ); // Set up motion data for hub, nacelle, root, and mesh - util::MeshMotionData hub_motion( - {{0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}}, {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}}, - {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}} - ); - util::MeshMotionData nacelle_motion( - {{0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}}, {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}}, - {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}} - ); - util::MeshMotionData root_motion( - {{0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}, - {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}, - {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}}, - {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}}, - {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}}, - 3 - ); - - // Get the number of mesh points from the structural mesh - size_t n_mesh_points = 1; - - // Create mesh motion data with the correct number of mesh points - std::vector> mesh_positions( - n_mesh_points, {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0} - ); - std::vector> mesh_velocities( - n_mesh_points, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f} - ); - std::vector> mesh_accelerations( - n_mesh_points, {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f} - ); - - util::MeshMotionData mesh_motion( - mesh_positions, mesh_velocities, mesh_accelerations, n_mesh_points - ); + util::MeshData hub_motion{1}; + util::MeshData nacelle_motion{1}; + util::MeshData root_motion{3}; + util::MeshData mesh_motion{1}; // Set up rotor motion EXPECT_NO_THROW(aerodyn_inflow_library.SetupRotorMotion( From 39aa10ca5fc4f04236d9596d1e0259b6d3f72f37 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Wed, 2 Oct 2024 19:40:44 -0600 Subject: [PATCH 66/87] Add TurbineInitialState struct to contain initial state of a turbine --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 3193e74c..bb8a9c4f 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -108,6 +108,47 @@ inline void SetPositionAndOrientation( std::copy(&orientation_2D[0][0], &orientation_2D[0][0] + 9, orientation.begin()); } +/** + * @brief Configuration for the initial state of a turbine + * + * This struct encapsulates the initial configuration data for a wind turbine, + * including its type, reference position, and initial positions of key components + * in 7x1 arrays [x, y, z, qw, qx, qy, qz] + */ +struct TurbineInitialState { + /** + * @brief Initial state for a single blade of a turbine + * + * Stores the initial positions of a blade's root and nodes. + */ + struct BladeInitialState { + Array_7 root_initial_pos; //< Initial root position of the blade + std::vector node_initial_pos; //< Initial node positions of the blade + + /// Constructor to initialize all data based on provided inputs + BladeInitialState(const Array_7& root, const std::vector& nodes) + : root_initial_pos(root), node_initial_pos(nodes) {} + }; + + bool is_horizontal_axis{true}; //< Is a horizontal axis turbine? + std::array reference_position{0.f, 0.f, 0.f}; //< Reference position of the turbine + Array_7 hub_initial_position; //< Initial hub position + Array_7 nacelle_initial_position; //< Initial nacelle position + std::vector + blade_initial_states; //< Initial root and node positions of blades + + /// Constructor to initialize all data based on provided inputs + TurbineInitialState( + bool is_horizontal_axis, const std::array& ref_pos, const Array_7& hub_pos, + const Array_7& nacelle_pos, const std::vector& blade_states + ) + : is_horizontal_axis(is_horizontal_axis), + reference_position(ref_pos), + hub_initial_position(hub_pos), + nacelle_initial_position(nacelle_pos), + blade_initial_states(blade_states) {} +}; + /** * @brief Struct to hold the initial settings/motion for the turbine * From 2d93edffefdc091b23911a27198fd3e3cdcd081f Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 3 Oct 2024 08:12:45 -0600 Subject: [PATCH 67/87] Add tests for TurbineConfig struct and do a refactor cycle --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 15 +++--- .../external/test_aerodyn_inflow.cpp | 52 +++++++++++++++++-- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index bb8a9c4f..86ae4e68 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -115,19 +116,19 @@ inline void SetPositionAndOrientation( * including its type, reference position, and initial positions of key components * in 7x1 arrays [x, y, z, qw, qx, qy, qz] */ -struct TurbineInitialState { +struct TurbineConfig { /** * @brief Initial state for a single blade of a turbine * * Stores the initial positions of a blade's root and nodes. */ struct BladeInitialState { - Array_7 root_initial_pos; //< Initial root position of the blade - std::vector node_initial_pos; //< Initial node positions of the blade + Array_7 root_initial_position; //< Initial root position of the blade + std::vector node_initial_positions; //< Initial node positions of the blade /// Constructor to initialize all data based on provided inputs BladeInitialState(const Array_7& root, const std::vector& nodes) - : root_initial_pos(root), node_initial_pos(nodes) {} + : root_initial_position(root), node_initial_positions(nodes) {} }; bool is_horizontal_axis{true}; //< Is a horizontal axis turbine? @@ -138,11 +139,11 @@ struct TurbineInitialState { blade_initial_states; //< Initial root and node positions of blades /// Constructor to initialize all data based on provided inputs - TurbineInitialState( - bool is_horizontal_axis, const std::array& ref_pos, const Array_7& hub_pos, + TurbineConfig( + bool is_hawt, const std::array& ref_pos, const Array_7& hub_pos, const Array_7& nacelle_pos, const std::vector& blade_states ) - : is_horizontal_axis(is_horizontal_axis), + : is_horizontal_axis(is_hawt), reference_position(ref_pos), hub_initial_position(hub_pos), nacelle_initial_position(nacelle_pos), diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index 695d2a7a..8b43bc7e 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -6,8 +6,6 @@ namespace openturbine::tests { -#ifdef OpenTurbine_BUILD_OPENFAST_ADI - TEST(AerodynInflowTest, ErrorHandling_NoThrow) { util::ErrorHandling error_handling; EXPECT_NO_THROW(error_handling.CheckError()); @@ -277,6 +275,54 @@ TEST(AerodynInflowTest, MeshData_Constructor_Data) { ExpectArrayNear(mesh_motion_data.loads[0], {13.f, 14.f, 15.f, 16.f, 17.f, 18.f}); } +TEST(AerodynInflowTest, BladeInitialState_Constructor) { + Array_7 root{1., 2., 3., 1., 0., 0., 0.}; + std::vector nodes{{4., 5., 6., 1., 0., 0., 0.}, {7., 8., 9., 1., 0., 0., 0.}}; + util::TurbineConfig::BladeInitialState blade_state{root, nodes}; + + EXPECT_EQ(blade_state.root_initial_position, root); + EXPECT_EQ(blade_state.node_initial_positions, nodes); +} + +TEST(AerodynInflowTest, TurbineConfig_Constructor) { + bool is_hawt{true}; + std::array ref_pos{10.f, 20.f, 30.f}; + Array_7 hub_pos{1., 2., 3., 1., 0., 0., 0.}; + Array_7 nacelle_pos{4., 5., 6., 1., 0., 0., 0.}; + + std::vector blade_states; + for (int i = 0; i < 3; ++i) { + Array_7 root = {static_cast(i), 0., 0., 1., 0., 0., 0.}; + std::vector nodes = { + {static_cast(i), 1., 0., 1., 0., 0., 0.}, + {static_cast(i), 2., 0., 1., 0., 0., 0.}}; + blade_states.emplace_back(root, nodes); + } + + util::TurbineConfig turbine_config{is_hawt, ref_pos, hub_pos, nacelle_pos, blade_states}; + + EXPECT_EQ(turbine_config.is_horizontal_axis, is_hawt); + EXPECT_EQ(turbine_config.reference_position, ref_pos); + EXPECT_EQ(turbine_config.hub_initial_position, hub_pos); + EXPECT_EQ(turbine_config.nacelle_initial_position, nacelle_pos); + EXPECT_EQ(turbine_config.blade_initial_states.size(), 3); + + for (size_t i = 0; i < 3; ++i) { + EXPECT_EQ( + turbine_config.blade_initial_states[i].root_initial_position[0], static_cast(i) + ); + EXPECT_EQ(turbine_config.blade_initial_states[i].node_initial_positions.size(), 2); + EXPECT_EQ( + turbine_config.blade_initial_states[i].node_initial_positions[0][0], + static_cast(i) + ); + EXPECT_EQ( + turbine_config.blade_initial_states[i].node_initial_positions[1][0], + static_cast(i) + ); + } +} + TEST(AerodynInflowTest, MeshData_CheckArraySize_NoThrow) { size_t n_mesh_points{1}; std::vector> mesh_data{{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; @@ -634,6 +680,4 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { EXPECT_NO_THROW(aerodyn_inflow_library.Finalize()); } -#endif - } // namespace openturbine::tests \ No newline at end of file From dc260bba5383a4478d78c50635092e695cb109f0 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 3 Oct 2024 09:19:23 -0600 Subject: [PATCH 68/87] Add TurbineData struct to hold turbine specific information --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 86ae4e68..83161d6f 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -436,6 +436,80 @@ struct MeshData { } }; +/** + * @brief Struct to hold turbine-specific data + * + * @details This struct contains data for a single turbine, including the number of blades, + * mesh data for various components, and blade node mappings. + */ +struct TurbineData { + int32_t n_blades; //< Number of blades + MeshData hub; //< Hub data (1 point) + MeshData nacelle; //< Nacelle data (1 point) + MeshData blade_roots; //< Blade roots data (n_blades points) + MeshData blade_nodes; //< Blade nodes data + std::vector blade_nodes_to_blade_num; //< Blade node to blade number mapping as 1D + // array + std::vector> blade_nodes_index; //< Blade nodes index as 2D array + + /// Constructor to initialize TurbineData from TurbineConfig + explicit TurbineData(const TurbineConfig& tc) + : n_blades(static_cast(tc.blade_initial_states.size())), + hub(1), + nacelle(1), + blade_roots(tc.blade_initial_states.size()), + blade_nodes(CalculateTotalBladeNodes(tc)), + blade_nodes_index(tc.blade_initial_states.size()) { + InitializeHubAndNacelle(tc); + InitializeBlades(tc); + } + +private: + /// Calculates the total number of blade nodes across all blades + static size_t CalculateTotalBladeNodes(const TurbineConfig& tc) { + return std::accumulate( + tc.blade_initial_states.begin(), tc.blade_initial_states.end(), size_t{0}, + [](size_t sum, const auto& blade) { + return sum + blade.node_initial_positions.size(); + } + ); + } + + /// Initializes the hub and nacelle positions and orientations + void InitializeHubAndNacelle(const TurbineConfig& tc) { + SetPositionAndOrientation(tc.hub_initial_position, hub.position[0], hub.orientation[0]); + SetPositionAndOrientation( + tc.nacelle_initial_position, nacelle.position[0], nacelle.orientation[0] + ); + } + + /// Initializes blade data including roots, nodes, and mappings + void InitializeBlades(const TurbineConfig& tc) { + size_t i_blade{0}; + size_t i_node{0}; + + // Initialize blade roots and nodes + for (const auto& blade : tc.blade_initial_states) { + // Initialize blade root + SetPositionAndOrientation( + blade.root_initial_position, blade_roots.position[i_blade], + blade_roots.orientation[i_blade] + ); + + // Initialize blade nodes + for (const auto& node : blade.node_initial_positions) { + SetPositionAndOrientation( + node, blade_nodes.position[i_node], blade_nodes.orientation[i_node] + ); + blade_nodes_index[i_blade].emplace_back(i_node); + blade_nodes_to_blade_num.emplace_back(static_cast(i_blade + 1)); + ++i_node; + } + ++i_blade; + } + } +}; + /** * @brief Struct to hold the settings for simulation controls * From e8341e90c14c7d83ca23a652e04f257e2b8a1368 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 3 Oct 2024 10:31:30 -0600 Subject: [PATCH 69/87] Add unit test for TurbineData and refactor cycle of TDD --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 37 ++++++--- .../external/test_aerodyn_inflow.cpp | 79 +++++++++++++++++++ 2 files changed, 105 insertions(+), 11 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 83161d6f..bcf40024 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -443,14 +443,29 @@ struct MeshData { * mesh data for various components, and blade node mappings. */ struct TurbineData { - int32_t n_blades; //< Number of blades - MeshData hub; //< Hub data (1 point) - MeshData nacelle; //< Nacelle data (1 point) - MeshData blade_roots; //< Blade roots data (n_blades points) - MeshData blade_nodes; //< Blade nodes data - std::vector blade_nodes_to_blade_num; //< Blade node to blade number mapping as 1D - // array - std::vector> blade_nodes_index; //< Blade nodes index as 2D array + int32_t n_blades; //< Number of blades + MeshData hub; //< Hub data (1 point) + MeshData nacelle; //< Nacelle data (1 point) + MeshData blade_roots; //< Blade roots data (n_blades points) + MeshData blade_nodes; //< Blade nodes data + /** + * @brief Mapping of blade nodes to blade numbers (1D array) + * + * This vector stores the blade number (1-based index) for each blade node. + * It allows quick lookup of which blade a particular node belongs to. + * The size of this vector is equal to the total number of blade nodes across all blades. + */ + std::vector blade_nodes_to_blade_num_mapping; + + /** + * @brief Unique indices of nodes for each blade (2D array) + * + * This is a vector of vectors where each inner vector contains the unique indices of nodes + * belonging to a specific blade. The outer vector's size is equal to the number of blades, + * and each inner vector's size is equal to the number of nodes on that blade. + * This structure allows quick access to all nodes of a particular blade. + */ + std::vector> node_indices_by_blade; /// Constructor to initialize TurbineData from TurbineConfig explicit TurbineData(const TurbineConfig& tc) @@ -459,7 +474,7 @@ struct TurbineData { nacelle(1), blade_roots(tc.blade_initial_states.size()), blade_nodes(CalculateTotalBladeNodes(tc)), - blade_nodes_index(tc.blade_initial_states.size()) { + node_indices_by_blade(tc.blade_initial_states.size()) { InitializeHubAndNacelle(tc); InitializeBlades(tc); } @@ -501,8 +516,8 @@ struct TurbineData { SetPositionAndOrientation( node, blade_nodes.position[i_node], blade_nodes.orientation[i_node] ); - blade_nodes_index[i_blade].emplace_back(i_node); - blade_nodes_to_blade_num.emplace_back(static_cast(i_blade + 1)); + node_indices_by_blade[i_blade].emplace_back(i_node); + blade_nodes_to_blade_num_mapping.emplace_back(static_cast(i_blade + 1)); ++i_node; } ++i_blade; diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index 8b43bc7e..1c80d041 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -386,6 +386,85 @@ TEST(AerodynInflowTest, SimulationControls_Default) { EXPECT_STREQ(simulation_controls.channel_units_c.data(), ""); } +TEST(AerodynInflowTest, TurbineData_Constructor) { + // Set up 3 blades with 2 nodes each + std::vector blade_states; + for (size_t i = 0; i < 3; ++i) { + util::TurbineConfig::BladeInitialState blade_state( + {0., 0., 90., 1., 0., 0., 0.}, // root_initial_position + { + {0., 5., 90., 1., 0., 0., 0.}, // node_initial_positions - 1 + {0., 10., 90., 1., 0., 0., 0.} // node_initial_positions - 2 + } + + ); + blade_states.push_back(blade_state); + } + util::TurbineConfig tc( + true, // is_horizontal_axis + {0.f, 0.f, 0.f}, // reference_position + {0., 0., 90., 1., 0., 0., 0.}, // hub_initial_position + {0., 0., 90., 1., 0., 0., 0.}, // nacelle_initial_position + blade_states + ); + + util::TurbineData turbine_data(tc); + + // Check basic properties + EXPECT_EQ(turbine_data.n_blades, 3); + EXPECT_EQ(turbine_data.hub.n_mesh_points, 1); + EXPECT_EQ(turbine_data.nacelle.n_mesh_points, 1); + EXPECT_EQ(turbine_data.blade_roots.n_mesh_points, 3); + EXPECT_EQ(turbine_data.blade_nodes.n_mesh_points, 6); // 3 blades * 2 nodes each + + // Check hub and nacelle positions + ExpectArrayNear(turbine_data.hub.position[0], {0.f, 0.f, 90.f}); + ExpectArrayNear(turbine_data.nacelle.position[0], {0.f, 0.f, 90.f}); + + // Check blade roots + for (size_t i = 0; i < 3; ++i) { + ExpectArrayNear(turbine_data.blade_roots.position[i], {0.f, 0.f, 90.f}); + } + + // Check blade nodes + for (size_t i = 0; i < 6; ++i) { + float expected_y = (i % 2 == 0) ? 5.f : 10.f; + ExpectArrayNear(turbine_data.blade_nodes.position[i], {0.f, expected_y, 90.f}); + } + + // Check blade_nodes_to_blade_num_mapping + std::vector expected_blade_nodes_to_blade_num_mapping = {1, 1, 2, 2, 3, 3}; + ASSERT_EQ(turbine_data.blade_nodes_to_blade_num_mapping.size(), 6); + for (size_t i = 0; i < 6; ++i) { + EXPECT_EQ( + turbine_data.blade_nodes_to_blade_num_mapping[i], + expected_blade_nodes_to_blade_num_mapping[i] + ) << "Mismatch at index " + << i; + } + + // Check node_indices_by_blade + ASSERT_EQ(turbine_data.node_indices_by_blade.size(), 3); + std::vector> expected_node_indices_by_blade = {{0, 1}, {2, 3}, {4, 5}}; + for (size_t blade = 0; blade < 3; ++blade) { + ASSERT_EQ(turbine_data.node_indices_by_blade[blade].size(), 2) + << "Incorrect number of nodes for blade " << blade; + EXPECT_EQ(turbine_data.node_indices_by_blade[blade], expected_node_indices_by_blade[blade]) + << "Incorrect node indices for blade " << blade; + } + + // Additional check: Verify that node_indices_by_blade correctly maps to + // blade_nodes_to_blade_num_mapping + for (size_t blade = 0; blade < 3; ++blade) { + for (size_t node : turbine_data.node_indices_by_blade[blade]) { + EXPECT_EQ(turbine_data.blade_nodes_to_blade_num_mapping[node], blade + 1) + << "Mismatch between node_indices_by_blade and blade_nodes_to_blade_num_mapping for " + "blade " + << blade; + } + } +} + TEST(AerodynInflowTest, SimulationControls_Set) { util::SimulationControls simulation_controls; simulation_controls.aerodyn_input_passed = 0; From 904b8c402ecdc55aad75ac2c22265f6ef2a24d38 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 3 Oct 2024 19:43:27 -0600 Subject: [PATCH 70/87] Refactor the AeroDynInflowLibrary to make things much more streamlined by using TurbineData class. Unnecessary class and methods were removed. --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 738 ++++++------------ .../external/test_aerodyn_inflow.cpp | 335 +------- 2 files changed, 249 insertions(+), 824 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index bcf40024..4e354987 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -86,29 +86,6 @@ struct EnvironmentalConditions { float msl_offset{0.f}; //< Mean sea level to still water level offset (m) }; -/** - * @brief Converts a 7-element array of position and quaternion to separate position and orientation - * arrays - * @param data Input array: [x, y, z, qw, qx, qy, qz] - * @param position Output array for position [x, y, z] - * @param orientation Output array for flattened 3x3 rotation matrix - */ -inline void SetPositionAndOrientation( - const std::array& data, std::array& position, - std::array& orientation -) { - // Set position (first 3 elements) - for (size_t i = 0; i < 3; ++i) { - position[i] = static_cast(data[i]); - } - - // Set orientation (convert last 4 elements i.e. quaternion to 3x3 rotation matrix) - auto orientation_2D = QuaternionToRotationMatrix({data[3], data[4], data[5], data[6]}); - - // Flatten the 3x3 matrix to a 1D array - std::copy(&orientation_2D[0][0], &orientation_2D[0][0] + 9, orientation.begin()); -} - /** * @brief Configuration for the initial state of a turbine * @@ -151,156 +128,27 @@ struct TurbineConfig { }; /** - * @brief Struct to hold the initial settings/motion for the turbine - * - * @details This struct holds the initial settings/motion for the turbine, including the number of - * turbines, the number of blades, the initial hub position, the initial hub orientation, the initial - * nacelle position, the initial nacelle orientation, the initial root positions, and the initial - * root orientations. - */ -struct TurbineSettings { - int n_turbines{1}; //< Number of turbines - 1 by default - int n_blades{3}; //< Number of blades - 3 by default - std::array initial_hub_position{0.f}; //< Initial hub position - std::array initial_hub_orientation{ - 1., 0., 0., 0., 1., 0., 0., 0., 1. //< Initial hub orientation - }; - std::array initial_nacelle_position{0.f}; //< Initial nacelle position - std::array initial_nacelle_orientation{ - 1., 0., 0., 0., 1., 0., 0., 0., 1. //< Initial nacelle orientation - }; - std::vector> initial_root_position{ - {0.f, 0.f, 0.f}, // blade 1 - {0.f, 0.f, 0.f}, // blade 2 - {0.f, 0.f, 0.f} // blade 3 - }; //< Initial root positions of blades - std::vector> initial_root_orientation{ - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, // blade 1 - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, // blade 2 - {1., 0., 0., 0., 1., 0., 0., 0., 1.} // blade 3 - }; //< Initial root orientations of blades - - /// Default constructor - TurbineSettings() = default; - - /// Constructor to initialize all data based on provided 7x1 inputs - TurbineSettings( - const std::array& hub_data, const std::array& nacelle_data, - const std::vector>& root_data, int n_turbines = 1, int n_blades = 3 - ) - : n_turbines(n_turbines), - n_blades(n_blades), - initial_root_position(static_cast(n_blades)), - initial_root_orientation(static_cast(n_blades)) { - if (root_data.size() != static_cast(n_blades)) { - throw std::invalid_argument("Number of root data entries must match n_blades"); - } - - // Set hub position and orientation - SetPositionAndOrientation(hub_data, initial_hub_position, initial_hub_orientation); - - // Set nacelle position and orientation - SetPositionAndOrientation( - nacelle_data, initial_nacelle_position, initial_nacelle_orientation - ); - - // Set root positions and orientations - for (size_t i = 0; i < static_cast(n_blades); ++i) { - SetPositionAndOrientation( - root_data[i], initial_root_position[i], initial_root_orientation[i] - ); - } - }; - - /** - * @brief Validates the turbine settings - * - * @details This method validates the turbine settings, including the number of blades, the - * initial root positions, and the initial root orientations. - */ - void Validate() const { - if (n_blades < 1) { - throw std::runtime_error("No blades. Set n_blades to number of AD blades in the model"); - } - - if (initial_root_position.size() != static_cast(n_blades) || - initial_root_orientation.size() != static_cast(n_blades)) { - throw std::invalid_argument( - "Number of blade root positions and orientations must match n_blades" - ); - } - } -}; - -/** - * @brief Struct to hold the initial motion of the structural mesh - * - * @details This struct holds the initial motion of the structural mesh, including the number of - * mesh points i.e. nodes, the initial mesh position, the initial mesh orientation, and the mapping - * of mesh points to blade numbers. + * @brief Converts a 7-element array of position and quaternion to separate position and orientation + * arrays + * @param data Input array: [x, y, z, qw, qx, qy, qz] + * @param position Output array for position [x, y, z] + * @param orientation Output array for flattened 3x3 rotation matrix */ -struct StructuralMesh { - int n_mesh_points{1}; //< Number of mesh points - std::vector> initial_mesh_position{ - {0.f, 0.f, 0.f} //< N x 3 array [x, y, z] - }; - std::vector> initial_mesh_orientation{ - {1., 0., 0., 0., 1., 0., 0., 0., 1.} //< N x 9 array [r11, r12, ..., r33] - }; - std::vector mesh_point_to_blade_num{ - 1 //< N x 1 array for mapping a mesh point to blade number - }; - - /// Default constructor - StructuralMesh() = default; - - /// Constructor to initialize all data based on provided 7x1 inputs and mapping of mesh points - /// to blade numbers - StructuralMesh( - const std::vector>& mesh_data, - std::vector mesh_point_to_blade_num, int n_mesh_points = 1 - ) - : n_mesh_points(n_mesh_points), - initial_mesh_position(static_cast(n_mesh_points)), - initial_mesh_orientation(static_cast(n_mesh_points)), - mesh_point_to_blade_num(std::move(mesh_point_to_blade_num)) { - if (mesh_data.size() != static_cast(n_mesh_points) || - this->mesh_point_to_blade_num.size() != static_cast(n_mesh_points)) { - throw std::invalid_argument( - "Number of mesh data entries and mesh point to blade number entries must match " - "n_mesh_points" - ); - } - - // Set mesh position and orientation - for (size_t i = 0; i < static_cast(n_mesh_points); ++i) { - SetPositionAndOrientation( - mesh_data[i], initial_mesh_position[i], initial_mesh_orientation[i] - ); - } +inline void SetPositionAndOrientation( + const std::array& data, std::array& position, + std::array& orientation +) { + // Set position (first 3 elements) + for (size_t i = 0; i < 3; ++i) { + position[i] = static_cast(data[i]); } - /** - * @brief Validates the structural mesh - * - * @details This method validates the structural mesh by checking: - * - The number of mesh points matches the size of initial position and orientation arrays - * - The size of the mesh point to blade number mapping matches the number of mesh points - * - All blade numbers in the mapping are valid (between 1 and the number of blades) - */ - void Validate() const { - if (initial_mesh_position.size() != static_cast(n_mesh_points) || - initial_mesh_orientation.size() != static_cast(n_mesh_points)) { - throw std::invalid_argument( - "Number of mesh positions and orientations must match n_mesh_points" - ); - } + // Set orientation (convert last 4 elements i.e. quaternion to 3x3 rotation matrix) + auto orientation_2D = QuaternionToRotationMatrix({data[3], data[4], data[5], data[6]}); - if (mesh_point_to_blade_num.size() != static_cast(n_mesh_points)) { - throw std::invalid_argument("Size of mesh_point_to_blade_num must match n_mesh_points"); - } - } -}; + // Flatten the 3x3 matrix to a 1D array + std::copy(&orientation_2D[0][0], &orientation_2D[0][0] + 9, orientation.begin()); +} /** * @brief Struct to hold the motion + loads data of any structural mesh component @@ -310,7 +158,7 @@ struct StructuralMesh { * the hub, nacelle, root, or mesh points/nodes */ struct MeshData { - size_t n_mesh_points; //< Number of mesh points (nodes) + int32_t n_mesh_points; //< Number of mesh points (nodes) std::vector> position; //< N x 3 array [x, y, z] std::vector> orientation; //< N x 9 array [r11, r12, ..., r33] std::vector> velocity; //< N x 6 array [u, v, w, p, q, r] @@ -320,7 +168,7 @@ struct MeshData { /// Constructor to initialize all mesh data to zero based on provided number of nodes MeshData(size_t n_nodes) - : n_mesh_points(n_nodes), + : n_mesh_points(static_cast(n_nodes)), position(std::vector>(n_nodes, {0.f, 0.f, 0.f})), orientation( std::vector>(n_nodes, {0., 0., 0., 0., 0., 0., 0., 0., 0.}) @@ -336,7 +184,7 @@ struct MeshData { const std::vector>& accelerations, const std::vector>& loads ) - : n_mesh_points(n_mesh_points), + : n_mesh_points(static_cast(n_mesh_points)), position(std::vector>(n_mesh_points, {0.f, 0.f, 0.f})), orientation( std::vector>(n_mesh_points, {0., 0., 0., 0., 0., 0., 0., 0., 0.}) @@ -468,7 +316,7 @@ struct TurbineData { std::vector> node_indices_by_blade; /// Constructor to initialize TurbineData from TurbineConfig - explicit TurbineData(const TurbineConfig& tc) + TurbineData(const TurbineConfig& tc) : n_blades(static_cast(tc.blade_initial_states.size())), hub(1), nacelle(1), @@ -532,11 +380,22 @@ struct TurbineData { * handling, interpolation order, time-related variables, and flags. */ struct SimulationControls { + /// Debug levels used in AeroDyn-Inflow C bindings + enum class DebugLevel { + kNone = 0, //< No debug output + kSummary = 1, //< Some summary info + kDetailed = 2, //< Above + all position/orientation info + kInputFiles = 3, //< Above + input files (if directly passed) + kAll = 4, //< Above + meshes + }; + static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames // Input file handling - bool aerodyn_input_passed{true}; //< Input file passed for AeroDyn module? (1: passed) - bool inflowwind_input_passed{true}; //< Input file passed for InflowWind module? (1: passed) + bool is_aerodyn_input_path{true}; //< Input file passed for AeroDyn module? + bool is_inflowwind_input_path{true}; //< Input file passed for InflowWind module? + std::string aerodyn_input; //< Path to AeroDyn input file + std::string inflowwind_input; //< Path to InflowWind input file // Interpolation order (must be either 1: linear, or 2: quadratic) int interpolation_order{1}; //< Interpolation order - linear by default @@ -550,17 +409,15 @@ struct SimulationControls { // Flags bool store_HH_wind_speed{true}; //< Flag to store HH wind speed bool transpose_DCM{true}; //< Flag to transpose the direction cosine matrix - int debug_level{0}; //< Debug level (0-4) + int debug_level{static_cast(DebugLevel::kNone)}; //< Debug level (0-4) // Outputs - int output_format{0}; //< File format for writing outputs - double output_time_step{0.}; //< Timestep for outputs to file - std::array output_root_name{ - "Output_ADIlib_default" //< Root name for output files - }; - int n_channels{0}; //< Number of channels returned - std::array channel_names_c{}; //< Output channel names - std::array channel_units_c{}; //< Output channel units + int output_format{1}; //< File format for writing outputs + double output_time_step{0.1}; //< Timestep for outputs to file + std::string output_root_name{"ADI_out"}; //< Root name for output files + int n_channels{0}; //< Number of channels returned + std::array channel_names_c{}; //< Output channel names + std::array channel_units_c{}; //< Output channel units }; /** @@ -577,148 +434,47 @@ struct VTKSettings { float vtk_hub_radius{1.5f}; //< Hub radius for VTK rendering }; -/** - * @brief Flattens a 2D array into a 1D array for Fortran compatibility - * - * @details This function flattens a 2D array into a 1D array for Fortran compatibility by - * inserting each element of the 2D array into the 1D array. - * - * @tparam T Type of the elements in the array - * @tparam N Number of elements in each row of the array - * @param input 2D array to flatten - * @return Flattened 1D array - */ -template -std::vector FlattenArray(const std::vector>& input) { - std::vector output; - output.reserve(input.size() * N); - for (const auto& arr : input) { - output.insert(output.end(), arr.begin(), arr.end()); - } - return output; -} - -/** - * @brief Validates a 2D array for the correct number of points and then flattens it to 1D - * - * @details This function validates a 2D array for the correct number of points and then - * flattens it to 1D by inserting each element of the 2D array into the 1D array. - * - * @tparam T Type of the elements in the array - * @tparam N Number of elements in each row of the array - * @param array 2D array to validate and flatten - * @param expected_size Expected size of the 2D array - * @return Flattened 1D array - */ -template -std::vector ValidateAndFlattenArray( - const std::vector>& array, size_t expected_size -) { - std::string array_name; - if constexpr (std::is_same_v && N == 3) { - array_name = "position"; - } else if constexpr (std::is_same_v && N == 9) { - array_name = "orientation"; - } else if constexpr (std::is_same_v && N == 6) { - array_name = "velocity/acceleration"; - } else { - array_name = "unknown"; - } - - if (array.size() != expected_size) { - throw std::runtime_error( - "The number of mesh points in the " + array_name + - " array changed from the initial value of " + std::to_string(expected_size) + " to " + - std::to_string(array.size()) + ". This is not permitted during the simulation." - ); - } - return FlattenArray(array); -} - -/** - * @brief Unflattens a 1D array into a 2D array - * - * @details This function unflattens a 1D array into a 2D array by inserting each element of - * the 1D array into the 2D array. - * - * @tparam T Type of the elements in the array - * @tparam N Number of elements in each row of the array - * @param input 1D array to unflatten - * @return Unflattened 2D array - */ -template -std::vector> UnflattenArray(const std::vector& input) { - std::vector> output(input.size() / N); - for (size_t i = 0; i < output.size(); ++i) { - for (size_t j = 0; j < N; ++j) { - output[i][j] = input[i * N + j]; - } - } - return output; -} - -/** - * @brief Joins a vector of strings into a single string with a delimiter - * - * @details This function joins a vector of strings into a single string with a delimiter by - * inserting each element of the vector into the string. - * - * @param input Vector of strings to join - * @param delimiter Delimiter to insert between the strings - * @return Joined string - */ -std::string JoinStringArray(const std::vector& input, char delimiter) { - if (input.empty()) { - return ""; - } - - std::ostringstream result; - std::copy( - input.begin(), input.end() - 1, - std::ostream_iterator(result, std::string(1, delimiter).c_str()) - ); - result << input.back(); - - return result.str(); -} - /** * @brief Wrapper class for the AeroDynInflow (ADI) shared library * - * @details This class provides an interface for interacting with the AeroDynInflow (ADI) shared - * library, which is a Fortran library offering C bindings for the AeroDyn x InflowWind modules - * of OpenFAST. + * @details This class provides a C++ interface for interacting with the AeroDynInflow (ADI) shared + * library, which offers C bindings for the AeroDyn x InflowWind modules of OpenFAST. + * It encapsulates key functions for AeroDyn/InflowWind simulation, including initialization, + * rotor setup, state updates, and load calculations. * - * The class encapsulates key functions for AeroDyn/InflowWind simulation: + * Key functions: + * - Initialize: Set up the simulation environment + * - SetupRotorMotion: Update rotor motion for each timestep + * - UpdateStates: Advance internal states + * - CalculateOutputChannels: Compute output values + * - GetRotorAerodynamicLoads: Retrieve aerodynamic forces and moments + * - Finalize: Clean up and release resources * - * - PreInitialize (ADI_C_PreInit): Set up general parameters - * - SetupRotor (ADI_C_SetupRotor): Configure rotor-specific settings - * - Initialize (ADI_C_Init): Complete initialization with input files - * - SetupRotorMotion (ADI_C_SetRotorMotion): Update rotor motion for each timestep - * - UpdateStates (ADI_C_UpdateStates): Advance internal states - * - CalculateOutputChannels (ADI_C_CalcOutput): Compute output values - * - GetRotorAerodynamicLoads (ADI_C_GetRotorLoads): Retrieve aerodynamic forces and moments - * - Finalize (ADI_C_End): Clean up and release resources - * - * Usage: Instantiate the class, call functions in the order listed above (iterating over - * turbines/timesteps as needed), and handle any errors using the ErrorHandling struct. + * @note This class manages the lifecycle of the ADI library, ensuring proper initialization + * and cleanup. */ class AeroDynInflowLibrary { public: - /// Constructor to initialize AeroDyn Inflow library with default settings and optional path + /** + * @brief Construct a new AeroDynInflowLibrary object + * + * @param shared_lib_path Path to the ADI shared library + * @param eh Error handling settings + * @param fp Fluid properties + * @param ec Environmental conditions + * @param sc Simulation control settings + * @param vtk VTK output settings + */ AeroDynInflowLibrary( std::string shared_lib_path = "aerodyn_inflow_c_binding.dll", ErrorHandling eh = ErrorHandling{}, FluidProperties fp = FluidProperties{}, EnvironmentalConditions ec = EnvironmentalConditions{}, - TurbineSettings ts = TurbineSettings{}, StructuralMesh sm = StructuralMesh{}, SimulationControls sc = SimulationControls{}, VTKSettings vtk = VTKSettings{} ) : lib_{shared_lib_path, util::dylib::no_filename_decorations}, error_handling_(std::move(eh)), air_(std::move(fp)), env_conditions_(std::move(ec)), - turbine_settings_(std::move(ts)), - structural_mesh_(std::move(sm)), sim_controls_(std::move(sc)), vtk_settings_(std::move(vtk)) {} @@ -737,27 +493,44 @@ class AeroDynInflowLibrary { const ErrorHandling& GetErrorHandling() const { return error_handling_; } const EnvironmentalConditions& GetEnvironmentalConditions() const { return env_conditions_; } const FluidProperties& GetFluidProperties() const { return air_; } - const TurbineSettings& GetTurbineSettings() const { return turbine_settings_; } - const StructuralMesh& GetStructuralMesh() const { return structural_mesh_; } const SimulationControls& GetSimulationControls() const { return sim_controls_; } const VTKSettings& GetVTKSettings() const { return vtk_settings_; } /** - * @brief Pre-initializes the AeroDyn Inflow library + * @brief Initialize the AeroDyn Inflow library + * + * @details Performs a complete initialization of the AeroDyn Inflow library through a + * three-step process: + * 1. Pre-initialization (ADI_C_PreInit) + * 2. Rotor setup (ADI_C_SetupRotor) + * 3. Final initialization (ADI_C_Init) + * + * @param turbine_configs Vector of TurbineConfig objects, each representing a single turbine + */ + void Initialize(std::vector turbine_configs) { + PreInitialize(turbine_configs.size()); + SetupRotors(turbine_configs); + FinalizeInitialization(); + is_initialized_ = true; + } + + /** + * @brief Performs pre-initialization setup * - * @details This function pre-initializes the AeroDyn Inflow library by setting up the number - * of turbines, the number of blades, and the transpose DCM flag. + * @param n_turbines Number of turbines in the simulation */ - void PreInitialize() { + void PreInitialize(size_t n_turbines) { auto ADI_C_PreInit = lib_.get_function("ADI_C_PreInit"); - // Convert bool -> int to pass to the Fortran routine - int transpose_DCM_int = sim_controls_.transpose_DCM ? 1 : 0; + // Convert bool and other types to int32_t for Fortran compatibility + int32_t debug_level_int = static_cast(sim_controls_.debug_level); + int32_t transpose_dcm_int = sim_controls_.transpose_DCM ? 1 : 0; + int32_t n_turbines_int = static_cast(n_turbines); ADI_C_PreInit( - &turbine_settings_.n_turbines, // input: Number of turbines - &transpose_DCM_int, // input: Transpose DCM? - &sim_controls_.debug_level, // input: Debug level + &n_turbines_int, // input: Number of turbines + &transpose_dcm_int, // input: Transpose DCM? + &debug_level_int, // input: Debug level &error_handling_.error_status, // output: Error status error_handling_.error_message.data() // output: Error message ); @@ -766,112 +539,90 @@ class AeroDynInflowLibrary { } /** - * @brief Sets up the rotor for the AeroDyn Inflow library - * - * @details This function sets up the rotor for the AeroDyn Inflow library by initializing the - * rotor motion data and passing it to the Fortran routine. + * @brief Sets up rotor configurations for all turbines * - * @param turbine_number Number of the current turbine - * @param is_horizontal_axis Flag to indicate if the turbine is a horizontal axis turbine - * @param turbine_ref_pos Reference position of the turbine + * @param turbine_configs Vector of turbine configurations */ - void SetupRotor( - int turbine_number, bool is_horizontal_axis, std::array turbine_ref_pos - ) { + void SetupRotors(const std::vector& turbine_configs) { auto ADI_C_SetupRotor = lib_.get_function< - void(int*, int*, float*, float*, double*, float*, double*, int*, float*, double*, int*, float*, double*, int*, int*, char*)>( + void(int*, int*, const float*, float*, double*, float*, double*, int*, float*, double*, int*, float*, double*, int*, int*, char*)>( "ADI_C_SetupRotor" ); - // Validate the turbine settings and structural mesh - turbine_settings_.Validate(); - structural_mesh_.Validate(); - - // Flatten arrays to pass to the Fortran routine - auto initial_root_position_flat = ValidateAndFlattenArray( - turbine_settings_.initial_root_position, static_cast(turbine_settings_.n_blades) - ); - auto initial_root_orientation_flat = ValidateAndFlattenArray( - turbine_settings_.initial_root_orientation, - static_cast(turbine_settings_.n_blades) - ); - auto init_mesh_pos_flat = ValidateAndFlattenArray( - structural_mesh_.initial_mesh_position, - static_cast(structural_mesh_.n_mesh_points) - ); - auto init_mesh_orient_flat = ValidateAndFlattenArray( - structural_mesh_.initial_mesh_orientation, - static_cast(structural_mesh_.n_mesh_points) - ); - - // Convert bool -> int to pass to the Fortran routine - int is_horizontal_axis_int = is_horizontal_axis ? 1 : 0; - - ADI_C_SetupRotor( - &turbine_number, // input: current turbine number - &is_horizontal_axis_int, // input: 1: HAWT, 0: VAWT or cross-flow - turbine_ref_pos.data(), // input: turbine reference position - turbine_settings_.initial_hub_position.data(), // input: initial hub position - turbine_settings_.initial_hub_orientation.data(), // input: initial hub orientation - turbine_settings_.initial_nacelle_position.data(), // input: initial nacelle position - turbine_settings_.initial_nacelle_orientation.data( - ), // input: initial nacelle orientation - &turbine_settings_.n_blades, // input: number of blades - initial_root_position_flat.data(), // input: initial blade root positions - initial_root_orientation_flat.data(), // input: initial blade root orientation - &structural_mesh_.n_mesh_points, // input: number of mesh points - init_mesh_pos_flat.data(), // input: initial node positions - init_mesh_orient_flat.data(), // input: initial node orientation - structural_mesh_.mesh_point_to_blade_num.data( - ), // input: initial mesh point to blade number mapping - &error_handling_.error_status, // output: Error status - error_handling_.error_message.data() // output: Error message buffer - ); + // Loop through turbine configurations + int32_t turbine_number{0}; + for (const auto& tc : turbine_configs) { + // Turbine number is 1 indexed i.e. 1, 2, 3, ... + ++turbine_number; + + // Validate the turbine config + // turbine_settings_.Validate(); + // structural_mesh_.Validate(); + + // Convert bool -> int to pass to the Fortran routine + int32_t is_horizontal_axis_int = tc.is_horizontal_axis ? 1 : 0; + + // Create new turbine data + turbines_.emplace_back(TurbineData(tc)); + auto& td = turbines_.back(); + + // Call setup rotor for each turbine + ADI_C_SetupRotor( + &turbine_number, // input: current turbine number + &is_horizontal_axis_int, // input: 1: HAWT, 0: VAWT or cross-flow + tc.reference_position.data(), // input: turbine reference position + td.hub.position.data()->data(), // input: initial hub position + td.hub.orientation.data()->data(), // input: initial hub orientation + td.nacelle.position.data()->data(), // input: initial nacelle position + td.nacelle.orientation.data()->data(), // input: initial nacelle orientation + &td.n_blades, // input: number of blades + td.blade_roots.position.data()->data(), // input: initial blade root positions + td.blade_roots.orientation.data()->data(), // input: initial blade root orientation + &td.blade_nodes.n_mesh_points, // input: number of mesh points + td.blade_nodes.position.data()->data(), // input: initial node positions + td.blade_nodes.orientation.data()->data(), // input: initial node orientation + td.blade_nodes_to_blade_num_mapping.data( + ), // input: blade node to blade number mapping + &error_handling_.error_status, // output: Error status + error_handling_.error_message.data() // output: Error message buffer + ); - error_handling_.CheckError(); + error_handling_.CheckError(); + } } /** - * @brief Initializes the AeroDyn Inflow library - * - * @details This function initializes the AeroDyn Inflow library by passing the input files and - * other parameters to the Fortran routine. - * - * @param aerodyn_input_string_array Input file for the AeroDyn module - * @param inflowwind_input_string_array Input file for the InflowWind module + * @brief Finalizes the initialization process */ - void Initialize( - std::vector aerodyn_input_string_array, - std::vector inflowwind_input_string_array - ) { - auto ADI_C_Init = lib_.get_function< - void(int*, char**, int*, int*, char**, int*, char*, float*, float*, float*, float*, float*, float*, float*, float*, int*, double*, double*, int*, int*, int*, float*, float*, int*, double*, int*, char*, char*, int*, char*)>( - "ADI_C_Init" - ); + void FinalizeInitialization() { + auto ADI_C_Init = + lib_.get_function< + void(int*, char**, int*, int*, char**, int*, const char*, float*, float*, float*, float*, float*, float*, float*, float*, int*, double*, double*, int*, int*, int*, float*, float*, int*, double*, int*, char*, char*, int*, char*)>( + "ADI_C_Init" + ); // Convert bool -> int to pass to the Fortran routine - int aerodyn_input_passed_int = sim_controls_.aerodyn_input_passed ? 1 : 0; - int inflowwind_input_passed_int = sim_controls_.inflowwind_input_passed ? 1 : 0; - int store_HH_wind_speed_int = sim_controls_.store_HH_wind_speed ? 1 : 0; - int write_vtk_int = vtk_settings_.write_vtk ? 1 : 0; + int32_t aerodyn_input_is_passed = sim_controls_.is_aerodyn_input_path ? 1 : 0; + int32_t inflowwind_input_is_passed = sim_controls_.is_inflowwind_input_path ? 1 : 0; + int32_t store_HH_wind_speed_int = sim_controls_.store_HH_wind_speed ? 1 : 0; + int32_t write_vtk_int = vtk_settings_.write_vtk ? 1 : 0; - // Primary input files will be passed as a single string joined by C_NULL_CHAR i.e. '\0' - std::string aerodyn_input_string = JoinStringArray(aerodyn_input_string_array, '\0'); - int aerodyn_input_string_length = static_cast(aerodyn_input_string.size()); - char* aerodyn_input_cstring = aerodyn_input_string.data(); + // Primary input file will be passed as path to the file + char* aerodyn_input_pointer{sim_controls_.aerodyn_input.data()}; + int32_t aerodyn_input_length = static_cast(sim_controls_.aerodyn_input.size()); - std::string inflowwind_input_string = JoinStringArray(inflowwind_input_string_array, '\0'); - int inflowwind_input_string_length = static_cast(inflowwind_input_string.size()); - char* inflowwind_input_cstring = inflowwind_input_string.data(); + char* inflowwind_input_pointer{sim_controls_.inflowwind_input.data()}; + int32_t inflowwind_input_length = + static_cast(sim_controls_.inflowwind_input.size()); ADI_C_Init( - &aerodyn_input_passed_int, // input: AD input file is passed? - &aerodyn_input_cstring, // input: AD input file as string array - &aerodyn_input_string_length, // input: AD input file string length - &inflowwind_input_passed_int, // input: IfW input file is passed? - &inflowwind_input_cstring, // input: IfW input file as string array - &inflowwind_input_string_length, // input: IfW input file string length - sim_controls_.output_root_name.data(), // input: rootname for ADI file writing + &aerodyn_input_is_passed, // input: AD input is passed + &aerodyn_input_pointer, // input: AD input file as string + &aerodyn_input_length, // input: AD input file string length + &inflowwind_input_is_passed, // input: IfW input is passed + &inflowwind_input_pointer, // input: IfW input file as string + &inflowwind_input_length, // input: IfW input file string length + sim_controls_.output_root_name.c_str(), // input: rootname for ADI file writing &env_conditions_.gravity, // input: gravity &air_.density, // input: air density &air_.kinematic_viscosity, // input: kinematic viscosity @@ -883,8 +634,8 @@ class AeroDynInflowLibrary { &sim_controls_.interpolation_order, // input: interpolation order &sim_controls_.time_step, // input: time step &sim_controls_.max_time, // input: maximum simulation time - &store_HH_wind_speed_int, // input: store HH wind speed? - &write_vtk_int, // input: write VTK output? + &store_HH_wind_speed_int, // input: store HH wind speed + &write_vtk_int, // input: write VTK output &vtk_settings_.vtk_type, // input: VTK output type vtk_settings_.vtk_nacelle_dimensions.data(), // input: VTK nacelle dimensions &vtk_settings_.vtk_hub_radius, // input: VTK hub radius @@ -902,127 +653,78 @@ class AeroDynInflowLibrary { } /** - * @brief Sets up the rotor motion for the AeroDyn Inflow library + * @brief Set up the rotor motion for the current simulation step * - * @details This function sets up the rotor motion for the AeroDyn Inflow library by passing - * the motion data for the hub, nacelle, root, and mesh points to the Fortran routine. - * - * @param turbine_number Number of the current turbine - * @param hub_motion Motion data for the hub - * @param nacelle_motion Motion data for the nacelle - * @param root_motion Motion data for the blade roots - * @param mesh_motion Motion data for the mesh points + * @details Updates the motion data (position, orientation, velocity, acceleration) for + * the hub, nacelle, blade roots, and blade nodes of each turbine. */ - void SetupRotorMotion( - int turbine_number, MeshData hub_motion, MeshData nacelle_motion, MeshData root_motion, - MeshData mesh_motion - ) { - auto ADI_C_SetRotorMotion = lib_.get_function< - void(int*, float*, double*, float*, float*, float*, double*, float*, float*, float*, double*, float*, float*, int*, float*, double*, float*, float*, int*, char*)>( - "ADI_C_SetRotorMotion" - ); - - // Check the input motions for hub, nacelle, root, and mesh points - hub_motion.CheckHubNacelleInputMotions("hub"); - nacelle_motion.CheckHubNacelleInputMotions("nacelle"); - root_motion.CheckRootInputMotions( - static_cast(turbine_settings_.n_blades), - static_cast(turbine_settings_.initial_root_position.size()) - ); - mesh_motion.CheckMeshInputMotions( - static_cast(structural_mesh_.n_mesh_points), - static_cast(structural_mesh_.initial_mesh_position.size()) - ); - - // Flatten arrays to pass to the Fortran routine - auto flatten_and_validate = [](const auto& motion, int n_pts) { - return std::make_tuple( - ValidateAndFlattenArray(motion.position, static_cast(n_pts)), - ValidateAndFlattenArray(motion.orientation, static_cast(n_pts)), - ValidateAndFlattenArray(motion.velocity, static_cast(n_pts)), - ValidateAndFlattenArray(motion.acceleration, static_cast(n_pts)) + void SetupRotorMotion() { + auto + ADI_C_SetRotorMotion = + lib_ + .get_function< + void(int*, const float*, const double*, const float*, const float*, const float*, const double*, const float*, const float*, const float*, const double*, const float*, const float*, const int*, const float*, const double*, const float*, const float*, int*, char*)>( + "ADI_C_SetRotorMotion" + ); + + // Loop through turbines and set the rotor motion + int32_t turbine_number{0}; + for (const auto& td : turbines_) { + // Turbine number is 1 indexed i.e. 1, 2, 3, ... + ++turbine_number; + ADI_C_SetRotorMotion( + &turbine_number, // input: current turbine number + td.hub.position.data()->data(), // input: hub positions + td.hub.orientation.data()->data(), // input: hub orientations + td.hub.velocity.data()->data(), // input: hub velocities + td.hub.acceleration.data()->data(), // input: hub accelerations + td.nacelle.position.data()->data(), // input: nacelle positions + td.nacelle.orientation.data()->data(), // input: nacelle orientations + td.nacelle.velocity.data()->data(), // input: nacelle velocities + td.nacelle.acceleration.data()->data(), // input: nacelle accelerations + td.blade_roots.position.data()->data(), // input: root positions + td.blade_roots.orientation.data()->data(), // input: root orientations + td.blade_roots.velocity.data()->data(), // input: root velocities + td.blade_roots.acceleration.data()->data(), // input: root accelerations + &td.blade_nodes.n_mesh_points, // input: number of mesh points + td.blade_nodes.position.data()->data(), // input: mesh positions + td.blade_nodes.orientation.data()->data(), // input: mesh orientations + td.blade_nodes.velocity.data()->data(), // input: mesh velocities + td.blade_nodes.acceleration.data()->data(), // input: mesh accelerations + &error_handling_.error_status, // output: error status + error_handling_.error_message.data() // output: error message buffer ); - }; - - // Hub and nacelle (1 point each) - auto [hub_pos_flat, hub_orient_flat, hub_vel_flat, hub_acc_flat] = - flatten_and_validate(hub_motion, 1); - auto [nacelle_pos_flat, nacelle_orient_flat, nacelle_vel_flat, nacelle_acc_flat] = - flatten_and_validate(nacelle_motion, 1); - - // Root (n_blades points) - auto [root_pos_flat, root_orient_flat, root_vel_flat, root_acc_flat] = - flatten_and_validate(root_motion, turbine_settings_.n_blades); - - // Mesh (n_mesh_points) - auto [mesh_pos_flat, mesh_orient_flat, mesh_vel_flat, mesh_acc_flat] = - flatten_and_validate(mesh_motion, structural_mesh_.n_mesh_points); - - ADI_C_SetRotorMotion( - &turbine_number, // input: current turbine number - hub_pos_flat.data(), // input: hub positions - hub_orient_flat.data(), // input: hub orientations - hub_vel_flat.data(), // input: hub velocities - hub_acc_flat.data(), // input: hub accelerations - nacelle_pos_flat.data(), // input: nacelle positions - nacelle_orient_flat.data(), // input: nacelle orientations - nacelle_vel_flat.data(), // input: nacelle velocities - nacelle_acc_flat.data(), // input: nacelle accelerations - root_pos_flat.data(), // input: root positions - root_orient_flat.data(), // input: root orientations - root_vel_flat.data(), // input: root velocities - root_acc_flat.data(), // input: root accelerations - &structural_mesh_.n_mesh_points, // input: number of mesh points - mesh_pos_flat.data(), // input: mesh positions - mesh_orient_flat.data(), // input: mesh orientations - mesh_vel_flat.data(), // input: mesh velocities - mesh_acc_flat.data(), // input: mesh accelerations - &error_handling_.error_status, // output: error status - error_handling_.error_message.data() // output: error message buffer - ); - error_handling_.CheckError(); + error_handling_.CheckError(); + } } /** * @brief Gets the aerodynamic loads on the rotor * - * @details This function gets the aerodynamic loads on the rotor by passing the mesh - * force/moment array to the Fortran routine. - * - * @param turbine_number Number of the current turbine - * @param mesh_force_moment Mesh force/moment array + * @details Fetches the current aerodynamic forces and moments acting on each blade node + * for all turbines in the simulation. */ - void GetRotorAerodynamicLoads( - int turbine_number, std::vector>& mesh_force_moment - ) { + void GetRotorAerodynamicLoads() { auto ADI_C_GetRotorLoads = lib_.get_function("ADI_C_GetRotorLoads"); - // Ensure the input vector has the correct size - if (mesh_force_moment.size() != static_cast(structural_mesh_.n_mesh_points)) { - throw std::invalid_argument( - "mesh_force_moment size (" + std::to_string(mesh_force_moment.size()) + - ") does not match n_mesh_points (" + std::to_string(structural_mesh_.n_mesh_points) + - ")" + // Loop through turbines and get the rotor loads + int32_t turbine_number{0}; + for (auto& td : turbines_) { + // Turbine number is 1 indexed i.e. 1, 2, 3, ... + ++turbine_number; + + ADI_C_GetRotorLoads( + &turbine_number, // input: current turbine number + &td.blade_nodes.n_mesh_points, // input: number of mesh points + td.blade_nodes.loads.data()->data(), // output: mesh force/moment array + &error_handling_.error_status, // output: error status + error_handling_.error_message.data() // output: error message buffer ); - } - // Flatten the mesh force/moment array - auto mesh_force_moment_flat = FlattenArray(mesh_force_moment); - - ADI_C_GetRotorLoads( - &turbine_number, // input: current turbine number - &structural_mesh_.n_mesh_points, // input: number of mesh points - mesh_force_moment_flat.data(), // output: mesh force/moment array - &error_handling_.error_status, // output: error status - error_handling_.error_message.data() // output: error message buffer - ); - - error_handling_.CheckError(); - - // Copy the flattened array back to the original array - mesh_force_moment = UnflattenArray(mesh_force_moment_flat); + error_handling_.CheckError(); + } } /** @@ -1093,6 +795,7 @@ class AeroDynInflowLibrary { ); error_handling_.CheckError(); + is_initialized_ = false; } private: @@ -1101,10 +804,9 @@ class AeroDynInflowLibrary { ErrorHandling error_handling_; //< Error handling settings FluidProperties air_; //< Properties of the working fluid (air) EnvironmentalConditions env_conditions_; //< Environmental conditions - TurbineSettings turbine_settings_; //< Turbine settings - StructuralMesh structural_mesh_; //< Structural mesh data SimulationControls sim_controls_; //< Simulation control settings VTKSettings vtk_settings_; //< VTK output settings + std::vector turbines_; //< Turbine data (1 per turbine) }; } // namespace openturbine::util diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index 1c80d041..0fd63171 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -82,166 +82,6 @@ TEST(AerodynInflowTest, SetPositionAndOrientation) { ExpectArrayNear(orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.}); } -TEST(AerodynInflowTest, TurbineSettings_Default) { - util::TurbineSettings turbine_settings; - EXPECT_EQ(turbine_settings.n_turbines, 1); - EXPECT_EQ(turbine_settings.n_blades, 3); - ExpectArrayNear(turbine_settings.initial_hub_position, {0.f, 0.f, 0.f}); - ExpectArrayNear(turbine_settings.initial_hub_orientation, {1., 0., 0., 0., 1., 0., 0., 0., 1.}); - ExpectArrayNear(turbine_settings.initial_nacelle_position, {0.f, 0.f, 0.f}); - ExpectArrayNear( - turbine_settings.initial_nacelle_orientation, {1., 0., 0., 0., 1., 0., 0., 0., 1.} - ); - for (size_t i = 0; i < turbine_settings.initial_root_position.size(); ++i) { - ExpectArrayNear(turbine_settings.initial_root_position[i], {0.f, 0.f, 0.f}); - ExpectArrayNear( - turbine_settings.initial_root_orientation[i], {1., 0., 0., 0., 1., 0., 0., 0., 1.} - ); - } -} - -TEST(AerodynInflowTest, TurbineSettings_Set_1T1B) { - int n_turbines{1}; - int n_blades{1}; - std::array hub_data = {1., 2., 3., 0.707107, 0.707107, 0., 0.}; - std::array nacelle_data = {4., 5., 6., 0.707107, 0.707107, 0., 0.}; - std::vector> root_data = {{7., 8., 9., 0.707107, 0.707107, 0., 0.}}; - - util::TurbineSettings turbine_settings(hub_data, nacelle_data, root_data, n_turbines, n_blades); - - EXPECT_EQ(turbine_settings.n_turbines, 1); - EXPECT_EQ(turbine_settings.n_blades, 1); - ExpectArrayNear(turbine_settings.initial_hub_position, {1.f, 2.f, 3.f}); - ExpectArrayNear(turbine_settings.initial_hub_orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.}); - ExpectArrayNear(turbine_settings.initial_nacelle_position, {4.f, 5.f, 6.f}); - ExpectArrayNear( - turbine_settings.initial_nacelle_orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.} - ); - ExpectArrayNear(turbine_settings.initial_root_position[0], {7.f, 8.f, 9.f}); - ExpectArrayNear( - turbine_settings.initial_root_orientation[0], {1., 0., 0., 0., 0., -1., 0., 1., 0.} - ); -} - -TEST(AerodynInflowTest, TurbineSettings_Validate_ExpectNoThrow) { - util::TurbineSettings turbine_settings; - turbine_settings.n_blades = 3; - turbine_settings.initial_root_position = {{0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}}; - turbine_settings.initial_root_orientation = { - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; - - EXPECT_NO_THROW(turbine_settings.Validate()); -} - -TEST(AerodynInflowTest, TurbineSettings_Validate_InvalidBlades) { - util::TurbineSettings turbine_settings; - turbine_settings.n_blades = 0; // Invalid number of blades - - EXPECT_THROW(turbine_settings.Validate(), std::runtime_error); -} - -TEST(AerodynInflowTest, TurbineSettings_Validate_InvalidRootPositionSize) { - util::TurbineSettings turbine_settings; - turbine_settings.n_blades = 3; - turbine_settings.initial_root_position = {{0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}}; // Only 2 positions - - EXPECT_THROW(turbine_settings.Validate(), std::invalid_argument); -} - -TEST(AerodynInflowTest, TurbineSettings_Validate_InvalidRootOrientationSize) { - util::TurbineSettings turbine_settings; - turbine_settings.n_blades = 3; - turbine_settings.initial_root_position = {{0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}, {0.f, 0.f, 0.f}}; - turbine_settings.initial_root_orientation = { - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.} // Only 2 orientations - }; - - EXPECT_THROW(turbine_settings.Validate(), std::invalid_argument); -} - -TEST(AerodynInflowTest, StructuralMesh_Default) { - util::StructuralMesh structural_mesh; - EXPECT_EQ(structural_mesh.n_mesh_points, 1); - EXPECT_EQ(structural_mesh.initial_mesh_position.size(), 1); - EXPECT_EQ(structural_mesh.initial_mesh_orientation.size(), 1); - EXPECT_EQ(structural_mesh.mesh_point_to_blade_num.size(), 1); - ExpectArrayNear(structural_mesh.initial_mesh_position[0], {0.f, 0.f, 0.f}); - ExpectArrayNear( - structural_mesh.initial_mesh_orientation[0], {1., 0., 0., 0., 1., 0., 0., 0., 1.} - ); - EXPECT_EQ(structural_mesh.mesh_point_to_blade_num[0], 1); -} - -TEST(AerodynInflowTest, StructuralMesh_Set) { - std::vector> mesh_data = {{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; - std::vector mesh_point_to_blade_num = {1}; - util::StructuralMesh structural_mesh(mesh_data, mesh_point_to_blade_num, 1); - - EXPECT_EQ(structural_mesh.n_mesh_points, 1); - EXPECT_EQ(structural_mesh.initial_mesh_position.size(), 1); - EXPECT_EQ(structural_mesh.initial_mesh_orientation.size(), 1); - EXPECT_EQ(structural_mesh.mesh_point_to_blade_num.size(), 1); - ExpectArrayNear(structural_mesh.initial_mesh_position[0], {1.f, 2.f, 3.f}); - ExpectArrayNear( - structural_mesh.initial_mesh_orientation[0], {1., 0., 0., 0., 0., -1., 0., 1., 0.} - ); - EXPECT_EQ(structural_mesh.mesh_point_to_blade_num[0], 1); -} - -TEST(AerodynInflowTest, StructuralMesh_Validate_ExpectNoThrow) { - util::StructuralMesh structural_mesh; - structural_mesh.n_mesh_points = 3; - structural_mesh.initial_mesh_position = {{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {2.f, 2.f, 2.f}}; - structural_mesh.initial_mesh_orientation = { - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; - structural_mesh.mesh_point_to_blade_num = {1, 2, 3}; - - EXPECT_NO_THROW(structural_mesh.Validate()); -} - -TEST(AerodynInflowTest, StructuralMesh_Validate_InvalidMeshPositionSize) { - util::StructuralMesh structural_mesh; - structural_mesh.n_mesh_points = 3; - structural_mesh.initial_mesh_position = {{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}; // Only 2 positions - structural_mesh.initial_mesh_orientation = { - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; - structural_mesh.mesh_point_to_blade_num = {1, 2, 3}; - - EXPECT_THROW(structural_mesh.Validate(), std::invalid_argument); -} - -TEST(AerodynInflowTest, StructuralMesh_Validate_InvalidMeshOrientationSize) { - util::StructuralMesh structural_mesh; - structural_mesh.n_mesh_points = 3; - structural_mesh.initial_mesh_position = {{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {2.f, 2.f, 2.f}}; - structural_mesh.initial_mesh_orientation = { - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.} // Only 2 orientations - }; - structural_mesh.mesh_point_to_blade_num = {1, 2, 3}; - EXPECT_THROW(structural_mesh.Validate(), std::invalid_argument); -} - -TEST(AerodynInflowTest, StructuralMesh_Validate_MismatchedBladeNumbers) { - util::StructuralMesh structural_mesh; - structural_mesh.n_mesh_points = 3; - structural_mesh.initial_mesh_position = {{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}, {2.f, 2.f, 2.f}}; - structural_mesh.initial_mesh_orientation = { - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, - {1., 0., 0., 0., 1., 0., 0., 0., 1.}}; - structural_mesh.mesh_point_to_blade_num = {1, 2}; // Only 2 blade numbers - - EXPECT_THROW(structural_mesh.Validate(), std::invalid_argument); -} - TEST(AerodynInflowTest, MeshData_Constructor_NumberOfNodes) { util::MeshData mesh_motion_data{1}; EXPECT_EQ(mesh_motion_data.n_mesh_points, 1); @@ -366,26 +206,6 @@ TEST(AerodynInflowTest, MeshData_CheckArraySize_ExpectThrow) { ); } -TEST(AerodynInflowTest, SimulationControls_Default) { - util::SimulationControls simulation_controls; - EXPECT_EQ(simulation_controls.aerodyn_input_passed, 1); - EXPECT_EQ(simulation_controls.inflowwind_input_passed, 1); - EXPECT_EQ(simulation_controls.interpolation_order, 1); - EXPECT_EQ(simulation_controls.time_step, 0.1); - EXPECT_EQ(simulation_controls.max_time, 600.0); - EXPECT_EQ(simulation_controls.total_elapsed_time, 0.0); - EXPECT_EQ(simulation_controls.n_time_steps, 0); - EXPECT_EQ(simulation_controls.store_HH_wind_speed, 1); - EXPECT_EQ(simulation_controls.transpose_DCM, 1); - EXPECT_EQ(simulation_controls.debug_level, 0); - EXPECT_EQ(simulation_controls.output_format, 0); - EXPECT_EQ(simulation_controls.output_time_step, 0.0); - EXPECT_STREQ(simulation_controls.output_root_name.data(), "Output_ADIlib_default"); - EXPECT_EQ(simulation_controls.n_channels, 0); - EXPECT_STREQ(simulation_controls.channel_names_c.data(), ""); - EXPECT_STREQ(simulation_controls.channel_units_c.data(), ""); -} - TEST(AerodynInflowTest, TurbineData_Constructor) { // Set up 3 blades with 2 nodes each std::vector blade_states; @@ -465,10 +285,30 @@ TEST(AerodynInflowTest, TurbineData_Constructor) { } } +TEST(AerodynInflowTest, SimulationControls_Default) { + util::SimulationControls simulation_controls; + EXPECT_EQ(simulation_controls.is_aerodyn_input_path, true); + EXPECT_EQ(simulation_controls.is_inflowwind_input_path, true); + EXPECT_EQ(simulation_controls.interpolation_order, 1); + EXPECT_EQ(simulation_controls.time_step, 0.1); + EXPECT_EQ(simulation_controls.max_time, 600.0); + EXPECT_EQ(simulation_controls.total_elapsed_time, 0.0); + EXPECT_EQ(simulation_controls.n_time_steps, 0); + EXPECT_EQ(simulation_controls.store_HH_wind_speed, 1); + EXPECT_EQ(simulation_controls.transpose_DCM, 1); + EXPECT_EQ(simulation_controls.debug_level, 0); + EXPECT_EQ(simulation_controls.output_format, 1); + EXPECT_EQ(simulation_controls.output_time_step, 0.1); + EXPECT_STREQ(simulation_controls.output_root_name.data(), "ADI_out"); + EXPECT_EQ(simulation_controls.n_channels, 0); + EXPECT_STREQ(simulation_controls.channel_names_c.data(), ""); + EXPECT_STREQ(simulation_controls.channel_units_c.data(), ""); +} + TEST(AerodynInflowTest, SimulationControls_Set) { util::SimulationControls simulation_controls; - simulation_controls.aerodyn_input_passed = 0; - simulation_controls.inflowwind_input_passed = 0; + simulation_controls.is_aerodyn_input_path = false; + simulation_controls.is_inflowwind_input_path = false; simulation_controls.interpolation_order = 2; simulation_controls.time_step = 0.2; simulation_controls.max_time = 1200.0; @@ -479,11 +319,6 @@ TEST(AerodynInflowTest, SimulationControls_Set) { simulation_controls.debug_level = 1; simulation_controls.output_format = 1; simulation_controls.output_time_step = 1.0; - std::strncpy( - simulation_controls.output_root_name.data(), "Output_ADIlib_test", - simulation_controls.output_root_name.size() - 1 - ); - simulation_controls.output_root_name[simulation_controls.output_root_name.size() - 1] = '\0'; simulation_controls.n_channels = 1; std::strncpy( simulation_controls.channel_names_c.data(), "test_channel", @@ -496,8 +331,8 @@ TEST(AerodynInflowTest, SimulationControls_Set) { ); simulation_controls.channel_units_c[simulation_controls.channel_units_c.size() - 1] = '\0'; - EXPECT_EQ(simulation_controls.aerodyn_input_passed, 0); - EXPECT_EQ(simulation_controls.inflowwind_input_passed, 0); + EXPECT_EQ(simulation_controls.is_aerodyn_input_path, 0); + EXPECT_EQ(simulation_controls.is_inflowwind_input_path, 0); EXPECT_EQ(simulation_controls.interpolation_order, 2); EXPECT_EQ(simulation_controls.time_step, 0.2); EXPECT_EQ(simulation_controls.max_time, 1200.0); @@ -508,7 +343,7 @@ TEST(AerodynInflowTest, SimulationControls_Set) { EXPECT_EQ(simulation_controls.debug_level, 1); EXPECT_EQ(simulation_controls.output_format, 1); EXPECT_EQ(simulation_controls.output_time_step, 1.0); - EXPECT_STREQ(simulation_controls.output_root_name.data(), "Output_ADIlib_test"); + EXPECT_STREQ(simulation_controls.output_root_name.data(), "ADI_out"); EXPECT_EQ(simulation_controls.n_channels, 1); EXPECT_STREQ(simulation_controls.channel_names_c.data(), "test_channel"); EXPECT_STREQ(simulation_controls.channel_units_c.data(), "test_unit"); @@ -535,116 +370,6 @@ TEST(AerodynInflowTest, VTKSettings_Set) { EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.0f); } -TEST(AerodynInflowTest, FlattenArray) { - std::vector> input = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; - std::vector expected = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; - - auto result = util::FlattenArray(input); - - ASSERT_EQ(result.size(), expected.size()); - for (size_t i = 0; i < expected.size(); ++i) { - EXPECT_FLOAT_EQ(result[i], expected[i]); - } -} - -TEST(AerodynInflowTest, ValidateAndFlattenArray_ValidPositionArray) { - std::vector> position_array = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; - size_t expected_size = 2; - std::vector expected = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; - - auto result = util::ValidateAndFlattenArray(position_array, expected_size); - ASSERT_EQ(result, expected); -} - -TEST(AerodynInflowTest, ValidateAndFlattenArray_ValidOrientationArray) { - std::vector> orientation_array = { - {1., 2., 3., 4., 5., 6., 7., 8., 9.}, {10., 11., 12., 13., 14., 15., 16., 17., 18.}}; - size_t expected_size = 2; - std::vector expected = {1., 2., 3., 4., 5., 6., 7., 8., 9., - 10., 11., 12., 13., 14., 15., 16., 17., 18.}; - - auto result = util::ValidateAndFlattenArray(orientation_array, expected_size); - ASSERT_EQ(result, expected); -} - -TEST(AerodynInflowTest, ValidateAndFlattenArray_ValidVelocityAccelerationArray) { - std::vector> velocity_array = { - {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}, {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; - size_t expected_size = 2; - std::vector expected = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 11.f, 12.f}; - - auto result = util::ValidateAndFlattenArray(velocity_array, expected_size); - ASSERT_EQ(result, expected); -} - -TEST(AerodynInflowTest, ValidateAndFlattenArray_InvalidArraySize) { - std::vector> position_array = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; - size_t expected_size = 3; // Incorrect size - - EXPECT_THROW( - { util::ValidateAndFlattenArray(position_array, expected_size); }, std::runtime_error - ); -} - -TEST(AerodynInflowTest, ValidateAndFlattenArray_UnknownArrayType) { - std::vector> unknown_array = {{1, 2}, {3, 4}}; - size_t expected_size = 2; - - auto result = util::ValidateAndFlattenArray(unknown_array, expected_size); - std::vector expected = {1, 2, 3, 4}; - ASSERT_EQ(result, expected); -} - -TEST(AerodynInflowTest, UnflattenArray) { - std::vector input = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; - std::vector> expected = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; - - auto result = util::UnflattenArray(input); - - ASSERT_EQ(result.size(), expected.size()); - for (size_t i = 0; i < expected.size(); ++i) { - EXPECT_EQ(result[i], expected[i]); - } -} - -TEST(AerodynInflowTest, JoinStringArray_NormalCase) { - std::vector input = {"apple", "banana", "cherry"}; - std::string expected = "apple,banana,cherry"; - EXPECT_EQ(util::JoinStringArray(input, ','), expected); -} - -TEST(AerodynInflowTest, JoinStringArray_EmptyInput) { - std::vector input = {}; - std::string expected = ""; - EXPECT_EQ(util::JoinStringArray(input, ','), expected); -} - -TEST(AerodynInflowTest, JoinStringArray_SingleElement) { - std::vector input = {"solo"}; - std::string expected = "solo"; - EXPECT_EQ(util::JoinStringArray(input, ','), expected); -} - -TEST(AerodynInflowTest, JoinStringArray_DifferentDelimiter) { - std::vector input = {"one", "two", "three"}; - std::string expected = "one|two|three"; - EXPECT_EQ(util::JoinStringArray(input, '|'), expected); -} - -TEST(AerodynInflowTest, JoinStringArray_StringsContainingDelimiter) { - std::vector input = {"com,ma", "semi;colon", "pipe|symbol"}; - std::string expected = "com,ma;semi;colon;pipe|symbol"; - EXPECT_EQ(util::JoinStringArray(input, ';'), expected); -} - -// Add a test with null characters in the input strings -TEST(AerodynInflowTest, JoinStringArray_NullCharacters) { - std::vector input = {"one", "two", "three"}; - std::string expected = "one\0two\0three"; - util::JoinStringArray(input, '\0'); - // EXPECT_EQ(util::JoinStringArray(input, '\0'), expected); -} - /// Helper function to get the shared library path std::string GetSharedLibraryPath() { const std::filesystem::path project_root = FindProjectRoot(); @@ -665,18 +390,16 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { // Check default values for other important members EXPECT_EQ(aerodyn_inflow_library.GetFluidProperties().density, 1.225f); EXPECT_EQ(aerodyn_inflow_library.GetEnvironmentalConditions().gravity, 9.80665f); - EXPECT_EQ(aerodyn_inflow_library.GetTurbineSettings().n_turbines, 1); EXPECT_EQ(aerodyn_inflow_library.GetSimulationControls().debug_level, 0); EXPECT_EQ(aerodyn_inflow_library.GetSimulationControls().transpose_DCM, 1); - EXPECT_EQ(aerodyn_inflow_library.GetStructuralMesh().n_mesh_points, 1); EXPECT_EQ(aerodyn_inflow_library.GetVTKSettings().write_vtk, 0); } -TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { +/* TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { // Set up simulation parameters util::SimulationControls sim_controls{ - .aerodyn_input_passed = false, - .inflowwind_input_passed = false, + .is_aerodyn_input_path = false, + .is_inflowwind_input_path = false, .time_step = 0.0125, .max_time = 10.0, .interpolation_order = 2, @@ -757,6 +480,6 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { // End simulation EXPECT_NO_THROW(aerodyn_inflow_library.Finalize()); -} +} */ } // namespace openturbine::tests \ No newline at end of file From cbe7ece5200ae18c045ab6100200f7fadcd808bf Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 4 Oct 2024 13:12:31 -0600 Subject: [PATCH 71/87] Add Validate() methods for structs TurbineConfig and TurbineData --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 66 +++++++++++++--- .../external/test_aerodyn_inflow.cpp | 79 +++++++++++++++++++ 2 files changed, 135 insertions(+), 10 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 4e354987..79b0655f 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -103,7 +103,6 @@ struct TurbineConfig { Array_7 root_initial_position; //< Initial root position of the blade std::vector node_initial_positions; //< Initial node positions of the blade - /// Constructor to initialize all data based on provided inputs BladeInitialState(const Array_7& root, const std::vector& nodes) : root_initial_position(root), node_initial_positions(nodes) {} }; @@ -115,16 +114,31 @@ struct TurbineConfig { std::vector blade_initial_states; //< Initial root and node positions of blades - /// Constructor to initialize all data based on provided inputs TurbineConfig( - bool is_hawt, const std::array& ref_pos, const Array_7& hub_pos, - const Array_7& nacelle_pos, const std::vector& blade_states + bool is_hawt, std::array ref_pos, Array_7 hub_pos, Array_7 nacelle_pos, + std::vector blade_states ) : is_horizontal_axis(is_hawt), - reference_position(ref_pos), - hub_initial_position(hub_pos), - nacelle_initial_position(nacelle_pos), - blade_initial_states(blade_states) {} + reference_position(std::move(ref_pos)), + hub_initial_position(std::move(hub_pos)), + nacelle_initial_position(std::move(nacelle_pos)), + blade_initial_states(std::move(blade_states)) { + Validate(); + } + + void Validate() const { + if (blade_initial_states.empty()) { + throw std::runtime_error("No blades defined. At least one blade is required."); + } + + for (const auto& blade : blade_initial_states) { + if (blade.node_initial_positions.empty()) { + throw std::runtime_error( + "No nodes defined for a blade. At least one node is required." + ); + } + } + } }; /** @@ -295,7 +309,7 @@ struct TurbineData { MeshData hub; //< Hub data (1 point) MeshData nacelle; //< Nacelle data (1 point) MeshData blade_roots; //< Blade roots data (n_blades points) - MeshData blade_nodes; //< Blade nodes data + MeshData blade_nodes; //< Blade nodes data (sum of nodes per blade) /** * @brief Mapping of blade nodes to blade numbers (1D array) * @@ -315,7 +329,6 @@ struct TurbineData { */ std::vector> node_indices_by_blade; - /// Constructor to initialize TurbineData from TurbineConfig TurbineData(const TurbineConfig& tc) : n_blades(static_cast(tc.blade_initial_states.size())), hub(1), @@ -325,6 +338,39 @@ struct TurbineData { node_indices_by_blade(tc.blade_initial_states.size()) { InitializeHubAndNacelle(tc); InitializeBlades(tc); + Validate(); + } + + void Validate() const { + if (n_blades < 1) { + throw std::runtime_error("Invalid number of blades. Must be at least 1."); + } + + if (blade_roots.n_mesh_points != n_blades) { + throw std::runtime_error("Number of blade roots does not match number of blades."); + } + + if (blade_nodes_to_blade_num_mapping.size() != + static_cast(blade_nodes.n_mesh_points)) { + throw std::runtime_error("Blade node to blade number mapping size mismatch."); + } + + if (node_indices_by_blade.size() != static_cast(n_blades)) { + throw std::runtime_error("Node indices by blade size mismatch."); + } + + size_t total_nodes = 0; + for (const auto& bn : node_indices_by_blade) { + total_nodes += bn.size(); + } + if (total_nodes != static_cast(blade_nodes.n_mesh_points)) { + throw std::runtime_error("Total number of blade nodes mismatch."); + } + + // Validate hub and nacelle data + if (hub.n_mesh_points != 1 || nacelle.n_mesh_points != 1) { + throw std::runtime_error("Hub and nacelle should have exactly one mesh point each."); + } } private: diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index 0fd63171..997474c7 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -163,6 +163,20 @@ TEST(AerodynInflowTest, TurbineConfig_Constructor) { } } +TEST(AerodynInflowTest, TurbineConfig_Validate_InvalidConfiguration) { + // Invalid configuration: No blades + bool is_hawt{true}; + std::array ref_pos{10.f, 20.f, 30.f}; + Array_7 hub_pos{1., 2., 3., 1., 0., 0., 0.}; + Array_7 nacelle_pos{4., 5., 6., 1., 0., 0., 0.}; + std::vector empty_blade_states; + + EXPECT_THROW( + util::TurbineConfig(is_hawt, ref_pos, hub_pos, nacelle_pos, empty_blade_states), + std::runtime_error + ); +} + TEST(AerodynInflowTest, MeshData_CheckArraySize_NoThrow) { size_t n_mesh_points{1}; std::vector> mesh_data{{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; @@ -285,6 +299,71 @@ TEST(AerodynInflowTest, TurbineData_Constructor) { } } +class TurbineDataValidationTest : public ::testing::Test { +protected: + void SetUp() override { + blade_states.clear(); + // Set up 3 blades with 2 nodes each + for (size_t i = 0; i < 3; ++i) { + util::TurbineConfig::BladeInitialState blade_state( + {0., 0., 90., 1., 0., 0., 0.}, // root_initial_position + { + {0., 5., 90., 1., 0., 0., 0.}, // node_initial_positions - 1 + {0., 10., 90., 1., 0., 0., 0.} // node_initial_positions - 2 + } + ); + blade_states.push_back(blade_state); + } + tc = std::make_unique( + true, // is_horizontal_axis + std::array{0.f, 0.f, 0.f}, // reference_position + std::array{0., 0., 90., 1., 0., 0., 0.}, // hub_initial_position + std::array{0., 0., 90., 1., 0., 0., 0.}, // nacelle_initial_position + blade_states + ); + turbine_data = std::make_unique(*tc); + } + + std::vector blade_states; + std::unique_ptr tc; + std::unique_ptr turbine_data; +}; + +TEST_F(TurbineDataValidationTest, InvalidNumberOfBlades) { + turbine_data->n_blades = 0; // Should be at least 1 + EXPECT_THROW(turbine_data->Validate(), std::runtime_error); +} + +TEST_F(TurbineDataValidationTest, MismatchBladeRoots) { + turbine_data->blade_roots.n_mesh_points = 2; // Should be 3 + EXPECT_THROW(turbine_data->Validate(), std::runtime_error); +} + +TEST_F(TurbineDataValidationTest, MismatchBladeNodeMapping) { + turbine_data->blade_nodes_to_blade_num_mapping.pop_back(); + EXPECT_THROW(turbine_data->Validate(), std::runtime_error); +} + +TEST_F(TurbineDataValidationTest, MismatchNodeIndices) { + turbine_data->node_indices_by_blade.pop_back(); + EXPECT_THROW(turbine_data->Validate(), std::runtime_error); +} + +TEST_F(TurbineDataValidationTest, MismatchTotalBladeNodes) { + turbine_data->blade_nodes.n_mesh_points = 5; // Should be 6 + EXPECT_THROW(turbine_data->Validate(), std::runtime_error); +} + +TEST_F(TurbineDataValidationTest, InvalidHubMeshPoints) { + turbine_data->hub.n_mesh_points = 2; // Should be 1 + EXPECT_THROW(turbine_data->Validate(), std::runtime_error); +} + +TEST_F(TurbineDataValidationTest, InvalidNacelleMeshPoints) { + turbine_data->nacelle.n_mesh_points = 0; // Should be 1 + EXPECT_THROW(turbine_data->Validate(), std::runtime_error); +} + TEST(AerodynInflowTest, SimulationControls_Default) { util::SimulationControls simulation_controls; EXPECT_EQ(simulation_controls.is_aerodyn_input_path, true); From c53f9b13241fb49efc242dd2d6d3760d813f117e Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 4 Oct 2024 13:51:25 -0600 Subject: [PATCH 72/87] Add Validate() method for MeshData class and remove the redundant CheckArraySize etc checks --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 99 +++------- .../external/test_aerodyn_inflow.cpp | 176 +++++++++--------- 2 files changed, 115 insertions(+), 160 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 79b0655f..8bbb3b0f 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -215,86 +215,37 @@ struct MeshData { for (size_t i = 0; i < n_mesh_points; ++i) { SetPositionAndOrientation(mesh_data[i], position[i], orientation[i]); } - } - /** - * @brief Method to check the dimensions of the input arrays - * - * @param array The input array to check - * @param expected_rows The expected number of rows - * @param expected_cols The expected number of columns - * @param array_name The name of the array - * @param node_label The label of the node (e.g., "hub", "nacelle", "root", "mesh") - */ - template - void CheckArraySize( - const std::vector>& array, size_t expected_rows, size_t expected_cols, - const std::string& array_name, const std::string& node_label - ) const { - if (array.size() != expected_rows) { - throw std::invalid_argument( - "Expecting a " + std::to_string(expected_rows) + "x" + - std::to_string(expected_cols) + " array of " + node_label + " " + array_name + - " with " + std::to_string(expected_rows) + " rows, but got " + - std::to_string(array.size()) + " rows." - ); - } + Validate(); + } - if (!array.empty() && array[0].size() != expected_cols) { - throw std::invalid_argument( - "Expecting a " + std::to_string(expected_rows) + "x" + - std::to_string(expected_cols) + " array of " + node_label + " " + array_name + - " with " + std::to_string(expected_cols) + " columns, but got " + - std::to_string(array[0].size()) + " columns." - ); + /// Validate the consistency and validity of the mesh data + void Validate() const { + if (n_mesh_points <= 0) { + throw std::invalid_argument("Number of mesh points must be at least 1"); } - } - void CheckInputMotions( - const std::string& node_label, size_t expected_number_of_nodes, - size_t expected_position_dim = 3, size_t expected_orientation_dim = 9, - size_t expected_vel_acc_dim = 6 - ) const { - CheckArraySize( - position, expected_number_of_nodes, expected_position_dim, "positions", node_label - ); - CheckArraySize( - orientation, expected_number_of_nodes, expected_orientation_dim, "orientations", - node_label - ); - CheckArraySize( - velocity, expected_number_of_nodes, expected_vel_acc_dim, "velocities", node_label - ); - CheckArraySize( - acceleration, expected_number_of_nodes, expected_vel_acc_dim, "accelerations", node_label - ); - } + const size_t expected_size = static_cast(n_mesh_points); - void CheckNodeInputMotions( - const std::string& node_name, size_t expected_number_of_nodes, size_t initial_number_of_nodes - ) const { - if (expected_number_of_nodes != initial_number_of_nodes) { - throw std::invalid_argument( - "The number of " + node_name + " points changed from the initial value of " + - std::to_string(initial_number_of_nodes) + " to " + - std::to_string(expected_number_of_nodes) + - ". This is not permitted during the simulation." - ); + if (position.size() != expected_size) { + throw std::invalid_argument("Position vector size does not match n_mesh_points"); } - CheckInputMotions(node_name, expected_number_of_nodes); - } + if (orientation.size() != expected_size) { + throw std::invalid_argument("Orientation vector size does not match n_mesh_points"); + } - void CheckHubNacelleInputMotions(const std::string& node_name) const { - CheckInputMotions(node_name, 1); - } + if (velocity.size() != expected_size) { + throw std::invalid_argument("Velocity vector size does not match n_mesh_points"); + } - void CheckRootInputMotions(size_t n_blades, size_t init_n_blades) const { - CheckNodeInputMotions("root", n_blades, init_n_blades); - } + if (acceleration.size() != expected_size) { + throw std::invalid_argument("Acceleration vector size does not match n_mesh_points"); + } - void CheckMeshInputMotions(size_t n_mesh_pts, size_t init_n_mesh_pts) const { - CheckNodeInputMotions("mesh", n_mesh_pts, init_n_mesh_pts); + if (loads.size() != expected_size) { + throw std::invalid_argument("Loads vector size does not match n_mesh_points"); + } } }; @@ -601,14 +552,14 @@ class AeroDynInflowLibrary { // Turbine number is 1 indexed i.e. 1, 2, 3, ... ++turbine_number; - // Validate the turbine config - // turbine_settings_.Validate(); - // structural_mesh_.Validate(); - // Convert bool -> int to pass to the Fortran routine int32_t is_horizontal_axis_int = tc.is_horizontal_axis ? 1 : 0; + // Validate the turbine config + tc.Validate(); + // Create new turbine data + // Note: TurbineData and MeshData are validated during construction turbines_.emplace_back(TurbineData(tc)); auto& td = turbines_.back(); diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index 997474c7..fb1bccaa 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -59,62 +59,6 @@ TEST(AerodynInflowTest, EnvironmentalConditions_Set) { EXPECT_NEAR(environmental_conditions.msl_offset, 10.f, 1e-6f); } -/// Check if members of the provided array is equal to the provided expected array -template -void ExpectArrayNear( - const std::array& actual, const std::array& expected, - T epsilon = static_cast(1e-6) -) { - ASSERT_EQ(actual.size(), expected.size()); - for (size_t i = 0; i < N; ++i) { - EXPECT_NEAR(actual[i], expected[i], epsilon) << "Element mismatch at index " << i; - } -} - -TEST(AerodynInflowTest, SetPositionAndOrientation) { - std::array data = {1., 2., 3., 0.707107, 0.707107, 0., 0.}; - std::array position; - std::array orientation; - - util::SetPositionAndOrientation(data, position, orientation); - - ExpectArrayNear(position, {1.f, 2.f, 3.f}); - ExpectArrayNear(orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.}); -} - -TEST(AerodynInflowTest, MeshData_Constructor_NumberOfNodes) { - util::MeshData mesh_motion_data{1}; - EXPECT_EQ(mesh_motion_data.n_mesh_points, 1); - EXPECT_EQ(mesh_motion_data.position.size(), 1); - EXPECT_EQ(mesh_motion_data.orientation.size(), 1); - EXPECT_EQ(mesh_motion_data.velocity.size(), 1); - EXPECT_EQ(mesh_motion_data.acceleration.size(), 1); - EXPECT_EQ(mesh_motion_data.loads.size(), 1); -} - -TEST(AerodynInflowTest, MeshData_Constructor_Data) { - size_t n_mesh_points{1}; - std::vector> mesh_data{{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; - std::vector> mesh_velocities{{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; - std::vector> mesh_accelerations{{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; - std::vector> mesh_loads = {{13.f, 14.f, 15.f, 16.f, 17.f, 18.f}}; - util::MeshData mesh_motion_data( - n_mesh_points, mesh_data, mesh_velocities, mesh_accelerations, mesh_loads - ); - - EXPECT_EQ(mesh_motion_data.n_mesh_points, n_mesh_points); - EXPECT_EQ(mesh_motion_data.position.size(), n_mesh_points); - EXPECT_EQ(mesh_motion_data.orientation.size(), n_mesh_points); - EXPECT_EQ(mesh_motion_data.velocity.size(), n_mesh_points); - EXPECT_EQ(mesh_motion_data.acceleration.size(), n_mesh_points); - EXPECT_EQ(mesh_motion_data.loads.size(), n_mesh_points); - ExpectArrayNear(mesh_motion_data.position[0], {1.f, 2.f, 3.f}); - ExpectArrayNear(mesh_motion_data.orientation[0], {1., 0., 0., 0., 0., -1., 0., 1., 0.}); - ExpectArrayNear(mesh_motion_data.velocity[0], {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}); - ExpectArrayNear(mesh_motion_data.acceleration[0], {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}); - ExpectArrayNear(mesh_motion_data.loads[0], {13.f, 14.f, 15.f, 16.f, 17.f, 18.f}); -} - TEST(AerodynInflowTest, BladeInitialState_Constructor) { Array_7 root{1., 2., 3., 1., 0., 0., 0.}; std::vector nodes{{4., 5., 6., 1., 0., 0., 0.}, {7., 8., 9., 1., 0., 0., 0.}}; @@ -177,47 +121,107 @@ TEST(AerodynInflowTest, TurbineConfig_Validate_InvalidConfiguration) { ); } -TEST(AerodynInflowTest, MeshData_CheckArraySize_NoThrow) { - size_t n_mesh_points{1}; - std::vector> mesh_data{{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; - std::vector> mesh_velocities{{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; - std::vector> mesh_accelerations{{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; - std::vector> mesh_loads{{13.f, 14.f, 15.f, 16.f, 17.f, 18.f}}; - util::MeshData mesh_motion_data( - n_mesh_points, mesh_data, mesh_velocities, mesh_accelerations, mesh_loads - ); +/// Check if members of the provided array is equal to the provided expected array +template +void ExpectArrayNear( + const std::array& actual, const std::array& expected, + T epsilon = static_cast(1e-6) +) { + ASSERT_EQ(actual.size(), expected.size()); + for (size_t i = 0; i < N; ++i) { + EXPECT_NEAR(actual[i], expected[i], epsilon) << "Element mismatch at index " << i; + } +} - mesh_motion_data.CheckArraySize( - mesh_motion_data.position, n_mesh_points, 3, "position", "mesh motion data" - ); - mesh_motion_data.CheckArraySize( - mesh_motion_data.orientation, n_mesh_points, 9, "orientation", "mesh motion data" - ); - mesh_motion_data.CheckArraySize(mesh_motion_data.velocity, 1, 6, "velocity", "mesh motion data"); - mesh_motion_data.CheckArraySize( - mesh_motion_data.acceleration, n_mesh_points, 6, "acceleration", "mesh motion data" - ); - mesh_motion_data.CheckArraySize( - mesh_motion_data.loads, n_mesh_points, 6, "loads", "mesh motion data" - ); +TEST(AerodynInflowTest, SetPositionAndOrientation) { + std::array data = {1., 2., 3., 0.707107, 0.707107, 0., 0.}; + std::array position; + std::array orientation; + + util::SetPositionAndOrientation(data, position, orientation); + + ExpectArrayNear(position, {1.f, 2.f, 3.f}); + ExpectArrayNear(orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.}); +} + +TEST(AerodynInflowTest, MeshData_Constructor_NumberOfNodes) { + util::MeshData mesh_motion_data{1}; + EXPECT_EQ(mesh_motion_data.n_mesh_points, 1); + EXPECT_EQ(mesh_motion_data.position.size(), 1); + EXPECT_EQ(mesh_motion_data.orientation.size(), 1); + EXPECT_EQ(mesh_motion_data.velocity.size(), 1); + EXPECT_EQ(mesh_motion_data.acceleration.size(), 1); + EXPECT_EQ(mesh_motion_data.loads.size(), 1); } -TEST(AerodynInflowTest, MeshData_CheckArraySize_ExpectThrow) { +TEST(AerodynInflowTest, MeshData_Constructor_Data) { size_t n_mesh_points{1}; std::vector> mesh_data{{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; std::vector> mesh_velocities{{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; std::vector> mesh_accelerations{{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; - std::vector> mesh_loads{{13.f, 14.f, 15.f, 16.f, 17.f, 18.f}}; + std::vector> mesh_loads = {{13.f, 14.f, 15.f, 16.f, 17.f, 18.f}}; util::MeshData mesh_motion_data( n_mesh_points, mesh_data, mesh_velocities, mesh_accelerations, mesh_loads ); - EXPECT_THROW( - mesh_motion_data.CheckArraySize( - mesh_motion_data.position, 2, 3, "position", "mesh motion data" // Expected 1 row - ), - std::invalid_argument - ); + EXPECT_EQ(mesh_motion_data.n_mesh_points, n_mesh_points); + EXPECT_EQ(mesh_motion_data.position.size(), n_mesh_points); + EXPECT_EQ(mesh_motion_data.orientation.size(), n_mesh_points); + EXPECT_EQ(mesh_motion_data.velocity.size(), n_mesh_points); + EXPECT_EQ(mesh_motion_data.acceleration.size(), n_mesh_points); + EXPECT_EQ(mesh_motion_data.loads.size(), n_mesh_points); + ExpectArrayNear(mesh_motion_data.position[0], {1.f, 2.f, 3.f}); + ExpectArrayNear(mesh_motion_data.orientation[0], {1., 0., 0., 0., 0., -1., 0., 1., 0.}); + ExpectArrayNear(mesh_motion_data.velocity[0], {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}); + ExpectArrayNear(mesh_motion_data.acceleration[0], {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}); + ExpectArrayNear(mesh_motion_data.loads[0], {13.f, 14.f, 15.f, 16.f, 17.f, 18.f}); +} + +class MeshDataValidationTest : public ::testing::Test { +protected: + void SetUp() override { + // Create a mesh data with 2 mesh points + mesh_data = std::make_unique(2); + mesh_data->position = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; + mesh_data->orientation = { + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, {0., 1., 0., -1., 0., 0., 0., 0., 1.}}; + mesh_data->velocity = {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}, {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + mesh_data->acceleration = { + {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}, {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + mesh_data->loads = {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}, {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + } + + std::unique_ptr mesh_data; +}; + +TEST_F(MeshDataValidationTest, InvalidNumberOfMeshPoints) { + mesh_data->n_mesh_points = 0; // Should be at least 1 + EXPECT_THROW(mesh_data->Validate(), std::invalid_argument); +} + +TEST_F(MeshDataValidationTest, MismatchedPositionSize) { + mesh_data->position.pop_back(); + EXPECT_THROW(mesh_data->Validate(), std::invalid_argument); +} + +TEST_F(MeshDataValidationTest, MismatchedOrientationSize) { + mesh_data->orientation.pop_back(); + EXPECT_THROW(mesh_data->Validate(), std::invalid_argument); +} + +TEST_F(MeshDataValidationTest, MismatchedVelocitySize) { + mesh_data->velocity.pop_back(); + EXPECT_THROW(mesh_data->Validate(), std::invalid_argument); +} + +TEST_F(MeshDataValidationTest, MismatchedAccelerationSize) { + mesh_data->acceleration.pop_back(); + EXPECT_THROW(mesh_data->Validate(), std::invalid_argument); +} + +TEST_F(MeshDataValidationTest, MismatchedLoadsSize) { + mesh_data->loads.pop_back(); + EXPECT_THROW(mesh_data->Validate(), std::invalid_argument); } TEST(AerodynInflowTest, TurbineData_Constructor) { From 99b3805b05983853c9fd84686adf898c56938ce3 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 4 Oct 2024 18:02:56 -0600 Subject: [PATCH 73/87] Define a SetBladeNodeValues() in TurbineData struct to simplify the blade node def process --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 72 ++++++---- .../external/test_aerodyn_inflow.cpp | 132 ++++++++++++------ 2 files changed, 140 insertions(+), 64 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 8bbb3b0f..4f5be723 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -206,11 +206,6 @@ struct MeshData { velocity(std::move(velocities)), acceleration(std::move(accelerations)), loads(std::move(loads)) { - if (mesh_data.size() != n_mesh_points || velocities.size() != n_mesh_points || - accelerations.size() != n_mesh_points || loads.size() != n_mesh_points) { - throw std::invalid_argument("Input vector sizes must match n_mesh_points"); - } - // Set mesh position and orientation for (size_t i = 0; i < n_mesh_points; ++i) { SetPositionAndOrientation(mesh_data[i], position[i], orientation[i]); @@ -219,7 +214,6 @@ struct MeshData { Validate(); } - /// Validate the consistency and validity of the mesh data void Validate() const { if (n_mesh_points <= 0) { throw std::invalid_argument("Number of mesh points must be at least 1"); @@ -227,25 +221,18 @@ struct MeshData { const size_t expected_size = static_cast(n_mesh_points); - if (position.size() != expected_size) { - throw std::invalid_argument("Position vector size does not match n_mesh_points"); - } - - if (orientation.size() != expected_size) { - throw std::invalid_argument("Orientation vector size does not match n_mesh_points"); - } - - if (velocity.size() != expected_size) { - throw std::invalid_argument("Velocity vector size does not match n_mesh_points"); - } - - if (acceleration.size() != expected_size) { - throw std::invalid_argument("Acceleration vector size does not match n_mesh_points"); - } + auto check_vector_size = [](const auto& vec, size_t expected_size, + const std::string& vec_name) { + if (vec.size() != expected_size) { + throw std::invalid_argument(vec_name + " vector size does not match n_mesh_points"); + } + }; - if (loads.size() != expected_size) { - throw std::invalid_argument("Loads vector size does not match n_mesh_points"); - } + check_vector_size(position, expected_size, "Position"); + check_vector_size(orientation, expected_size, "Orientation"); + check_vector_size(velocity, expected_size, "Velocity"); + check_vector_size(acceleration, expected_size, "Acceleration"); + check_vector_size(loads, expected_size, "Loads"); } }; @@ -253,7 +240,8 @@ struct MeshData { * @brief Struct to hold turbine-specific data * * @details This struct contains data for a single turbine, including the number of blades, - * mesh data for various components, and blade node mappings. + * mesh data for various components (hub, nacelle, blade roots, blade nodes), and blade node + * mappings. */ struct TurbineData { int32_t n_blades; //< Number of blades @@ -324,6 +312,40 @@ struct TurbineData { } } + /** + * @brief Sets the blade node values based on blade number and node number. + * + * This method updates the blade node values in the mesh based on the provided blade number and + * node number. It simplifies the process of updating blade node values by abstracting away the + * indexing logic. + * + * @param blade_number The number of the blade to update. + * @param node_number The number of the node to update within the specified blade. + * @param position The new position of the node [x, y, z] + * @param orientation The new orientation of the node [r11, r12, ..., r33] + * @param velocity The new velocity of the node [u, v, w, p, q, r] + * @param acceleration The new acceleration of the node [u_dot, v_dot, w_dot, p_dot, q_dot, + * r_dot] + * @param loads The new loads on the node [Fx, Fy, Fz, Mx, My, Mz] + */ + void SetBladeNodeValues( + size_t blade_number, size_t node_number, const std::array& position, + const std::array& orientation, const std::array& velocity, + const std::array& acceleration, const std::array& loads + ) { + if (blade_number >= static_cast(n_blades) || + node_number >= node_indices_by_blade[blade_number].size()) { + throw std::out_of_range("Blade or node number out of range."); + } + + size_t node_index = node_indices_by_blade[blade_number][node_number]; + blade_nodes.position[node_index] = position; + blade_nodes.orientation[node_index] = orientation; + blade_nodes.velocity[node_index] = velocity; + blade_nodes.acceleration[node_index] = acceleration; + blade_nodes.loads[node_index] = loads; + } + private: /// Calculates the total number of blade nodes across all blades static size_t CalculateTotalBladeNodes(const TurbineConfig& tc) { diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index fb1bccaa..e0737861 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -368,6 +368,64 @@ TEST_F(TurbineDataValidationTest, InvalidNacelleMeshPoints) { EXPECT_THROW(turbine_data->Validate(), std::runtime_error); } +TEST(AerodynInflowTest, TurbineData_SetBladeNodeValues) { + // Set up 3 blades with 2 nodes each + std::vector blade_states; + for (size_t i = 0; i < 3; ++i) { + util::TurbineConfig::BladeInitialState blade_state( + {0., 0., 90., 1., 0., 0., 0.}, // root_initial_position + { + {0., 5., 90., 1., 0., 0., 0.}, // node_initial_positions - 1 + {0., 10., 90., 1., 0., 0., 0.} // node_initial_positions - 2 + } + ); + blade_states.push_back(blade_state); + } + util::TurbineConfig tc( + true, // is_horizontal_axis + {0.f, 0.f, 0.f}, // reference_position + {0., 0., 90., 1., 0., 0., 0.}, // hub_initial_position + {0., 0., 90., 1., 0., 0., 0.}, // nacelle_initial_position + blade_states + ); + + util::TurbineData turbine_data(tc); + + // Verify the current values for the first blade and node + size_t blade_number = 1; + size_t node_number = 0; + size_t node_index = turbine_data.node_indices_by_blade[blade_number][node_number]; + ExpectArrayNear(turbine_data.blade_nodes.position[node_index], {0.f, 5.f, 90.f}); + ExpectArrayNear( + turbine_data.blade_nodes.orientation[node_index], {1., 0., 0., 0., 1., 0., 0., 0., 1.} + ); + ExpectArrayNear(turbine_data.blade_nodes.velocity[node_index], {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}); + ExpectArrayNear( + turbine_data.blade_nodes.acceleration[node_index], {0.f, 0.f, 0.f, 0.f, 0.f, 0.f} + ); + ExpectArrayNear(turbine_data.blade_nodes.loads[node_index], {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}); + + // Define new values for the node + std::array new_position = {1.f, 2.f, 3.f}; + std::array new_orientation = {1., 0., 0., 0., 1., 0., 0., 0., 1.}; + std::array new_velocity = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; + std::array new_acceleration = {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}; + std::array new_loads = {13.f, 14.f, 15.f, 16.f, 17.f, 18.f}; + + // Call the method to set new values + turbine_data.SetBladeNodeValues( + blade_number, node_number, new_position, new_orientation, new_velocity, new_acceleration, + new_loads + ); + + // Verify that the values were set correctly + ExpectArrayNear(turbine_data.blade_nodes.position[node_index], new_position); + ExpectArrayNear(turbine_data.blade_nodes.orientation[node_index], new_orientation); + ExpectArrayNear(turbine_data.blade_nodes.velocity[node_index], new_velocity); + ExpectArrayNear(turbine_data.blade_nodes.acceleration[node_index], new_acceleration); + ExpectArrayNear(turbine_data.blade_nodes.loads[node_index], new_loads); +} + TEST(AerodynInflowTest, SimulationControls_Default) { util::SimulationControls simulation_controls; EXPECT_EQ(simulation_controls.is_aerodyn_input_path, true); @@ -481,14 +539,15 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { /* TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { // Set up simulation parameters util::SimulationControls sim_controls{ - .is_aerodyn_input_path = false, - .is_inflowwind_input_path = false, + .is_aerodyn_input_path = true, + .is_inflowwind_input_path = true, + .time_step = 0.0125, .max_time = 10.0, .interpolation_order = 2, .store_HH_wind_speed = false, - .transpose_DCM = 1, - .debug_level = 1, + .transpose_DCM = true, + .debug_level = static_cast(util::SimulationControls::DebugLevel::kSummary), .output_format = 0, .output_time_step = 0.1}; @@ -502,60 +561,55 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { .sound_speed = 335.0f, .vapor_pressure = 1700.0f}; - // Set up turbine settings - std::array hub_data = {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}; - std::array nacelle_data = {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}; - std::vector> root_data(3, {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}); - - util::TurbineSettings turbine_settings(hub_data, nacelle_data, root_data, 1, 3); - // Set up VTK settings util::VTKSettings vtk_settings{}; + // Set up turbine configuration + std::array hub_pos = {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}; + std::array nacelle_pos = {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}; + std::vector blade_states; + for (int i = 0; i < 3; ++i) { + util::TurbineConfig::BladeInitialState blade_state( + {0., 0., 90., 1., 0., 0., 0.}, // root_initial_position + { + {0., 5., 90., 1., 0., 0., 0.}, // node_initial_positions - 1 + {0., 10., 90., 1., 0., 0., 0.}, // node_initial_positions - 2 + } + ); + blade_states.push_back(blade_state); + } + util::TurbineConfig turbine_config( + true, // is_horizontal_axis + {0.f, 0.f, 0.f}, // reference_position + hub_pos, // hub_initial_position + nacelle_pos, // nacelle_initial_position + blade_states + ); + // Load the shared library and initialize AeroDynInflowLibrary const std::string path = GetSharedLibraryPath(); util::AeroDynInflowLibrary aerodyn_inflow_library( - path, util::ErrorHandling{}, fluid_props, env_conditions, turbine_settings, - util::StructuralMesh{}, sim_controls, vtk_settings - ); - - // Pre-initialize and setup rotor - EXPECT_NO_THROW(aerodyn_inflow_library.PreInitialize()); - EXPECT_NO_THROW(aerodyn_inflow_library.SetupRotor(1, true, {0.0f, 0.0f, 0.0f})); - - // Initialize with input files - const std::filesystem::path project_root = FindProjectRoot(); - std::filesystem::path input_path = project_root / "tests/unit_tests/external/"; - std::vector adiAD_input_string_array = {(input_path / "ad_primary.dat").string()}; - std::vector adiIfW_input_string_array = {(input_path / "ifw_primary.dat").string()}; - - EXPECT_NO_THROW( - aerodyn_inflow_library.Initialize(adiAD_input_string_array, adiIfW_input_string_array) + path, util::ErrorHandling{}, fluid_props, env_conditions, sim_controls, vtk_settings ); - // Set up motion data for hub, nacelle, root, and mesh - util::MeshData hub_motion{1}; - util::MeshData nacelle_motion{1}; - util::MeshData root_motion{3}; - util::MeshData mesh_motion{1}; - - // Set up rotor motion - EXPECT_NO_THROW(aerodyn_inflow_library.SetupRotorMotion( - 1, hub_motion, nacelle_motion, root_motion, mesh_motion - )); + // Initialize with turbine configuration + std::vector turbine_configs = {turbine_config}; + EXPECT_NO_THROW(aerodyn_inflow_library.Initialize(turbine_configs)); // Simulate for a few time steps double current_time = 0.0; double next_time = sim_controls.time_step; std::vector output_channel_values; - std::vector> mesh_force_moment(1); // Assuming 1 mesh point for (int i = 0; i < 10; ++i) { + // Update motion data for each time step (if needed) + EXPECT_NO_THROW(aerodyn_inflow_library.SetupRotorMotion()); + EXPECT_NO_THROW(aerodyn_inflow_library.UpdateStates(current_time, next_time)); EXPECT_NO_THROW( aerodyn_inflow_library.CalculateOutputChannels(next_time, output_channel_values) ); - EXPECT_NO_THROW(aerodyn_inflow_library.GetRotorAerodynamicLoads(1, mesh_force_moment)); + EXPECT_NO_THROW(aerodyn_inflow_library.GetRotorAerodynamicLoads()); current_time = next_time; next_time += sim_controls.time_step; From 54ce3c723fe4acdbf0c7870f10158d41fff0fedc Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 4 Oct 2024 18:58:59 -0600 Subject: [PATCH 74/87] Get the AeroDynInflowLibrary_FullLoop unit test working with refactored logic --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 10 ++++++---- tests/unit_tests/external/test_aerodyn_inflow.cpp | 10 +++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 4f5be723..0266d1b4 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -621,8 +621,10 @@ class AeroDynInflowLibrary { ); // Convert bool -> int to pass to the Fortran routine - int32_t aerodyn_input_is_passed = sim_controls_.is_aerodyn_input_path ? 1 : 0; - int32_t inflowwind_input_is_passed = sim_controls_.is_inflowwind_input_path ? 1 : 0; + int32_t is_aerodyn_input_passed_as_string = + sim_controls_.is_aerodyn_input_path ? 0 : 1; // reverse of is_aerodyn_input_path + int32_t is_inflowwind_input_passed_as_string = + sim_controls_.is_inflowwind_input_path ? 0 : 1; // reverse of is_inflowwind_input_path int32_t store_HH_wind_speed_int = sim_controls_.store_HH_wind_speed ? 1 : 0; int32_t write_vtk_int = vtk_settings_.write_vtk ? 1 : 0; @@ -635,10 +637,10 @@ class AeroDynInflowLibrary { static_cast(sim_controls_.inflowwind_input.size()); ADI_C_Init( - &aerodyn_input_is_passed, // input: AD input is passed + &is_aerodyn_input_passed_as_string, // input: AD input is passed &aerodyn_input_pointer, // input: AD input file as string &aerodyn_input_length, // input: AD input file string length - &inflowwind_input_is_passed, // input: IfW input is passed + &is_inflowwind_input_passed_as_string, // input: IfW input is passed &inflowwind_input_pointer, // input: IfW input file as string &inflowwind_input_length, // input: IfW input file string length sim_controls_.output_root_name.c_str(), // input: rootname for ADI file writing diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index e0737861..4b67c5a3 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -536,12 +536,16 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { EXPECT_EQ(aerodyn_inflow_library.GetVTKSettings().write_vtk, 0); } -/* TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { +TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { + const std::filesystem::path project_root = FindProjectRoot(); + std::filesystem::path input_path = project_root / "tests/unit_tests/external/"; + // Set up simulation parameters util::SimulationControls sim_controls{ .is_aerodyn_input_path = true, .is_inflowwind_input_path = true, - + .aerodyn_input = (input_path / "ad_primary.dat").string(), + .inflowwind_input = (input_path / "ifw_primary.dat").string(), .time_step = 0.0125, .max_time = 10.0, .interpolation_order = 2, @@ -617,6 +621,6 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { // End simulation EXPECT_NO_THROW(aerodyn_inflow_library.Finalize()); -} */ +} } // namespace openturbine::tests \ No newline at end of file From adddaaae1d7fd481e3dc51eef91ab02112e9fd2f Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 8 Oct 2024 10:35:38 -0600 Subject: [PATCH 75/87] Refactor TurbineConfig, MeshData, and TurbineData structs to increase readability and efficiency --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 93 +++++++++++++------ 1 file changed, 65 insertions(+), 28 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 0266d1b4..1a300d4f 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -90,17 +90,18 @@ struct EnvironmentalConditions { * @brief Configuration for the initial state of a turbine * * This struct encapsulates the initial configuration data for a wind turbine, - * including its type, reference position, and initial positions of key components + * including its type, reference position, and initial position of key components * in 7x1 arrays [x, y, z, qw, qx, qy, qz] */ struct TurbineConfig { /** * @brief Initial state for a single blade of a turbine * - * Stores the initial positions of a blade's root and nodes. + * Stores the initial position of a blade's root and nodes in 7x1 arrays [x, y, z, qw, qx, qy, + * qz] */ struct BladeInitialState { - Array_7 root_initial_position; //< Initial root position of the blade + Array_7 root_initial_position; //< Initial root position of the blade (1 per blade) std::vector node_initial_positions; //< Initial node positions of the blade BladeInitialState(const Array_7& root, const std::vector& nodes) @@ -112,7 +113,7 @@ struct TurbineConfig { Array_7 hub_initial_position; //< Initial hub position Array_7 nacelle_initial_position; //< Initial nacelle position std::vector - blade_initial_states; //< Initial root and node positions of blades + blade_initial_states; //< Initial root and node positions of blades (size = n_blades) TurbineConfig( bool is_hawt, std::array ref_pos, Array_7 hub_pos, Array_7 nacelle_pos, @@ -123,14 +124,17 @@ struct TurbineConfig { hub_initial_position(std::move(hub_pos)), nacelle_initial_position(std::move(nacelle_pos)), blade_initial_states(std::move(blade_states)) { + // Make sure the initial states are valid Validate(); } void Validate() const { + // Check if there are any blades defined if (blade_initial_states.empty()) { throw std::runtime_error("No blades defined. At least one blade is required."); } + // Check if there are any nodes defined for each blade for (const auto& blade : blade_initial_states) { if (blade.node_initial_positions.empty()) { throw std::runtime_error( @@ -139,6 +143,9 @@ struct TurbineConfig { } } } + + /// Returns the number of blades in the turbine + size_t NumberOfBlades() const { return blade_initial_states.size(); } }; /** @@ -157,7 +164,7 @@ inline void SetPositionAndOrientation( position[i] = static_cast(data[i]); } - // Set orientation (convert last 4 elements i.e. quaternion to 3x3 rotation matrix) + // Set orientation (converts last 4 elements i.e. quaternion -> 3x3 rotation matrix) auto orientation_2D = QuaternionToRotationMatrix({data[3], data[4], data[5], data[6]}); // Flatten the 3x3 matrix to a 1D array @@ -165,14 +172,15 @@ inline void SetPositionAndOrientation( } /** - * @brief Struct to hold the motion + loads data of any structural mesh component + * @brief Struct to hold the motion + loads data of any structural mesh component in + * AeroDyn/InflowWind compatible format * - * @details This struct holds the motion data (i.e. position, orientation, - * velocity, and acceleration) and loads of the structural mesh, which can be - * the hub, nacelle, root, or mesh points/nodes + * @details This struct holds the motion data (i.e. position 3x1, orientation 9x1, + * velocity 6x1, and acceleration 6x1) and aerodynamic loads 6x1 of the structural + * mesh components-- which can be the hub, nacelle, root, or blade */ struct MeshData { - int32_t n_mesh_points; //< Number of mesh points (nodes) + int32_t n_mesh_points; //< Number of mesh points/nodes, N std::vector> position; //< N x 3 array [x, y, z] std::vector> orientation; //< N x 9 array [r11, r12, ..., r33] std::vector> velocity; //< N x 6 array [u, v, w, p, q, r] @@ -206,21 +214,23 @@ struct MeshData { velocity(std::move(velocities)), acceleration(std::move(accelerations)), loads(std::move(loads)) { - // Set mesh position and orientation + // Set mesh position and orientation from 7x1 array [x, y, z, qw, qx, qy, qz] for (size_t i = 0; i < n_mesh_points; ++i) { SetPositionAndOrientation(mesh_data[i], position[i], orientation[i]); } + // Make sure the mesh data is valid Validate(); } void Validate() const { + // Check we have at least one node if (n_mesh_points <= 0) { throw std::invalid_argument("Number of mesh points must be at least 1"); } + // Check all vectors are the same size as the number of mesh points const size_t expected_size = static_cast(n_mesh_points); - auto check_vector_size = [](const auto& vec, size_t expected_size, const std::string& vec_name) { if (vec.size() != expected_size) { @@ -234,10 +244,13 @@ struct MeshData { check_vector_size(acceleration, expected_size, "Acceleration"); check_vector_size(loads, expected_size, "Loads"); } + + /// Returns the number of mesh points as size_t + size_t NumberOfMeshPoints() const { return static_cast(n_mesh_points); } }; /** - * @brief Struct to hold turbine-specific data + * @brief Struct to hold and manage turbine-specific data * * @details This struct contains data for a single turbine, including the number of blades, * mesh data for various components (hub, nacelle, blade roots, blade nodes), and blade node @@ -268,50 +281,72 @@ struct TurbineData { */ std::vector> node_indices_by_blade; + /** + * @brief Constructor for TurbineData based on a TurbineConfig object + * + * @details This constructor initializes the turbine data in AeroDyn/InflowWind compatible format + * based on the provided TurbineConfig object in 7x1 arrays i.e. OpenTurbine format. + * + * @param tc The TurbineConfig object containing the initial state of the turbine + */ TurbineData(const TurbineConfig& tc) - : n_blades(static_cast(tc.blade_initial_states.size())), + : n_blades(static_cast(tc.NumberOfBlades())), hub(1), nacelle(1), - blade_roots(tc.blade_initial_states.size()), + blade_roots(tc.NumberOfBlades()), blade_nodes(CalculateTotalBladeNodes(tc)), - node_indices_by_blade(tc.blade_initial_states.size()) { + node_indices_by_blade(tc.NumberOfBlades()) { + // Initialize hub and nacelle data InitializeHubAndNacelle(tc); + + // Initialize blade data InitializeBlades(tc); + + // Make sure the turbine data is valid Validate(); } void Validate() const { + // Check if the number of blades is valid if (n_blades < 1) { throw std::runtime_error("Invalid number of blades. Must be at least 1."); } - if (blade_roots.n_mesh_points != n_blades) { + // Validate hub and nacelle data - should have exactly one mesh point each + if (hub.NumberOfMeshPoints() != 1 || nacelle.NumberOfMeshPoints() != 1) { + throw std::runtime_error("Hub and nacelle should have exactly one mesh point each."); + } + + // Check if the number of blade roots matches the number of blades + if (blade_roots.NumberOfMeshPoints() != NumberOfBlades()) { throw std::runtime_error("Number of blade roots does not match number of blades."); } + // Check if the blade nodes to blade number mapping is valid - should be the same size if (blade_nodes_to_blade_num_mapping.size() != - static_cast(blade_nodes.n_mesh_points)) { + static_cast(blade_nodes.NumberOfMeshPoints())) { throw std::runtime_error("Blade node to blade number mapping size mismatch."); } - if (node_indices_by_blade.size() != static_cast(n_blades)) { + // Check if the node indices by blade are valid - should be same size as number of blades + if (node_indices_by_blade.size() != NumberOfBlades()) { throw std::runtime_error("Node indices by blade size mismatch."); } + // Check if the total number of blade nodes is valid - should be the same as the number of + // aggregrated blade nodes size_t total_nodes = 0; - for (const auto& bn : node_indices_by_blade) { - total_nodes += bn.size(); + for (const auto& bl : node_indices_by_blade) { + total_nodes += bl.size(); } - if (total_nodes != static_cast(blade_nodes.n_mesh_points)) { + if (total_nodes != blade_nodes.NumberOfMeshPoints()) { throw std::runtime_error("Total number of blade nodes mismatch."); } - - // Validate hub and nacelle data - if (hub.n_mesh_points != 1 || nacelle.n_mesh_points != 1) { - throw std::runtime_error("Hub and nacelle should have exactly one mesh point each."); - } } + /// Returns the number of blades as size_t + size_t NumberOfBlades() const { return static_cast(n_blades); } + /** * @brief Sets the blade node values based on blade number and node number. * @@ -333,11 +368,13 @@ struct TurbineData { const std::array& orientation, const std::array& velocity, const std::array& acceleration, const std::array& loads ) { - if (blade_number >= static_cast(n_blades) || + // Check if the blade and node numbers are within the valid range + if (blade_number >= NumberOfBlades() || node_number >= node_indices_by_blade[blade_number].size()) { throw std::out_of_range("Blade or node number out of range."); } + // Get the node index and set the values for the node size_t node_index = node_indices_by_blade[blade_number][node_number]; blade_nodes.position[node_index] = position; blade_nodes.orientation[node_index] = orientation; From c10fd13846b3341c103eb2bf9d20a963fc1ad21f Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 8 Oct 2024 11:49:16 -0600 Subject: [PATCH 76/87] Remove C++20 feature usage --- .../external/test_aerodyn_inflow.cpp | 225 +++++++++--------- 1 file changed, 115 insertions(+), 110 deletions(-) diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index 4b67c5a3..e8be7a36 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -6,59 +6,6 @@ namespace openturbine::tests { -TEST(AerodynInflowTest, ErrorHandling_NoThrow) { - util::ErrorHandling error_handling; - EXPECT_NO_THROW(error_handling.CheckError()); -} - -TEST(AerodynInflowTest, ErrorHandling_Throw) { - util::ErrorHandling error_handling; - error_handling.error_status = 1; // Set error status to 1 to trigger an error - EXPECT_THROW(error_handling.CheckError(), std::runtime_error); -} - -TEST(AerodynInflowTest, FluidProperties_Default) { - util::FluidProperties fluid_properties; - EXPECT_NEAR(fluid_properties.density, 1.225f, 1e-6f); - EXPECT_NEAR(fluid_properties.kinematic_viscosity, 1.464E-5f, 1e-6f); - EXPECT_NEAR(fluid_properties.sound_speed, 335.f, 1e-6f); - EXPECT_NEAR(fluid_properties.vapor_pressure, 1700.f, 1e-6f); -} - -TEST(AerodynInflowTest, FluidProperties_Set) { - util::FluidProperties fluid_properties; - fluid_properties.density = 1.1f; - fluid_properties.kinematic_viscosity = 1.5E-5f; - fluid_properties.sound_speed = 340.f; - fluid_properties.vapor_pressure = 1800.f; - - EXPECT_NEAR(fluid_properties.density, 1.1f, 1e-6f); - EXPECT_NEAR(fluid_properties.kinematic_viscosity, 1.5E-5f, 1e-6f); - EXPECT_NEAR(fluid_properties.sound_speed, 340.f, 1e-6f); - EXPECT_NEAR(fluid_properties.vapor_pressure, 1800.f, 1e-6f); -} - -TEST(AerodynInflowTest, EnvironmentalConditions_Default) { - util::EnvironmentalConditions environmental_conditions; - EXPECT_NEAR(environmental_conditions.gravity, 9.80665f, 1e-6f); - EXPECT_NEAR(environmental_conditions.atm_pressure, 103500.f, 1e-6f); - EXPECT_NEAR(environmental_conditions.water_depth, 0.f, 1e-6f); - EXPECT_NEAR(environmental_conditions.msl_offset, 0.f, 1e-6f); -} - -TEST(AerodynInflowTest, EnvironmentalConditions_Set) { - util::EnvironmentalConditions environmental_conditions; - environmental_conditions.gravity = 9.79665f; - environmental_conditions.atm_pressure = 103000.f; - environmental_conditions.water_depth = 100.f; - environmental_conditions.msl_offset = 10.f; - - EXPECT_NEAR(environmental_conditions.gravity, 9.79665f, 1e-6f); - EXPECT_NEAR(environmental_conditions.atm_pressure, 103000.f, 1e-6f); - EXPECT_NEAR(environmental_conditions.water_depth, 100.f, 1e-6f); - EXPECT_NEAR(environmental_conditions.msl_offset, 10.f, 1e-6f); -} - TEST(AerodynInflowTest, BladeInitialState_Constructor) { Array_7 root{1., 2., 3., 1., 0., 0., 0.}; std::vector nodes{{4., 5., 6., 1., 0., 0., 0.}, {7., 8., 9., 1., 0., 0., 0.}}; @@ -74,6 +21,7 @@ TEST(AerodynInflowTest, TurbineConfig_Constructor) { Array_7 hub_pos{1., 2., 3., 1., 0., 0., 0.}; Array_7 nacelle_pos{4., 5., 6., 1., 0., 0., 0.}; + // Create 3 blades with 2 nodes each std::vector blade_states; for (int i = 0; i < 3; ++i) { Array_7 root = {static_cast(i), 0., 0., 1., 0., 0., 0.}; @@ -89,9 +37,9 @@ TEST(AerodynInflowTest, TurbineConfig_Constructor) { EXPECT_EQ(turbine_config.reference_position, ref_pos); EXPECT_EQ(turbine_config.hub_initial_position, hub_pos); EXPECT_EQ(turbine_config.nacelle_initial_position, nacelle_pos); - EXPECT_EQ(turbine_config.blade_initial_states.size(), 3); + EXPECT_EQ(turbine_config.NumberOfBlades(), 3); - for (size_t i = 0; i < 3; ++i) { + for (size_t i = 0; i < turbine_config.NumberOfBlades(); ++i) { EXPECT_EQ( turbine_config.blade_initial_states[i].root_initial_position[0], static_cast(i) ); @@ -146,7 +94,7 @@ TEST(AerodynInflowTest, SetPositionAndOrientation) { TEST(AerodynInflowTest, MeshData_Constructor_NumberOfNodes) { util::MeshData mesh_motion_data{1}; - EXPECT_EQ(mesh_motion_data.n_mesh_points, 1); + EXPECT_EQ(mesh_motion_data.NumberOfMeshPoints(), 1); EXPECT_EQ(mesh_motion_data.position.size(), 1); EXPECT_EQ(mesh_motion_data.orientation.size(), 1); EXPECT_EQ(mesh_motion_data.velocity.size(), 1); @@ -164,7 +112,7 @@ TEST(AerodynInflowTest, MeshData_Constructor_Data) { n_mesh_points, mesh_data, mesh_velocities, mesh_accelerations, mesh_loads ); - EXPECT_EQ(mesh_motion_data.n_mesh_points, n_mesh_points); + EXPECT_EQ(mesh_motion_data.NumberOfMeshPoints(), n_mesh_points); EXPECT_EQ(mesh_motion_data.position.size(), n_mesh_points); EXPECT_EQ(mesh_motion_data.orientation.size(), n_mesh_points); EXPECT_EQ(mesh_motion_data.velocity.size(), n_mesh_points); @@ -249,31 +197,34 @@ TEST(AerodynInflowTest, TurbineData_Constructor) { util::TurbineData turbine_data(tc); // Check basic properties - EXPECT_EQ(turbine_data.n_blades, 3); - EXPECT_EQ(turbine_data.hub.n_mesh_points, 1); - EXPECT_EQ(turbine_data.nacelle.n_mesh_points, 1); - EXPECT_EQ(turbine_data.blade_roots.n_mesh_points, 3); - EXPECT_EQ(turbine_data.blade_nodes.n_mesh_points, 6); // 3 blades * 2 nodes each + EXPECT_EQ(turbine_data.NumberOfBlades(), 3); + EXPECT_EQ(turbine_data.hub.NumberOfMeshPoints(), 1); + EXPECT_EQ(turbine_data.nacelle.NumberOfMeshPoints(), 1); + EXPECT_EQ(turbine_data.blade_roots.NumberOfMeshPoints(), 3); + EXPECT_EQ(turbine_data.blade_nodes.NumberOfMeshPoints(), 6); // 3 blades * 2 nodes each // Check hub and nacelle positions ExpectArrayNear(turbine_data.hub.position[0], {0.f, 0.f, 90.f}); ExpectArrayNear(turbine_data.nacelle.position[0], {0.f, 0.f, 90.f}); // Check blade roots - for (size_t i = 0; i < 3; ++i) { + for (size_t i = 0; i < turbine_data.NumberOfBlades(); ++i) { ExpectArrayNear(turbine_data.blade_roots.position[i], {0.f, 0.f, 90.f}); } // Check blade nodes - for (size_t i = 0; i < 6; ++i) { + for (size_t i = 0; i < turbine_data.blade_nodes.NumberOfMeshPoints(); ++i) { float expected_y = (i % 2 == 0) ? 5.f : 10.f; ExpectArrayNear(turbine_data.blade_nodes.position[i], {0.f, expected_y, 90.f}); } // Check blade_nodes_to_blade_num_mapping std::vector expected_blade_nodes_to_blade_num_mapping = {1, 1, 2, 2, 3, 3}; - ASSERT_EQ(turbine_data.blade_nodes_to_blade_num_mapping.size(), 6); - for (size_t i = 0; i < 6; ++i) { + ASSERT_EQ( + turbine_data.blade_nodes_to_blade_num_mapping.size(), + turbine_data.blade_nodes.NumberOfMeshPoints() + ); + for (size_t i = 0; i < turbine_data.blade_nodes.NumberOfMeshPoints(); ++i) { EXPECT_EQ( turbine_data.blade_nodes_to_blade_num_mapping[i], expected_blade_nodes_to_blade_num_mapping[i] @@ -282,9 +233,9 @@ TEST(AerodynInflowTest, TurbineData_Constructor) { } // Check node_indices_by_blade - ASSERT_EQ(turbine_data.node_indices_by_blade.size(), 3); + ASSERT_EQ(turbine_data.node_indices_by_blade.size(), turbine_data.NumberOfBlades()); std::vector> expected_node_indices_by_blade = {{0, 1}, {2, 3}, {4, 5}}; - for (size_t blade = 0; blade < 3; ++blade) { + for (size_t blade = 0; blade < turbine_data.NumberOfBlades(); ++blade) { ASSERT_EQ(turbine_data.node_indices_by_blade[blade].size(), 2) << "Incorrect number of nodes for blade " << blade; EXPECT_EQ(turbine_data.node_indices_by_blade[blade], expected_node_indices_by_blade[blade]) @@ -293,7 +244,7 @@ TEST(AerodynInflowTest, TurbineData_Constructor) { // Additional check: Verify that node_indices_by_blade correctly maps to // blade_nodes_to_blade_num_mapping - for (size_t blade = 0; blade < 3; ++blade) { + for (size_t blade = 0; blade < turbine_data.NumberOfBlades(); ++blade) { for (size_t node : turbine_data.node_indices_by_blade[blade]) { EXPECT_EQ(turbine_data.blade_nodes_to_blade_num_mapping[node], blade + 1) << "Mismatch between node_indices_by_blade and blade_nodes_to_blade_num_mapping for " @@ -392,9 +343,10 @@ TEST(AerodynInflowTest, TurbineData_SetBladeNodeValues) { util::TurbineData turbine_data(tc); // Verify the current values for the first blade and node - size_t blade_number = 1; - size_t node_number = 0; - size_t node_index = turbine_data.node_indices_by_blade[blade_number][node_number]; + size_t blade_number{1}; + size_t node_number{0}; + size_t node_index{turbine_data.node_indices_by_blade[blade_number][node_number]}; + ExpectArrayNear(turbine_data.blade_nodes.position[node_index], {0.f, 5.f, 90.f}); ExpectArrayNear( turbine_data.blade_nodes.orientation[node_index], {1., 0., 0., 0., 1., 0., 0., 0., 1.} @@ -411,8 +363,6 @@ TEST(AerodynInflowTest, TurbineData_SetBladeNodeValues) { std::array new_velocity = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; std::array new_acceleration = {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}; std::array new_loads = {13.f, 14.f, 15.f, 16.f, 17.f, 18.f}; - - // Call the method to set new values turbine_data.SetBladeNodeValues( blade_number, node_number, new_position, new_orientation, new_velocity, new_acceleration, new_loads @@ -432,8 +382,8 @@ TEST(AerodynInflowTest, SimulationControls_Default) { EXPECT_EQ(simulation_controls.is_inflowwind_input_path, true); EXPECT_EQ(simulation_controls.interpolation_order, 1); EXPECT_EQ(simulation_controls.time_step, 0.1); - EXPECT_EQ(simulation_controls.max_time, 600.0); - EXPECT_EQ(simulation_controls.total_elapsed_time, 0.0); + EXPECT_EQ(simulation_controls.max_time, 600.); + EXPECT_EQ(simulation_controls.total_elapsed_time, 0.); EXPECT_EQ(simulation_controls.n_time_steps, 0); EXPECT_EQ(simulation_controls.store_HH_wind_speed, 1); EXPECT_EQ(simulation_controls.transpose_DCM, 1); @@ -452,14 +402,14 @@ TEST(AerodynInflowTest, SimulationControls_Set) { simulation_controls.is_inflowwind_input_path = false; simulation_controls.interpolation_order = 2; simulation_controls.time_step = 0.2; - simulation_controls.max_time = 1200.0; - simulation_controls.total_elapsed_time = 100.0; + simulation_controls.max_time = 1200.; + simulation_controls.total_elapsed_time = 100.; simulation_controls.n_time_steps = 10; simulation_controls.store_HH_wind_speed = 0; simulation_controls.transpose_DCM = 0; simulation_controls.debug_level = 1; simulation_controls.output_format = 1; - simulation_controls.output_time_step = 1.0; + simulation_controls.output_time_step = 1.; simulation_controls.n_channels = 1; std::strncpy( simulation_controls.channel_names_c.data(), "test_channel", @@ -476,20 +426,73 @@ TEST(AerodynInflowTest, SimulationControls_Set) { EXPECT_EQ(simulation_controls.is_inflowwind_input_path, 0); EXPECT_EQ(simulation_controls.interpolation_order, 2); EXPECT_EQ(simulation_controls.time_step, 0.2); - EXPECT_EQ(simulation_controls.max_time, 1200.0); - EXPECT_EQ(simulation_controls.total_elapsed_time, 100.0); + EXPECT_EQ(simulation_controls.max_time, 1200.); + EXPECT_EQ(simulation_controls.total_elapsed_time, 100.); EXPECT_EQ(simulation_controls.n_time_steps, 10); EXPECT_EQ(simulation_controls.store_HH_wind_speed, 0); EXPECT_EQ(simulation_controls.transpose_DCM, 0); EXPECT_EQ(simulation_controls.debug_level, 1); EXPECT_EQ(simulation_controls.output_format, 1); - EXPECT_EQ(simulation_controls.output_time_step, 1.0); + EXPECT_EQ(simulation_controls.output_time_step, 1.); EXPECT_STREQ(simulation_controls.output_root_name.data(), "ADI_out"); EXPECT_EQ(simulation_controls.n_channels, 1); EXPECT_STREQ(simulation_controls.channel_names_c.data(), "test_channel"); EXPECT_STREQ(simulation_controls.channel_units_c.data(), "test_unit"); } +TEST(AerodynInflowTest, ErrorHandling_NoThrow) { + util::ErrorHandling error_handling; + EXPECT_NO_THROW(error_handling.CheckError()); +} + +TEST(AerodynInflowTest, ErrorHandling_Throw) { + util::ErrorHandling error_handling; + error_handling.error_status = 1; // Set error status to 1 to trigger an error + EXPECT_THROW(error_handling.CheckError(), std::runtime_error); +} + +TEST(AerodynInflowTest, FluidProperties_Default) { + util::FluidProperties fluid_properties; + EXPECT_NEAR(fluid_properties.density, 1.225f, 1e-6f); + EXPECT_NEAR(fluid_properties.kinematic_viscosity, 1.464E-5f, 1e-6f); + EXPECT_NEAR(fluid_properties.sound_speed, 335.f, 1e-6f); + EXPECT_NEAR(fluid_properties.vapor_pressure, 1700.f, 1e-6f); +} + +TEST(AerodynInflowTest, FluidProperties_Set) { + util::FluidProperties fluid_properties; + fluid_properties.density = 1.1f; + fluid_properties.kinematic_viscosity = 1.5E-5f; + fluid_properties.sound_speed = 340.f; + fluid_properties.vapor_pressure = 1800.f; + + EXPECT_NEAR(fluid_properties.density, 1.1f, 1e-6f); + EXPECT_NEAR(fluid_properties.kinematic_viscosity, 1.5E-5f, 1e-6f); + EXPECT_NEAR(fluid_properties.sound_speed, 340.f, 1e-6f); + EXPECT_NEAR(fluid_properties.vapor_pressure, 1800.f, 1e-6f); +} + +TEST(AerodynInflowTest, EnvironmentalConditions_Default) { + util::EnvironmentalConditions environmental_conditions; + EXPECT_NEAR(environmental_conditions.gravity, 9.80665f, 1e-6f); + EXPECT_NEAR(environmental_conditions.atm_pressure, 103500.f, 1e-6f); + EXPECT_NEAR(environmental_conditions.water_depth, 0.f, 1e-6f); + EXPECT_NEAR(environmental_conditions.msl_offset, 0.f, 1e-6f); +} + +TEST(AerodynInflowTest, EnvironmentalConditions_Set) { + util::EnvironmentalConditions environmental_conditions; + environmental_conditions.gravity = 9.79665f; + environmental_conditions.atm_pressure = 103000.f; + environmental_conditions.water_depth = 100.f; + environmental_conditions.msl_offset = 10.f; + + EXPECT_NEAR(environmental_conditions.gravity, 9.79665f, 1e-6f); + EXPECT_NEAR(environmental_conditions.atm_pressure, 103000.f, 1e-6f); + EXPECT_NEAR(environmental_conditions.water_depth, 100.f, 1e-6f); + EXPECT_NEAR(environmental_conditions.msl_offset, 10.f, 1e-6f); +} + TEST(AerodynInflowTest, VTKSettings_Default) { util::VTKSettings vtk_settings; EXPECT_EQ(vtk_settings.write_vtk, 0); @@ -503,12 +506,12 @@ TEST(AerodynInflowTest, VTKSettings_Set) { vtk_settings.write_vtk = 1; vtk_settings.vtk_type = 2; vtk_settings.vtk_nacelle_dimensions = {-1.5f, -1.5f, 0.f, 5.f, 2.5f, 2.5f}; - vtk_settings.vtk_hub_radius = 1.0f; + vtk_settings.vtk_hub_radius = 1.f; EXPECT_EQ(vtk_settings.write_vtk, 1); EXPECT_EQ(vtk_settings.vtk_type, 2); ExpectArrayNear(vtk_settings.vtk_nacelle_dimensions, {-1.5f, -1.5f, 0.f, 5.f, 2.5f, 2.5f}); - EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.0f); + EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.f); } /// Helper function to get the shared library path @@ -541,36 +544,38 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { std::filesystem::path input_path = project_root / "tests/unit_tests/external/"; // Set up simulation parameters - util::SimulationControls sim_controls{ - .is_aerodyn_input_path = true, - .is_inflowwind_input_path = true, - .aerodyn_input = (input_path / "ad_primary.dat").string(), - .inflowwind_input = (input_path / "ifw_primary.dat").string(), - .time_step = 0.0125, - .max_time = 10.0, - .interpolation_order = 2, - .store_HH_wind_speed = false, - .transpose_DCM = true, - .debug_level = static_cast(util::SimulationControls::DebugLevel::kSummary), - .output_format = 0, - .output_time_step = 0.1}; - - // Set up environmental conditions and fluid properties - util::EnvironmentalConditions env_conditions{ - .gravity = 9.80665f, .atm_pressure = 103500.0f, .water_depth = 0.0f, .msl_offset = 0.0f}; - - util::FluidProperties fluid_props{ - .density = 1.225f, - .kinematic_viscosity = 1.464E-05f, - .sound_speed = 335.0f, - .vapor_pressure = 1700.0f}; + util::SimulationControls sim_controls; + sim_controls.is_aerodyn_input_path = true; + sim_controls.is_inflowwind_input_path = true; + sim_controls.aerodyn_input = (input_path / "ad_primary.dat").string(); + sim_controls.inflowwind_input = (input_path / "ifw_primary.dat").string(); + sim_controls.time_step = 0.0125; + sim_controls.max_time = 10.; + sim_controls.interpolation_order = 2; + sim_controls.store_HH_wind_speed = false; + sim_controls.transpose_DCM = true; + sim_controls.debug_level = static_cast(util::SimulationControls::DebugLevel::kSummary); + + // Set up fluid properties + util::FluidProperties fluid_props; + fluid_props.density = 1.225f; + fluid_props.kinematic_viscosity = 1.464E-05f; + fluid_props.sound_speed = 335.f; + fluid_props.vapor_pressure = 1700.f; + + // Set up environmental conditions + util::EnvironmentalConditions env_conditions; + env_conditions.gravity = 9.80665f; + env_conditions.atm_pressure = 103500.f; + env_conditions.water_depth = 0.f; + env_conditions.msl_offset = 0.f; // Set up VTK settings util::VTKSettings vtk_settings{}; // Set up turbine configuration - std::array hub_pos = {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}; - std::array nacelle_pos = {0.0, 0.0, 90.0, 1.0, 0.0, 0.0, 0.0}; + std::array hub_pos = {0., 0., 90., 1., 0., 0., 0.}; + std::array nacelle_pos = {0., 0., 90., 1., 0., 0., 0.}; std::vector blade_states; for (int i = 0; i < 3; ++i) { util::TurbineConfig::BladeInitialState blade_state( @@ -601,7 +606,7 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { EXPECT_NO_THROW(aerodyn_inflow_library.Initialize(turbine_configs)); // Simulate for a few time steps - double current_time = 0.0; + double current_time = 0.; double next_time = sim_controls.time_step; std::vector output_channel_values; From 4cf722f257b44b33a69c44988bd52756123838da Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Tue, 8 Oct 2024 13:09:36 -0600 Subject: [PATCH 77/87] Add some assertions on the aero loads for AeroDynInflowLibrary_FullLoopSimulation test --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 108 +++++++++--------- .../external/test_aerodyn_inflow.cpp | 28 ++++- 2 files changed, 79 insertions(+), 57 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 1a300d4f..3ecbac1c 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -38,54 +38,6 @@ namespace openturbine::util { * https://github.com/OpenFAST/openfast/blob/dev/modules/aerodyn/src/AeroDyn_Inflow_C_Binding.f90 */ -/** - * @brief Struct for error handling settings - * - * @details This struct holds the error handling settings for the AeroDynInflow library wrapper. It - * includes an error level enum, a maximum error message length, and methods for checking and - * handling errors. - */ -struct ErrorHandling { - /// Error levels used in InflowWind - enum class ErrorLevel { - kNone = 0, - kInfo = 1, - kWarning = 2, - kSevereError = 3, - kFatalError = 4 - }; - - static constexpr size_t kErrorMessagesLength{1025U}; //< Max error message length - int abort_error_level{ - static_cast(ErrorLevel::kFatalError) //< Error level at which to abort - }; - int error_status{0}; //< Error status - std::array error_message{}; //< Error message buffer - - /// Checks for errors and throws an exception if found - void CheckError() const { - if (error_status != 0) { - throw std::runtime_error(error_message.data()); - } - } -}; - -/// Struct to hold the properties of the working fluid (air) -struct FluidProperties { - float density{1.225f}; //< Air density (kg/m^3) - float kinematic_viscosity{1.464E-5f}; //< Kinematic viscosity (m^2/s) - float sound_speed{335.f}; //< Speed of sound in the working fluid (m/s) - float vapor_pressure{1700.f}; //< Vapor pressure of the working fluid (Pa) -}; - -/// Struct to hold the environmental conditions -struct EnvironmentalConditions { - float gravity{9.80665f}; //< Gravitational acceleration (m/s^2) - float atm_pressure{103500.f}; //< Atmospheric pressure (Pa) - float water_depth{0.f}; //< Water depth (m) - float msl_offset{0.f}; //< Mean sea level to still water level offset (m) -}; - /** * @brief Configuration for the initial state of a turbine * @@ -476,6 +428,54 @@ struct SimulationControls { std::array channel_units_c{}; //< Output channel units }; +/** + * @brief Struct for error handling settings + * + * @details This struct holds the error handling settings for the AeroDynInflow library wrapper. It + * includes an error level enum, a maximum error message length, and methods for checking and + * handling errors. + */ +struct ErrorHandling { + /// Error levels used in InflowWind + enum class ErrorLevel { + kNone = 0, + kInfo = 1, + kWarning = 2, + kSevereError = 3, + kFatalError = 4 + }; + + static constexpr size_t kErrorMessagesLength{1025U}; //< Max error message length + int abort_error_level{ + static_cast(ErrorLevel::kFatalError) //< Error level at which to abort + }; + int error_status{0}; //< Error status + std::array error_message{}; //< Error message buffer + + /// Checks for errors and throws an exception if found + void CheckError() const { + if (error_status != 0) { + throw std::runtime_error(error_message.data()); + } + } +}; + +/// Struct to hold the properties of the working fluid (air) +struct FluidProperties { + float density{1.225f}; //< Air density (kg/m^3) + float kinematic_viscosity{1.464E-5f}; //< Kinematic viscosity (m^2/s) + float sound_speed{335.f}; //< Speed of sound in the working fluid (m/s) + float vapor_pressure{1700.f}; //< Vapor pressure of the working fluid (Pa) +}; + +/// Struct to hold the environmental conditions +struct EnvironmentalConditions { + float gravity{9.80665f}; //< Gravitational acceleration (m/s^2) + float atm_pressure{103500.f}; //< Atmospheric pressure (Pa) + float water_depth{0.f}; //< Water depth (m) + float msl_offset{0.f}; //< Mean sea level to still water level offset (m) +}; + /** * @brief Struct to hold the settings for VTK output * @@ -551,6 +551,7 @@ class AeroDynInflowLibrary { const FluidProperties& GetFluidProperties() const { return air_; } const SimulationControls& GetSimulationControls() const { return sim_controls_; } const VTKSettings& GetVTKSettings() const { return vtk_settings_; } + const std::vector& GetTurbines() const { return turbines_; } /** * @brief Initialize the AeroDyn Inflow library @@ -611,14 +612,15 @@ class AeroDynInflowLibrary { // Turbine number is 1 indexed i.e. 1, 2, 3, ... ++turbine_number; - // Convert bool -> int to pass to the Fortran routine + // Convert bool -> int32_t to pass to the Fortran routine int32_t is_horizontal_axis_int = tc.is_horizontal_axis ? 1 : 0; // Validate the turbine config tc.Validate(); - // Create new turbine data - // Note: TurbineData and MeshData are validated during construction + // Create new turbine data to store the updates + // Note: TurbineData and MeshData are validated during construction, so no need to + // perform additional checks here turbines_.emplace_back(TurbineData(tc)); auto& td = turbines_.back(); @@ -657,7 +659,7 @@ class AeroDynInflowLibrary { "ADI_C_Init" ); - // Convert bool -> int to pass to the Fortran routine + // Convert bool -> int32_t to pass to the Fortran routine int32_t is_aerodyn_input_passed_as_string = sim_controls_.is_aerodyn_input_path ? 0 : 1; // reverse of is_aerodyn_input_path int32_t is_inflowwind_input_passed_as_string = @@ -713,7 +715,7 @@ class AeroDynInflowLibrary { /** * @brief Set up the rotor motion for the current simulation step * - * @details Updates the motion data (position, orientation, velocity, acceleration) for + * @details Sets up the motion data (position, orientation, velocity, acceleration) for * the hub, nacelle, blade roots, and blade nodes of each turbine. */ void SetupRotorMotion() { diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index e8be7a36..81f48683 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -539,7 +539,7 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { EXPECT_EQ(aerodyn_inflow_library.GetVTKSettings().write_vtk, 0); } -TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { +TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoopSimulation) { const std::filesystem::path project_root = FindProjectRoot(); std::filesystem::path input_path = project_root / "tests/unit_tests/external/"; @@ -554,7 +554,7 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { sim_controls.interpolation_order = 2; sim_controls.store_HH_wind_speed = false; sim_controls.transpose_DCM = true; - sim_controls.debug_level = static_cast(util::SimulationControls::DebugLevel::kSummary); + sim_controls.debug_level = static_cast(util::SimulationControls::DebugLevel::kNone); // Set up fluid properties util::FluidProperties fluid_props; @@ -605,13 +605,12 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { std::vector turbine_configs = {turbine_config}; EXPECT_NO_THROW(aerodyn_inflow_library.Initialize(turbine_configs)); - // Simulate for a few time steps + // Simulate for 10 time steps double current_time = 0.; double next_time = sim_controls.time_step; std::vector output_channel_values; for (int i = 0; i < 10; ++i) { - // Update motion data for each time step (if needed) EXPECT_NO_THROW(aerodyn_inflow_library.SetupRotorMotion()); EXPECT_NO_THROW(aerodyn_inflow_library.UpdateStates(current_time, next_time)); @@ -620,6 +619,27 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoop) { ); EXPECT_NO_THROW(aerodyn_inflow_library.GetRotorAerodynamicLoads()); + // Assert loads on blade nodes - they don't change since we're not updating the motion + auto expected_loads = std::vector>{ + {11132.2f, -1938.43f, 0.f, 44472.6f, 323391.f, 50444.8f}, // Blade 1, Node 1 + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // Blade 1, Node 2 + {11132.2f, -1938.43f, 0.f, 44472.6f, 323391.f, 50444.8f}, // Blade 2, Node 1 + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // Blade 2, Node 2 + {11132.2f, -1938.43f, 0.f, 44472.6f, 323391.f, 50444.8f}, // Blade 3, Node 1 + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f} // Blade 3, Node 2 + }; + + const auto& turbine = aerodyn_inflow_library.GetTurbines()[0]; + for (size_t ii = 0; ii < turbine.NumberOfBlades(); ++ii) { + for (size_t jj = 0; jj < turbine.node_indices_by_blade[ii].size(); ++jj) { + auto node_index = turbine.node_indices_by_blade[ii][jj]; + const auto& node_loads = turbine.blade_nodes.loads[node_index]; + const auto& e_loads = expected_loads[ii * 2 + jj]; + for (size_t k = 0; k < 3; ++k) { + EXPECT_NEAR(node_loads[k], e_loads[k], 1e-1f); + } + } + } current_time = next_time; next_time += sim_controls.time_step; } From f0f5697161f572470e64a748286c39c336a0e5d1 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Wed, 9 Oct 2024 23:16:16 +0000 Subject: [PATCH 78/87] Remove some variable shadowing errors from MeshData --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 3ecbac1c..98fda68c 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -153,22 +153,22 @@ struct MeshData { /// Constructor to initialize all mesh data based on provided inputs MeshData( - size_t n_mesh_points, const std::vector>& mesh_data, - const std::vector>& velocities, - const std::vector>& accelerations, - const std::vector>& loads + size_t n_mesh_pts, const std::vector>& pos, + const std::vector>& vel, + const std::vector>& acc, + const std::vector>& ld ) - : n_mesh_points(static_cast(n_mesh_points)), - position(std::vector>(n_mesh_points, {0.f, 0.f, 0.f})), + : n_mesh_points(static_cast(n_mesh_pts)), + position(std::vector>(n_mesh_pts, {0.f, 0.f, 0.f})), orientation( - std::vector>(n_mesh_points, {0., 0., 0., 0., 0., 0., 0., 0., 0.}) + std::vector>(n_mesh_pts, {0., 0., 0., 0., 0., 0., 0., 0., 0.}) ), - velocity(std::move(velocities)), - acceleration(std::move(accelerations)), - loads(std::move(loads)) { + velocity(std::move(vel)), + acceleration(std::move(acc)), + loads(std::move(ld)) { // Set mesh position and orientation from 7x1 array [x, y, z, qw, qx, qy, qz] - for (size_t i = 0; i < n_mesh_points; ++i) { - SetPositionAndOrientation(mesh_data[i], position[i], orientation[i]); + for (size_t i = 0; i < NumberOfMeshPoints(); ++i) { + SetPositionAndOrientation(pos[i], position[i], orientation[i]); } // Make sure the mesh data is valid @@ -182,14 +182,14 @@ struct MeshData { } // Check all vectors are the same size as the number of mesh points - const size_t expected_size = static_cast(n_mesh_points); - auto check_vector_size = [](const auto& vec, size_t expected_size, + auto check_vector_size = [](const auto& vec, size_t exp_size, const std::string& vec_name) { - if (vec.size() != expected_size) { + if (vec.size() != exp_size) { throw std::invalid_argument(vec_name + " vector size does not match n_mesh_points"); } }; + const auto expected_size = NumberOfMeshPoints(); check_vector_size(position, expected_size, "Position"); check_vector_size(orientation, expected_size, "Orientation"); check_vector_size(velocity, expected_size, "Velocity"); @@ -275,8 +275,7 @@ struct TurbineData { } // Check if the blade nodes to blade number mapping is valid - should be the same size - if (blade_nodes_to_blade_num_mapping.size() != - static_cast(blade_nodes.NumberOfMeshPoints())) { + if (blade_nodes_to_blade_num_mapping.size() != blade_nodes.NumberOfMeshPoints()) { throw std::runtime_error("Blade node to blade number mapping size mismatch."); } From 843fab082fff70b1af3c80618829182e38b35c97 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 10 Oct 2024 15:24:34 +0000 Subject: [PATCH 79/87] Make a bunch of minor changes to pass clang-tidy checks, mostly using const correctness, auto, style improvements etc. --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 113 ++++---- .../external/test_aerodyn_inflow.cpp | 248 +++++++++--------- 2 files changed, 177 insertions(+), 184 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index 98fda68c..bade05a8 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -61,7 +61,7 @@ struct TurbineConfig { }; bool is_horizontal_axis{true}; //< Is a horizontal axis turbine? - std::array reference_position{0.f, 0.f, 0.f}; //< Reference position of the turbine + std::array reference_position{0.F, 0.F, 0.F}; //< Reference position of the turbine Array_7 hub_initial_position; //< Initial hub position Array_7 nacelle_initial_position; //< Initial nacelle position std::vector @@ -72,9 +72,9 @@ struct TurbineConfig { std::vector blade_states ) : is_horizontal_axis(is_hawt), - reference_position(std::move(ref_pos)), - hub_initial_position(std::move(hub_pos)), - nacelle_initial_position(std::move(nacelle_pos)), + reference_position(ref_pos), + hub_initial_position(hub_pos), + nacelle_initial_position(nacelle_pos), blade_initial_states(std::move(blade_states)) { // Make sure the initial states are valid Validate(); @@ -97,7 +97,7 @@ struct TurbineConfig { } /// Returns the number of blades in the turbine - size_t NumberOfBlades() const { return blade_initial_states.size(); } + [[nodiscard]] size_t NumberOfBlades() const { return blade_initial_states.size(); } }; /** @@ -120,7 +120,11 @@ inline void SetPositionAndOrientation( auto orientation_2D = QuaternionToRotationMatrix({data[3], data[4], data[5], data[6]}); // Flatten the 3x3 matrix to a 1D array - std::copy(&orientation_2D[0][0], &orientation_2D[0][0] + 9, orientation.begin()); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) { + orientation[i * 3 + j] = orientation_2D[i][j]; + } + } } /** @@ -143,13 +147,13 @@ struct MeshData { /// Constructor to initialize all mesh data to zero based on provided number of nodes MeshData(size_t n_nodes) : n_mesh_points(static_cast(n_nodes)), - position(std::vector>(n_nodes, {0.f, 0.f, 0.f})), + position(std::vector>(n_nodes, {0.F, 0.F, 0.F})), orientation( std::vector>(n_nodes, {0., 0., 0., 0., 0., 0., 0., 0., 0.}) ), - velocity(std::vector>(n_nodes, {0.f, 0.f, 0.f, 0.f, 0.f, 0.f})), - acceleration(std::vector>(n_nodes, {0.f, 0.f, 0.f, 0.f, 0.f, 0.f})), - loads(std::vector>(n_nodes, {0.f, 0.f, 0.f, 0.f, 0.f, 0.f})) {} + velocity(std::vector>(n_nodes, {0.F, 0.F, 0.F, 0.F, 0.F, 0.F})), + acceleration(std::vector>(n_nodes, {0.F, 0.F, 0.F, 0.F, 0.F, 0.F})), + loads(std::vector>(n_nodes, {0.F, 0.F, 0.F, 0.F, 0.F, 0.F})) {} /// Constructor to initialize all mesh data based on provided inputs MeshData( @@ -159,13 +163,13 @@ struct MeshData { const std::vector>& ld ) : n_mesh_points(static_cast(n_mesh_pts)), - position(std::vector>(n_mesh_pts, {0.f, 0.f, 0.f})), + position(std::vector>(n_mesh_pts, {0.F, 0.F, 0.F})), orientation( std::vector>(n_mesh_pts, {0., 0., 0., 0., 0., 0., 0., 0., 0.}) ), - velocity(std::move(vel)), - acceleration(std::move(acc)), - loads(std::move(ld)) { + velocity(vel), + acceleration(acc), + loads(ld) { // Set mesh position and orientation from 7x1 array [x, y, z, qw, qx, qy, qz] for (size_t i = 0; i < NumberOfMeshPoints(); ++i) { SetPositionAndOrientation(pos[i], position[i], orientation[i]); @@ -198,7 +202,7 @@ struct MeshData { } /// Returns the number of mesh points as size_t - size_t NumberOfMeshPoints() const { return static_cast(n_mesh_points); } + [[nodiscard]] size_t NumberOfMeshPoints() const { return static_cast(n_mesh_points); } }; /** @@ -296,7 +300,7 @@ struct TurbineData { } /// Returns the number of blades as size_t - size_t NumberOfBlades() const { return static_cast(n_blades); } + [[nodiscard]] size_t NumberOfBlades() const { return static_cast(n_blades); } /** * @brief Sets the blade node values based on blade number and node number. @@ -326,7 +330,7 @@ struct TurbineData { } // Get the node index and set the values for the node - size_t node_index = node_indices_by_blade[blade_number][node_number]; + const size_t node_index = node_indices_by_blade[blade_number][node_number]; blade_nodes.position[node_index] = position; blade_nodes.orientation[node_index] = orientation; blade_nodes.velocity[node_index] = velocity; @@ -388,7 +392,7 @@ struct TurbineData { */ struct SimulationControls { /// Debug levels used in AeroDyn-Inflow C bindings - enum class DebugLevel { + enum class DebugLevel : std::uint8_t { kNone = 0, //< No debug output kSummary = 1, //< Some summary info kDetailed = 2, //< Above + all position/orientation info @@ -436,7 +440,7 @@ struct SimulationControls { */ struct ErrorHandling { /// Error levels used in InflowWind - enum class ErrorLevel { + enum class ErrorLevel : std::uint8_t { kNone = 0, kInfo = 1, kWarning = 2, @@ -461,18 +465,18 @@ struct ErrorHandling { /// Struct to hold the properties of the working fluid (air) struct FluidProperties { - float density{1.225f}; //< Air density (kg/m^3) - float kinematic_viscosity{1.464E-5f}; //< Kinematic viscosity (m^2/s) - float sound_speed{335.f}; //< Speed of sound in the working fluid (m/s) - float vapor_pressure{1700.f}; //< Vapor pressure of the working fluid (Pa) + float density{1.225F}; //< Air density (kg/m^3) + float kinematic_viscosity{1.464E-5F}; //< Kinematic viscosity (m^2/s) + float sound_speed{335.F}; //< Speed of sound in the working fluid (m/s) + float vapor_pressure{1700.F}; //< Vapor pressure of the working fluid (Pa) }; /// Struct to hold the environmental conditions struct EnvironmentalConditions { - float gravity{9.80665f}; //< Gravitational acceleration (m/s^2) - float atm_pressure{103500.f}; //< Atmospheric pressure (Pa) - float water_depth{0.f}; //< Water depth (m) - float msl_offset{0.f}; //< Mean sea level to still water level offset (m) + float gravity{9.80665F}; //< Gravitational acceleration (m/s^2) + float atm_pressure{103500.F}; //< Atmospheric pressure (Pa) + float water_depth{0.F}; //< Water depth (m) + float msl_offset{0.F}; //< Mean sea level to still water level offset (m) }; /** @@ -485,8 +489,8 @@ struct VTKSettings { bool write_vtk{false}; //< Flag to write VTK output int vtk_type{1}; //< Type of VTK output (1: surface meshes) std::array vtk_nacelle_dimensions{//< Nacelle dimensions for VTK rendering - -2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}; - float vtk_hub_radius{1.5f}; //< Hub radius for VTK rendering + -2.5F, -2.5F, 0.F, 10.F, 5.F, 5.F}; + float vtk_hub_radius{1.5F}; //< Hub radius for VTK rendering }; /** @@ -521,36 +525,25 @@ class AeroDynInflowLibrary { * @param vtk VTK output settings */ AeroDynInflowLibrary( - std::string shared_lib_path = "aerodyn_inflow_c_binding.dll", + const std::string& shared_lib_path = "aerodyn_inflow_c_binding.dll", ErrorHandling eh = ErrorHandling{}, FluidProperties fp = FluidProperties{}, EnvironmentalConditions ec = EnvironmentalConditions{}, SimulationControls sc = SimulationControls{}, VTKSettings vtk = VTKSettings{} ) : lib_{shared_lib_path, util::dylib::no_filename_decorations}, - error_handling_(std::move(eh)), - air_(std::move(fp)), - env_conditions_(std::move(ec)), - sim_controls_(std::move(sc)), - vtk_settings_(std::move(vtk)) {} - - /// Destructor to take care of Fortran-side cleanup if the library is initialized - ~AeroDynInflowLibrary() noexcept { - try { - if (is_initialized_) { - Finalize(); - } - } catch (const std::exception& e) { - std::cerr << "Error during AeroDynInflowLibrary destruction: " << e.what() << std::endl; - } - } + error_handling_{eh}, + air_{fp}, + env_conditions_{ec}, + sim_controls_{std::move(sc)}, + vtk_settings_{vtk} {} /// Getter methods for the private member variables - const ErrorHandling& GetErrorHandling() const { return error_handling_; } - const EnvironmentalConditions& GetEnvironmentalConditions() const { return env_conditions_; } - const FluidProperties& GetFluidProperties() const { return air_; } - const SimulationControls& GetSimulationControls() const { return sim_controls_; } - const VTKSettings& GetVTKSettings() const { return vtk_settings_; } - const std::vector& GetTurbines() const { return turbines_; } + [[nodiscard]] const ErrorHandling& GetErrorHandling() const { return error_handling_; } + [[nodiscard]] const EnvironmentalConditions& GetEnvironmentalConditions() const { return env_conditions_; } + [[nodiscard]] const FluidProperties& GetFluidProperties() const { return air_; } + [[nodiscard]] const SimulationControls& GetSimulationControls() const { return sim_controls_; } + [[nodiscard]] const VTKSettings& GetVTKSettings() const { return vtk_settings_; } + [[nodiscard]] const std::vector& GetTurbines() const { return turbines_; } /** * @brief Initialize the AeroDyn Inflow library @@ -563,7 +556,7 @@ class AeroDynInflowLibrary { * * @param turbine_configs Vector of TurbineConfig objects, each representing a single turbine */ - void Initialize(std::vector turbine_configs) { + void Initialize(const std::vector& turbine_configs) { PreInitialize(turbine_configs.size()); SetupRotors(turbine_configs); FinalizeInitialization(); @@ -579,9 +572,9 @@ class AeroDynInflowLibrary { auto ADI_C_PreInit = lib_.get_function("ADI_C_PreInit"); // Convert bool and other types to int32_t for Fortran compatibility - int32_t debug_level_int = static_cast(sim_controls_.debug_level); - int32_t transpose_dcm_int = sim_controls_.transpose_DCM ? 1 : 0; - int32_t n_turbines_int = static_cast(n_turbines); + auto debug_level_int = static_cast(sim_controls_.debug_level); + auto transpose_dcm_int = sim_controls_.transpose_DCM ? 1 : 0; + auto n_turbines_int = static_cast(n_turbines); ADI_C_PreInit( &n_turbines_int, // input: Number of turbines @@ -620,7 +613,7 @@ class AeroDynInflowLibrary { // Create new turbine data to store the updates // Note: TurbineData and MeshData are validated during construction, so no need to // perform additional checks here - turbines_.emplace_back(TurbineData(tc)); + turbines_.emplace_back(tc); auto& td = turbines_.back(); // Call setup rotor for each turbine @@ -668,10 +661,10 @@ class AeroDynInflowLibrary { // Primary input file will be passed as path to the file char* aerodyn_input_pointer{sim_controls_.aerodyn_input.data()}; - int32_t aerodyn_input_length = static_cast(sim_controls_.aerodyn_input.size()); + auto aerodyn_input_length = static_cast(sim_controls_.aerodyn_input.size()); char* inflowwind_input_pointer{sim_controls_.inflowwind_input.data()}; - int32_t inflowwind_input_length = + auto inflowwind_input_length = static_cast(sim_controls_.inflowwind_input.size()); ADI_C_Init( @@ -801,7 +794,7 @@ class AeroDynInflowLibrary { // Set up output channel values auto output_channel_values_c = - std::vector(static_cast(sim_controls_.n_channels), 0.f); + std::vector(static_cast(sim_controls_.n_channels), 0.F); ADI_C_CalcOutput( &time, // input: time at which to calculate output forces diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index 81f48683..67233fc3 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -7,25 +7,25 @@ namespace openturbine::tests { TEST(AerodynInflowTest, BladeInitialState_Constructor) { - Array_7 root{1., 2., 3., 1., 0., 0., 0.}; - std::vector nodes{{4., 5., 6., 1., 0., 0., 0.}, {7., 8., 9., 1., 0., 0., 0.}}; - util::TurbineConfig::BladeInitialState blade_state{root, nodes}; + const Array_7 root{1., 2., 3., 1., 0., 0., 0.}; + const std::vector nodes{{4., 5., 6., 1., 0., 0., 0.}, {7., 8., 9., 1., 0., 0., 0.}}; + const util::TurbineConfig::BladeInitialState blade_state{root, nodes}; EXPECT_EQ(blade_state.root_initial_position, root); EXPECT_EQ(blade_state.node_initial_positions, nodes); } TEST(AerodynInflowTest, TurbineConfig_Constructor) { - bool is_hawt{true}; - std::array ref_pos{10.f, 20.f, 30.f}; - Array_7 hub_pos{1., 2., 3., 1., 0., 0., 0.}; - Array_7 nacelle_pos{4., 5., 6., 1., 0., 0., 0.}; + const bool is_hawt{true}; + const std::array ref_pos{10.F, 20.F, 30.F}; + const Array_7 hub_pos{1., 2., 3., 1., 0., 0., 0.}; + const Array_7 nacelle_pos{4., 5., 6., 1., 0., 0., 0.}; // Create 3 blades with 2 nodes each std::vector blade_states; for (int i = 0; i < 3; ++i) { - Array_7 root = {static_cast(i), 0., 0., 1., 0., 0., 0.}; - std::vector nodes = { + const Array_7 root = {static_cast(i), 0., 0., 1., 0., 0., 0.}; + const std::vector nodes = { {static_cast(i), 1., 0., 1., 0., 0., 0.}, {static_cast(i), 2., 0., 1., 0., 0., 0.}}; blade_states.emplace_back(root, nodes); @@ -57,11 +57,11 @@ TEST(AerodynInflowTest, TurbineConfig_Constructor) { TEST(AerodynInflowTest, TurbineConfig_Validate_InvalidConfiguration) { // Invalid configuration: No blades - bool is_hawt{true}; - std::array ref_pos{10.f, 20.f, 30.f}; - Array_7 hub_pos{1., 2., 3., 1., 0., 0., 0.}; - Array_7 nacelle_pos{4., 5., 6., 1., 0., 0., 0.}; - std::vector empty_blade_states; + const bool is_hawt{true}; + const std::array ref_pos{10.F, 20.F, 30.F}; + const Array_7 hub_pos{1., 2., 3., 1., 0., 0., 0.}; + const Array_7 nacelle_pos{4., 5., 6., 1., 0., 0., 0.}; + const std::vector empty_blade_states; EXPECT_THROW( util::TurbineConfig(is_hawt, ref_pos, hub_pos, nacelle_pos, empty_blade_states), @@ -82,18 +82,18 @@ void ExpectArrayNear( } TEST(AerodynInflowTest, SetPositionAndOrientation) { - std::array data = {1., 2., 3., 0.707107, 0.707107, 0., 0.}; - std::array position; - std::array orientation; + const std::array data = {1., 2., 3., 0.707107, 0.707107, 0., 0.}; + std::array position{0.F}; + std::array orientation{0.}; util::SetPositionAndOrientation(data, position, orientation); - ExpectArrayNear(position, {1.f, 2.f, 3.f}); + ExpectArrayNear(position, {1.F, 2.F, 3.F}); ExpectArrayNear(orientation, {1., 0., 0., 0., 0., -1., 0., 1., 0.}); } TEST(AerodynInflowTest, MeshData_Constructor_NumberOfNodes) { - util::MeshData mesh_motion_data{1}; + const util::MeshData mesh_motion_data{1}; EXPECT_EQ(mesh_motion_data.NumberOfMeshPoints(), 1); EXPECT_EQ(mesh_motion_data.position.size(), 1); EXPECT_EQ(mesh_motion_data.orientation.size(), 1); @@ -103,11 +103,11 @@ TEST(AerodynInflowTest, MeshData_Constructor_NumberOfNodes) { } TEST(AerodynInflowTest, MeshData_Constructor_Data) { - size_t n_mesh_points{1}; - std::vector> mesh_data{{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; - std::vector> mesh_velocities{{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}}; - std::vector> mesh_accelerations{{7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; - std::vector> mesh_loads = {{13.f, 14.f, 15.f, 16.f, 17.f, 18.f}}; + const size_t n_mesh_points{1}; + const std::vector> mesh_data{{1., 2., 3., 0.707107, 0.707107, 0., 0.}}; + const std::vector> mesh_velocities{{1.F, 2.F, 3.F, 4.F, 5.F, 6.F}}; + const std::vector> mesh_accelerations{{7.F, 8.F, 9.F, 10.F, 11.F, 12.F}}; + const std::vector> mesh_loads = {{13.F, 14.F, 15.F, 16.F, 17.F, 18.F}}; util::MeshData mesh_motion_data( n_mesh_points, mesh_data, mesh_velocities, mesh_accelerations, mesh_loads ); @@ -118,11 +118,11 @@ TEST(AerodynInflowTest, MeshData_Constructor_Data) { EXPECT_EQ(mesh_motion_data.velocity.size(), n_mesh_points); EXPECT_EQ(mesh_motion_data.acceleration.size(), n_mesh_points); EXPECT_EQ(mesh_motion_data.loads.size(), n_mesh_points); - ExpectArrayNear(mesh_motion_data.position[0], {1.f, 2.f, 3.f}); + ExpectArrayNear(mesh_motion_data.position[0], {1.F, 2.F, 3.F}); ExpectArrayNear(mesh_motion_data.orientation[0], {1., 0., 0., 0., 0., -1., 0., 1., 0.}); - ExpectArrayNear(mesh_motion_data.velocity[0], {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}); - ExpectArrayNear(mesh_motion_data.acceleration[0], {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}); - ExpectArrayNear(mesh_motion_data.loads[0], {13.f, 14.f, 15.f, 16.f, 17.f, 18.f}); + ExpectArrayNear(mesh_motion_data.velocity[0], {1.F, 2.F, 3.F, 4.F, 5.F, 6.F}); + ExpectArrayNear(mesh_motion_data.acceleration[0], {7.F, 8.F, 9.F, 10.F, 11.F, 12.F}); + ExpectArrayNear(mesh_motion_data.loads[0], {13.F, 14.F, 15.F, 16.F, 17.F, 18.F}); } class MeshDataValidationTest : public ::testing::Test { @@ -130,13 +130,13 @@ class MeshDataValidationTest : public ::testing::Test { void SetUp() override { // Create a mesh data with 2 mesh points mesh_data = std::make_unique(2); - mesh_data->position = {{1.f, 2.f, 3.f}, {4.f, 5.f, 6.f}}; + mesh_data->position = {{1.F, 2.F, 3.F}, {4.F, 5.F, 6.F}}; mesh_data->orientation = { {1., 0., 0., 0., 1., 0., 0., 0., 1.}, {0., 1., 0., -1., 0., 0., 0., 0., 1.}}; - mesh_data->velocity = {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}, {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + mesh_data->velocity = {{1.F, 2.F, 3.F, 4.F, 5.F, 6.F}, {7.F, 8.F, 9.F, 10.F, 11.F, 12.F}}; mesh_data->acceleration = { - {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}, {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; - mesh_data->loads = {{1.f, 2.f, 3.f, 4.f, 5.f, 6.f}, {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}}; + {1.F, 2.F, 3.F, 4.F, 5.F, 6.F}, {7.F, 8.F, 9.F, 10.F, 11.F, 12.F}}; + mesh_data->loads = {{1.F, 2.F, 3.F, 4.F, 5.F, 6.F}, {7.F, 8.F, 9.F, 10.F, 11.F, 12.F}}; } std::unique_ptr mesh_data; @@ -176,7 +176,7 @@ TEST(AerodynInflowTest, TurbineData_Constructor) { // Set up 3 blades with 2 nodes each std::vector blade_states; for (size_t i = 0; i < 3; ++i) { - util::TurbineConfig::BladeInitialState blade_state( + const util::TurbineConfig::BladeInitialState blade_state( {0., 0., 90., 1., 0., 0., 0.}, // root_initial_position { {0., 5., 90., 1., 0., 0., 0.}, // node_initial_positions - 1 @@ -186,9 +186,9 @@ TEST(AerodynInflowTest, TurbineData_Constructor) { ); blade_states.push_back(blade_state); } - util::TurbineConfig tc( + const util::TurbineConfig tc( true, // is_horizontal_axis - {0.f, 0.f, 0.f}, // reference_position + {0.F, 0.F, 0.F}, // reference_position {0., 0., 90., 1., 0., 0., 0.}, // hub_initial_position {0., 0., 90., 1., 0., 0., 0.}, // nacelle_initial_position blade_states @@ -204,18 +204,18 @@ TEST(AerodynInflowTest, TurbineData_Constructor) { EXPECT_EQ(turbine_data.blade_nodes.NumberOfMeshPoints(), 6); // 3 blades * 2 nodes each // Check hub and nacelle positions - ExpectArrayNear(turbine_data.hub.position[0], {0.f, 0.f, 90.f}); - ExpectArrayNear(turbine_data.nacelle.position[0], {0.f, 0.f, 90.f}); + ExpectArrayNear(turbine_data.hub.position[0], {0.F, 0.F, 90.F}); + ExpectArrayNear(turbine_data.nacelle.position[0], {0.F, 0.F, 90.F}); // Check blade roots for (size_t i = 0; i < turbine_data.NumberOfBlades(); ++i) { - ExpectArrayNear(turbine_data.blade_roots.position[i], {0.f, 0.f, 90.f}); + ExpectArrayNear(turbine_data.blade_roots.position[i], {0.F, 0.F, 90.F}); } // Check blade nodes for (size_t i = 0; i < turbine_data.blade_nodes.NumberOfMeshPoints(); ++i) { - float expected_y = (i % 2 == 0) ? 5.f : 10.f; - ExpectArrayNear(turbine_data.blade_nodes.position[i], {0.f, expected_y, 90.f}); + const float expected_y = (i % 2 == 0) ? 5.F : 10.F; + ExpectArrayNear(turbine_data.blade_nodes.position[i], {0.F, expected_y, 90.F}); } // Check blade_nodes_to_blade_num_mapping @@ -245,7 +245,7 @@ TEST(AerodynInflowTest, TurbineData_Constructor) { // Additional check: Verify that node_indices_by_blade correctly maps to // blade_nodes_to_blade_num_mapping for (size_t blade = 0; blade < turbine_data.NumberOfBlades(); ++blade) { - for (size_t node : turbine_data.node_indices_by_blade[blade]) { + for (const size_t node : turbine_data.node_indices_by_blade[blade]) { EXPECT_EQ(turbine_data.blade_nodes_to_blade_num_mapping[node], blade + 1) << "Mismatch between node_indices_by_blade and blade_nodes_to_blade_num_mapping for " "blade " @@ -260,7 +260,7 @@ class TurbineDataValidationTest : public ::testing::Test { blade_states.clear(); // Set up 3 blades with 2 nodes each for (size_t i = 0; i < 3; ++i) { - util::TurbineConfig::BladeInitialState blade_state( + const util::TurbineConfig::BladeInitialState blade_state( {0., 0., 90., 1., 0., 0., 0.}, // root_initial_position { {0., 5., 90., 1., 0., 0., 0.}, // node_initial_positions - 1 @@ -271,7 +271,7 @@ class TurbineDataValidationTest : public ::testing::Test { } tc = std::make_unique( true, // is_horizontal_axis - std::array{0.f, 0.f, 0.f}, // reference_position + std::array{0.F, 0.F, 0.F}, // reference_position std::array{0., 0., 90., 1., 0., 0., 0.}, // hub_initial_position std::array{0., 0., 90., 1., 0., 0., 0.}, // nacelle_initial_position blade_states @@ -323,7 +323,7 @@ TEST(AerodynInflowTest, TurbineData_SetBladeNodeValues) { // Set up 3 blades with 2 nodes each std::vector blade_states; for (size_t i = 0; i < 3; ++i) { - util::TurbineConfig::BladeInitialState blade_state( + const util::TurbineConfig::BladeInitialState blade_state( {0., 0., 90., 1., 0., 0., 0.}, // root_initial_position { {0., 5., 90., 1., 0., 0., 0.}, // node_initial_positions - 1 @@ -332,9 +332,9 @@ TEST(AerodynInflowTest, TurbineData_SetBladeNodeValues) { ); blade_states.push_back(blade_state); } - util::TurbineConfig tc( + const util::TurbineConfig tc( true, // is_horizontal_axis - {0.f, 0.f, 0.f}, // reference_position + {0.F, 0.F, 0.F}, // reference_position {0., 0., 90., 1., 0., 0., 0.}, // hub_initial_position {0., 0., 90., 1., 0., 0., 0.}, // nacelle_initial_position blade_states @@ -343,26 +343,26 @@ TEST(AerodynInflowTest, TurbineData_SetBladeNodeValues) { util::TurbineData turbine_data(tc); // Verify the current values for the first blade and node - size_t blade_number{1}; - size_t node_number{0}; - size_t node_index{turbine_data.node_indices_by_blade[blade_number][node_number]}; + const size_t blade_number{1}; + const size_t node_number{0}; + const size_t node_index{turbine_data.node_indices_by_blade[blade_number][node_number]}; - ExpectArrayNear(turbine_data.blade_nodes.position[node_index], {0.f, 5.f, 90.f}); + ExpectArrayNear(turbine_data.blade_nodes.position[node_index], {0.F, 5.F, 90.F}); ExpectArrayNear( turbine_data.blade_nodes.orientation[node_index], {1., 0., 0., 0., 1., 0., 0., 0., 1.} ); - ExpectArrayNear(turbine_data.blade_nodes.velocity[node_index], {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}); + ExpectArrayNear(turbine_data.blade_nodes.velocity[node_index], {0.F, 0.F, 0.F, 0.F, 0.F, 0.F}); ExpectArrayNear( - turbine_data.blade_nodes.acceleration[node_index], {0.f, 0.f, 0.f, 0.f, 0.f, 0.f} + turbine_data.blade_nodes.acceleration[node_index], {0.F, 0.F, 0.F, 0.F, 0.F, 0.F} ); - ExpectArrayNear(turbine_data.blade_nodes.loads[node_index], {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}); + ExpectArrayNear(turbine_data.blade_nodes.loads[node_index], {0.F, 0.F, 0.F, 0.F, 0.F, 0.F}); // Define new values for the node - std::array new_position = {1.f, 2.f, 3.f}; - std::array new_orientation = {1., 0., 0., 0., 1., 0., 0., 0., 1.}; - std::array new_velocity = {1.f, 2.f, 3.f, 4.f, 5.f, 6.f}; - std::array new_acceleration = {7.f, 8.f, 9.f, 10.f, 11.f, 12.f}; - std::array new_loads = {13.f, 14.f, 15.f, 16.f, 17.f, 18.f}; + const std::array new_position = {1.F, 2.F, 3.F}; + const std::array new_orientation = {1., 0., 0., 0., 1., 0., 0., 0., 1.}; + const std::array new_velocity = {1.F, 2.F, 3.F, 4.F, 5.F, 6.F}; + const std::array new_acceleration = {7.F, 8.F, 9.F, 10.F, 11.F, 12.F}; + const std::array new_loads = {13.F, 14.F, 15.F, 16.F, 17.F, 18.F}; turbine_data.SetBladeNodeValues( blade_number, node_number, new_position, new_orientation, new_velocity, new_acceleration, new_loads @@ -405,9 +405,9 @@ TEST(AerodynInflowTest, SimulationControls_Set) { simulation_controls.max_time = 1200.; simulation_controls.total_elapsed_time = 100.; simulation_controls.n_time_steps = 10; - simulation_controls.store_HH_wind_speed = 0; - simulation_controls.transpose_DCM = 0; - simulation_controls.debug_level = 1; + simulation_controls.store_HH_wind_speed = false; + simulation_controls.transpose_DCM = false; + simulation_controls.debug_level = static_cast(util::SimulationControls::DebugLevel::kSummary); simulation_controls.output_format = 1; simulation_controls.output_time_step = 1.; simulation_controls.n_channels = 1; @@ -441,7 +441,7 @@ TEST(AerodynInflowTest, SimulationControls_Set) { } TEST(AerodynInflowTest, ErrorHandling_NoThrow) { - util::ErrorHandling error_handling; + const util::ErrorHandling error_handling; EXPECT_NO_THROW(error_handling.CheckError()); } @@ -452,66 +452,66 @@ TEST(AerodynInflowTest, ErrorHandling_Throw) { } TEST(AerodynInflowTest, FluidProperties_Default) { - util::FluidProperties fluid_properties; - EXPECT_NEAR(fluid_properties.density, 1.225f, 1e-6f); - EXPECT_NEAR(fluid_properties.kinematic_viscosity, 1.464E-5f, 1e-6f); - EXPECT_NEAR(fluid_properties.sound_speed, 335.f, 1e-6f); - EXPECT_NEAR(fluid_properties.vapor_pressure, 1700.f, 1e-6f); + const util::FluidProperties fluid_properties; + EXPECT_NEAR(fluid_properties.density, 1.225F, 1e-6F); + EXPECT_NEAR(fluid_properties.kinematic_viscosity, 1.464E-5F, 1e-6F); + EXPECT_NEAR(fluid_properties.sound_speed, 335.F, 1e-6F); + EXPECT_NEAR(fluid_properties.vapor_pressure, 1700.F, 1e-6F); } TEST(AerodynInflowTest, FluidProperties_Set) { util::FluidProperties fluid_properties; - fluid_properties.density = 1.1f; - fluid_properties.kinematic_viscosity = 1.5E-5f; - fluid_properties.sound_speed = 340.f; - fluid_properties.vapor_pressure = 1800.f; + fluid_properties.density = 1.1F; + fluid_properties.kinematic_viscosity = 1.5E-5F; + fluid_properties.sound_speed = 340.F; + fluid_properties.vapor_pressure = 1800.F; - EXPECT_NEAR(fluid_properties.density, 1.1f, 1e-6f); - EXPECT_NEAR(fluid_properties.kinematic_viscosity, 1.5E-5f, 1e-6f); - EXPECT_NEAR(fluid_properties.sound_speed, 340.f, 1e-6f); - EXPECT_NEAR(fluid_properties.vapor_pressure, 1800.f, 1e-6f); + EXPECT_NEAR(fluid_properties.density, 1.1F, 1e-6F); + EXPECT_NEAR(fluid_properties.kinematic_viscosity, 1.5E-5F, 1e-6F); + EXPECT_NEAR(fluid_properties.sound_speed, 340.F, 1e-6F); + EXPECT_NEAR(fluid_properties.vapor_pressure, 1800.F, 1e-6F); } TEST(AerodynInflowTest, EnvironmentalConditions_Default) { - util::EnvironmentalConditions environmental_conditions; - EXPECT_NEAR(environmental_conditions.gravity, 9.80665f, 1e-6f); - EXPECT_NEAR(environmental_conditions.atm_pressure, 103500.f, 1e-6f); - EXPECT_NEAR(environmental_conditions.water_depth, 0.f, 1e-6f); - EXPECT_NEAR(environmental_conditions.msl_offset, 0.f, 1e-6f); + const util::EnvironmentalConditions environmental_conditions; + EXPECT_NEAR(environmental_conditions.gravity, 9.80665F, 1e-6F); + EXPECT_NEAR(environmental_conditions.atm_pressure, 103500.F, 1e-6F); + EXPECT_NEAR(environmental_conditions.water_depth, 0.F, 1e-6F); + EXPECT_NEAR(environmental_conditions.msl_offset, 0.F, 1e-6F); } TEST(AerodynInflowTest, EnvironmentalConditions_Set) { util::EnvironmentalConditions environmental_conditions; - environmental_conditions.gravity = 9.79665f; - environmental_conditions.atm_pressure = 103000.f; - environmental_conditions.water_depth = 100.f; - environmental_conditions.msl_offset = 10.f; + environmental_conditions.gravity = 9.79665F; + environmental_conditions.atm_pressure = 103000.F; + environmental_conditions.water_depth = 100.F; + environmental_conditions.msl_offset = 10.F; - EXPECT_NEAR(environmental_conditions.gravity, 9.79665f, 1e-6f); - EXPECT_NEAR(environmental_conditions.atm_pressure, 103000.f, 1e-6f); - EXPECT_NEAR(environmental_conditions.water_depth, 100.f, 1e-6f); - EXPECT_NEAR(environmental_conditions.msl_offset, 10.f, 1e-6f); + EXPECT_NEAR(environmental_conditions.gravity, 9.79665F, 1e-6F); + EXPECT_NEAR(environmental_conditions.atm_pressure, 103000.F, 1e-6F); + EXPECT_NEAR(environmental_conditions.water_depth, 100.F, 1e-6F); + EXPECT_NEAR(environmental_conditions.msl_offset, 10.F, 1e-6F); } TEST(AerodynInflowTest, VTKSettings_Default) { - util::VTKSettings vtk_settings; - EXPECT_EQ(vtk_settings.write_vtk, 0); + const util::VTKSettings vtk_settings; + EXPECT_EQ(vtk_settings.write_vtk, false); EXPECT_EQ(vtk_settings.vtk_type, 1); - ExpectArrayNear(vtk_settings.vtk_nacelle_dimensions, {-2.5f, -2.5f, 0.f, 10.f, 5.f, 5.f}); - EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.5f); + ExpectArrayNear(vtk_settings.vtk_nacelle_dimensions, {-2.5F, -2.5F, 0.F, 10.F, 5.F, 5.F}); + EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.5F); } TEST(AerodynInflowTest, VTKSettings_Set) { util::VTKSettings vtk_settings; - vtk_settings.write_vtk = 1; + vtk_settings.write_vtk = true; vtk_settings.vtk_type = 2; - vtk_settings.vtk_nacelle_dimensions = {-1.5f, -1.5f, 0.f, 5.f, 2.5f, 2.5f}; - vtk_settings.vtk_hub_radius = 1.f; + vtk_settings.vtk_nacelle_dimensions = {-1.5F, -1.5F, 0.F, 5.F, 2.5F, 2.5F}; + vtk_settings.vtk_hub_radius = 1.F; - EXPECT_EQ(vtk_settings.write_vtk, 1); + EXPECT_EQ(vtk_settings.write_vtk, true); EXPECT_EQ(vtk_settings.vtk_type, 2); - ExpectArrayNear(vtk_settings.vtk_nacelle_dimensions, {-1.5f, -1.5f, 0.f, 5.f, 2.5f, 2.5f}); - EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.f); + ExpectArrayNear(vtk_settings.vtk_nacelle_dimensions, {-1.5F, -1.5F, 0.F, 5.F, 2.5F, 2.5F}); + EXPECT_EQ(vtk_settings.vtk_hub_radius, 1.F); } /// Helper function to get the shared library path @@ -525,15 +525,15 @@ std::string GetSharedLibraryPath() { TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { // Load the shared library const std::string path = GetSharedLibraryPath(); - util::AeroDynInflowLibrary aerodyn_inflow_library(path); + const util::AeroDynInflowLibrary aerodyn_inflow_library(path); // Check initial error handling state EXPECT_EQ(aerodyn_inflow_library.GetErrorHandling().error_status, 0); EXPECT_STREQ(aerodyn_inflow_library.GetErrorHandling().error_message.data(), ""); // Check default values for other important members - EXPECT_EQ(aerodyn_inflow_library.GetFluidProperties().density, 1.225f); - EXPECT_EQ(aerodyn_inflow_library.GetEnvironmentalConditions().gravity, 9.80665f); + EXPECT_EQ(aerodyn_inflow_library.GetFluidProperties().density, 1.225F); + EXPECT_EQ(aerodyn_inflow_library.GetEnvironmentalConditions().gravity, 9.80665F); EXPECT_EQ(aerodyn_inflow_library.GetSimulationControls().debug_level, 0); EXPECT_EQ(aerodyn_inflow_library.GetSimulationControls().transpose_DCM, 1); EXPECT_EQ(aerodyn_inflow_library.GetVTKSettings().write_vtk, 0); @@ -541,7 +541,7 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_DefaultConstructor) { TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoopSimulation) { const std::filesystem::path project_root = FindProjectRoot(); - std::filesystem::path input_path = project_root / "tests/unit_tests/external/"; + const std::filesystem::path input_path = project_root / "tests/unit_tests/external/"; // Set up simulation parameters util::SimulationControls sim_controls; @@ -558,27 +558,27 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoopSimulation) { // Set up fluid properties util::FluidProperties fluid_props; - fluid_props.density = 1.225f; - fluid_props.kinematic_viscosity = 1.464E-05f; - fluid_props.sound_speed = 335.f; - fluid_props.vapor_pressure = 1700.f; + fluid_props.density = 1.225F; + fluid_props.kinematic_viscosity = 1.464E-05F; + fluid_props.sound_speed = 335.F; + fluid_props.vapor_pressure = 1700.F; // Set up environmental conditions util::EnvironmentalConditions env_conditions; - env_conditions.gravity = 9.80665f; - env_conditions.atm_pressure = 103500.f; - env_conditions.water_depth = 0.f; - env_conditions.msl_offset = 0.f; + env_conditions.gravity = 9.80665F; + env_conditions.atm_pressure = 103500.F; + env_conditions.water_depth = 0.F; + env_conditions.msl_offset = 0.F; // Set up VTK settings - util::VTKSettings vtk_settings{}; + const util::VTKSettings vtk_settings{}; // Set up turbine configuration - std::array hub_pos = {0., 0., 90., 1., 0., 0., 0.}; - std::array nacelle_pos = {0., 0., 90., 1., 0., 0., 0.}; + const std::array hub_pos = {0., 0., 90., 1., 0., 0., 0.}; + const std::array nacelle_pos = {0., 0., 90., 1., 0., 0., 0.}; std::vector blade_states; for (int i = 0; i < 3; ++i) { - util::TurbineConfig::BladeInitialState blade_state( + const util::TurbineConfig::BladeInitialState blade_state( {0., 0., 90., 1., 0., 0., 0.}, // root_initial_position { {0., 5., 90., 1., 0., 0., 0.}, // node_initial_positions - 1 @@ -587,9 +587,9 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoopSimulation) { ); blade_states.push_back(blade_state); } - util::TurbineConfig turbine_config( + const util::TurbineConfig turbine_config( true, // is_horizontal_axis - {0.f, 0.f, 0.f}, // reference_position + {0.F, 0.F, 0.F}, // reference_position hub_pos, // hub_initial_position nacelle_pos, // nacelle_initial_position blade_states @@ -602,7 +602,7 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoopSimulation) { ); // Initialize with turbine configuration - std::vector turbine_configs = {turbine_config}; + const std::vector turbine_configs = {turbine_config}; EXPECT_NO_THROW(aerodyn_inflow_library.Initialize(turbine_configs)); // Simulate for 10 time steps @@ -621,12 +621,12 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoopSimulation) { // Assert loads on blade nodes - they don't change since we're not updating the motion auto expected_loads = std::vector>{ - {11132.2f, -1938.43f, 0.f, 44472.6f, 323391.f, 50444.8f}, // Blade 1, Node 1 - {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // Blade 1, Node 2 - {11132.2f, -1938.43f, 0.f, 44472.6f, 323391.f, 50444.8f}, // Blade 2, Node 1 - {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // Blade 2, Node 2 - {11132.2f, -1938.43f, 0.f, 44472.6f, 323391.f, 50444.8f}, // Blade 3, Node 1 - {0.f, 0.f, 0.f, 0.f, 0.f, 0.f} // Blade 3, Node 2 + {11132.2F, -1938.43F, 0.F, 44472.6F, 323391.F, 50444.8F}, // Blade 1, Node 1 + {0.F, 0.F, 0.F, 0.F, 0.F, 0.F}, // Blade 1, Node 2 + {11132.2F, -1938.43F, 0.F, 44472.6F, 323391.F, 50444.8F}, // Blade 2, Node 1 + {0.F, 0.F, 0.F, 0.F, 0.F, 0.F}, // Blade 2, Node 2 + {11132.2F, -1938.43F, 0.F, 44472.6F, 323391.F, 50444.8F}, // Blade 3, Node 1 + {0.F, 0.F, 0.F, 0.F, 0.F, 0.F} // Blade 3, Node 2 }; const auto& turbine = aerodyn_inflow_library.GetTurbines()[0]; @@ -636,7 +636,7 @@ TEST(AerodynInflowTest, AeroDynInflowLibrary_FullLoopSimulation) { const auto& node_loads = turbine.blade_nodes.loads[node_index]; const auto& e_loads = expected_loads[ii * 2 + jj]; for (size_t k = 0; k < 3; ++k) { - EXPECT_NEAR(node_loads[k], e_loads[k], 1e-1f); + EXPECT_NEAR(node_loads[k], e_loads[k], 1e-1F); } } } From d0da9c4d14f98d313ac0fa36277c6075b3c77208 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Thu, 10 Oct 2024 13:02:00 -0600 Subject: [PATCH 80/87] Fix formatting for clang-format@18 --- src/utilities/aerodynamics/aerodyn_inflow.hpp | 16 ++++++++-------- .../unit_tests/external/test_aerodyn_inflow.cpp | 12 ++++++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/utilities/aerodynamics/aerodyn_inflow.hpp b/src/utilities/aerodynamics/aerodyn_inflow.hpp index bade05a8..4151a048 100644 --- a/src/utilities/aerodynamics/aerodyn_inflow.hpp +++ b/src/utilities/aerodynamics/aerodyn_inflow.hpp @@ -158,8 +158,7 @@ struct MeshData { /// Constructor to initialize all mesh data based on provided inputs MeshData( size_t n_mesh_pts, const std::vector>& pos, - const std::vector>& vel, - const std::vector>& acc, + const std::vector>& vel, const std::vector>& acc, const std::vector>& ld ) : n_mesh_points(static_cast(n_mesh_pts)), @@ -186,8 +185,7 @@ struct MeshData { } // Check all vectors are the same size as the number of mesh points - auto check_vector_size = [](const auto& vec, size_t exp_size, - const std::string& vec_name) { + auto check_vector_size = [](const auto& vec, size_t exp_size, const std::string& vec_name) { if (vec.size() != exp_size) { throw std::invalid_argument(vec_name + " vector size does not match n_mesh_points"); } @@ -489,7 +487,8 @@ struct VTKSettings { bool write_vtk{false}; //< Flag to write VTK output int vtk_type{1}; //< Type of VTK output (1: surface meshes) std::array vtk_nacelle_dimensions{//< Nacelle dimensions for VTK rendering - -2.5F, -2.5F, 0.F, 10.F, 5.F, 5.F}; + -2.5F, -2.5F, 0.F, 10.F, 5.F, 5.F + }; float vtk_hub_radius{1.5F}; //< Hub radius for VTK rendering }; @@ -539,7 +538,9 @@ class AeroDynInflowLibrary { /// Getter methods for the private member variables [[nodiscard]] const ErrorHandling& GetErrorHandling() const { return error_handling_; } - [[nodiscard]] const EnvironmentalConditions& GetEnvironmentalConditions() const { return env_conditions_; } + [[nodiscard]] const EnvironmentalConditions& GetEnvironmentalConditions() const { + return env_conditions_; + } [[nodiscard]] const FluidProperties& GetFluidProperties() const { return air_; } [[nodiscard]] const SimulationControls& GetSimulationControls() const { return sim_controls_; } [[nodiscard]] const VTKSettings& GetVTKSettings() const { return vtk_settings_; } @@ -664,8 +665,7 @@ class AeroDynInflowLibrary { auto aerodyn_input_length = static_cast(sim_controls_.aerodyn_input.size()); char* inflowwind_input_pointer{sim_controls_.inflowwind_input.data()}; - auto inflowwind_input_length = - static_cast(sim_controls_.inflowwind_input.size()); + auto inflowwind_input_length = static_cast(sim_controls_.inflowwind_input.size()); ADI_C_Init( &is_aerodyn_input_passed_as_string, // input: AD input is passed diff --git a/tests/unit_tests/external/test_aerodyn_inflow.cpp b/tests/unit_tests/external/test_aerodyn_inflow.cpp index 67233fc3..17ae2341 100644 --- a/tests/unit_tests/external/test_aerodyn_inflow.cpp +++ b/tests/unit_tests/external/test_aerodyn_inflow.cpp @@ -27,7 +27,8 @@ TEST(AerodynInflowTest, TurbineConfig_Constructor) { const Array_7 root = {static_cast(i), 0., 0., 1., 0., 0., 0.}; const std::vector nodes = { {static_cast(i), 1., 0., 1., 0., 0., 0.}, - {static_cast(i), 2., 0., 1., 0., 0., 0.}}; + {static_cast(i), 2., 0., 1., 0., 0., 0.} + }; blade_states.emplace_back(root, nodes); } @@ -132,10 +133,12 @@ class MeshDataValidationTest : public ::testing::Test { mesh_data = std::make_unique(2); mesh_data->position = {{1.F, 2.F, 3.F}, {4.F, 5.F, 6.F}}; mesh_data->orientation = { - {1., 0., 0., 0., 1., 0., 0., 0., 1.}, {0., 1., 0., -1., 0., 0., 0., 0., 1.}}; + {1., 0., 0., 0., 1., 0., 0., 0., 1.}, {0., 1., 0., -1., 0., 0., 0., 0., 1.} + }; mesh_data->velocity = {{1.F, 2.F, 3.F, 4.F, 5.F, 6.F}, {7.F, 8.F, 9.F, 10.F, 11.F, 12.F}}; mesh_data->acceleration = { - {1.F, 2.F, 3.F, 4.F, 5.F, 6.F}, {7.F, 8.F, 9.F, 10.F, 11.F, 12.F}}; + {1.F, 2.F, 3.F, 4.F, 5.F, 6.F}, {7.F, 8.F, 9.F, 10.F, 11.F, 12.F} + }; mesh_data->loads = {{1.F, 2.F, 3.F, 4.F, 5.F, 6.F}, {7.F, 8.F, 9.F, 10.F, 11.F, 12.F}}; } @@ -407,7 +410,8 @@ TEST(AerodynInflowTest, SimulationControls_Set) { simulation_controls.n_time_steps = 10; simulation_controls.store_HH_wind_speed = false; simulation_controls.transpose_DCM = false; - simulation_controls.debug_level = static_cast(util::SimulationControls::DebugLevel::kSummary); + simulation_controls.debug_level = + static_cast(util::SimulationControls::DebugLevel::kSummary); simulation_controls.output_format = 1; simulation_controls.output_time_step = 1.; simulation_controls.n_channels = 1; From 190289cfdafa1682a45b9247c64310d2aebedb57 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 11 Oct 2024 16:33:23 +0000 Subject: [PATCH 81/87] Refactor methods in BeamsInput struct to avoid duplicating code + unit tests for TDD --- src/beams/beams_input.hpp | 76 +++++++----- tests/unit_tests/CMakeLists.txt | 2 +- tests/unit_tests/beams/test_beams_input.cpp | 127 ++++++++++++++++++++ 3 files changed, 176 insertions(+), 29 deletions(-) create mode 100644 tests/unit_tests/beams/test_beams_input.cpp diff --git a/src/beams/beams_input.hpp b/src/beams/beams_input.hpp index 883d1241..06c7dbec 100644 --- a/src/beams/beams_input.hpp +++ b/src/beams/beams_input.hpp @@ -9,55 +9,75 @@ namespace openturbine { +/** + * @brief Represents the input data for beam simulations + * + * This struct encapsulates all necessary input parameters for beam simulations, + * including the beam elements and environmental factors such as gravity. + * It also provides utility methods for accessing and computing various + * properties of the beam structure. + */ struct BeamsInput { - std::vector elements; - std::array gravity; + std::vector elements; //< Elements in the beam + std::array gravity; //< Gravity vector BeamsInput(std::vector elems, std::array g) : elements(std::move(elems)), gravity(g) {} - [[nodiscard]] size_t NumElements() const { return elements.size(); }; + /// Returns the number of elements in the beam + [[nodiscard]] size_t NumElements() const { return elements.size(); } - [[nodiscard]] size_t NumNodes() const { - return std::transform_reduce( - elements.begin(), elements.end(), size_t{0U}, std::plus{}, - [](const BeamElement& e) { - return e.nodes.size(); - } - ); - } - - [[nodiscard]] size_t NumQuadraturePoints() const { + /// Computes the sum of a value across all elements + template + [[nodiscard]] size_t ComputeSum(Accessor accessor) const { return std::transform_reduce( elements.begin(), elements.end(), size_t{0U}, std::plus{}, - [](const BeamElement& e) { - return e.quadrature.size(); + [&accessor](const BeamElement& e) { + return accessor(e); } ); } - [[nodiscard]] size_t MaxElemNodes() const { + /// Computes the maximum of a value across all elements + template + [[nodiscard]] size_t ComputeMax(Accessor accessor) const { return std::transform_reduce( elements.begin(), elements.end(), size_t{0U}, - [](size_t a, size_t b) { + [](auto a, auto b) { return std::max(a, b); }, - [](const BeamElement& e) { - return e.nodes.size(); + [&accessor](const auto& e) { + return accessor(e); } ); } + /// Returns the total number of nodes in the beam + [[nodiscard]] size_t NumNodes() const { + return ComputeSum([](const BeamElement& e) { + return e.nodes.size(); + }); + } + + /// Returns the total number of quadrature points in the beam + [[nodiscard]] size_t NumQuadraturePoints() const { + return ComputeSum([](const BeamElement& e) { + return e.quadrature.size(); + }); + } + + /// Returns the maximum number of nodes in any element of the beam + [[nodiscard]] size_t MaxElemNodes() const { + return ComputeMax([](const BeamElement& e) { + return e.nodes.size(); + }); + } + + /// Returns the maximum number of quadrature points in any element of the beam [[nodiscard]] size_t MaxElemQuadraturePoints() const { - return std::transform_reduce( - elements.begin(), elements.end(), size_t{0U}, - [](size_t a, size_t b) { - return std::max(a, b); - }, - [](const BeamElement& e) { - return e.quadrature.size(); - } - ); + return ComputeMax([](const BeamElement& e) { + return e.quadrature.size(); + }); } }; diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 986e66dc..84949c9a 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -24,7 +24,7 @@ target_link_libraries(openturbine_unit_tests PRIVATE target_link_system_libraries(openturbine_unit_tests PRIVATE KokkosKernels::kokkoskernels Amesos2::amesos2 - yaml-cpp + #yaml-cpp GTest::gtest GTest::gtest_main ) diff --git a/tests/unit_tests/beams/test_beams_input.cpp b/tests/unit_tests/beams/test_beams_input.cpp new file mode 100644 index 00000000..b147a4cc --- /dev/null +++ b/tests/unit_tests/beams/test_beams_input.cpp @@ -0,0 +1,127 @@ +#include + +#include "src/beams/beams_input.hpp" +#include "src/model/model.hpp" + +namespace openturbine::tests { + +class BeamsInputTest : public ::testing::Test { +protected: + static auto CreateTestElements() { + // Create mass and stiffness matrices (identity matrices) + constexpr auto mass_matrix = std::array{ + std::array{1.0, 0.0, 0.0, 0.0, 0.0, 0.0}, std::array{0.0, 1.0, 0.0, 0.0, 0.0, 0.0}, + std::array{0.0, 0.0, 1.0, 0.0, 0.0, 0.0}, std::array{0.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + std::array{0.0, 0.0, 0.0, 0.0, 1.0, 0.0}, std::array{0.0, 0.0, 0.0, 0.0, 0.0, 1.0}, + }; + + constexpr auto stiffness_matrix = std::array{ + std::array{1.0, 0.0, 0.0, 0.0, 0.0, 0.0}, std::array{0.0, 1.0, 0.0, 0.0, 0.0, 0.0}, + std::array{0.0, 0.0, 1.0, 0.0, 0.0, 0.0}, std::array{0.0, 0.0, 0.0, 1.0, 0.0, 0.0}, + std::array{0.0, 0.0, 0.0, 0.0, 1.0, 0.0}, std::array{0.0, 0.0, 0.0, 0.0, 0.0, 1.0}, + }; + + // Create a mock Model for creating nodes + auto model = Model(); + for (int i = 0; i < 9; ++i) { + model.AddNode( + {static_cast(i), 0.0, 0.0, 1.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0} + ); + } + + // Create 3 elements with different numbers of nodes and quadrature points + return std::vector{ + // Element 1 - 3 nodes, 2 quadrature points + BeamElement( + { + BeamNode(0.0, model.GetNode(0)), + BeamNode(0.5, model.GetNode(1)), + BeamNode(1.0, model.GetNode(2)), + }, + { + BeamSection(0.0, mass_matrix, stiffness_matrix), + BeamSection(1.0, mass_matrix, stiffness_matrix), + }, + BeamQuadrature{{-0.5, 0.5}, {0.5, 0.5}} + ), + // Element 2 - 2 nodes, 2 quadrature points + BeamElement( + { + BeamNode(0.0, model.GetNode(3)), + BeamNode(1.0, model.GetNode(4)), + }, + { + BeamSection(0.0, mass_matrix, stiffness_matrix), + BeamSection(1.0, mass_matrix, stiffness_matrix), + }, + BeamQuadrature{{-0.5773502691896257, 1.0}, {0.5773502691896257, 1.0}} + ), + // Element 3 - 4 nodes, 4 quadrature points + BeamElement( + { + BeamNode(0.0, model.GetNode(5)), + BeamNode(0.33, model.GetNode(6)), + BeamNode(0.67, model.GetNode(7)), + BeamNode(1.0, model.GetNode(8)), + }, + { + BeamSection(0.0, mass_matrix, stiffness_matrix), + BeamSection(1.0, mass_matrix, stiffness_matrix), + }, + BeamQuadrature{ + {-0.861136311594053, 0.347854845137454}, + {-0.339981043584856, 0.652145154862546}, + {0.339981043584856, 0.652145154862546}, + {0.861136311594053, 0.347854845137454} + } + ) + }; + } + + static BeamsInput CreateTestBeamsInput() { + return BeamsInput(CreateTestElements(), {0.0, 0.0, -9.81}); + } +}; + +TEST_F(BeamsInputTest, Constructor) { + const auto elements = CreateTestElements(); + const std::array gravity = {0.0, 0.0, -9.81}; + const BeamsInput input(elements, gravity); + + EXPECT_EQ(input.elements.size(), 3); + EXPECT_EQ(input.gravity, gravity); +} + +TEST_F(BeamsInputTest, NumElements) { + const auto input = CreateTestBeamsInput(); + // 3 elements + EXPECT_EQ(input.NumElements(), 3); +} + +TEST_F(BeamsInputTest, NumNodes) { + const auto input = CreateTestBeamsInput(); + // 9 nodes across all elements + EXPECT_EQ(input.NumNodes(), 9); +} + +TEST_F(BeamsInputTest, NumQuadraturePoints) { + const auto input = CreateTestBeamsInput(); + // 8 quadrature points across all elements + EXPECT_EQ(input.NumQuadraturePoints(), 8); +} + +TEST_F(BeamsInputTest, MaxElemNodes) { + const auto input = CreateTestBeamsInput(); + // Element 3 has the most nodes (4) + EXPECT_EQ(input.MaxElemNodes(), 4); +} + +TEST_F(BeamsInputTest, MaxElemQuadraturePoints) { + const auto input = CreateTestBeamsInput(); + // Element 3 has the most quadrature points (4) + EXPECT_EQ(input.MaxElemQuadraturePoints(), 4); +} + +} // namespace openturbine::tests From c20555a05dd9276bc215a0691840ee85561ad390 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 11 Oct 2024 16:48:10 +0000 Subject: [PATCH 82/87] Fix the error is CMakeLists file in previous commit --- tests/unit_tests/CMakeLists.txt | 2 +- tests/unit_tests/beams/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index 84949c9a..986e66dc 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -24,7 +24,7 @@ target_link_libraries(openturbine_unit_tests PRIVATE target_link_system_libraries(openturbine_unit_tests PRIVATE KokkosKernels::kokkoskernels Amesos2::amesos2 - #yaml-cpp + yaml-cpp GTest::gtest GTest::gtest_main ) diff --git a/tests/unit_tests/beams/CMakeLists.txt b/tests/unit_tests/beams/CMakeLists.txt index a569f1ad..093fb3eb 100644 --- a/tests/unit_tests/beams/CMakeLists.txt +++ b/tests/unit_tests/beams/CMakeLists.txt @@ -2,6 +2,7 @@ target_sources( openturbine_unit_tests PRIVATE + test_beams_input.cpp test_interpolate_QP_state.cpp test_interpolate_QP_vector.cpp ) From 19d3a2f9c943069329c47186dc71cc0b7f9d516d Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 11 Oct 2024 17:03:44 +0000 Subject: [PATCH 83/87] Add a mock implementation of CalculateExternalForces struct and add it to quadrature pt calculations --- src/beams/beams.hpp | 2 + src/step/update_system_variables.hpp | 1 + src/system/calculate_external_forces.hpp | 66 +++++++++++ .../calculate_quadrature_point_values.hpp | 12 ++ tests/unit_tests/system/CMakeLists.txt | 1 + .../system/test_calculate_external_forces.cpp | 103 ++++++++++++++++++ 6 files changed, 185 insertions(+) create mode 100644 src/system/calculate_external_forces.hpp create mode 100644 tests/unit_tests/system/test_calculate_external_forces.cpp diff --git a/src/beams/beams.hpp b/src/beams/beams.hpp index e49dc091..0e2b3110 100644 --- a/src/beams/beams.hpp +++ b/src/beams/beams.hpp @@ -69,6 +69,7 @@ struct Beams { Kokkos::View qp_Fc; // Elastic force Kokkos::View qp_Fd; // Elastic force Kokkos::View qp_Fi; // Inertial force + Kokkos::View qp_Fe; // External force Kokkos::View qp_Fg; // Gravity force Kokkos::View qp_RR0; // Global rotation Kokkos::View qp_Muu; // Mass in global frame @@ -139,6 +140,7 @@ struct Beams { qp_Fc("qp_Fc", num_elems, max_elem_qps), qp_Fd("qp_Fd", num_elems, max_elem_qps), qp_Fi("qp_Fi", num_elems, max_elem_qps), + qp_Fe("qp_Fe", num_elems, max_elem_qps), qp_Fg("qp_Fg", num_elems, max_elem_qps), qp_RR0("qp_RR0", num_elems, max_elem_qps), qp_Muu("qp_Muu", num_elems, max_elem_qps), diff --git a/src/step/update_system_variables.hpp b/src/step/update_system_variables.hpp index cdf0763a..ef4f9833 100644 --- a/src/step/update_system_variables.hpp +++ b/src/step/update_system_variables.hpp @@ -74,6 +74,7 @@ inline void UpdateSystemVariables(StepParameters& parameters, const Beams& beams beams.qp_Fc, beams.qp_Fd, beams.qp_Fi, + beams.qp_Fe, beams.qp_Fg, beams.qp_Muu, beams.qp_Cuu, diff --git a/src/system/calculate_external_forces.hpp b/src/system/calculate_external_forces.hpp new file mode 100644 index 00000000..a3e81319 --- /dev/null +++ b/src/system/calculate_external_forces.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include + +#include "src/math/vector_operations.hpp" + +namespace openturbine { + +// TODO This is a mock implementation of the external forces +struct CalculateExternalForces { + using NoTranspose = KokkosBatched::Trans::NoTranspose; + using GemmDefault = KokkosBatched::Algo::Gemm::Default; + using GemvDefault = KokkosBlas::Algo::Gemv::Default; + using Gemm = KokkosBatched::SerialGemm; + using Gemv = KokkosBlas::SerialGemv; + size_t i_elem; + Kokkos::View::const_type qp_Muu_; + Kokkos::View::const_type qp_u_ddot_; + Kokkos::View::const_type qp_omega_; + Kokkos::View::const_type qp_omega_dot_; + Kokkos::View::const_type eta_tilde_; + Kokkos::View omega_tilde_; + Kokkos::View omega_dot_tilde_; + Kokkos::View::const_type rho_; + Kokkos::View::const_type eta_; + Kokkos::View qp_FI_; + + KOKKOS_FUNCTION + void operator()(int i_qp) const { + auto Muu = Kokkos::subview(qp_Muu_, i_elem, i_qp, Kokkos::ALL, Kokkos::ALL); + auto u_ddot = Kokkos::subview(qp_u_ddot_, i_elem, i_qp, Kokkos::ALL); + auto omega = Kokkos::subview(qp_omega_, i_elem, i_qp, Kokkos::ALL); + auto omega_dot = Kokkos::subview(qp_omega_dot_, i_elem, i_qp, Kokkos::ALL); + auto eta_tilde = Kokkos::subview(eta_tilde_, i_elem, i_qp, Kokkos::ALL, Kokkos::ALL); + auto omega_tilde = Kokkos::subview(omega_tilde_, i_elem, i_qp, Kokkos::ALL, Kokkos::ALL); + auto omega_dot_tilde = + Kokkos::subview(omega_dot_tilde_, i_elem, i_qp, Kokkos::ALL, Kokkos::ALL); + auto rho = Kokkos::subview(rho_, i_elem, i_qp, Kokkos::ALL, Kokkos::ALL); + auto eta = Kokkos::subview(eta_, i_elem, i_qp, Kokkos::ALL); + auto v1 = Kokkos::Array{}; + auto V1 = View_3(v1.data()); + auto m1 = Kokkos::Array{}; + auto M1 = View_3x3(m1.data()); + auto FI = Kokkos::subview(qp_FI_, i_elem, i_qp, Kokkos::ALL); + + auto m = Muu(0, 0); + VecTilde(omega, omega_tilde); + VecTilde(omega_dot, omega_dot_tilde); + auto FI_1 = Kokkos::subview(FI, Kokkos::make_pair(0, 3)); + Gemm::invoke(m, omega_tilde, omega_tilde, 0., M1); + KokkosBlas::serial_axpy(m, omega_dot_tilde, M1); + + Gemv::invoke(1., M1, eta, 0., FI_1); + KokkosBlas::serial_axpy(m, u_ddot, FI_1); + auto FI_2 = Kokkos::subview(FI, Kokkos::make_pair(3, 6)); + KokkosBlas::serial_axpy(m, u_ddot, V1); + Gemv::invoke(1., eta_tilde, V1, 0., FI_2); + Gemv::invoke(1., rho, omega_dot, 1., FI_2); + Gemm::invoke(1., omega_tilde, rho, 0., M1); + Gemv::invoke(1., M1, omega, 1., FI_2); + } +}; + +} // namespace openturbine diff --git a/src/system/calculate_quadrature_point_values.hpp b/src/system/calculate_quadrature_point_values.hpp index 237d5988..0413538a 100644 --- a/src/system/calculate_quadrature_point_values.hpp +++ b/src/system/calculate_quadrature_point_values.hpp @@ -6,6 +6,7 @@ #include "calculate_Puu.hpp" #include "calculate_Quu.hpp" #include "calculate_RR0.hpp" +#include "calculate_external_forces.hpp" #include "calculate_force_FC.hpp" #include "calculate_force_FD.hpp" #include "calculate_gravity_force.hpp" @@ -47,6 +48,7 @@ struct CalculateQuadraturePointValues { Kokkos::View qp_FC_; Kokkos::View qp_FD_; Kokkos::View qp_FI_; + Kokkos::View qp_Fe_; Kokkos::View qp_FG_; Kokkos::View qp_Muu_; Kokkos::View qp_Cuu_; @@ -104,6 +106,15 @@ struct CalculateQuadraturePointValues { ); member.team_barrier(); + Kokkos::parallel_for( + Kokkos::TeamThreadRange(member, num_qps), + CalculateExternalForces{ + i_elem, qp_Muu_, qp_u_ddot_, qp_omega_, qp_omega_dot_, qp_eta_tilde_, + qp_omega_tilde_, qp_omega_dot_tilde_, qp_rho_, qp_eta_, qp_Fe_ + } + ); + member.team_barrier(); + Kokkos::parallel_for( Kokkos::TeamThreadRange(member, num_qps), CalculateForceFD{i_elem, qp_x0pupSS_, qp_FC_, qp_FD_} @@ -145,4 +156,5 @@ struct CalculateQuadraturePointValues { ); } }; + } // namespace openturbine diff --git a/tests/unit_tests/system/CMakeLists.txt b/tests/unit_tests/system/CMakeLists.txt index 09937a1d..522bc921 100644 --- a/tests/unit_tests/system/CMakeLists.txt +++ b/tests/unit_tests/system/CMakeLists.txt @@ -11,6 +11,7 @@ target_sources( test_calculate_strain.cpp test_calculate_mass_matrix_components.cpp test_calculate_temporary_variables.cpp + test_calculate_external_forces.cpp test_calculate_force_FC.cpp test_calculate_force_FD.cpp test_calculate_inertial_forces.cpp diff --git a/tests/unit_tests/system/test_calculate_external_forces.cpp b/tests/unit_tests/system/test_calculate_external_forces.cpp new file mode 100644 index 00000000..d525e4d1 --- /dev/null +++ b/tests/unit_tests/system/test_calculate_external_forces.cpp @@ -0,0 +1,103 @@ +#include +#include + +#include "test_calculate.hpp" + +#include "src/system/calculate_external_forces.hpp" + +namespace openturbine::tests { + +TEST(CalculateExternalForcesTests, OneNode) { + const auto Muu = Kokkos::View("Muu"); + constexpr auto Muu_data = std::array{1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., + 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., + 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36.}; + const auto Muu_host = Kokkos::View(Muu_data.data()); + const auto Muu_mirror = Kokkos::create_mirror(Muu); + Kokkos::deep_copy(Muu_mirror, Muu_host); + Kokkos::deep_copy(Muu, Muu_mirror); + + const auto u_ddot = Kokkos::View("u_ddot"); + constexpr auto u_ddot_data = std::array{37., 38., 39.}; + const auto u_ddot_host = + Kokkos::View(u_ddot_data.data()); + const auto u_ddot_mirror = Kokkos::create_mirror(u_ddot); + Kokkos::deep_copy(u_ddot_mirror, u_ddot_host); + Kokkos::deep_copy(u_ddot, u_ddot_mirror); + + const auto omega = Kokkos::View("omega"); + constexpr auto omega_data = std::array{40., 41., 42.}; + const auto omega_host = + Kokkos::View(omega_data.data()); + const auto omega_mirror = Kokkos::create_mirror(omega); + Kokkos::deep_copy(omega_mirror, omega_host); + Kokkos::deep_copy(omega, omega_mirror); + + const auto omega_dot = Kokkos::View("omega_dot"); + constexpr auto omega_dot_data = std::array{43., 44., 45.}; + const auto omega_dot_host = + Kokkos::View(omega_dot_data.data()); + const auto omega_dot_mirror = Kokkos::create_mirror(omega_dot); + Kokkos::deep_copy(omega_dot_mirror, omega_dot_host); + Kokkos::deep_copy(omega_dot, omega_dot_mirror); + + const auto eta_tilde = Kokkos::View("eta_tilde"); + constexpr auto eta_tilde_data = std::array{46., 47., 48., 49., 50., 51., 52., 53., 54.}; + const auto eta_tilde_host = + Kokkos::View(eta_tilde_data.data()); + const auto eta_tilde_mirror = Kokkos::create_mirror(eta_tilde); + Kokkos::deep_copy(eta_tilde_mirror, eta_tilde_host); + Kokkos::deep_copy(eta_tilde, eta_tilde_mirror); + + const auto rho = Kokkos::View("rho"); + const auto rho_data = std::array{55., 56., 57., 58., 59., 60., 61., 62., 63.}; + const auto rho_host = Kokkos::View(rho_data.data()); + const auto rho_mirror = Kokkos::create_mirror(rho); + Kokkos::deep_copy(rho_mirror, rho_host); + Kokkos::deep_copy(rho, rho_mirror); + + const auto eta = Kokkos::View("eta"); + const auto eta_data = std::array{64., 65., 66.}; + const auto eta_host = Kokkos::View(eta_data.data()); + const auto eta_mirror = Kokkos::create_mirror(eta); + Kokkos::deep_copy(eta_mirror, eta_host); + Kokkos::deep_copy(eta, eta_mirror); + + const auto omega_tilde = Kokkos::View("omega_tilde"); + const auto omega_dot_tilde = Kokkos::View("omega_dot_tilde"); + const auto FE = Kokkos::View("FE"); + + Kokkos::parallel_for( + "CalculateExternalForces", 1, + CalculateExternalForces{ + 0, Muu, u_ddot, omega, omega_dot, eta_tilde, omega_tilde, omega_dot_tilde, rho, eta, FE + } + ); + + constexpr auto omega_tilde_exact_data = std::array{0., -42., 41., 42., 0., -40., -41., 40., 0.}; + const auto omega_tilde_exact = + Kokkos::View(omega_tilde_exact_data.data()); + + const auto omega_tilde_mirror = Kokkos::create_mirror(omega_tilde); + Kokkos::deep_copy(omega_tilde_mirror, omega_tilde); + CompareWithExpected(omega_tilde_mirror, omega_tilde_exact); + + constexpr auto omega_dot_tilde_exact_data = + std::array{0., -45., 44., 45., 0., -43., -44., 43., 0.}; + const auto omega_dot_tilde_exact = + Kokkos::View(omega_dot_tilde_exact_data.data()); + + const auto omega_dot_tilde_mirror = Kokkos::create_mirror(omega_dot_tilde); + Kokkos::deep_copy(omega_dot_tilde_mirror, omega_dot_tilde); + CompareWithExpected(omega_dot_tilde_mirror, omega_dot_tilde_exact); + + constexpr auto FE_exact_data = std::array{-2984., 32., 2922., 20624., -2248., 22100.}; + const auto FE_exact = + Kokkos::View(FE_exact_data.data()); + + const auto FE_mirror = Kokkos::create_mirror(FE); + Kokkos::deep_copy(FE_mirror, FE); + CompareWithExpected(FE_mirror, FE_exact); +} + +} // namespace openturbine::tests From 74883c54a447defe78aff9530eded737eaacb392 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 11 Oct 2024 17:11:50 +0000 Subject: [PATCH 84/87] Fix a typo --- src/system/calculate_quadrature_point_values.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/system/calculate_quadrature_point_values.hpp b/src/system/calculate_quadrature_point_values.hpp index 0413538a..7572286d 100644 --- a/src/system/calculate_quadrature_point_values.hpp +++ b/src/system/calculate_quadrature_point_values.hpp @@ -48,7 +48,7 @@ struct CalculateQuadraturePointValues { Kokkos::View qp_FC_; Kokkos::View qp_FD_; Kokkos::View qp_FI_; - Kokkos::View qp_Fe_; + Kokkos::View qp_FE_; Kokkos::View qp_FG_; Kokkos::View qp_Muu_; Kokkos::View qp_Cuu_; @@ -110,7 +110,7 @@ struct CalculateQuadraturePointValues { Kokkos::TeamThreadRange(member, num_qps), CalculateExternalForces{ i_elem, qp_Muu_, qp_u_ddot_, qp_omega_, qp_omega_dot_, qp_eta_tilde_, - qp_omega_tilde_, qp_omega_dot_tilde_, qp_rho_, qp_eta_, qp_Fe_ + qp_omega_tilde_, qp_omega_dot_tilde_, qp_rho_, qp_eta_, qp_FE_ } ); member.team_barrier(); From c121d777c3b5306a6cbdf0cfdce2dbd00ac28038 Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 11 Oct 2024 18:04:20 +0000 Subject: [PATCH 85/87] Remove calc for external forces, rather add it to residual vector calc --- src/step/assemble_residual_vector.hpp | 2 +- src/system/calculate_external_forces.hpp | 66 ----------- .../calculate_quadrature_point_values.hpp | 10 -- src/system/integrate_residual_vector.hpp | 14 ++- tests/unit_tests/system/CMakeLists.txt | 1 - .../system/test_calculate_external_forces.cpp | 103 ------------------ .../system/test_integrate_residual_vector.cpp | 26 +++-- 7 files changed, 27 insertions(+), 195 deletions(-) delete mode 100644 src/system/calculate_external_forces.hpp delete mode 100644 tests/unit_tests/system/test_calculate_external_forces.cpp diff --git a/src/step/assemble_residual_vector.hpp b/src/step/assemble_residual_vector.hpp index 5327ca24..bcd5caf0 100644 --- a/src/step/assemble_residual_vector.hpp +++ b/src/step/assemble_residual_vector.hpp @@ -25,7 +25,7 @@ inline void AssembleResidualVector(const Beams& beams) { IntegrateResidualVector{ beams.num_nodes_per_element, beams.num_qps_per_element, beams.qp_weight, beams.qp_jacobian, beams.shape_interp, beams.shape_deriv, beams.node_FX, beams.qp_Fc, - beams.qp_Fd, beams.qp_Fi, beams.qp_Fg, beams.residual_vector_terms + beams.qp_Fd, beams.qp_Fi, beams.qp_Fe, beams.qp_Fg, beams.residual_vector_terms } ); } diff --git a/src/system/calculate_external_forces.hpp b/src/system/calculate_external_forces.hpp deleted file mode 100644 index a3e81319..00000000 --- a/src/system/calculate_external_forces.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "src/math/vector_operations.hpp" - -namespace openturbine { - -// TODO This is a mock implementation of the external forces -struct CalculateExternalForces { - using NoTranspose = KokkosBatched::Trans::NoTranspose; - using GemmDefault = KokkosBatched::Algo::Gemm::Default; - using GemvDefault = KokkosBlas::Algo::Gemv::Default; - using Gemm = KokkosBatched::SerialGemm; - using Gemv = KokkosBlas::SerialGemv; - size_t i_elem; - Kokkos::View::const_type qp_Muu_; - Kokkos::View::const_type qp_u_ddot_; - Kokkos::View::const_type qp_omega_; - Kokkos::View::const_type qp_omega_dot_; - Kokkos::View::const_type eta_tilde_; - Kokkos::View omega_tilde_; - Kokkos::View omega_dot_tilde_; - Kokkos::View::const_type rho_; - Kokkos::View::const_type eta_; - Kokkos::View qp_FI_; - - KOKKOS_FUNCTION - void operator()(int i_qp) const { - auto Muu = Kokkos::subview(qp_Muu_, i_elem, i_qp, Kokkos::ALL, Kokkos::ALL); - auto u_ddot = Kokkos::subview(qp_u_ddot_, i_elem, i_qp, Kokkos::ALL); - auto omega = Kokkos::subview(qp_omega_, i_elem, i_qp, Kokkos::ALL); - auto omega_dot = Kokkos::subview(qp_omega_dot_, i_elem, i_qp, Kokkos::ALL); - auto eta_tilde = Kokkos::subview(eta_tilde_, i_elem, i_qp, Kokkos::ALL, Kokkos::ALL); - auto omega_tilde = Kokkos::subview(omega_tilde_, i_elem, i_qp, Kokkos::ALL, Kokkos::ALL); - auto omega_dot_tilde = - Kokkos::subview(omega_dot_tilde_, i_elem, i_qp, Kokkos::ALL, Kokkos::ALL); - auto rho = Kokkos::subview(rho_, i_elem, i_qp, Kokkos::ALL, Kokkos::ALL); - auto eta = Kokkos::subview(eta_, i_elem, i_qp, Kokkos::ALL); - auto v1 = Kokkos::Array{}; - auto V1 = View_3(v1.data()); - auto m1 = Kokkos::Array{}; - auto M1 = View_3x3(m1.data()); - auto FI = Kokkos::subview(qp_FI_, i_elem, i_qp, Kokkos::ALL); - - auto m = Muu(0, 0); - VecTilde(omega, omega_tilde); - VecTilde(omega_dot, omega_dot_tilde); - auto FI_1 = Kokkos::subview(FI, Kokkos::make_pair(0, 3)); - Gemm::invoke(m, omega_tilde, omega_tilde, 0., M1); - KokkosBlas::serial_axpy(m, omega_dot_tilde, M1); - - Gemv::invoke(1., M1, eta, 0., FI_1); - KokkosBlas::serial_axpy(m, u_ddot, FI_1); - auto FI_2 = Kokkos::subview(FI, Kokkos::make_pair(3, 6)); - KokkosBlas::serial_axpy(m, u_ddot, V1); - Gemv::invoke(1., eta_tilde, V1, 0., FI_2); - Gemv::invoke(1., rho, omega_dot, 1., FI_2); - Gemm::invoke(1., omega_tilde, rho, 0., M1); - Gemv::invoke(1., M1, omega, 1., FI_2); - } -}; - -} // namespace openturbine diff --git a/src/system/calculate_quadrature_point_values.hpp b/src/system/calculate_quadrature_point_values.hpp index 7572286d..f3297ce2 100644 --- a/src/system/calculate_quadrature_point_values.hpp +++ b/src/system/calculate_quadrature_point_values.hpp @@ -6,7 +6,6 @@ #include "calculate_Puu.hpp" #include "calculate_Quu.hpp" #include "calculate_RR0.hpp" -#include "calculate_external_forces.hpp" #include "calculate_force_FC.hpp" #include "calculate_force_FD.hpp" #include "calculate_gravity_force.hpp" @@ -106,15 +105,6 @@ struct CalculateQuadraturePointValues { ); member.team_barrier(); - Kokkos::parallel_for( - Kokkos::TeamThreadRange(member, num_qps), - CalculateExternalForces{ - i_elem, qp_Muu_, qp_u_ddot_, qp_omega_, qp_omega_dot_, qp_eta_tilde_, - qp_omega_tilde_, qp_omega_dot_tilde_, qp_rho_, qp_eta_, qp_FE_ - } - ); - member.team_barrier(); - Kokkos::parallel_for( Kokkos::TeamThreadRange(member, num_qps), CalculateForceFD{i_elem, qp_x0pupSS_, qp_FC_, qp_FD_} diff --git a/src/system/integrate_residual_vector.hpp b/src/system/integrate_residual_vector.hpp index cd60557e..f0fae964 100644 --- a/src/system/integrate_residual_vector.hpp +++ b/src/system/integrate_residual_vector.hpp @@ -15,6 +15,7 @@ struct IntegrateResidualVectorElement { Kokkos::View::const_type qp_Fc_; Kokkos::View::const_type qp_Fd_; Kokkos::View::const_type qp_Fi_; + Kokkos::View::const_type qp_Fe_; Kokkos::View::const_type qp_Fg_; Kokkos::View residual_vector_terms_; @@ -26,9 +27,9 @@ struct IntegrateResidualVectorElement { const auto coeff_c = weight * shape_deriv_(i_index, j_index); const auto coeff_dig = weight * qp_jacobian_(j_index) * shape_interp_(i_index, j_index); for (auto k = 0U; k < 6U; ++k) { - local_residual[k] += - coeff_c * qp_Fc_(j_index, k) + - coeff_dig * (qp_Fd_(j_index, k) + qp_Fi_(j_index, k) - qp_Fg_(j_index, k)); + local_residual[k] += coeff_c * qp_Fc_(j_index, k) + + coeff_dig * (qp_Fd_(j_index, k) + qp_Fi_(j_index, k) - + qp_Fe_(j_index, k) - qp_Fg_(j_index, k)); } } for (auto k = 0U; k < 6U; ++k) { @@ -48,6 +49,7 @@ struct IntegrateResidualVector { Kokkos::View::const_type qp_Fc_; Kokkos::View::const_type qp_Fd_; Kokkos::View::const_type qp_Fi_; + Kokkos::View::const_type qp_Fe_; Kokkos::View::const_type qp_Fg_; Kokkos::View residual_vector_terms_; @@ -66,6 +68,7 @@ struct IntegrateResidualVector { const auto qp_Fc = Kokkos::View(member.team_scratch(1), num_qps); const auto qp_Fd = Kokkos::View(member.team_scratch(1), num_qps); const auto qp_Fi = Kokkos::View(member.team_scratch(1), num_qps); + const auto qp_Fe = Kokkos::View(member.team_scratch(1), num_qps); const auto qp_Fg = Kokkos::View(member.team_scratch(1), num_qps); Kokkos::parallel_for(Kokkos::TeamThreadRange(member, num_qps), [&](size_t k) { @@ -79,6 +82,7 @@ struct IntegrateResidualVector { qp_Fc(k, i) = qp_Fc_(i_elem, k, i); qp_Fd(k, i) = qp_Fd_(i_elem, k, i); qp_Fi(k, i) = qp_Fi_(i_elem, k, i); + qp_Fe(k, i) = qp_Fe_(i_elem, k, i); qp_Fg(k, i) = qp_Fg_(i_elem, k, i); } }); @@ -91,8 +95,8 @@ struct IntegrateResidualVector { const auto node_range = Kokkos::TeamThreadRange(member, num_nodes); const auto element_integrator = IntegrateResidualVectorElement{ - i_elem, num_qps, qp_weight, qp_jacobian, shape_interp, shape_deriv, - node_FX, qp_Fc, qp_Fd, qp_Fi, qp_Fg, residual_vector_terms_ + i_elem, num_qps, qp_weight, qp_jacobian, shape_interp, shape_deriv, node_FX, + qp_Fc, qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms_ }; Kokkos::parallel_for(node_range, element_integrator); } diff --git a/tests/unit_tests/system/CMakeLists.txt b/tests/unit_tests/system/CMakeLists.txt index 522bc921..09937a1d 100644 --- a/tests/unit_tests/system/CMakeLists.txt +++ b/tests/unit_tests/system/CMakeLists.txt @@ -11,7 +11,6 @@ target_sources( test_calculate_strain.cpp test_calculate_mass_matrix_components.cpp test_calculate_temporary_variables.cpp - test_calculate_external_forces.cpp test_calculate_force_FC.cpp test_calculate_force_FD.cpp test_calculate_inertial_forces.cpp diff --git a/tests/unit_tests/system/test_calculate_external_forces.cpp b/tests/unit_tests/system/test_calculate_external_forces.cpp deleted file mode 100644 index d525e4d1..00000000 --- a/tests/unit_tests/system/test_calculate_external_forces.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include - -#include "test_calculate.hpp" - -#include "src/system/calculate_external_forces.hpp" - -namespace openturbine::tests { - -TEST(CalculateExternalForcesTests, OneNode) { - const auto Muu = Kokkos::View("Muu"); - constexpr auto Muu_data = std::array{1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., - 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., - 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36.}; - const auto Muu_host = Kokkos::View(Muu_data.data()); - const auto Muu_mirror = Kokkos::create_mirror(Muu); - Kokkos::deep_copy(Muu_mirror, Muu_host); - Kokkos::deep_copy(Muu, Muu_mirror); - - const auto u_ddot = Kokkos::View("u_ddot"); - constexpr auto u_ddot_data = std::array{37., 38., 39.}; - const auto u_ddot_host = - Kokkos::View(u_ddot_data.data()); - const auto u_ddot_mirror = Kokkos::create_mirror(u_ddot); - Kokkos::deep_copy(u_ddot_mirror, u_ddot_host); - Kokkos::deep_copy(u_ddot, u_ddot_mirror); - - const auto omega = Kokkos::View("omega"); - constexpr auto omega_data = std::array{40., 41., 42.}; - const auto omega_host = - Kokkos::View(omega_data.data()); - const auto omega_mirror = Kokkos::create_mirror(omega); - Kokkos::deep_copy(omega_mirror, omega_host); - Kokkos::deep_copy(omega, omega_mirror); - - const auto omega_dot = Kokkos::View("omega_dot"); - constexpr auto omega_dot_data = std::array{43., 44., 45.}; - const auto omega_dot_host = - Kokkos::View(omega_dot_data.data()); - const auto omega_dot_mirror = Kokkos::create_mirror(omega_dot); - Kokkos::deep_copy(omega_dot_mirror, omega_dot_host); - Kokkos::deep_copy(omega_dot, omega_dot_mirror); - - const auto eta_tilde = Kokkos::View("eta_tilde"); - constexpr auto eta_tilde_data = std::array{46., 47., 48., 49., 50., 51., 52., 53., 54.}; - const auto eta_tilde_host = - Kokkos::View(eta_tilde_data.data()); - const auto eta_tilde_mirror = Kokkos::create_mirror(eta_tilde); - Kokkos::deep_copy(eta_tilde_mirror, eta_tilde_host); - Kokkos::deep_copy(eta_tilde, eta_tilde_mirror); - - const auto rho = Kokkos::View("rho"); - const auto rho_data = std::array{55., 56., 57., 58., 59., 60., 61., 62., 63.}; - const auto rho_host = Kokkos::View(rho_data.data()); - const auto rho_mirror = Kokkos::create_mirror(rho); - Kokkos::deep_copy(rho_mirror, rho_host); - Kokkos::deep_copy(rho, rho_mirror); - - const auto eta = Kokkos::View("eta"); - const auto eta_data = std::array{64., 65., 66.}; - const auto eta_host = Kokkos::View(eta_data.data()); - const auto eta_mirror = Kokkos::create_mirror(eta); - Kokkos::deep_copy(eta_mirror, eta_host); - Kokkos::deep_copy(eta, eta_mirror); - - const auto omega_tilde = Kokkos::View("omega_tilde"); - const auto omega_dot_tilde = Kokkos::View("omega_dot_tilde"); - const auto FE = Kokkos::View("FE"); - - Kokkos::parallel_for( - "CalculateExternalForces", 1, - CalculateExternalForces{ - 0, Muu, u_ddot, omega, omega_dot, eta_tilde, omega_tilde, omega_dot_tilde, rho, eta, FE - } - ); - - constexpr auto omega_tilde_exact_data = std::array{0., -42., 41., 42., 0., -40., -41., 40., 0.}; - const auto omega_tilde_exact = - Kokkos::View(omega_tilde_exact_data.data()); - - const auto omega_tilde_mirror = Kokkos::create_mirror(omega_tilde); - Kokkos::deep_copy(omega_tilde_mirror, omega_tilde); - CompareWithExpected(omega_tilde_mirror, omega_tilde_exact); - - constexpr auto omega_dot_tilde_exact_data = - std::array{0., -45., 44., 45., 0., -43., -44., 43., 0.}; - const auto omega_dot_tilde_exact = - Kokkos::View(omega_dot_tilde_exact_data.data()); - - const auto omega_dot_tilde_mirror = Kokkos::create_mirror(omega_dot_tilde); - Kokkos::deep_copy(omega_dot_tilde_mirror, omega_dot_tilde); - CompareWithExpected(omega_dot_tilde_mirror, omega_dot_tilde_exact); - - constexpr auto FE_exact_data = std::array{-2984., 32., 2922., 20624., -2248., 22100.}; - const auto FE_exact = - Kokkos::View(FE_exact_data.data()); - - const auto FE_mirror = Kokkos::create_mirror(FE); - Kokkos::deep_copy(FE_mirror, FE); - CompareWithExpected(FE_mirror, FE_exact); -} - -} // namespace openturbine::tests diff --git a/tests/unit_tests/system/test_integrate_residual_vector.cpp b/tests/unit_tests/system/test_integrate_residual_vector.cpp index 4e8301f5..c2d7b2fd 100644 --- a/tests/unit_tests/system/test_integrate_residual_vector.cpp +++ b/tests/unit_tests/system/test_integrate_residual_vector.cpp @@ -28,6 +28,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fc) { const auto qp_Fc = get_qp_Fc({1., 2., 3., 4., 5., 6.}); const auto qp_Fd = QpVectorView("qp_Fd"); const auto qp_Fi = QpVectorView("qp_Fi"); + const auto qp_Fe = QpVectorView("qp_Fe"); const auto qp_Fg = QpVectorView("qp_Fg"); const auto residual_vector_terms = @@ -37,7 +38,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fc) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms } ); @@ -71,6 +72,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fd) { const auto qp_Fc = QpVectorView("qp_Fc"); const auto qp_Fd = get_qp_Fd({1., 2., 3., 4., 5., 6.}); const auto qp_Fi = QpVectorView("qp_Fi"); + const auto qp_Fe = QpVectorView("qp_Fe"); const auto qp_Fg = QpVectorView("qp_Fg"); const auto residual_vector_terms = @@ -80,7 +82,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fd) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms } ); @@ -114,6 +116,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fi) { const auto qp_Fc = QpVectorView("qp_Fc"); const auto qp_Fd = QpVectorView("qp_Fd"); const auto qp_Fi = get_qp_Fi({1., 2., 3., 4., 5., 6.}); + const auto qp_Fe = QpVectorView("qp_Fe"); const auto qp_Fg = QpVectorView("qp_Fg"); const auto residual_vector_terms = @@ -123,7 +126,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fi) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms } ); @@ -157,6 +160,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fg) { const auto qp_Fc = QpVectorView("qp_Fc"); const auto qp_Fd = QpVectorView("qp_Fd"); const auto qp_Fi = QpVectorView("qp_Fi"); + const auto qp_Fe = QpVectorView("qp_Fe"); const auto qp_Fg = get_qp_Fg({1., 2., 3., 4., 5., 6.}); const auto residual_vector_terms = @@ -166,7 +170,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fg) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms } ); @@ -199,6 +203,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_FX) { const auto qp_Fc = QpVectorView("qp_Fc"); const auto qp_Fd = QpVectorView("qp_Fd"); const auto qp_Fi = QpVectorView("qp_Fi"); + const auto qp_Fe = QpVectorView("qp_Fe"); const auto qp_Fg = QpVectorView("qp_Fg"); const auto residual_vector_terms = @@ -208,7 +213,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_FX) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms } ); @@ -243,6 +248,7 @@ TEST(IntegrateResidualVector, TwoElementsOneNodeOneQP) { const auto qp_Fc_2 = get_qp_Fc({2., 4., 6., 8., 10., 12.}); const auto qp_Fd = QpVectorView("qp_Fd"); const auto qp_Fi = QpVectorView("qp_Fi"); + const auto qp_Fe = QpVectorView("qp_Fe"); const auto qp_Fg = QpVectorView("qp_Fg"); const auto residual_vector_terms = @@ -252,7 +258,7 @@ TEST(IntegrateResidualVector, TwoElementsOneNodeOneQP) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc_1, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms } ); @@ -260,7 +266,7 @@ TEST(IntegrateResidualVector, TwoElementsOneNodeOneQP) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 1U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc_2, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms } ); @@ -295,6 +301,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeTwoQPs) { const auto qp_Fc = get_qp_Fc({1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.}); const auto qp_Fd = QpVectorView("qp_Fd"); const auto qp_Fi = QpVectorView("qp_Fi"); + const auto qp_Fe = QpVectorView("qp_Fe"); const auto qp_Fg = QpVectorView("qp_Fg"); const auto residual_vector_terms = @@ -304,7 +311,7 @@ TEST(IntegrateResidualVector, OneElementOneNodeTwoQPs) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms } ); @@ -338,6 +345,7 @@ TEST(IntegrateResidualVector, OneElementTwoNodesOneQP) { const auto qp_Fc = get_qp_Fc({1., 2., 3., 4., 5., 6.}); const auto qp_Fd = QpVectorView("qp_Fd"); const auto qp_Fi = QpVectorView("qp_Fi"); + const auto qp_Fe = QpVectorView("qp_Fe"); const auto qp_Fg = QpVectorView("qp_Fg"); const auto residual_vector_terms = @@ -347,7 +355,7 @@ TEST(IntegrateResidualVector, OneElementTwoNodesOneQP) { "IntegrateResidualVectorElement", number_of_nodes, IntegrateResidualVectorElement{ 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, - qp_Fd, qp_Fi, qp_Fg, residual_vector_terms + qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms } ); From 1af6c0009f79a04088d6ae2de420441c6678ebff Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 11 Oct 2024 18:15:12 +0000 Subject: [PATCH 86/87] Add unit test for the qp external force feature --- .../system/test_integrate_matrix.hpp | 5 +++ .../system/test_integrate_residual_vector.cpp | 44 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/tests/unit_tests/system/test_integrate_matrix.hpp b/tests/unit_tests/system/test_integrate_matrix.hpp index 98f1a34e..42ca1df9 100644 --- a/tests/unit_tests/system/test_integrate_matrix.hpp +++ b/tests/unit_tests/system/test_integrate_matrix.hpp @@ -147,6 +147,11 @@ auto get_qp_Fi(const std::array& Fi_data) { return get_qp_vector("Fi", Fi_data); } +template +auto get_qp_Fe(const std::array& Fe_data) { + return get_qp_vector("Fe", Fe_data); +} + template auto get_qp_Fg(const std::array& Fg_data) { return get_qp_vector("Fg", Fg_data); diff --git a/tests/unit_tests/system/test_integrate_residual_vector.cpp b/tests/unit_tests/system/test_integrate_residual_vector.cpp index c2d7b2fd..8ba8dbf0 100644 --- a/tests/unit_tests/system/test_integrate_residual_vector.cpp +++ b/tests/unit_tests/system/test_integrate_residual_vector.cpp @@ -141,6 +141,50 @@ TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fi) { CompareWithExpected(residual_vector_terms_mirror, resid_exact); } +TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fe) { + constexpr auto number_of_nodes = size_t{1U}; + constexpr auto number_of_qps = size_t{1U}; + + const auto qp_weights = get_qp_weights({2.}); + const auto qp_jacobian = get_qp_jacobian({3.}); + const auto shape_interp_left = get_shape_interp({4.}); + const auto shape_interp = Kokkos::View("shape_interp"); + Kokkos::deep_copy(shape_interp, shape_interp_left); + const auto shape_deriv_left = get_shape_interp_deriv({0.}); + const auto shape_deriv = Kokkos::View("shape_deriv"); + Kokkos::deep_copy(shape_deriv, shape_deriv_left); + using NodeVectorView = Kokkos::View; + using QpVectorView = Kokkos::View; + + const auto node_FX = NodeVectorView("node_FX"); + const auto qp_Fc = QpVectorView("qp_Fc"); + const auto qp_Fd = QpVectorView("qp_Fd"); + const auto qp_Fi = QpVectorView("qp_Fi"); + const auto qp_Fe = get_qp_Fe({1., 2., 3., 4., 5., 6.}); + const auto qp_Fg = QpVectorView("qp_Fg"); + + const auto residual_vector_terms = + Kokkos::View("residual_vector_terms"); + + Kokkos::parallel_for( + "IntegrateResidualVectorElement", number_of_nodes, + IntegrateResidualVectorElement{ + 0U, number_of_qps, qp_weights, qp_jacobian, shape_interp, shape_deriv, node_FX, qp_Fc, + qp_Fd, qp_Fi, qp_Fe, qp_Fg, residual_vector_terms + } + ); + + constexpr auto resid_exact_data = + std::array{-24., -48., -72., -96., -120., -144.}; + const auto resid_exact = + Kokkos::View(resid_exact_data.data() + ); + + const auto residual_vector_terms_mirror = Kokkos::create_mirror(residual_vector_terms); + Kokkos::deep_copy(residual_vector_terms_mirror, residual_vector_terms); + CompareWithExpected(residual_vector_terms_mirror, resid_exact); +} + TEST(IntegrateResidualVector, OneElementOneNodeOneQP_Fg) { constexpr auto number_of_nodes = size_t{1U}; constexpr auto number_of_qps = size_t{1U}; From e2fabcc825bd0e24dbdac257688eaaed39f2dc0f Mon Sep 17 00:00:00 2001 From: faisal-bhuiyan Date: Fri, 11 Oct 2024 20:14:27 +0000 Subject: [PATCH 87/87] Fix the scratch space size for the added parameter to qp calcs --- src/step/assemble_residual_vector.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/step/assemble_residual_vector.hpp b/src/step/assemble_residual_vector.hpp index bcd5caf0..3a1c6dd7 100644 --- a/src/step/assemble_residual_vector.hpp +++ b/src/step/assemble_residual_vector.hpp @@ -16,9 +16,11 @@ inline void AssembleResidualVector(const Beams& beams) { const auto weight_size = Kokkos::View::shmem_size(beams.max_elem_qps); const auto node_variable_size = Kokkos::View::shmem_size(beams.max_elem_nodes); const auto qp_variable_size = Kokkos::View::shmem_size(beams.max_elem_qps); + // TODO We need to make sure that the scratch space is sufficient for all the views and not use + // magic numbers here range_policy.set_scratch_size( 1, - Kokkos::PerTeam(2 * shape_size + 2 * weight_size + node_variable_size + 4 * qp_variable_size) + Kokkos::PerTeam(2 * shape_size + 2 * weight_size + node_variable_size + 5 * qp_variable_size) ); Kokkos::parallel_for( "IntegrateResidualVector", range_policy,