/home/runner/work/kynema/kynema/kynema/src/utilities/aerodynamics/aerodyn_inflow.hpp Source File

Kynema API: /home/runner/work/kynema/kynema/kynema/src/utilities/aerodynamics/aerodyn_inflow.hpp Source File
Kynema API
A flexible multibody structural dynamics code for wind turbines
Loading...
Searching...
No Matches
aerodyn_inflow.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <cmath>
5#include <numbers>
6#include <numeric>
7#include <span>
8#include <stdexcept>
9#include <string>
10#include <vector>
11
14
15namespace kynema::util {
16
53 enum class ErrorLevel : uint8_t {
54 kNone = 0,
55 kInfo = 1,
56 kWarning = 2,
57 kSevereError = 3,
58 kFatalError = 4
59 };
60
61 static constexpr size_t kErrorMessagesLength{1025U}; //< Max error message length
63 static_cast<int>(ErrorLevel::kFatalError) //< Error level at which to abort
64 };
65 int error_status{0}; //< Error status
66 std::array<char, kErrorMessagesLength> error_message{}; //< Error message buffer
67
69 void CheckError() const {
71 throw std::runtime_error(error_message.data());
72 }
73 }
74};
75
78 float density{1.225F}; //< Air density (kg/m^3)
79 float kinematic_viscosity{1.464E-5F}; //< Kinematic viscosity (m^2/s)
80 float sound_speed{335.F}; //< Speed of sound in the working fluid (m/s)
81 float vapor_pressure{1700.F}; //< Vapor pressure of the working fluid (Pa)
82};
83
86 float gravity{9.81F}; //< Gravitational acceleration (m/s^2)
87 float atm_pressure{103500.F}; //< Atmospheric pressure (Pa)
88 float water_depth{30.F}; //< Water depth (m)
89 float msl_offset{0.F}; //< Mean sea level to still water level offset (m)
90};
91
107 std::array<double, 7> root_initial_position{
108 }; //< Initial root position of the blade (1 per blade)
109 std::vector<std::array<double, 7>>
110 node_initial_positions; //< Initial node positions of the blade
111
112 BladeInitialState(std::span<double, 7> root, std::span<const std::array<double, 7>> nodes) {
113 std::ranges::copy(root, std::begin(root_initial_position));
114 node_initial_positions.assign(std::begin(nodes), std::end(nodes));
115 }
116 };
117
118 bool is_horizontal_axis{true}; //< Is a horizontal axis turbine?
119 std::array<float, 3> reference_position{0.F, 0.F, 0.F}; //< Reference position of the turbine
120 std::array<double, 7> hub_initial_position{}; //< Initial hub position
121 std::array<double, 7> nacelle_initial_position{}; //< Initial nacelle position
122 std::vector<BladeInitialState>
123 blade_initial_states; //< Initial root and node positions of blades (size = n_blades)
124
126 bool is_hawt, std::span<const float, 3> ref_pos, std::span<const double, 7> hub_pos,
127 std::span<const double, 7> nacelle_pos, std::span<const BladeInitialState> blade_states
128 )
129 : is_horizontal_axis(is_hawt) {
130 std::ranges::copy(ref_pos, std::begin(reference_position));
131 std::ranges::copy(hub_pos, std::begin(hub_initial_position));
132 std::ranges::copy(nacelle_pos, std::begin(nacelle_initial_position));
133 blade_initial_states.assign(std::begin(blade_states), std::end(blade_states));
134 // Make sure the initial states are valid
135 Validate();
136 }
137
138 void Validate() const {
139 // Check if there are any blades defined
140 if (blade_initial_states.empty()) {
141 throw std::runtime_error("No blades defined. At least one blade is required.");
142 }
143
144 // Check if there are any nodes defined for each blade
145 for (const auto& blade : blade_initial_states) {
146 if (blade.node_initial_positions.empty()) {
147 throw std::runtime_error(
148 "No nodes defined for a blade. At least one node is required."
149 );
150 }
151 }
152 }
153
155 [[nodiscard]] size_t NumberOfBlades() const { return blade_initial_states.size(); }
156};
157
166 std::span<const double, 7> data, std::span<float, 3> position,
167 std::array<std::array<double, 3>, 3>& orientation
168) {
169 // Set position (first 3 elements)
170 for (auto i : std::views::iota(0U, 3U)) {
171 position[i] = static_cast<float>(data[i]);
172 }
173
174 // Set orientation (converts last 4 elements i.e. quaternion -> 3x3 rotation matrix)
175 orientation = math::QuaternionToRotationMatrix({data[3], data[4], data[5], data[6]});
176}
177
186struct MeshData {
187 int32_t n_points; //< Number of mesh points (nodes)
188 std::vector<std::array<float, 3>> position; //< N x 3 array [x, y, z]
189 std::vector<std::array<std::array<double, 3>, 3>>
190 orientation; //< N x 9 array [r11, r12, ..., r33]
191 std::vector<std::array<float, 6>> velocity; //< N x 6 array [u, v, w, p, q, r]
192 std::vector<std::array<float, 6>>
193 acceleration; //< N x 6 array [u_dot, v_dot, w_dot, p_dot, q_dot, r_dot]
194 std::vector<std::array<float, 6>> load; //< N x 6 array [Fx, Fy, Fz, Mx, My, Mz]
195
197 MeshData(size_t n_nodes)
198 : n_points(static_cast<int32_t>(n_nodes)),
199 position(n_nodes, std::array{0.F, 0.F, 0.F}),
201 n_nodes,
202 std::array{std::array{0., 0., 0.}, std::array{0., 0., 0.}, std::array{0., 0., 0.}}
203 ),
204 velocity(n_nodes, std::array{0.F, 0.F, 0.F, 0.F, 0.F, 0.F}),
205 acceleration(n_nodes, std::array{0.F, 0.F, 0.F, 0.F, 0.F, 0.F}),
206 load(n_nodes, std::array{0.F, 0.F, 0.F, 0.F, 0.F, 0.F}) {}
207
210 size_t n_mesh_pts, std::span<const std::array<double, 7>> pos,
211 std::span<const std::array<float, 6>> vel, std::span<const std::array<float, 6>> acc,
212 std::span<const std::array<float, 6>> ld
213 )
214 : n_points(static_cast<int32_t>(n_mesh_pts)),
215 position(n_mesh_pts, std::array{0.F, 0.F, 0.F}),
217 n_mesh_pts,
218 std::array{std::array{0., 0., 0.}, std::array{0., 0., 0.}, std::array{0., 0., 0.}}
219 ) {
220 velocity.assign(std::begin(vel), std::end(vel));
221 acceleration.assign(std::begin(acc), std::end(acc));
222 load.assign(std::begin(ld), std::end(ld));
223 // Set mesh position and orientation from 7x1 array [x, y, z, qw, qx, qy, qz]
224 for (auto i : std::views::iota(0U, NumberOfMeshPoints())) {
226 }
227
228 // Make sure the mesh data is valid
229 Validate();
230 }
231
232 void Validate() const {
233 // Check we have at least one node
234 if (n_points <= 0) {
235 throw std::invalid_argument("Number of mesh points must be at least 1");
236 }
237
238 // Check all vectors are the same size as the number of mesh points
239 auto check_vector_size = [](const auto& vec, size_t exp_size, const std::string& vec_name) {
240 if (vec.size() != exp_size) {
241 throw std::invalid_argument(vec_name + " vector size does not match n_mesh_points");
242 }
243 };
244
245 const auto expected_size = NumberOfMeshPoints();
246 check_vector_size(position, expected_size, "Position");
247 check_vector_size(orientation, expected_size, "Orientation");
248 check_vector_size(velocity, expected_size, "Velocity");
249 check_vector_size(acceleration, expected_size, "Acceleration");
250 check_vector_size(load, expected_size, "Loads");
251 }
252
254 [[nodiscard]] size_t NumberOfMeshPoints() const { return static_cast<size_t>(n_points); }
255
256 // Updates the values at the given point number
258 size_t point_number, std::span<const double, 7> pos, std::span<const double, 6> vel,
259 std::span<const double, 6> acc
260 ) {
261 if (point_number >= static_cast<size_t>(n_points)) {
262 throw std::out_of_range("point number out of range.");
263 }
265 pos, this->position[point_number], this->orientation[point_number]
266 );
267 std::ranges::copy(vel, std::begin(velocity[point_number]));
268 std::ranges::copy(acc, std::begin(acceleration[point_number]));
269 }
270};
271
280 int32_t n_blades; //< Number of blades
281 MeshData hub; //< Hub data (1 point)
282 MeshData nacelle; //< Nacelle data (1 point)
283 MeshData blade_roots; //< Blade roots data (n_blades points)
284 MeshData blade_nodes; //< Blade nodes data (sum of nodes per blade)
285 std::array<float, 3> hh_vel; // Hub height wind velocity
286
295
304 std::vector<std::vector<size_t>> node_indices_by_blade;
305
315 : n_blades(static_cast<int32_t>(tc.NumberOfBlades())),
316 hub(1),
317 nacelle(1),
319 blade_nodes(CalculateTotalBladeNodes(tc)),
320 hh_vel({0.F, 0.F, 0.F}),
321 node_indices_by_blade(tc.NumberOfBlades()) {
322 // Initialize hub and nacelle data
323 InitializeHubAndNacelle(tc);
324
325 // Initialize blade data
326 InitializeBlades(tc);
327
328 // Make sure the turbine data is valid
329 Validate();
330 }
331
332 void Validate() const {
333 // Check if the number of blades is valid
334 if (n_blades < 1) {
335 throw std::runtime_error("Invalid number of blades. Must be at least 1.");
336 }
337
338 // Validate hub and nacelle data - should have exactly one mesh point each
339 if (hub.NumberOfMeshPoints() != 1 || nacelle.NumberOfMeshPoints() != 1) {
340 throw std::runtime_error("Hub and nacelle should have exactly one mesh point each.");
341 }
342
343 // Check if the number of blade roots matches the number of blades
345 throw std::runtime_error("Number of blade roots does not match number of blades.");
346 }
347
348 // Check if the blade nodes to blade number mapping is valid - should be the same size
350 throw std::runtime_error("Blade node to blade number mapping size mismatch.");
351 }
352
353 // Check if the node indices by blade are valid - should be same size as number of blades
354 if (node_indices_by_blade.size() != NumberOfBlades()) {
355 throw std::runtime_error("Node indices by blade size mismatch.");
356 }
357
358 // Check if the total number of blade nodes is valid - should be the same as the number of
359 // aggregrated blade nodes
360 size_t total_nodes = 0;
361 for (const auto& bl : node_indices_by_blade) {
362 total_nodes += bl.size();
363 }
364 if (total_nodes != blade_nodes.NumberOfMeshPoints()) {
365 throw std::runtime_error("Total number of blade nodes mismatch.");
366 }
367 }
368
370 [[nodiscard]] size_t NumberOfBlades() const { return static_cast<size_t>(n_blades); }
371
387 size_t blade_number, size_t node_number, const std::array<double, 7>& position,
388 const std::array<double, 6>& velocity, const std::array<double, 6>& acceleration
389 ) {
390 // Check if the blade and node numbers are within the valid range
391 if (blade_number >= NumberOfBlades() ||
392 node_number >= node_indices_by_blade[blade_number].size()) {
393 throw std::out_of_range("Blade or node number out of range.");
394 }
395
396 // Rotation to convert blade orientation
397 const auto r_x2z = math::RotationVectorToQuaternion({0., std::numbers::pi / 2., 0.});
398
399 // Original orientation
400 const std::array<double, 4> r{position[3], position[4], position[5], position[6]};
401
402 // Converted orientation
403 const auto r_adi = math::QuaternionCompose(r, r_x2z);
404
405 // Position with new orientation
406 const std::array<double, 7> position_new{position[0], position[1], position[2], r_adi[0],
407 r_adi[1], r_adi[2], r_adi[3]};
408
409 const size_t node_index = node_indices_by_blade[blade_number][node_number];
410 blade_nodes.SetValues(node_index, position_new, velocity, acceleration);
411 }
412
426 size_t blade_number, const std::array<double, 7>& position,
427 const std::array<double, 6>& velocity, const std::array<double, 6>& acceleration
428 ) {
429 if (blade_number >= static_cast<size_t>(n_blades)) {
430 throw std::out_of_range("Blade number out of range.");
431 }
432
433 // Rotation to convert blade orientation
434 const auto r_x2z = math::RotationVectorToQuaternion({0., std::numbers::pi / 2., 0.});
435
436 // Original orientation
437 const auto r = std::array{position[3], position[4], position[5], position[6]};
438
439 // Converted orientation
440 const auto r_adi = math::QuaternionCompose(r, r_x2z);
441
442 // Position with new orientation
443 const auto position_new = std::array{position[0], position[1], position[2], r_adi[0],
444 r_adi[1], r_adi[2], r_adi[3]};
445
446 blade_roots.SetValues(blade_number, position_new, velocity, acceleration);
447 }
448
460 const std::array<double, 7>& position, const std::array<double, 6>& velocity,
461 const std::array<double, 6>& acceleration
462 ) {
463 this->hub.SetValues(0, position, velocity, acceleration);
464 }
465
477 const std::array<double, 7>& position, const std::array<double, 6>& velocity,
478 const std::array<double, 6>& acceleration
479 ) {
480 this->nacelle.SetValues(0, position, velocity, acceleration);
481 }
482
492 [[nodiscard]] std::array<double, 6> GetBladeNodeLoad(size_t blade_number, size_t node_number)
493 const {
494 if (blade_number >= static_cast<size_t>(n_blades) ||
495 node_number >= node_indices_by_blade[blade_number].size()) {
496 throw std::out_of_range("Blade or node number out of range.");
497 }
498
499 // Get the loads for this blade and node
500 const size_t node_index = node_indices_by_blade[blade_number][node_number];
501 const auto loads = blade_nodes.load[node_index];
502
503 return std::array<double, 6>{
504 static_cast<double>(loads[0]), static_cast<double>(loads[1]),
505 static_cast<double>(loads[2]), static_cast<double>(loads[3]),
506 static_cast<double>(loads[4]), static_cast<double>(loads[5]),
507 };
508 }
509
510private:
512 static size_t CalculateTotalBladeNodes(const TurbineConfig& tc) {
513 return std::accumulate(
514 tc.blade_initial_states.begin(), tc.blade_initial_states.end(), size_t{0},
515 [](size_t sum, const auto& blade) {
516 return sum + blade.node_initial_positions.size();
517 }
518 );
519 }
520
522 void InitializeHubAndNacelle(const TurbineConfig& tc) {
523 SetPositionAndOrientation(tc.hub_initial_position, hub.position[0], hub.orientation[0]);
525 tc.nacelle_initial_position, nacelle.position[0], nacelle.orientation[0]
526 );
527 }
528
530 void InitializeBlades(const TurbineConfig& tc) {
531 size_t i_blade{0};
532 size_t i_node_total{0};
533
534 // Initialize blade roots and nodes
535 for (const auto& blade : tc.blade_initial_states) {
536 // Initialize blade root
537 this->SetBladeRootMotion(
538 i_blade, blade.root_initial_position, std::array<double, 6>{0., 0., 0., 0., 0., 0.},
539 std::array<double, 6>{0., 0., 0., 0., 0., 0.}
540 );
541
542 // Initialize blade nodes
543 size_t i_node{0};
544 for (const auto& position : blade.node_initial_positions) {
545 node_indices_by_blade[i_blade].emplace_back(i_node_total);
546 blade_nodes_to_blade_num_mapping.emplace_back(static_cast<int32_t>(i_blade + 1));
547
548 this->SetBladeNodeMotion(
549 i_blade, i_node, position,
550 std::array<double, 6>{0., 0., 0., 0., 0., 0.}, // velocity
551 std::array<double, 6>{0., 0., 0., 0., 0., 0.} // acceleration
552 );
553 ++i_node;
554 ++i_node_total;
555 }
556 ++i_blade;
557 }
558 }
559};
560
569 enum class DebugLevel : std::uint8_t {
570 kNone = 0, //< No debug output
571 kSummary = 1, //< Some summary info
572 kDetailed = 2, //< Above + all position/orientation info
573 kInputFiles = 3, //< Above + input files (if directly passed)
574 kAll = 4, //< Above + meshes
575 };
576
578 enum class OutputFormat : std::uint8_t {
579 kNone = 0, //< Disable write output
580 kText = 1, //< Write text file
581 kBinary = 2, //< Write binary file
582 kText_Binary = 3, //< Write text file and binary file
583 };
584
585 static constexpr size_t kDefaultStringLength{1025}; //< Max length for output filenames
586
587 // Input file handling
588 std::string aerodyn_input; //< Path to AeroDyn input file
589 std::string inflowwind_input; //< Path to InflowWind input file
590
591 // Interpolation order (must be either 1: linear, or 2: quadratic)
592 int interpolation_order{1}; //< Interpolation order - linear by default
593
594 // Initial time related variables
595 double time_step{0.1}; //< Simulation timestep (s)
596 double max_time{600.}; //< Maximum simulation time (s)
597 double total_elapsed_time{0.}; //< Total elapsed time (s)
598 int n_time_steps{0}; //< Number of time steps
599
600 // Flags
601 bool transpose_DCM{true}; //< Flag to transpose the direction cosine matrix
602 bool point_load_output{true}; //< Flag to output point loads
604
605 // Outputs
606 OutputFormat output_format{OutputFormat::kNone}; //< File format for writing outputs
607 std::string output_root_name{"ADI_out"}; //< Root name for output files
608 double output_time_step{0.1}; //< Time step for outputs to file
609 size_t n_channels{0}; //< Number of channels returned
610};
611
620 enum class WriteType : std::uint8_t {
621 kNone = 0, //< Disable VTK output
622 kInit = 1, //< Data at initialization
623 kAnimation = 2, //< Animation
624 };
625
627 enum class OutputType : std::uint8_t {
628 kSurface = 1, //< Surfaces
629 kLine = 2, //< Lines
630 kSurface_Line = 3, //< Both surfaces and lines
631 };
632
633 std::string output_dir_name{"ADI_vtk"}; //< VTK output directory name
634 WriteType write_vtk{WriteType::kNone}; //< Flag to write VTK output
635 OutputType vtk_type{OutputType::kLine}; //< Type of VTK output
636 double vtk_dt{0.}; //< VTK output time step
637 std::array<float, 6> vtk_nacelle_dimensions{//< Nacelle dimensions for VTK rendering
638 -2.5F, -2.5F, 0.F, 10.F, 5.F, 5.F
639 };
640 float vtk_hub_radius{1.5F}; //< Hub radius for VTK rendering
641};
642
663public:
675 const std::string& shared_lib_path = "aerodyn_inflow_c_binding.dll",
677 EnvironmentalConditions ec = EnvironmentalConditions{},
678 SimulationControls sc = SimulationControls{}, VTKSettings vtk = VTKSettings{}
679 )
680 : lib_{shared_lib_path, util::dylib::no_filename_decorations},
681 error_handling_{eh},
682 air_{fp},
683 env_conditions_{ec},
684 sim_controls_{std::move(sc)},
685 vtk_settings_{std::move(vtk)} {}
686
688 [[nodiscard]] const ErrorHandling& GetErrorHandling() const { return error_handling_; }
690 return env_conditions_;
691 }
692 [[nodiscard]] const FluidProperties& GetFluidProperties() const { return air_; }
693 [[nodiscard]] const SimulationControls& GetSimulationControls() const { return sim_controls_; }
694 [[nodiscard]] const VTKSettings& GetVTKSettings() const { return vtk_settings_; }
695
707 void Initialize(std::span<const TurbineConfig> turbine_configs) {
708 PreInitialize(turbine_configs.size());
709 SetupRotors(turbine_configs);
711 is_initialized_ = true;
712 }
713
719 void PreInitialize(size_t n_turbines) {
720 auto ADI_C_PreInit =
721 lib_.get_function<void(int*, int*, int*, int*, int*, char*)>("ADI_C_PreInit");
722
723 // Convert bool and other types to int32_t for Fortran compatibility
724 auto debug_level_int = static_cast<int32_t>(sim_controls_.debug_level);
725 auto transpose_dcm_int = sim_controls_.transpose_DCM ? 1 : 0;
726 auto n_turbines_int = static_cast<int32_t>(n_turbines);
727 auto point_load_output_int = sim_controls_.point_load_output ? 1 : 0;
728
729 ADI_C_PreInit(
730 &n_turbines_int, // input: Number of turbines
731 &transpose_dcm_int, // input: Transpose DCM?
732 &point_load_output_int, // input: output point loads?
733 &debug_level_int, // input: Debug level
734 &error_handling_.error_status, // output: Error status
735 error_handling_.error_message.data() // output: Error message
736 );
737
738 error_handling_.CheckError();
739 }
740
746 void SetupRotors(std::span<const TurbineConfig> turbine_configs) {
747 auto ADI_C_SetupRotor = lib_.get_function<
748 void(int*, int*, const float*, float*, double*, float*, double*, int*, float*, double*, int*, float*, double*, int*, int*, char*)>(
749 "ADI_C_SetupRotor"
750 );
751
752 // Loop through turbine configurations
753 int32_t turbine_number{0};
754 for (const auto& tc : turbine_configs) {
755 // Turbine number is 1 indexed i.e. 1, 2, 3, ...
756 ++turbine_number;
757
758 // Convert bool -> int32_t to pass to the Fortran routine
759 int32_t is_horizontal_axis_int = tc.is_horizontal_axis ? 1 : 0;
760
761 // Validate the turbine config
762 tc.Validate();
763
764 // Create new turbine data to store the updates
765 // Note: TurbineData and MeshData are validated during construction, so no need to
766 // perform additional checks here
767 turbines.emplace_back(tc);
768 auto& td = turbines.back();
769
770 // Call setup rotor for each turbine
771 ADI_C_SetupRotor(
772 &turbine_number, // input: current turbine number
773 &is_horizontal_axis_int, // input: 1: HAWT, 0: VAWT or cross-flow
774 tc.reference_position.data(), // input: turbine reference position
775 td.hub.position[0].data(), // input: initial hub position
776 td.hub.orientation[0][0].data(), // input: initial hub orientation
777 td.nacelle.position[0].data(), // input: initial nacelle position
778 td.nacelle.orientation[0][0].data(), // input: initial nacelle orientation
779 &td.n_blades, // input: number of blades
780 td.blade_roots.position[0].data(), // input: initial blade root positions
781 td.blade_roots.orientation[0].data()->data(
782 ), // input: initial blade root orientation
783 &td.blade_nodes.n_points, // input: number of mesh points
784 td.blade_nodes.position[0].data(), // input: initial node positions
785 td.blade_nodes.orientation[0][0].data(), // input: initial node orientation
786 td.blade_nodes_to_blade_num_mapping.data(
787 ), // input: blade node to blade number mapping
788 &error_handling_.error_status, // output: Error status
789 error_handling_.error_message.data() // output: Error message buffer
790 );
791
792 error_handling_.CheckError();
793 }
794 }
795
800 auto ADI_C_Init = lib_.get_function<
801 void(int*, char**, int*, int*, char**, int*, const char*, const char*, float*, float*, float*, float*, float*, float*, float*, float*, int*, double*, double*, int*, int*, int*, double*, float*, float*, int*, double*, int*, char*, char*, int*, char*)>(
802 "ADI_C_Init"
803 );
804
805 // Convert bool -> int32_t to pass to the Fortran routine
806 int32_t is_aerodyn_input_passed_as_string = 0; // AeroDyn input is path to file
807 int32_t is_inflowwind_input_passed_as_string = 0; // InflowWind input is path to file
808 int32_t store_HH_wind_speed_int = 1;
809
810 // Primary input file will be passed as path to the file
811 char* aerodyn_input_pointer{sim_controls_.aerodyn_input.data()};
812 auto aerodyn_input_length = static_cast<int32_t>(sim_controls_.aerodyn_input.size());
813
814 char* inflowwind_input_pointer{sim_controls_.inflowwind_input.data()};
815 auto inflowwind_input_length = static_cast<int32_t>(sim_controls_.inflowwind_input.size());
816
817 // Channel name and channel unit string arrays
818 std::string channel_names_c(20 * 8000 + 1, ' '); //< Output channel names
819 std::string channel_units_c(20 * 8000 + 1, ' '); //< Output channel units
820
821 // Number of output channels (set by ADI_C_Init)
822 int32_t n_channels{0};
823
824 // Write output format as integer for Fortran routine
825 auto output_format = static_cast<int32_t>(sim_controls_.output_format);
826
827 // VTK write type and output type as integers
828 auto write_vtk = static_cast<int32_t>(vtk_settings_.write_vtk);
829 auto vtk_type = static_cast<int32_t>(vtk_settings_.vtk_type);
830
831 ADI_C_Init(
832 &is_aerodyn_input_passed_as_string, // input: AD input is passed
833 &aerodyn_input_pointer, // input: AD input file as string
834 &aerodyn_input_length, // input: AD input file string length
835 &is_inflowwind_input_passed_as_string, // input: IfW input is passed
836 &inflowwind_input_pointer, // input: IfW input file as string
837 &inflowwind_input_length, // input: IfW input file string length
838 sim_controls_.output_root_name.c_str(), // input: rootname for ADI file writing
839 vtk_settings_.output_dir_name.c_str(), // input: VTK output directory name
840 &env_conditions_.gravity, // input: gravity
841 &air_.density, // input: air density
842 &air_.kinematic_viscosity, // input: kinematic viscosity
843 &air_.sound_speed, // input: speed of sound
844 &env_conditions_.atm_pressure, // input: atmospheric pressure
845 &air_.vapor_pressure, // input: vapor pressure
846 &env_conditions_.water_depth, // input: water depth
847 &env_conditions_.msl_offset, // input: MSL to SWL offset
848 &sim_controls_.interpolation_order, // input: interpolation order
849 &sim_controls_.time_step, // input: time step
850 &sim_controls_.max_time, // input: maximum simulation time
851 &store_HH_wind_speed_int, // input: store HH wind speed
852 &write_vtk, // input: write VTK output
853 &vtk_type, // input: VTK output type
854 &vtk_settings_.vtk_dt, // input: VTK output time step
855 vtk_settings_.vtk_nacelle_dimensions.data(), // input: VTK nacelle dimensions
856 &vtk_settings_.vtk_hub_radius, // input: VTK hub radius
857 &output_format, // input: output format
858 &sim_controls_.output_time_step, // input: output time step
859 &n_channels, // output: number of channels
860 channel_names_c.data(), // output: output channel names
861 channel_units_c.data(), // output: output channel units
862 &error_handling_.error_status, // output: error status
863 error_handling_.error_message.data() // output: error message buffer
864 );
865
866 // Allocate vector of output channel values
867 sim_controls_.n_channels = static_cast<size_t>(n_channels);
868 channel_values = std::vector<float>(sim_controls_.n_channels, 0.F);
869 channel_names.resize(sim_controls_.n_channels);
870 channel_units.resize(sim_controls_.n_channels);
871 std::string tmp;
872 for (auto i : std::views::iota(0U, sim_controls_.n_channels)) {
873 tmp = channel_names_c.substr(20UL * i, 20);
874 tmp.erase(tmp.find_last_not_of(' ') + 1);
875 channel_names[i] = tmp;
876 tmp = channel_units_c.substr(20UL * i, 20);
877 tmp.erase(tmp.find_last_not_of(' ') + 1);
878 channel_units[i] = tmp;
879 }
880
881 error_handling_.CheckError();
882 is_initialized_ = true;
883 }
884
892 auto
893 ADI_C_SetRotorMotion =
894 lib_
896 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*)>(
897 "ADI_C_SetRotorMotion"
898 );
899
900 // Loop through turbines and set the rotor motion
901 int32_t turbine_number{0};
902 for (const auto& td : turbines) {
903 // Turbine number is 1 indexed i.e. 1, 2, 3, ...
904 ++turbine_number;
905 ADI_C_SetRotorMotion(
906 &turbine_number, // input: current turbine number
907 td.hub.position[0].data(), // input: hub positions
908 td.hub.orientation[0][0].data(), // input: hub orientations
909 td.hub.velocity[0].data(), // input: hub velocities
910 td.hub.acceleration[0].data(), // input: hub accelerations
911 td.nacelle.position[0].data(), // input: nacelle positions
912 td.nacelle.orientation[0][0].data(), // input: nacelle orientations
913 td.nacelle.velocity[0].data(), // input: nacelle velocities
914 td.nacelle.acceleration[0].data(), // input: nacelle accelerations
915 td.blade_roots.position[0].data(), // input: root positions
916 td.blade_roots.orientation[0][0].data(), // input: root orientations
917 td.blade_roots.velocity[0].data(), // input: root velocities
918 td.blade_roots.acceleration[0].data(), // input: root accelerations
919 &td.blade_nodes.n_points, // input: number of mesh points
920 td.blade_nodes.position[0].data(), // input: mesh positions
921 td.blade_nodes.orientation[0][0].data(), // input: mesh orientations
922 td.blade_nodes.velocity[0].data(), // input: mesh velocities
923 td.blade_nodes.acceleration[0].data(), // input: mesh accelerations
924 &error_handling_.error_status, // output: error status
925 error_handling_.error_message.data() // output: error message buffer
926 );
927
928 error_handling_.CheckError();
929 }
930 }
931
940 void CalculateOutput(double time) {
941 auto ADI_C_CalcOutput =
942 lib_.get_function<void(double*, float*, int*, char*)>("ADI_C_CalcOutput");
943
944 ADI_C_CalcOutput(
945 &time, // input: time at which to calculate output forces
946 channel_values.data(), // output: output channel values
947 &error_handling_.error_status, // output: error status
948 error_handling_.error_message.data() // output: error message buffer
949 );
950
951 error_handling_.CheckError();
952
953 auto ADI_C_GetRotorLoads =
954 lib_.get_function<void(int*, int*, float*, float*, int*, char*)>("ADI_C_GetRotorLoads");
955
956 // Loop through turbines and get the rotor loads
957 int32_t turbine_number{0};
958 for (auto& td : turbines) {
959 // Turbine number is 1 indexed i.e. 1, 2, 3, ...
960 ++turbine_number;
961
962 ADI_C_GetRotorLoads(
963 &turbine_number, // input: current turbine number
964 &td.blade_nodes.n_points, // input: number of mesh points
965 td.blade_nodes.load[0].data(), // output: mesh force/moment array
966 td.hh_vel.data(), // output: hub height wind velocity array
967 &error_handling_.error_status, // output: error status
968 error_handling_.error_message.data() // output: error message buffer
969 );
970
971 error_handling_.CheckError();
972 }
973 }
974
984 void UpdateStates(double current_time, double next_time) {
985 auto ADI_C_UpdateStates =
986 lib_.get_function<void(double*, double*, int*, char*)>("ADI_C_UpdateStates");
987
988 ADI_C_UpdateStates(
989 &current_time, // input: current time
990 &next_time, // input: next time step
991 &error_handling_.error_status, // output: error status
992 error_handling_.error_message.data() // output: error message buffer
993 );
994
995 error_handling_.CheckError();
996 }
997
1004 void Finalize() {
1005 auto ADI_C_End = lib_.get_function<void(int*, char*)>("ADI_C_End");
1006
1007 ADI_C_End(
1008 &error_handling_.error_status, // output: error status
1009 error_handling_.error_message.data() // output: error message buffer
1010 );
1011
1012 error_handling_.CheckError();
1013 is_initialized_ = false;
1014 }
1015
1016 std::vector<TurbineData> turbines; //< Turbine data (1 per turbine)
1017 std::vector<std::string> channel_names; //< Output channel names
1018 std::vector<std::string> channel_units; //< Output channel units
1019 std::vector<float> channel_values; //< Output channel values
1020
1021private:
1022 bool is_initialized_{false}; //< Flag to check if the library is initialized
1023 util::dylib lib_; //< Dynamic library object for AeroDyn Inflow
1024 ErrorHandling error_handling_; //< Error handling settings
1025 FluidProperties air_; //< Properties of the working fluid (air)
1026 EnvironmentalConditions env_conditions_; //< Environmental conditions
1027 SimulationControls sim_controls_; //< Simulation control settings
1028 VTKSettings vtk_settings_; //< VTK output settings
1029};
1030
1031} // namespace kynema::util
Wrapper class for the AeroDynInflow (ADI) shared library.
Definition aerodyn_inflow.hpp:662
void Finalize()
Ends the simulation and frees memory.
Definition aerodyn_inflow.hpp:1004
const VTKSettings & GetVTKSettings() const
Definition aerodyn_inflow.hpp:694
void CalculateOutput(double time)
Calculates the output channels at a given time.
Definition aerodyn_inflow.hpp:940
std::vector< std::string > channel_names
Definition aerodyn_inflow.hpp:1017
void Initialize(std::span< const TurbineConfig > turbine_configs)
Initialize the AeroDyn Inflow library.
Definition aerodyn_inflow.hpp:707
void FinalizeInitialization()
Finalizes the initialization process.
Definition aerodyn_inflow.hpp:799
void SetupRotors(std::span< const TurbineConfig > turbine_configs)
Sets up rotor configurations for all turbines.
Definition aerodyn_inflow.hpp:746
const FluidProperties & GetFluidProperties() const
Definition aerodyn_inflow.hpp:692
void SetRotorMotion()
Set up the rotor motion for the current simulation step.
Definition aerodyn_inflow.hpp:891
AeroDynInflowLibrary(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{})
Construct a new AeroDynInflowLibrary object.
Definition aerodyn_inflow.hpp:674
const SimulationControls & GetSimulationControls() const
Definition aerodyn_inflow.hpp:693
std::vector< std::string > channel_units
Definition aerodyn_inflow.hpp:1018
std::vector< float > channel_values
Definition aerodyn_inflow.hpp:1019
const ErrorHandling & GetErrorHandling() const
Getter methods for the private member variables.
Definition aerodyn_inflow.hpp:688
const EnvironmentalConditions & GetEnvironmentalConditions() const
Definition aerodyn_inflow.hpp:689
void UpdateStates(double current_time, double next_time)
Updates the states of the AeroDyn Inflow library.
Definition aerodyn_inflow.hpp:984
std::vector< TurbineData > turbines
Definition aerodyn_inflow.hpp:1016
void PreInitialize(size_t n_turbines)
Performs pre-initialization setup.
Definition aerodyn_inflow.hpp:719
Definition dylib.hpp:62
T * get_function(const char *symbol_name) const
Definition dylib.hpp:246
C++ cross-platform wrapper around dynamic loading of shared libraries.
KOKKOS_INLINE_FUNCTION void QuaternionCompose(const Quaternion1 &q1, const Quaternion2 &q2, QuaternionN &qn)
Composes (i.e. multiplies) two quaternions and stores the result in a third quaternion.
Definition quaternion_operations.hpp:204
KOKKOS_INLINE_FUNCTION void RotationVectorToQuaternion(const Vector &phi, const Quaternion &quaternion)
Returns a 4-D quaternion from provided 3-D rotation vector, i.e. the exponential map.
Definition quaternion_operations.hpp:231
KOKKOS_INLINE_FUNCTION void QuaternionToRotationMatrix(const Quaternion &q, const RotationMatrix &R)
Converts a 4x1 quaternion to a 3x3 rotation matrix and returns the result.
Definition quaternion_operations.hpp:20
Definition aerodyn_inflow.hpp:15
void SetPositionAndOrientation(std::span< const double, 7 > data, std::span< float, 3 > position, std::array< std::array< double, 3 >, 3 > &orientation)
Converts a 7-element array of position and quaternion to separate position and orientation arrays.
Definition aerodyn_inflow.hpp:165
Struct to hold the environmental conditions.
Definition aerodyn_inflow.hpp:85
float water_depth
Definition aerodyn_inflow.hpp:88
float msl_offset
Definition aerodyn_inflow.hpp:89
float gravity
Definition aerodyn_inflow.hpp:86
float atm_pressure
Definition aerodyn_inflow.hpp:87
Struct for error handling settings.
Definition aerodyn_inflow.hpp:51
void CheckError() const
Checks for errors and throws an exception if found.
Definition aerodyn_inflow.hpp:69
std::array< char, kErrorMessagesLength > error_message
Definition aerodyn_inflow.hpp:66
int error_status
Definition aerodyn_inflow.hpp:65
ErrorLevel
Error levels used in InflowWind.
Definition aerodyn_inflow.hpp:53
static constexpr size_t kErrorMessagesLength
Definition aerodyn_inflow.hpp:61
int abort_error_level
Definition aerodyn_inflow.hpp:62
Struct to hold the properties of the working fluid (air)
Definition aerodyn_inflow.hpp:77
float density
Definition aerodyn_inflow.hpp:78
float kinematic_viscosity
Definition aerodyn_inflow.hpp:79
float vapor_pressure
Definition aerodyn_inflow.hpp:81
float sound_speed
Definition aerodyn_inflow.hpp:80
Struct to hold the motion + loads data of any structural mesh component in AeroDyn/InflowWind compati...
Definition aerodyn_inflow.hpp:186
int32_t n_points
Definition aerodyn_inflow.hpp:187
MeshData(size_t n_nodes)
Constructor to initialize all mesh data to zero based on provided number of nodes.
Definition aerodyn_inflow.hpp:197
std::vector< std::array< float, 6 > > velocity
Definition aerodyn_inflow.hpp:191
std::vector< std::array< std::array< double, 3 >, 3 > > orientation
Definition aerodyn_inflow.hpp:190
size_t NumberOfMeshPoints() const
Returns the number of mesh points as size_t.
Definition aerodyn_inflow.hpp:254
MeshData(size_t n_mesh_pts, std::span< const std::array< double, 7 > > pos, std::span< const std::array< float, 6 > > vel, std::span< const std::array< float, 6 > > acc, std::span< const std::array< float, 6 > > ld)
Constructor to initialize all mesh data based on provided inputs.
Definition aerodyn_inflow.hpp:209
std::vector< std::array< float, 6 > > load
Definition aerodyn_inflow.hpp:194
void Validate() const
Definition aerodyn_inflow.hpp:232
std::vector< std::array< float, 3 > > position
Definition aerodyn_inflow.hpp:188
void SetValues(size_t point_number, std::span< const double, 7 > pos, std::span< const double, 6 > vel, std::span< const double, 6 > acc)
Definition aerodyn_inflow.hpp:257
std::vector< std::array< float, 6 > > acceleration
Definition aerodyn_inflow.hpp:193
Struct to hold the settings for simulation controls.
Definition aerodyn_inflow.hpp:567
size_t n_channels
Definition aerodyn_inflow.hpp:609
std::string aerodyn_input
Definition aerodyn_inflow.hpp:588
std::string inflowwind_input
Definition aerodyn_inflow.hpp:589
int interpolation_order
Definition aerodyn_inflow.hpp:592
double total_elapsed_time
Definition aerodyn_inflow.hpp:597
std::string output_root_name
Definition aerodyn_inflow.hpp:607
bool point_load_output
Definition aerodyn_inflow.hpp:602
double max_time
Definition aerodyn_inflow.hpp:596
static constexpr size_t kDefaultStringLength
Definition aerodyn_inflow.hpp:585
double time_step
Definition aerodyn_inflow.hpp:595
OutputFormat
Write output format.
Definition aerodyn_inflow.hpp:578
bool transpose_DCM
Definition aerodyn_inflow.hpp:601
int n_time_steps
Definition aerodyn_inflow.hpp:598
DebugLevel debug_level
Definition aerodyn_inflow.hpp:603
double output_time_step
Definition aerodyn_inflow.hpp:608
DebugLevel
Debug levels used in AeroDyn-Inflow C bindings.
Definition aerodyn_inflow.hpp:569
OutputFormat output_format
Definition aerodyn_inflow.hpp:606
Initial state for a single blade of a turbine.
Definition aerodyn_inflow.hpp:106
std::vector< std::array< double, 7 > > node_initial_positions
Definition aerodyn_inflow.hpp:110
std::array< double, 7 > root_initial_position
Definition aerodyn_inflow.hpp:107
BladeInitialState(std::span< double, 7 > root, std::span< const std::array< double, 7 > > nodes)
Definition aerodyn_inflow.hpp:112
Configuration for the initial state of a turbine.
Definition aerodyn_inflow.hpp:99
std::array< double, 7 > nacelle_initial_position
Definition aerodyn_inflow.hpp:121
TurbineConfig(bool is_hawt, std::span< const float, 3 > ref_pos, std::span< const double, 7 > hub_pos, std::span< const double, 7 > nacelle_pos, std::span< const BladeInitialState > blade_states)
Definition aerodyn_inflow.hpp:125
std::array< float, 3 > reference_position
Definition aerodyn_inflow.hpp:119
void Validate() const
Definition aerodyn_inflow.hpp:138
std::array< double, 7 > hub_initial_position
Definition aerodyn_inflow.hpp:120
bool is_horizontal_axis
Definition aerodyn_inflow.hpp:118
std::vector< BladeInitialState > blade_initial_states
Definition aerodyn_inflow.hpp:123
size_t NumberOfBlades() const
Returns the number of blades in the turbine.
Definition aerodyn_inflow.hpp:155
Struct to hold and manage turbine-specific data.
Definition aerodyn_inflow.hpp:279
void SetBladeNodeMotion(size_t blade_number, size_t node_number, const std::array< double, 7 > &position, const std::array< double, 6 > &velocity, const std::array< double, 6 > &acceleration)
Sets the blade node values based on blade number and node number.
Definition aerodyn_inflow.hpp:386
MeshData blade_roots
Definition aerodyn_inflow.hpp:283
void SetNacelleMotion(const std::array< double, 7 > &position, const std::array< double, 6 > &velocity, const std::array< double, 6 > &acceleration)
Sets the nacelle motion values.
Definition aerodyn_inflow.hpp:476
std::array< double, 6 > GetBladeNodeLoad(size_t blade_number, size_t node_number) const
Gets the blade node loads based on blade number and node number.
Definition aerodyn_inflow.hpp:492
void SetBladeRootMotion(size_t blade_number, const std::array< double, 7 > &position, const std::array< double, 6 > &velocity, const std::array< double, 6 > &acceleration)
Sets the blade root values based on blade number.
Definition aerodyn_inflow.hpp:425
MeshData hub
Definition aerodyn_inflow.hpp:281
std::array< float, 3 > hh_vel
Definition aerodyn_inflow.hpp:285
void Validate() const
Definition aerodyn_inflow.hpp:332
MeshData nacelle
Definition aerodyn_inflow.hpp:282
std::vector< int32_t > blade_nodes_to_blade_num_mapping
Mapping of blade nodes to blade numbers (1D array)
Definition aerodyn_inflow.hpp:294
TurbineData(const TurbineConfig &tc)
Constructor for TurbineData based on a TurbineConfig object.
Definition aerodyn_inflow.hpp:314
std::vector< std::vector< size_t > > node_indices_by_blade
Unique indices of nodes for each blade (2D array)
Definition aerodyn_inflow.hpp:304
size_t NumberOfBlades() const
Returns the number of blades as size_t.
Definition aerodyn_inflow.hpp:370
int32_t n_blades
Definition aerodyn_inflow.hpp:280
void SetHubMotion(const std::array< double, 7 > &position, const std::array< double, 6 > &velocity, const std::array< double, 6 > &acceleration)
Sets the hub motion values.
Definition aerodyn_inflow.hpp:459
MeshData blade_nodes
Definition aerodyn_inflow.hpp:284
Struct to hold the settings for VTK output.
Definition aerodyn_inflow.hpp:618
OutputType vtk_type
Definition aerodyn_inflow.hpp:635
float vtk_hub_radius
Definition aerodyn_inflow.hpp:640
std::string output_dir_name
Definition aerodyn_inflow.hpp:633
double vtk_dt
Definition aerodyn_inflow.hpp:636
WriteType write_vtk
Definition aerodyn_inflow.hpp:634
std::array< float, 6 > vtk_nacelle_dimensions
Definition aerodyn_inflow.hpp:637
WriteType
Write type.
Definition aerodyn_inflow.hpp:620
OutputType
Mesh output type.
Definition aerodyn_inflow.hpp:627