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/AMRWindSimulation.py b/openfast_toolbox/fastfarm/AMRWindSimulation.py index 15c7d99..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 @@ -84,8 +85,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 @@ -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): @@ -131,7 +146,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 +156,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 +324,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 +663,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 +673,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 +696,71 @@ 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 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': + # 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 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 + 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 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: + 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_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" # Write out low resolution sampling plane info zoffsets_lr_str = " ".join(str(item) for item in self.zoffsets_lr) @@ -706,10 +775,12 @@ 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_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" # Write out high resolution sampling plane info for turbkey in self.hr_domains: @@ -742,23 +813,169 @@ 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_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" + + 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_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" + + 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..9109284 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. @@ -303,7 +306,7 @@ def __init__(self, self.attempt = 1 self.skipchecks = skipchecks self.flat = flat - # Set aux variable + # Initialize aux variable self.templateFilesCreatedBool = False self.TSlowBoxFilesCreatedBool = False self.TShighBoxFilesCreatedBool = False @@ -317,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 = [] @@ -341,13 +345,11 @@ def __init__(self, self._setRotorParameters() if self.verbose>0: print(f'Setting rotor parameters... Done.') - # TODO: Creating Cases and Conditions should have its own function interface so the user can call if self.verbose>0: print(f'Creating auxiliary arrays for all conditions and cases...', end='\r') self.createAuxArrays() if self.verbose>0: print(f'Creating auxiliary arrays for all conditions and cases... Done.') - if self.path is not None: # TODO this should only be done when user ask for input file creation if self.verbose>0: print(f'Creating directory structure and copying files...', end='\r') @@ -721,7 +723,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 @@ -736,26 +738,51 @@ 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.') + + # 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': + 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' + # Format inflowPath + if isinstance(self.inflowPath,str): self.inflowPath = [self.inflowPath]*len(self.vhub) + # Check LES parameters if self.inflowType == 'TS': self.inflowStr = 'TurbSim' self.Mod_AmbWind = 3 - elif self.inflowType == 'LES': - if isinstance(self.inflowPath,str): self.inflowPath = [self.inflowPath]*len(self.vhub) - self.inflowStr = 'LES' - self.Mod_AmbWind = 1 - for p in self.inflowPath: - if not os.path.isdir(p): + elif self.inflowType in ['LES_VTK', 'LES_AMReX']: + if self.inflowType == 'LES_VTK': + self.inflowStr = 'LES_VTK' + self.Mod_AmbWind = 1 + else: + self.inflowStr = 'LES_AMReX' + self.Mod_AmbWind = 4 + 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): 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]: @@ -973,14 +1000,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: @@ -1699,7 +1730,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"') @@ -1709,10 +1740,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() @@ -1760,6 +1795,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' @@ -2011,7 +2051,7 @@ def _isclose(a, b, tol=1): elif _isclose(self.D, 178): # DTU 10MW W turbine - print(f'CHANGE THE _setRotorParameters of the DTU 10MW turbine') + WARN(f'CHANGE THE _setRotorParameters of the DTU 10MW turbine') self.bins = xr.Dataset({'WaveHs': (['wspd'], [ 1.429, 1.429]), # 1.429 comes from Matt's hydrodyn input file 'WaveTp': (['wspd'], [ 7.073, 7.073]), # 7.073 comes from Matt's hydrodyn input file 'RotSpeed': (['wspd'], [ 4.0, 4.0]), # 4 rpm comes from Matt's ED input file @@ -2022,7 +2062,7 @@ def _isclose(a, b, tol=1): elif _isclose(self.D, 82): # Vestas V82, 1.5MW, 82 m diameter - print(f'CHANGE THE _setRotorParameters of the V82 1.5MW turbine') + WARN(f'CHANGE THE _setRotorParameters of the V82 1.5MW turbine') self.bins = xr.Dataset({'WaveHs': (['wspd'], [ 1.429, 1.429]), # 1.429 comes from Matt's hydrodyn input file 'WaveTp': (['wspd'], [ 7.073, 7.073]), # 7.073 comes from Matt's hydrodyn input file 'RotSpeed': (['wspd'], [ 4.0, 4.0]), # 4 rpm comes from Matt's ED input file @@ -2046,7 +2086,9 @@ def _isclose(a, b, tol=1): def TS_low_setup(self, writeFiles=True, runOnce=False): - INFO('Preparing TurbSim low resolution input files.') + if self.inflowType == 'TS': + # This function is called once for domain limits even when LES, so only printing the info when relevant + INFO('Preparing TurbSim low resolution input files.') boxType='lowres' lowFilesName = [] @@ -2262,11 +2304,10 @@ def TS_low_createSymlinks(self): def getDomainParameters(self): - INFO('Computing low and high res extent according to TurbSim capabilities') + INFO('Computing low- and high-res extents') # If the low box setup hasn't been called (e.g. LES run), do it once to get domain extents if not self.TSlowBoxFilesCreatedBool: - if self.verbose>1: print(' Running a TurbSim setup once to get domain extents') self.TS_low_setup(writeFiles=False, runOnce=True) # Figure out how many (and which) high boxes actually need to be executed. Remember that SED/ADsk models @@ -2593,6 +2634,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: @@ -2647,7 +2690,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 +2705,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': @@ -2689,48 +2732,151 @@ 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 __