From 60811b64ce12759530633282e15f8128dbf379b3 Mon Sep 17 00:00:00 2001 From: Regis Thedin Date: Fri, 27 Feb 2026 13:34:22 -0700 Subject: [PATCH 1/6] FF: Make IEA15MW data more explicit in terms of openfast version --- data/IEA15MW/{ => v4}/AD.dat | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF00_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF01_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF02_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF03_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF04_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF05_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF06_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF07_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF08_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF09_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF10_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF11_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF12_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF13_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF14_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF15_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF16_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF17_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF18_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF19_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF20_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF21_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF22_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF23_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF24_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF25_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF26_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF27_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF28_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF29_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF30_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF31_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF32_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF33_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF34_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF35_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF36_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF37_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF38_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF39_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF40_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF41_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF42_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF43_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF44_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF45_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF46_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF47_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF48_Coords.txt | 0 data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF49_Coords.txt | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_00.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_01.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_02.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_03.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_04.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_05.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_06.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_07.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_08.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_09.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_10.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_11.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_12.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_13.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_14.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_15.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_16.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_17.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_18.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_19.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_20.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_21.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_22.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_23.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_24.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_25.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_26.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_27.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_28.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_29.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_30.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_31.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_32.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_33.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_34.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_35.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_36.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_37.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_38.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_39.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_40.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_41.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_42.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_43.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_44.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_45.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_46.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_47.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_48.dat | 0 .../{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_49.dat | 0 data/IEA15MW/{ => v4}/Cp_Ct_Cq.IEA15MW.txt | 0 data/IEA15MW/{ => v4}/DISCON_ROSCOv2.9.IN | 0 data/IEA15MW/{ => v4}/ED.T.dat | 0 data/IEA15MW/{ => v4}/FF.fstf | 0 .../{ => v4}/IEA-15-240-RWT-Monopile_ElastoDyn_tower.dat | 0 data/IEA15MW/{ => v4}/IEA-15-240-RWT_AeroDyn15_blade.dat | 0 data/IEA15MW/{ => v4}/IEA-15-240-RWT_ElastoDyn_blade.dat | 0 data/IEA15MW/{ => v4}/IW_WT.dat | 0 data/IEA15MW/{ => v4}/README.md | 0 data/IEA15MW/{ => v4}/SvD.T.dat | 0 data/IEA15MW/{ => v4}/WT.T.fst | 0 .../fastfarm/examples/Ex0_FASTFarm_PlotSetup_ModifyInputs.py | 2 +- .../fastfarm/examples/Ex2a_FASTFarm_TurbSim_driven.py | 2 +- openfast_toolbox/fastfarm/examples/Ex2b_FASTFarm_LES_driven.py | 2 +- 115 files changed, 3 insertions(+), 3 deletions(-) rename data/IEA15MW/{ => v4}/AD.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF00_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF01_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF02_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF03_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF04_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF05_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF06_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF07_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF08_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF09_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF10_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF11_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF12_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF13_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF14_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF15_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF16_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF17_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF18_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF19_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF20_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF21_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF22_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF23_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF24_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF25_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF26_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF27_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF28_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF29_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF30_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF31_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF32_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF33_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF34_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF35_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF36_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF37_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF38_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF39_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF40_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF41_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF42_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF43_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF44_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF45_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF46_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF47_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF48_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AF49_Coords.txt (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_00.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_01.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_02.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_03.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_04.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_05.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_06.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_07.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_08.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_09.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_10.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_11.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_12.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_13.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_14.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_15.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_16.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_17.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_18.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_19.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_20.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_21.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_22.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_23.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_24.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_25.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_26.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_27.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_28.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_29.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_30.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_31.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_32.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_33.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_34.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_35.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_36.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_37.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_38.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_39.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_40.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_41.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_42.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_43.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_44.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_45.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_46.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_47.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_48.dat (100%) rename data/IEA15MW/{ => v4}/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_49.dat (100%) rename data/IEA15MW/{ => v4}/Cp_Ct_Cq.IEA15MW.txt (100%) rename data/IEA15MW/{ => v4}/DISCON_ROSCOv2.9.IN (100%) rename data/IEA15MW/{ => v4}/ED.T.dat (100%) rename data/IEA15MW/{ => v4}/FF.fstf (100%) rename data/IEA15MW/{ => v4}/IEA-15-240-RWT-Monopile_ElastoDyn_tower.dat (100%) rename data/IEA15MW/{ => v4}/IEA-15-240-RWT_AeroDyn15_blade.dat (100%) rename data/IEA15MW/{ => v4}/IEA-15-240-RWT_ElastoDyn_blade.dat (100%) rename data/IEA15MW/{ => v4}/IW_WT.dat (100%) rename data/IEA15MW/{ => v4}/README.md (100%) rename data/IEA15MW/{ => v4}/SvD.T.dat (100%) rename data/IEA15MW/{ => v4}/WT.T.fst (100%) diff --git a/data/IEA15MW/AD.dat b/data/IEA15MW/v4/AD.dat similarity index 100% rename from data/IEA15MW/AD.dat rename to data/IEA15MW/v4/AD.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF00_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF00_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF00_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF00_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF01_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF01_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF01_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF01_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF02_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF02_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF02_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF02_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF03_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF03_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF03_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF03_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF04_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF04_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF04_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF04_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF05_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF05_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF05_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF05_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF06_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF06_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF06_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF06_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF07_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF07_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF07_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF07_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF08_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF08_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF08_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF08_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF09_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF09_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF09_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF09_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF10_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF10_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF10_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF10_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF11_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF11_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF11_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF11_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF12_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF12_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF12_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF12_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF13_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF13_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF13_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF13_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF14_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF14_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF14_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF14_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF15_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF15_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF15_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF15_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF16_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF16_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF16_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF16_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF17_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF17_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF17_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF17_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF18_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF18_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF18_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF18_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF19_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF19_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF19_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF19_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF20_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF20_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF20_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF20_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF21_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF21_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF21_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF21_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF22_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF22_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF22_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF22_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF23_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF23_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF23_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF23_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF24_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF24_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF24_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF24_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF25_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF25_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF25_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF25_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF26_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF26_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF26_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF26_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF27_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF27_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF27_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF27_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF28_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF28_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF28_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF28_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF29_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF29_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF29_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF29_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF30_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF30_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF30_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF30_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF31_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF31_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF31_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF31_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF32_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF32_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF32_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF32_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF33_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF33_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF33_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF33_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF34_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF34_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF34_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF34_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF35_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF35_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF35_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF35_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF36_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF36_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF36_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF36_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF37_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF37_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF37_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF37_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF38_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF38_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF38_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF38_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF39_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF39_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF39_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF39_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF40_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF40_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF40_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF40_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF41_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF41_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF41_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF41_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF42_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF42_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF42_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF42_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF43_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF43_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF43_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF43_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF44_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF44_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF44_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF44_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF45_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF45_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF45_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF45_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF46_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF46_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF46_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF46_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF47_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF47_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF47_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF47_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF48_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF48_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF48_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF48_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AF49_Coords.txt b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF49_Coords.txt similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AF49_Coords.txt rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AF49_Coords.txt diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_00.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_00.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_00.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_00.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_01.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_01.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_01.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_01.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_02.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_02.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_02.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_02.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_03.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_03.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_03.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_03.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_04.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_04.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_04.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_04.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_05.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_05.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_05.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_05.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_06.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_06.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_06.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_06.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_07.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_07.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_07.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_07.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_08.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_08.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_08.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_08.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_09.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_09.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_09.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_09.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_10.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_10.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_10.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_10.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_11.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_11.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_11.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_11.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_12.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_12.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_12.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_12.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_13.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_13.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_13.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_13.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_14.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_14.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_14.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_14.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_15.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_15.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_15.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_15.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_16.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_16.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_16.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_16.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_17.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_17.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_17.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_17.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_18.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_18.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_18.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_18.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_19.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_19.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_19.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_19.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_20.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_20.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_20.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_20.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_21.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_21.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_21.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_21.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_22.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_22.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_22.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_22.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_23.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_23.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_23.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_23.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_24.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_24.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_24.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_24.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_25.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_25.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_25.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_25.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_26.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_26.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_26.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_26.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_27.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_27.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_27.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_27.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_28.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_28.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_28.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_28.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_29.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_29.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_29.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_29.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_30.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_30.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_30.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_30.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_31.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_31.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_31.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_31.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_32.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_32.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_32.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_32.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_33.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_33.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_33.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_33.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_34.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_34.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_34.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_34.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_35.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_35.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_35.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_35.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_36.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_36.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_36.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_36.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_37.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_37.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_37.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_37.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_38.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_38.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_38.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_38.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_39.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_39.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_39.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_39.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_40.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_40.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_40.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_40.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_41.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_41.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_41.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_41.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_42.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_42.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_42.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_42.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_43.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_43.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_43.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_43.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_44.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_44.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_44.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_44.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_45.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_45.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_45.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_45.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_46.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_46.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_46.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_46.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_47.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_47.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_47.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_47.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_48.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_48.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_48.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_48.dat diff --git a/data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_49.dat b/data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_49.dat similarity index 100% rename from data/IEA15MW/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_49.dat rename to data/IEA15MW/v4/Airfoils/IEA-15-240-RWT_AeroDyn15_Polar_49.dat diff --git a/data/IEA15MW/Cp_Ct_Cq.IEA15MW.txt b/data/IEA15MW/v4/Cp_Ct_Cq.IEA15MW.txt similarity index 100% rename from data/IEA15MW/Cp_Ct_Cq.IEA15MW.txt rename to data/IEA15MW/v4/Cp_Ct_Cq.IEA15MW.txt diff --git a/data/IEA15MW/DISCON_ROSCOv2.9.IN b/data/IEA15MW/v4/DISCON_ROSCOv2.9.IN similarity index 100% rename from data/IEA15MW/DISCON_ROSCOv2.9.IN rename to data/IEA15MW/v4/DISCON_ROSCOv2.9.IN diff --git a/data/IEA15MW/ED.T.dat b/data/IEA15MW/v4/ED.T.dat similarity index 100% rename from data/IEA15MW/ED.T.dat rename to data/IEA15MW/v4/ED.T.dat diff --git a/data/IEA15MW/FF.fstf b/data/IEA15MW/v4/FF.fstf similarity index 100% rename from data/IEA15MW/FF.fstf rename to data/IEA15MW/v4/FF.fstf diff --git a/data/IEA15MW/IEA-15-240-RWT-Monopile_ElastoDyn_tower.dat b/data/IEA15MW/v4/IEA-15-240-RWT-Monopile_ElastoDyn_tower.dat similarity index 100% rename from data/IEA15MW/IEA-15-240-RWT-Monopile_ElastoDyn_tower.dat rename to data/IEA15MW/v4/IEA-15-240-RWT-Monopile_ElastoDyn_tower.dat diff --git a/data/IEA15MW/IEA-15-240-RWT_AeroDyn15_blade.dat b/data/IEA15MW/v4/IEA-15-240-RWT_AeroDyn15_blade.dat similarity index 100% rename from data/IEA15MW/IEA-15-240-RWT_AeroDyn15_blade.dat rename to data/IEA15MW/v4/IEA-15-240-RWT_AeroDyn15_blade.dat diff --git a/data/IEA15MW/IEA-15-240-RWT_ElastoDyn_blade.dat b/data/IEA15MW/v4/IEA-15-240-RWT_ElastoDyn_blade.dat similarity index 100% rename from data/IEA15MW/IEA-15-240-RWT_ElastoDyn_blade.dat rename to data/IEA15MW/v4/IEA-15-240-RWT_ElastoDyn_blade.dat diff --git a/data/IEA15MW/IW_WT.dat b/data/IEA15MW/v4/IW_WT.dat similarity index 100% rename from data/IEA15MW/IW_WT.dat rename to data/IEA15MW/v4/IW_WT.dat diff --git a/data/IEA15MW/README.md b/data/IEA15MW/v4/README.md similarity index 100% rename from data/IEA15MW/README.md rename to data/IEA15MW/v4/README.md diff --git a/data/IEA15MW/SvD.T.dat b/data/IEA15MW/v4/SvD.T.dat similarity index 100% rename from data/IEA15MW/SvD.T.dat rename to data/IEA15MW/v4/SvD.T.dat diff --git a/data/IEA15MW/WT.T.fst b/data/IEA15MW/v4/WT.T.fst similarity index 100% rename from data/IEA15MW/WT.T.fst rename to data/IEA15MW/v4/WT.T.fst diff --git a/openfast_toolbox/fastfarm/examples/Ex0_FASTFarm_PlotSetup_ModifyInputs.py b/openfast_toolbox/fastfarm/examples/Ex0_FASTFarm_PlotSetup_ModifyInputs.py index befb732..192ccf6 100644 --- a/openfast_toolbox/fastfarm/examples/Ex0_FASTFarm_PlotSetup_ModifyInputs.py +++ b/openfast_toolbox/fastfarm/examples/Ex0_FASTFarm_PlotSetup_ModifyInputs.py @@ -15,7 +15,7 @@ def main(test=False): - FFfilepath = os.path.join(scriptDir, '../../../data/IEA15MW/FF.fstf') + FFfilepath = os.path.join(scriptDir, '../../../data/IEA15MW/v4/FF.fstf') # --- 0.2 Read and Modify an existing FAST.Farm file diff --git a/openfast_toolbox/fastfarm/examples/Ex2a_FASTFarm_TurbSim_driven.py b/openfast_toolbox/fastfarm/examples/Ex2a_FASTFarm_TurbSim_driven.py index dd280c1..22419db 100644 --- a/openfast_toolbox/fastfarm/examples/Ex2a_FASTFarm_TurbSim_driven.py +++ b/openfast_toolbox/fastfarm/examples/Ex2a_FASTFarm_TurbSim_driven.py @@ -119,7 +119,7 @@ def main(test=False): # ----------- Template files # --- Option 1 - templateFSTF = os.path.join(scriptDir, '../../../data/IEA15MW/FF.fstf') + templateFSTF = os.path.join(scriptDir, '../../../data/IEA15MW/v4/FF.fstf') templateFiles = {'libdisconfilepath' : libdiscon} # --- Option 2 #templatePath = '/full/path/where/template/files/are' diff --git a/openfast_toolbox/fastfarm/examples/Ex2b_FASTFarm_LES_driven.py b/openfast_toolbox/fastfarm/examples/Ex2b_FASTFarm_LES_driven.py index 46e9948..31bb10f 100644 --- a/openfast_toolbox/fastfarm/examples/Ex2b_FASTFarm_LES_driven.py +++ b/openfast_toolbox/fastfarm/examples/Ex2b_FASTFarm_LES_driven.py @@ -88,7 +88,7 @@ def main(test=False): # ----------- Template files # --- Option 1 - templateFSTF = os.path.join(scriptDir, '../../../data/IEA15MW/FF.fstf') + templateFSTF = os.path.join(scriptDir, '../../../data/IEA15MW/v4/FF.fstf') templateFiles = {'libdisconfilepath' : libdiscon} # --- Option 2 #templatePath = '/full/path/where/template/files/are' From 1e3ec8936a445c273c35fa3c945bf4d06d928771 Mon Sep 17 00:00:00 2001 From: Regis Thedin Date: Tue, 10 Mar 2026 15:30:32 -0600 Subject: [PATCH 2/6] FF: Add support for AMReX-based sampling includes case setup and input file writing --- .../fastfarm/AMRWindSimulation.py | 294 +++++++++++++++--- .../fastfarm/FASTFarmCaseCreation.py | 124 +++++--- .../examples/Ex2b_FASTFarm_LES_driven.py | 2 +- 3 files changed, 327 insertions(+), 93 deletions(-) diff --git a/openfast_toolbox/fastfarm/AMRWindSimulation.py b/openfast_toolbox/fastfarm/AMRWindSimulation.py index 15c7d99..7a8faff 100644 --- a/openfast_toolbox/fastfarm/AMRWindSimulation.py +++ b/openfast_toolbox/fastfarm/AMRWindSimulation.py @@ -84,8 +84,8 @@ def __init__(self, wts:dict, self.verbose = verbose # Placeholder variables, to be calculated by FFCaseCreation - self.output_frequency_lr = None - self.output_frequency_hr = None + self.output_interval_lr = None + self.output_interval_hr = None self.sampling_labels_lr = None self.sampling_labels_hr = None self.nx_lr = None @@ -131,7 +131,7 @@ def __repr__(self): s += f'\n' s += f'Low-res domain: \n' s += f' - ds low: {self.ds_low_les} m\n' - s += f' - dt low: {self.dt_low_les} s (with LES dt = {self.dt} s, output frequency is {self.output_frequency_lr})\n' + s += f' - dt low: {self.dt_low_les} s (with LES dt = {self.dt} s, output interval is {self.output_interval_lr})\n' s += f' - Sampling labels: {self.sampling_labels_lr}\n' s += f' - Extents: ({self.xdist_lr}, {self.ydist_lr}, {self.zdist_lr}) m\n' s += f' - x: {self.xlow_lr}:{self.ds_low_les}:{self.xhigh_lr} m,\t ({self.nx_lr} points at level {self.level_lr})\n' @@ -141,7 +141,7 @@ def __repr__(self): s += f'\n' s += f'High-res domain: \n' s += f' - ds high: {self.ds_high_les} m\n' - s += f' - dt high: {self.dt_high_les} s (with LES dt = {self.dt} s, output frequency is {self.output_frequency_hr})\n' + s += f' - dt high: {self.dt_high_les} s (with LES dt = {self.dt} s, output interval is {self.output_interval_hr})\n' s += f' - Sampling labels: {self.sampling_labels_hr}\n' for t in np.arange(len(self.hr_domains)): s += f" - Turbine {t}, base located at ({self.wts[t]['x']:.2f}, {self.wts[t]['y']:.2f}, {self.wts[t]['z']:.2f}), hub height of {self.wts[t]['zhub']} m\n" @@ -309,13 +309,16 @@ def _calc_sampling_time(self): raise ValueError(f"Low resolution timestep ({self.dt_low_les}) is finer than high resolution timestep ({self.dt_high_les})!") - # Sampling frequency - self.output_frequency_hr = int(np.floor(round(self.dt_high_les/self.dt,4))) - self.output_frequency_lr = getMultipleOf(self.dt_low_les/self.dt, multipleof=self.output_frequency_hr) + # Sampling interval + self.output_interval_hr = int(np.floor(round(self.dt_high_les/self.dt,4))) + self.output_interval_lr = getMultipleOf(self.dt_low_les/self.dt, multipleof=self.output_interval_hr) - if self.output_frequency_lr % self.output_frequency_hr != 0: - raise ValueError(f"Low resolution output frequency of {self.output_frequency_lr} not a multiple of the high resolution frequency {self.output_frequency_hr}!") + if self.output_interval_lr % self.output_interval_hr != 0: + raise ValueError(f"Low resolution output interval of {self.output_interval_lr} not a multiple of the high resolution interval {self.output_interval_hr}!") + # Convert sampling interval to sampling time interval + self.output_time_interval_hr = float(np.round(self.output_interval_hr * self.dt, decimals=10)) + self.output_time_interval_lr = float(np.round(self.output_interval_lr * self.dt, decimals=10)) def _calc_grid_resolution(self): @@ -645,7 +648,7 @@ def _check_grid_placement_single(self, sampling_xyzgrid_lhr, amr_xyzgrid_at_lhr_ - def write_sampling_params(self, out=None, format='netcdf', terrain=None, overwrite=False): + def write_sampling_params(self, out=None, format='netcdf', terrain=None, overwrite=False, chunk_size_vec=(8,8,1)): ''' Write out text that can be used for the sampling planes in an AMR-Wind input file @@ -655,16 +658,21 @@ def write_sampling_params(self, out=None, format='netcdf', terrain=None, overwri to. If None, result is written to screen. format: str Format requested for the output to be saved from AMR-Wind. Options are - native and netcdf + native, netcdf, amrex_erf, or amrex_amrwind terrain: bool (required) If the case contains terrain. If it does, mu_turb will also be requested. This is necessary to process terrain files and set NaN inside the terrain. overwrite: bool If saving to a file, whether or not to overwrite potentially existing file + chunk_size_vec: vector of int + Size of AMReX-based chunk, used with the sub-volume sampling. + ''' - if format not in ['netcdf','native']: - raise ValueError(f'format should be either native or netcdf') + + if format not in ['netcdf','native','amrex_erf','amrex_amrwind']: + raise ValueError(f'format should be one of: native, netcdf, amrex_erf, amrex_amrwind') + self.sampling_format = format if terrain == True: fields = 'velocity mu_turb' @@ -673,25 +681,70 @@ def write_sampling_params(self, out=None, format='netcdf', terrain=None, overwri else: raise ValueError(f'The `terrain` input should be explicitly set to True or False') - # Write time step information for consistenty with sampling frequency - s = f"time.fixed_dt = {self.dt}\n\n" - # Write flow velocity info for consistency - s += f"incflo.velocity = {self.incflo_velocity_hh[0]} {self.incflo_velocity_hh[1]} {self.incflo_velocity_hh[2]}\n\n\n" + if self.sampling_format == 'amrex_erf': + s = f"#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#\n" + s += f"# ERF SUBVOLUME OUTPUT #\n" + s += f"#.......................................#\n" + s += f"# Sampling info generated by AMRWindSamplingCreation.py on {self.curr_datetime}\n" + s += f"erf.fixed_dt = {self.dt}\n\n" + s += self._write_sampling_params_amrex_erf(fields, chunk_size_vec) + elif self.sampling_format == 'amrex_amrwind': + # Write time step information for consistency with sampling interval + s = f"time.fixed_dt = {self.dt}\n\n" + # Write flow velocity info for consistency + s += f"incflo.velocity = {self.incflo_velocity_hh[0]} {self.incflo_velocity_hh[1]} {self.incflo_velocity_hh[2]}\n\n\n" + + s += f"#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#\n" + s += f"# AMRWIND AMREX SAMPLING #\n" + s += f"#.......................................#\n" + s += f"# Sampling info generated by AMRWindSamplingCreation.py on {self.curr_datetime}\n" + s += f"incflo.post_processing = {self.postproc_name_lr} {self.postproc_name_hr} # averaging\n\n\n" + s += self._write_sampling_params_amrex_amrwind(fields, chunk_size_vec) + else: + # Write time step information for consistency with sampling interval + s = f"time.fixed_dt = {self.dt}\n\n" + # Write flow velocity info for consistency + s += f"incflo.velocity = {self.incflo_velocity_hh[0]} {self.incflo_velocity_hh[1]} {self.incflo_velocity_hh[2]}\n\n\n" + + # Write high-level info for sampling + s += f"#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#\n" + s += f"# POST-PROCESSING #\n" + s += f"#.......................................#\n" + s += f"# Sampling info generated by AMRWindSamplingCreation.py on {self.curr_datetime}\n" + s += f"incflo.post_processing = {self.postproc_name_lr} {self.postproc_name_hr} # averaging\n\n\n" + s += self._write_sampling_params_netcdfnative(fields) + + if out is None: + return s + elif os.path.isdir(out): + outfile = os.path.join(out, 'sampling_config.i') + else: + # full file given + outfile = out + if not os.path.exists(os.path.dirname(outfile)): + os.makedirs(os.path.dirname(outfile)) + if not overwrite: + if os.path.isfile(outfile): + raise FileExistsError(f"{str(outfile)} already exists! Aborting...") + + with open(outfile,"w") as out: + out.write(s) + + + + def _write_sampling_params_netcdfnative(self, fields): - # Write high-level info for sampling sampling_labels_lr_str = " ".join(str(item) for item in self.sampling_labels_lr) sampling_labels_hr_str = " ".join(str(item) for item in self.sampling_labels_hr) - s += f"#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#\n" - s += f"# POST-PROCESSING #\n" - s += f"#.......................................#\n" - s += f"# Sampling info generated by AMRWindSamplingCreation.py on {self.curr_datetime}\n" - s += f"incflo.post_processing = {self.postproc_name_lr} {self.postproc_name_hr} # averaging\n\n\n" - - s += f"# ---- Low-res sampling parameters ----\n" - s += f"{self.postproc_name_lr}.output_format = {format}\n" - s += f"{self.postproc_name_lr}.output_frequency = {self.output_frequency_lr}\n" - s += f"{self.postproc_name_lr}.fields = {fields}\n" - s += f"{self.postproc_name_lr}.labels = {sampling_labels_lr_str}\n\n" + output_time_interval_lr_str = f"{self.output_time_interval_lr:.10f}".rstrip('0').rstrip('.') + output_time_interval_hr_str = f"{self.output_time_interval_hr:.10f}".rstrip('0').rstrip('.') + + s = f"# ---- Low-res sampling parameters ----\n" + s += f"{self.postproc_name_lr}.output_format = {self.sampling_format}\n" + s += f"{self.postproc_name_lr}.output_after_final_step = false\n" + s += f"{self.postproc_name_lr}.output_time_interval = {output_time_interval_lr_str}\n" + s += f"{self.postproc_name_lr}.fields = {fields}\n" + s += f"{self.postproc_name_lr}.labels = {sampling_labels_lr_str}\n\n" # Write out low resolution sampling plane info zoffsets_lr_str = " ".join(str(item) for item in self.zoffsets_lr) @@ -706,10 +759,11 @@ def write_sampling_params(self, out=None, format='netcdf', terrain=None, overwri s += f"{self.postproc_name_lr}.Low.offsets = {zoffsets_lr_str}\n\n\n" s += f"# ---- High-res sampling parameters ----\n" - s += f"{self.postproc_name_hr}.output_format = {format}\n" - s += f"{self.postproc_name_hr}.output_frequency = {self.output_frequency_hr}\n" - s += f"{self.postproc_name_hr}.fields = {fields}\n" - s += f"{self.postproc_name_hr}.labels = {sampling_labels_hr_str}\n" + s += f"{self.postproc_name_hr}.output_format = {self.sampling_format}\n" + s += f"{self.postproc_name_hr}.output_after_final_step = false\n" + s += f"{self.postproc_name_hr}.output_time_interval = {output_time_interval_hr_str}\n" + s += f"{self.postproc_name_hr}.fields = {fields}\n" + s += f"{self.postproc_name_hr}.labels = {sampling_labels_hr_str}\n" # Write out high resolution sampling plane info for turbkey in self.hr_domains: @@ -742,23 +796,167 @@ def write_sampling_params(self, out=None, format='netcdf', terrain=None, overwri s += f"{self.postproc_name_hr}.{sampling_name}.offset_vector = 0.0 0.0 1.0\n" s += f"{self.postproc_name_hr}.{sampling_name}.offsets = {zoffsets_hr_str}\n" + return s - if out is None: - print(s) - return - elif os.path.isdir(out): - outfile = os.path.join(out, 'sampling_config.i') - else: - # full file given - outfile = out - if not os.path.exists(os.path.dirname(outfile)): - os.makedirs(os.path.dirname(outfile)) - if not overwrite: - if os.path.isfile(outfile): - raise FileExistsError(f"{str(outfile)} already exists! Aborting...") - with open(outfile,"w") as out: - out.write(s) + def _write_sampling_params_amrex_erf(self, fields, chunk_size_vec): + def _to_erf_sampling_vars(fields_in): + vars_out = [] + for field in fields_in.split(): + if field == 'velocity': + vars_out.extend(['x_velocity', 'y_velocity', 'z_velocity']) + else: + vars_out.append(field) + return " ".join(vars_out) + + # ERF subvolume sampling uses cell-corner origins. + # Internal placement in this class stores cell-centered origins, so shift by half + # of the AMR-Wind cell size at the corresponding level. + xlow_lr_corner = self.xlow_lr - 0.5 * self.dx_at_lr_level + ylow_lr_corner = self.ylow_lr - 0.5 * self.dy_at_lr_level + zlow_lr_corner = self.zlow_lr - 0.5 * self.dz_at_lr_level + + origins = [f"{xlow_lr_corner:.4f} {ylow_lr_corner:.4f} {zlow_lr_corner:.4f}"] + nxnynz = [f"{self.nx_lr} {self.ny_lr} {self.nz_lr}"] + dxdydz = [f"{self.ds_low_les} {self.ds_low_les} {self.ds_low_les}"] + + s = f"# ---- ERF subvolume sampling parameters ----\n" + s += f"# Order of boxes in origin/nxnynz/dxdydz: low-res first, then each high-res turbine box\n" + + for turbkey in self.hr_domains: + xlow_hr_corner = self.hr_domains[turbkey]['xlow_hr'] - 0.5 * self.dx_at_hr_level + ylow_hr_corner = self.hr_domains[turbkey]['ylow_hr'] - 0.5 * self.dy_at_hr_level + zlow_hr_corner = self.hr_domains[turbkey]['zlow_hr'] - 0.5 * self.dz_at_hr_level + + origins.append(f"{xlow_hr_corner:.4f} {ylow_hr_corner:.4f} {zlow_hr_corner:.4f}") + nxnynz.append( + f"{self.hr_domains[turbkey]['nx_hr']} {self.hr_domains[turbkey]['ny_hr']} {self.hr_domains[turbkey]['nz_hr']}" + ) + dxdydz.append(f"{self.ds_high_les} {self.ds_high_les} {self.ds_high_les}") + + erf_sampling_vars = _to_erf_sampling_vars(fields) + subvol_file = os.path.join('post_processing',f"{self.postproc_name_lr}_{self.postproc_name_hr}") # TODO fix + + s += f"erf.subvol_file = \"{subvol_file}\"\n" + s += f"erf.subvol_int = {self.output_interval_lr} {self.output_interval_hr}\n" + s += f"erf.subvol_sampling_vars = {erf_sampling_vars}\n" + s += f"erf.subvol.origin = {' '.join(origins)}\n" + s += f"erf.subvol.nxnynz = {' '.join(nxnynz)}\n" + s += f"erf.subvol.dxdydz = {' '.join(dxdydz)}\n" + s += f"erf.subvol.chunk_size = {chunk_size_vec[0]} {chunk_size_vec[1]} {chunk_size_vec[2]}\n" + + return s + + + def _write_sampling_params_amrex_amrwind(self, fields, chunk_size_vec): + + # Here we need to modify the labels so that is consistent with ERF-based AMReX samping + sampling_labels_lr = ['0_'] + sampling_labels_hr = [f"{i+1}_" for i, s in enumerate(self.sampling_labels_hr)] + + sampling_labels_lr_str = " ".join(str(item) for item in sampling_labels_lr) + sampling_labels_hr_str = " ".join(str(item) for item in sampling_labels_hr) + output_time_interval_lr_str = f"{self.output_time_interval_lr:.10f}".rstrip('0').rstrip('.') + output_time_interval_hr_str = f"{self.output_time_interval_hr:.10f}".rstrip('0').rstrip('.') + + is_lr_aligned = ( + np.isclose(self.dx_at_lr_level, self.ds_low_les) + and np.isclose(self.dy_at_lr_level, self.ds_low_les) + and np.isclose(self.dz_at_lr_level, self.ds_low_les) + ) + if not is_lr_aligned: + raise ValueError( + "amrex_amrwind sampling requires low-res AMR cell spacing to match sampling spacing. " + f"Received dx/dy/dz at level {self.level_lr} = ({self.dx_at_lr_level}, {self.dy_at_lr_level}, {self.dz_at_lr_level}) " + f"and ds_low_les = {self.ds_low_les}." + ) + + is_hr_aligned = ( + np.isclose(self.dx_at_hr_level, self.ds_high_les) + and np.isclose(self.dy_at_hr_level, self.ds_high_les) + and np.isclose(self.dz_at_hr_level, self.ds_high_les) + ) + if not is_hr_aligned: + raise ValueError( + "amrex_amrwind sampling requires high-res AMR cell spacing to match sampling spacing. " + f"Received dx/dy/dz at level {self.level_hr} = ({self.dx_at_hr_level}, {self.dy_at_hr_level}, {self.dz_at_hr_level}) " + f"and ds_high_les = {self.ds_high_les}." + ) + + #sampleAt = 'cell_centers' + sampleAt = 'cell_corners' + + if sampleAt == 'cell_centers': + xlow_lr_out = self.xlow_lr + ylow_lr_out = self.ylow_lr + zlow_lr_out = self.zlow_lr + xshift_hr = 0.0 + yshift_hr = 0.0 + zshift_hr = 0.0 + origin_comment = 'cell centers' + elif sampleAt == 'cell_corners': + xlow_lr_out = self.xlow_lr - 0.5 * self.dx_at_lr_level + ylow_lr_out = self.ylow_lr - 0.5 * self.dy_at_lr_level + zlow_lr_out = self.zlow_lr - 0.5 * self.dz_at_lr_level + xshift_hr = -0.5 * self.dx_at_hr_level + yshift_hr = -0.5 * self.dy_at_hr_level + zshift_hr = -0.5 * self.dz_at_hr_level + origin_comment = 'cell corners' + else: + raise ValueError(f"Unknown sampleAt='{sampleAt}'. Expected 'cell_centers' or 'cell_corners'.") + + s = f"# ---- Low-res sampling parameters ----\n" + s += f"{self.postproc_name_lr}.type = Subvolume\n" + s += f"{self.postproc_name_lr}.output_rename = ffboxes\n" + s += f"{self.postproc_name_lr}.output_after_final_step = false\n" + s += f"{self.postproc_name_lr}.output_time_interval = {output_time_interval_lr_str}\n" + s += f"{self.postproc_name_lr}.fields = {fields}\n" + s += f"{self.postproc_name_lr}.labels = {sampling_labels_lr_str}\n\n" + + s += f"# Low sampling grid spacing = {self.ds_lr} m\n" + s += f"{self.postproc_name_lr}.{sampling_labels_lr_str}.type = Rectangular\n" + s += f"{self.postproc_name_lr}.{sampling_labels_lr_str}.origin = {xlow_lr_out:.4f} {ylow_lr_out:.4f} {zlow_lr_out:.4f} # {origin_comment}\n" + s += f"{self.postproc_name_lr}.{sampling_labels_lr_str}.num_points = {self.nx_lr} {self.ny_lr} {self.nz_lr}\n" + s += f"{self.postproc_name_lr}.{sampling_labels_lr_str}.dx_vec = {self.ds_low_les} {self.ds_low_les} {self.ds_low_les}\n" + s += f"{self.postproc_name_lr}.{sampling_labels_lr_str}.chunk_size_vec = {chunk_size_vec[0]} {chunk_size_vec[1]} {chunk_size_vec[2]}\n\n\n" + + s += f"# ---- High-res sampling parameters ----\n" + s += f"{self.postproc_name_hr}.type = Subvolume\n" + s += f"{self.postproc_name_hr}.output_rename = ffboxes\n" + s += f"{self.postproc_name_hr}.output_after_final_step = false\n" + s += f"{self.postproc_name_hr}.output_time_interval = {output_time_interval_hr_str}\n" + s += f"{self.postproc_name_hr}.fields = {fields}\n" + s += f"{self.postproc_name_hr}.labels = {sampling_labels_hr_str}\n" + + for turbkey in self.hr_domains: + wt_x = self.wts[turbkey]['x'] + wt_y = self.wts[turbkey]['y'] + wt_z = self.wts[turbkey]['z'] + wt_h = self.wts[turbkey]['zhub'] + wt_D = self.wts[turbkey]['D'] + if 'name' in self.wts[turbkey].keys(): + wt_name = self.wts[turbkey]['name'] + else: + wt_name = f'T{turbkey+1}' + sampling_name = sampling_labels_hr[turbkey] + nx_hr = self.hr_domains[turbkey]['nx_hr'] + ny_hr = self.hr_domains[turbkey]['ny_hr'] + nz_hr = self.hr_domains[turbkey]['nz_hr'] + xlow_hr = self.hr_domains[turbkey]['xlow_hr'] + ylow_hr = self.hr_domains[turbkey]['ylow_hr'] + zlow_hr = self.hr_domains[turbkey]['zlow_hr'] + xlow_hr_out = xlow_hr + xshift_hr + ylow_hr_out = ylow_hr + yshift_hr + zlow_hr_out = zlow_hr + zshift_hr + + s += f"\n# Turbine {wt_name} with base at (x,y,z) = ({wt_x:.4f}, {wt_y:.4f}, {wt_z:.4f}), with hh = {wt_h}, D = {wt_D}, grid spacing = {self.ds_hr} m\n" + s += f"{self.postproc_name_hr}.{sampling_name}.type = Rectangular\n" + s += f"{self.postproc_name_hr}.{sampling_name}.origin = {xlow_hr_out:.4f} {ylow_hr_out:.4f} {zlow_hr_out:.4f} # {origin_comment}\n" + s += f"{self.postproc_name_hr}.{sampling_name}.num_points = {nx_hr} {ny_hr} {nz_hr}\n" + s += f"{self.postproc_name_hr}.{sampling_name}.dx_vec = {self.ds_high_les} {self.ds_high_les} {self.ds_high_les}\n" + s += f"{self.postproc_name_hr}.{sampling_name}.chunk_size_vec = {chunk_size_vec[0]} {chunk_size_vec[1]} {chunk_size_vec[2]}\n" + + return s diff --git a/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py b/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py index f25876c..0f3a575 100644 --- a/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py +++ b/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py @@ -1,6 +1,7 @@ import pandas as pd import os, sys, shutil import subprocess +import warnings from contextlib import contextmanager import numpy as np import xarray as xr @@ -252,8 +253,10 @@ def __init__(self, seedValues: list of int Seed value for each seed of requested TurbSim simulations if nSeeds!=6 inflowType: str - Inflow type (LES or TS) This variable will dictate whether it is a TurbSim-driven or LES-driven case - Choose 'LES' or 'TS' (no default is set) + Inflow type (LES_VTK/LES_AMReX or TS) This variable will dictate whether it is a TurbSim-driven or LES-driven case. + LES cases can be given by VTK-based input or AMReX-based input. + Choose 'LES_VTK', 'LES_AMReX', or 'TS' (no default is set). + Backward compatibility: 'LES' is accepted and mapped to 'LES_VTK' with a deprecation warning. inflowPath: str or list of strings Full path of the LES data, if driven by LES. If None, the setup will be for TurbSim inflow. inflowPath can be a single path, or a list of paths of the same length as the sweep in conditions. @@ -736,17 +739,37 @@ def _checkInputs(self): 7693202, 587924, 890090, 435646, -454899, -785138, -78564, -17944, -99021, 389432] self.seedValues = self.seedValues[:self.nSeeds] if len(self.seedValues) != self.nSeeds: - raise ValueError(f'The array seedValues has been passed, but its length does not correspond '\ + raise ValueError(f'The array seedValues has been passed, but its length does not corraaespond '\ f'to the number of seeds requested.') + + if not isinstance(self.inflowType, str): + raise ValueError(f"Inflow type `inflowType` should be a string. Received {type(self.inflowType)}.") + + if self.inflowType.lower() == 'ts': + self.inflowType = 'TS' + elif self.inflowType.lower() == 'les': + deprec_msg = "inflowType='LES' is deprecated and will be removed in a future release. Use inflowType='LES_VTK'. Mapping to 'LES_VTK'." + if self.verbose > 0: + WARN(deprec_msg) + warnings.warn(deprec_msg, DeprecationWarning, stacklevel=2) + self.inflowType = 'LES_VTK' + elif self.inflowType.lower() == 'les_vtk': + self.inflowType = 'LES_VTK' + elif self.inflowType.lower() == 'les_amrex': + self.inflowType = 'LES_AMReX' # Check LES parameters if self.inflowType == 'TS': self.inflowStr = 'TurbSim' self.Mod_AmbWind = 3 - elif self.inflowType == 'LES': + elif self.inflowType in ['LES_VTK', 'LES_AMReX']: if isinstance(self.inflowPath,str): self.inflowPath = [self.inflowPath]*len(self.vhub) - self.inflowStr = 'LES' - self.Mod_AmbWind = 1 + if self.inflowType == 'LES_VTK': + self.inflowStr = 'LES_VTK' + self.Mod_AmbWind = 1 + else: + self.inflowStr = 'LES_AMReX' + self.Mod_AmbWind = 4 for p in self.inflowPath: if not os.path.isdir(p): WARN(f'The LES path {p} does not exist') @@ -755,7 +778,7 @@ def _checkInputs(self): raise ValueError (f'An LES-driven case was requested, but one or more grid parameters were not given. '\ 'Set `dt_high`, `ds_high`, `dt_low`, and `ds_low` based on your LES boxes.') else: - raise ValueError (f"Inflow type `inflowType` should be 'TS' or 'LES'. Received {self.inflowType}.") + raise ValueError (f"Inflow type `inflowType` should be 'TS', 'LES_VTK', or 'LES_AMReX'. Received {self.inflowType}.") # Check the wake model (1:Polar; 2:Curl; 3:Cartesian) if self.mod_wake not in [1,2,3]: @@ -2647,7 +2670,7 @@ def FF_setup(self, outlistFF=None, **kwargs): # Turbine location in TurbSim reference frame xWT = alignedTurbs['Tx'].values + self.xoffset_turbsOrigin2TSOrigin yWT = alignedTurbs['Ty'].values + self.yoffset_turbsOrigin2TSOrigin - elif self.inflowStr == 'LES': + elif self.inflowStr in ['LES_VTK', 'LES_AMReX']: # Turbine location in LES reference frame xWT = alignedTurbs['Tx'].values yWT = alignedTurbs['Ty'].values @@ -2662,7 +2685,7 @@ def FF_setup(self, outlistFF=None, **kwargs): self.planes_yz = planes_yz[0:9] self.planes_xz = planes_xz[0:9] - if self.inflowStr == 'LES': + if self.inflowStr in ['LES_VTK', 'LES_AMReX']: self._FF_setup_LES(**kwargs) elif self.inflowStr == 'TurbSim': @@ -2699,38 +2722,37 @@ def _FF_setup_LES(self, seedsToKeep=1): for seed in range(self.nSeeds): currpath = self.getCondSeedPath(cond, seed) if os.path.isdir(currpath): shutil.rmtree(currpath) - for case in range(self.nCases): for seed in range(seedsToKeep,self.nSeeds): currpath = self.getCaseSeedPath(cond, case, seed) if os.path.isdir(currpath): shutil.rmtree(currpath) - # Create symlinks for the processed-and-renamed vtk files - LESboxesDirName = 'LESboxes' - - for cond in range(self.nConditions): - for case in range(self.nCases): - for seed in range(self.seedsToKeep): - # Remove TurbSim dir - currpath = self.getHRTurbSimPath(cond, case, seed) - if os.path.isdir(currpath): shutil.rmtree(currpath) - # Create LES boxes dir - seedPath = self.getCaseSeedPath(cond, case, seed) - currpath = os.path.join(seedPath, LESboxesDirName) - if not os.path.isdir(currpath): os.makedirs(currpath) - - # Low-res box - src = os.path.join(self.inflowPath[cond], 'Low') - dst = os.path.join(seedPath, LESboxesDirName, 'Low') - self._symlink(src, dst) - - # High-res boxes - for t in range(self.nTurbines): - src = os.path.join(self.inflowPath[cond], f"HighT{t+1}_inflow{str(self.allCases.sel(case=case).inflow_deg.values).replace('-','m')}deg") - dst = os.path.join(seedPath, LESboxesDirName, f'HighT{t+1}') + if self.inflowStr == 'LES_VTK': + # Create symlinks for the processed-and-renamed vtk files + LESboxesDirName = 'LESboxes' + for cond in range(self.nConditions): + for case in range(self.nCases): + for seed in range(self.seedsToKeep): + # Remove TurbSim dir + currpath = self.getHRTurbSimPath(cond, case, seed) + if os.path.isdir(currpath): shutil.rmtree(currpath) + # Create LES boxes dir + seedPath = self.getCaseSeedPath(cond, case, seed) + currpath = os.path.join(seedPath, LESboxesDirName) + if not os.path.isdir(currpath): os.makedirs(currpath) + + # Low-res box + src = os.path.join(self.inflowPath[cond], 'Low') + dst = os.path.join(seedPath, LESboxesDirName, 'Low') self._symlink(src, dst) - + + # High-res boxes + for t in range(self.nTurbines): + src = os.path.join(self.inflowPath[cond], f"HighT{t+1}_inflow{str(self.allCases.sel(case=case).inflow_deg.values).replace('-','m')}deg") + dst = os.path.join(seedPath, LESboxesDirName, f'HighT{t+1}') + self._symlink(src, dst) + # Loops on all conditions/cases and cases for FAST.Farm for cond in range(self.nConditions): @@ -2759,15 +2781,24 @@ def _FF_setup_LES(self, seedsToKeep=1): # Open output file and change additional values manually or make sure we have the correct ones ff_file['InflowFile'] = f'"unused"' - ff_file['Mod_AmbWind'] = self.Mod_AmbWind # LES + ff_file['Mod_AmbWind'] = self.Mod_AmbWind # LES_VTK (1) or LES_AMReX (4) ff_file['TMax'] = self.tmax # LES-related parameters - ff_file['DT_Low-VTK'] = self.dt_low - ff_file['DT_High-VTK'] = self.dt_high - ff_file['WindFilePath'] = f'''"{os.path.join(seedPath, LESboxesDirName)}"''' - #if checkWindFiles: - # ff_file['ChkWndFiles'] = 'TRUE' + if self.inflowStr == 'LES_VTK': + ff_file['DT_Low-VTK'] = self.dt_low + ff_file['DT_High-VTK'] = self.dt_high + ff_file['WindFilePath'] = f'''"{os.path.join(seedPath, LESboxesDirName)}"''' + #if checkWindFiles: + # ff_file['ChkWndFiles'] = 'TRUE' + elif self.inflowStr == 'LES_AMReX': + ff_file['DT_Low-AMReX'] = self.dt_low + ff_file['DT_High-AMReX'] = self.dt_high + ff_file['WindDirPrefix'] = f'''"{os.path.join(self.inflowPath[cond], 'ffboxes')}"''' + ff_file['DirStartIndex'] = 0 # TODO + WARN('For LES_AMReX, make sure to set the correct DirStartIndex and that the files are named accordingly.') + + # Super controller if 'UseSC' in ff_file.keys(): @@ -2789,7 +2820,8 @@ def _FF_setup_LES(self, seedsToKeep=1): self.dr = round(self.D/15) ff_file['dr'] = self.dr ff_file['NumRadii'] = int(np.ceil(3*D_/(2*self.dr) + 1)) - ff_file['NumPlanes'] = int(np.ceil( 20*D_/(self.dt_low*Vhub_*(1-1/6)) ) ) + if 'NumPlanes' in ff_file.keys(): + ff_file['NumPlanes'] = int(np.ceil( 20*D_/(self.dt_low*Vhub_*(1-1/6)) ) ) ff_file['OutRadii'] = [ff_file['OutRadii']] if isinstance(ff_file['OutRadii'],(float,int)) else ff_file['OutRadii'] # If NOutRadii is 0 we find some default radii @@ -2909,7 +2941,6 @@ def _FF_setup_TS(self): ff_file['dr'] = self.dr ff_file['NumRadii'] = int(np.ceil(3*D_/(2*self.dr) + 1)) ff_file['NumPlanes'] = int(np.ceil( 20*D_/(self.dt_low*Vhub_*(1-1/6)) ) ) - ff_file['OutRadii'] = [ff_file['OutRadii']] if isinstance(ff_file['OutRadii'],(float,int)) else ff_file['OutRadii'] # If NOutRadii is 0 we find some default radii if ff_file['NOutRadii']==0: @@ -2922,6 +2953,11 @@ def _FF_setup_TS(self): ff_file['NOutRadii'] = i ff_file['OutRadii'] = ff_file['OutRadii'][:i] break + + # WAT + if 'WAT_ScaleBox' in ff_file.keys(): + # Ensure new default is always enabled + ff_file['WAT_ScaleBox'] = True # Vizualization outputs ff_file['WrDisWind'] = 'False' @@ -3020,7 +3056,7 @@ def _getBoxesParamsForFF(self, lowbts, highbts, dt_low_desired, D, HubHt, xWT, y if self.inflowType == 'TS': X0_High = X0_desired Y0_High = Y0_desired - elif self.inflowType == 'LES': + elif self.inflowType in ['LES_VTK', 'LES_AMReX']: X0_High = X0_Low + np.floor((X0_desired-X0_Low)/dX_High)*dX_High Y0_High = Y0_Low + np.floor((Y0_desired-Y0_Low)/dY_High)*dY_High @@ -3071,7 +3107,7 @@ def _getBoxesParamsForFF(self, lowbts, highbts, dt_low_desired, D, HubHt, xWT, y # --- Sanity check: check that the high res is at "almost" an integer location - if self.inflowType == 'LES': + if self.inflowType in ['LES_VTK', 'LES_AMReX']: X_rel = (np.array(d['X0_High'])-d['X0_Low'])/d['dX_High'] Y_rel = (np.array(d['Y0_High'])-d['Y0_Low'])/d['dY_High'] dX = X_rel - np.round(X_rel) # Should be close to zero diff --git a/openfast_toolbox/fastfarm/examples/Ex2b_FASTFarm_LES_driven.py b/openfast_toolbox/fastfarm/examples/Ex2b_FASTFarm_LES_driven.py index 31bb10f..5412384 100644 --- a/openfast_toolbox/fastfarm/examples/Ex2b_FASTFarm_LES_driven.py +++ b/openfast_toolbox/fastfarm/examples/Ex2b_FASTFarm_LES_driven.py @@ -72,7 +72,7 @@ def main(test=False): zbot = 1 # Bottom of your domain mod_wake = 2 # Wake model. 1: Polar, 2: Curled, 3: Cartesian # ----------- Inflow parameters - inflowType = 'LES' + inflowType = 'LES_VTK' inflowPath = '/full/path/to/LES/case/.../LESboxes' # ----------- Desired sweeps From 0b0607aa8c2661d2edc7b57882ade34350354c01 Mon Sep 17 00:00:00 2001 From: Regis Thedin Date: Wed, 11 Mar 2026 11:07:17 -0600 Subject: [PATCH 3/6] FF: Add `output_from_restart` and print commit hash --- .../fastfarm/AMRWindSimulation.py | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/openfast_toolbox/fastfarm/AMRWindSimulation.py b/openfast_toolbox/fastfarm/AMRWindSimulation.py index 7a8faff..5834f68 100644 --- a/openfast_toolbox/fastfarm/AMRWindSimulation.py +++ b/openfast_toolbox/fastfarm/AMRWindSimulation.py @@ -1,5 +1,6 @@ import numpy as np import os +import subprocess from openfast_toolbox.fastfarm.FASTFarmCaseCreation import getMultipleOf from openfast_toolbox.tools.strings import INFO, FAIL, OK, WARN, print_bold @@ -108,6 +109,20 @@ def __init__(self, wts:dict, # Get execution time from datetime import datetime self.curr_datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + self.commit = self._get_git_commit_hash() + + + def _get_git_commit_hash(self): + ''' + Return current git commit hash for this repository. + ''' + try: + repo_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + commit_hash = subprocess.check_output(['git', '-C', repo_root, 'rev-parse', '--short', 'HEAD'], + stderr=subprocess.DEVNULL, text=True).strip() + return commit_hash if commit_hash else 'unknown' + except Exception: + return 'unknown' def __repr__(self): @@ -685,7 +700,7 @@ def write_sampling_params(self, out=None, format='netcdf', terrain=None, overwri s = f"#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#\n" s += f"# ERF SUBVOLUME OUTPUT #\n" s += f"#.......................................#\n" - s += f"# Sampling info generated by AMRWindSamplingCreation.py on {self.curr_datetime}\n" + s += f"# Sampling info generated by openfast_toolbox on {self.curr_datetime}, commit {self.commit}.\n" s += f"erf.fixed_dt = {self.dt}\n\n" s += self._write_sampling_params_amrex_erf(fields, chunk_size_vec) elif self.sampling_format == 'amrex_amrwind': @@ -697,8 +712,8 @@ def write_sampling_params(self, out=None, format='netcdf', terrain=None, overwri s += f"#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#\n" s += f"# AMRWIND AMREX SAMPLING #\n" s += f"#.......................................#\n" - s += f"# Sampling info generated by AMRWindSamplingCreation.py on {self.curr_datetime}\n" - s += f"incflo.post_processing = {self.postproc_name_lr} {self.postproc_name_hr} # averaging\n\n\n" + s += f"# Sampling info generated by openfast_toolbox on {self.curr_datetime}, commit {self.commit}.\n" + s += f"incflo.post_processing = {self.postproc_name_lr} {self.postproc_name_hr}\n\n\n" s += self._write_sampling_params_amrex_amrwind(fields, chunk_size_vec) else: # Write time step information for consistency with sampling interval @@ -710,8 +725,8 @@ def write_sampling_params(self, out=None, format='netcdf', terrain=None, overwri s += f"#¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨#\n" s += f"# POST-PROCESSING #\n" s += f"#.......................................#\n" - s += f"# Sampling info generated by AMRWindSamplingCreation.py on {self.curr_datetime}\n" - s += f"incflo.post_processing = {self.postproc_name_lr} {self.postproc_name_hr} # averaging\n\n\n" + s += f"# Sampling info generated by openfast_toolbox on {self.curr_datetime}, commit {self.commit}.\n" + s += f"incflo.post_processing = {self.postproc_name_lr} {self.postproc_name_hr}\n\n\n" s += self._write_sampling_params_netcdfnative(fields) if out is None: @@ -742,6 +757,7 @@ def _write_sampling_params_netcdfnative(self, fields): s = f"# ---- Low-res sampling parameters ----\n" s += f"{self.postproc_name_lr}.output_format = {self.sampling_format}\n" s += f"{self.postproc_name_lr}.output_after_final_step = false\n" + s += f"{self.postproc_name_lr}.output_from_restart = true\n" s += f"{self.postproc_name_lr}.output_time_interval = {output_time_interval_lr_str}\n" s += f"{self.postproc_name_lr}.fields = {fields}\n" s += f"{self.postproc_name_lr}.labels = {sampling_labels_lr_str}\n\n" @@ -761,6 +777,7 @@ def _write_sampling_params_netcdfnative(self, fields): s += f"# ---- High-res sampling parameters ----\n" s += f"{self.postproc_name_hr}.output_format = {self.sampling_format}\n" s += f"{self.postproc_name_hr}.output_after_final_step = false\n" + s += f"{self.postproc_name_hr}.output_from_restart = true\n" s += f"{self.postproc_name_hr}.output_time_interval = {output_time_interval_hr_str}\n" s += f"{self.postproc_name_hr}.fields = {fields}\n" s += f"{self.postproc_name_hr}.labels = {sampling_labels_hr_str}\n" @@ -909,6 +926,7 @@ def _write_sampling_params_amrex_amrwind(self, fields, chunk_size_vec): s += f"{self.postproc_name_lr}.type = Subvolume\n" s += f"{self.postproc_name_lr}.output_rename = ffboxes\n" s += f"{self.postproc_name_lr}.output_after_final_step = false\n" + s += f"{self.postproc_name_lr}.output_from_restart = true\n" s += f"{self.postproc_name_lr}.output_time_interval = {output_time_interval_lr_str}\n" s += f"{self.postproc_name_lr}.fields = {fields}\n" s += f"{self.postproc_name_lr}.labels = {sampling_labels_lr_str}\n\n" @@ -924,6 +942,7 @@ def _write_sampling_params_amrex_amrwind(self, fields, chunk_size_vec): s += f"{self.postproc_name_hr}.type = Subvolume\n" s += f"{self.postproc_name_hr}.output_rename = ffboxes\n" s += f"{self.postproc_name_hr}.output_after_final_step = false\n" + s += f"{self.postproc_name_hr}.output_from_restart = true\n" s += f"{self.postproc_name_hr}.output_time_interval = {output_time_interval_hr_str}\n" s += f"{self.postproc_name_hr}.fields = {fields}\n" s += f"{self.postproc_name_hr}.labels = {sampling_labels_hr_str}\n" From 9f5c6fd6b270fb3522a4da55e5bb07041c7b660d Mon Sep 17 00:00:00 2001 From: Regis Thedin Date: Mon, 16 Mar 2026 12:12:36 -0600 Subject: [PATCH 4/6] FF: improve HydroData handling. Add printout on `io` before pdb --- .../fastfarm/FASTFarmCaseCreation.py | 44 ++++++++++++------- openfast_toolbox/io/fast_input_file.py | 2 + 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py b/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py index 0f3a575..18743d5 100644 --- a/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py +++ b/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py @@ -320,6 +320,7 @@ def __init__(self, self.hasBD = False self.multi_HD = False self.multi_MD = False + self.hasHydroData = False self.tmax_low = tmax self.condDirList = [] self.caseDirList = [] @@ -724,7 +725,7 @@ def _checkInputs(self): if self.nTurbines != np.shape(self.ADmodel)[1]: raise ValueError(f'The number of turbines in wts ({len(self.wts)}) should match the number of turbines '\ f'in the ADmodel and EDmodel arrays ({np.shape(self.ADmodel)[1]})') - + # Check on seed parameters if self.nSeeds is None: self.nSeeds = 1 @@ -742,9 +743,9 @@ def _checkInputs(self): raise ValueError(f'The array seedValues has been passed, but its length does not corraaespond '\ f'to the number of seeds requested.') + # Check inflow type if not isinstance(self.inflowType, str): raise ValueError(f"Inflow type `inflowType` should be a string. Received {type(self.inflowType)}.") - if self.inflowType.lower() == 'ts': self.inflowType = 'TS' elif self.inflowType.lower() == 'les': @@ -996,14 +997,18 @@ def copyTurbineFilesForEachCase(self, writeFiles=True): if self.hasHD and writeFiles: if not self.multi_HD: self.HydroDynFile.write(os.path.join(currPath, self.HDfilename)) - # Copy HydroDyn Data directory - srcF = self.hydrodatafilepath - dstF = os.path.join(currPath, self.hydroDatapath) - os.makedirs(dstF, exist_ok=True) - for file in os.listdir(srcF): - src = os.path.join(srcF, file) - dst = os.path.join(dstF, file) - shutil.copy2(src, dst) + # Check if potential flow was requested and check if hydrodata path has been give + if self.HydroDynFile['PotMod'] == 0 and self.hasHydroData: + WARN('HydroDyn does not request potential flow (PotMod=0), but HydroData files have been given. Change PotMod to 1 or remove the HydroData files.') + if self.HydroDynFile['PotMod'] == 1 and not self.hasHydroData: + raise FFException('HydroDyn requests potential flow (PotMod=1), but no path for the HydroDyn data files has been given.') + elif self.HydroDynFile['PotMod'] == 2: + WARN('PotMod 2 has been requested in the HydroDyn input file. Not sure what to do with HydroPath.') + if self.hasHydroData: + # Copy HydroDyn Data directory + srcF = self.hydroDatapath + dstF = os.path.join(currPath, os.path.basename(self.hydroDatapath)) + shutil.copytree(srcF, dstF, dirs_exist_ok=True) # BeamDyn if self.hasBD and writeFiles: @@ -1722,7 +1727,7 @@ def checkIfExists(f): self.controllerInputfilepath = value checkIfExists(self.controllerInputfilepath) self.controllerInputfilename = os.path.basename(value) - + elif key == 'coeffTablefilename': if not value.lower().endswith('.csv'): raise ValueError(f'The performance table file should end in "*.csv"') @@ -1732,10 +1737,14 @@ def checkIfExists(f): # --- Directories and files given with full path elif key == 'hydroDatapath': - self.hydrodatafilepath = value - if not os.path.isdir(self.hydrodatafilepath): + if os.path.isabs(value): + self.hydroDatapath = value + else: + self.hydroDatapath = os.path.abspath(value).replace('\\','/') + if not os.path.isdir(self.hydroDatapath): raise ValueError(f'The hydroData directory hydroDatapath should be a directory. Received {value}.') - self.hydroDatapath = os.path.basename(value) + self.hasHydroData = True + elif key == 'libdisconfilepath': ext = os.path.splitext(value)[1].lower() @@ -1783,6 +1792,11 @@ def checkIfExists(f): raise ValueError("SeaState must be used when HydroDyn is used.") if self.hasBD: raise ValueError(f'ElastoDyn+BeamDyn for CompElast is currently not supported. Remove BeamDyn.') + if self.hasHydroData and not self.hasHD: + raise ValueError(f'hydroDatapath is given but HDfilename is not given. Remove hydroDatapath or provide HDfilename.') + if self.hasController and not self.hasSrvD: + raise ValueError (f'libdiscon has been given but no ServoDyn file is provided. Stopping.') + # Set output FAST.Farm filename for convenience self.outputFFfilename = 'FF.fstf' @@ -2808,7 +2822,7 @@ def _FF_setup_LES(self, seedsToKeep=1): # Shared mooring system if self.hasMD and not self.multi_MD: ff_file['Mod_SharedMooring'] = 3 # {0: None, 3=MoorDyn} - ff_file['SharedMoorFile'] = f'"{self.MDfilename}' + ff_file['SharedMoorFile'] = f'"{self.MDfilename}"' # Wake dynamics ff_file['Mod_Wake'] = self.mod_wake diff --git a/openfast_toolbox/io/fast_input_file.py b/openfast_toolbox/io/fast_input_file.py index 9a2e558..c8fd7ec 100644 --- a/openfast_toolbox/io/fast_input_file.py +++ b/openfast_toolbox/io/fast_input_file.py @@ -190,6 +190,7 @@ def insertComment(self, i, comment='', error=False): try: self.data.insert(i, d) except: + print(f'Failed at {self.filename}') import pdb; pdb.set_trace() def insertKeyVal(self, i, key, value, description='', error=False): @@ -846,6 +847,7 @@ def _read(self, IComment=None, verbose=False): try: d['value'], d['tabColumnNames'], d['tabUnits'] = parseFASTNumTable(self.filename,lines[i:i+nTabLines+nHeaders+nOffset],nTabLines,i, nHeaders, tableType=tab_type, nOffset=nOffset, varNumLines=d['tabDimVar']) except: + print(f'Failed at {self.filename}') import pdb; pdb.set_trace() d['descr'] = '' # i += nTabLines+1-nOffset From 94671ca39bee0b790f34691b1c9fa368da00a4ce Mon Sep 17 00:00:00 2001 From: Regis Thedin Date: Wed, 18 Mar 2026 14:31:14 -0600 Subject: [PATCH 5/6] FF: add robust checks on the AMReX data to avoid user problems --- .../fastfarm/FASTFarmCaseCreation.py | 111 +++++++++++++++++- 1 file changed, 107 insertions(+), 4 deletions(-) diff --git a/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py b/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py index 18743d5..0aa2804 100644 --- a/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py +++ b/openfast_toolbox/fastfarm/FASTFarmCaseCreation.py @@ -771,8 +771,11 @@ def _checkInputs(self): else: self.inflowStr = 'LES_AMReX' self.Mod_AmbWind = 4 - for p in self.inflowPath: - if not os.path.isdir(p): + self.les_path_exists = [False]*len(self.inflowPath) + for i, p in enumerate(self.inflowPath): + if os.path.isdir(p): + self.les_path_exists[i] = True + else: WARN(f'The LES path {p} does not exist') # LES is requested, so domain limits must be given if None in (self.dt_high, self.ds_high, self.dt_low, self.ds_low): @@ -2630,6 +2633,8 @@ def FF_setup(self, outlistFF=None, **kwargs): **kwargs: seedsToKeep: int For the LES setup. Often 1, but if you want to run multiple times the same thing, pick a different value + dir_start_index: int + Required for LES_AMReX inflow. Ignored for LES_VTK (warning issued if provided). ''' if outlistFF is None: @@ -2726,10 +2731,108 @@ def check_turbsim_success(self, btsfiles, logfiles): return True + def _check_les_amrex_start(self): + ''' + Check if the AMReX start index provided by the user exists in the directory structure and + add the necessary zero-padding when writing FAST.Farm input files. The expected directory structure for each condition is: + inflowPath[cond]/ + ffboxes_0_000/ (low-res box, index 0) + ffboxes_1_000/ (high-res box for turbine 1, index 0) + ffboxes_2_000/ (high-res box for turbine 2, index 0) + ... + ffboxes_n_000/ (high-res box for turbine n, index 0) + ''' + + # Initialization + self.dir_start_index_str_by_cond = ['']*self.nConditions + + if not all(self.les_path_exists): + WARN(f'No LES path(s) given. Skipping checking of AMReX start index. Setting AMReX start index exactly like ' + f'provided: "{self.dir_start_index_requested}".') + for cond in range(self.nConditions): + self.dir_start_index_str_by_cond[cond] = str(self.dir_start_index_requested) + return + + # Normalize user-provided start index to integer (allow zero-padded strings) + if isinstance(self.dir_start_index_requested, int): + dir_start_index_int = self.dir_start_index_requested + elif isinstance(self.dir_start_index_requested, str): + # Let's also take a string just in case + dir_start_index_str = self.dir_start_index_requested.strip() + if dir_start_index_str == '' or not dir_start_index_str.isdigit(): + raise FFException(f'For inflowType="LES_AMReX", `dir_start_index` must be an integer. ' + f'Provided value: {self.dir_start_index_requested}.') + dir_start_index_int = int(dir_start_index_str) + else: + raise FFException(f'For inflowType="LES_AMReX", `dir_start_index` must be an integer. ' + f'Provided type: {type(self.dir_start_index_requested)} with value {self.dir_start_index_requested}.') + if dir_start_index_int < 0: + raise FFException(f'For inflowType="LES_AMReX", `dir_start_index` must be non-negative. ' + f'Provided value: {self.dir_start_index_requested}.') + self.dir_start_index_int_requested = dir_start_index_int + + # Check that requested ffboxes index exists for all boxes in all conditions + #self.dir_start_index_str_by_cond = ['']*self.nConditions + for cond in range(self.nConditions): + cond_path = self.inflowPath[cond] + + # Parse __