diff --git a/floris/simulation/farm.py b/floris/simulation/farm.py index 8bab263f1..1524e75e5 100644 --- a/floris/simulation/farm.py +++ b/floris/simulation/farm.py @@ -184,6 +184,10 @@ def __attrs_post_init__(self) -> None: if len(_turbine_types) == 1: _turbine_types *= self.n_turbines + # Check that turbine definitions contain any v3 keys + for t in _turbine_types: + check_turbine_definition_for_v3_keys(turbine_definition_cache[t]) + # Map each turbine definition to its index in this list self.turbine_definitions = [ copy.deepcopy(turbine_definition_cache[t]) for t in _turbine_types @@ -404,3 +408,38 @@ def coordinates(self): @property def n_turbines(self): return len(self.layout_x) + +def check_turbine_definition_for_v3_keys(turbine_definition: dict): + """Check that the turbine definition does not contain any v3 keys.""" + v3_deprecation_msg = ( + "Consider using the convert_turbine_v3_to_v4.py utility in floris/tools " + + "to convert from a FLORIS v3 turbine definition to FLORIS v4. " + + "See https://nrel.github.io/floris/upgrade_guides/v3_to_v4.html for more information." + ) + if "generator_efficiency" in turbine_definition: + raise ValueError( + "generator_efficiency is no longer supported as power is specified in absolute terms " + + "in FLORIS v4. " + + v3_deprecation_msg + ) + + v3_renamed_keys = ["pP", "pT", "ref_density_cp_ct", "ref_tilt_cp_ct"] + if any(k in turbine_definition for k in v3_renamed_keys): + v3_list_keys = ", ".join(map(str,v3_renamed_keys[:-1]))+", and "+v3_renamed_keys[-1] + v4_versions = ( + "cosine_loss_exponent_yaw, cosine_loss_exponent_tilt, ref_air_density, and ref_tilt" + ) + raise ValueError( + v3_list_keys + + " have been renamed to " + + v4_versions + + ", respectively, and placed under the power_thrust_table field in FLORIS v4. " + + v3_deprecation_msg + ) + + if "thrust" in turbine_definition["power_thrust_table"]: + raise ValueError( + "thrust has been renamed thrust_coefficient in FLORIS v4 (and power is now specified " + "in absolute terms with units kW, rather than as a coefficient). " + + v3_deprecation_msg + ) diff --git a/floris/simulation/floris.py b/floris/simulation/floris.py index 2f04b4a13..a71377a60 100644 --- a/floris/simulation/floris.py +++ b/floris/simulation/floris.py @@ -347,6 +347,7 @@ def from_file(cls, input_file_path: str | Path) -> Floris: Floris: The class object instance. """ input_dict = load_yaml(Path(input_file_path).resolve()) + check_input_file_for_v3_keys(input_dict) return Floris.from_dict(input_dict) def to_file(self, output_file_path: str) -> None: @@ -362,3 +363,36 @@ def to_file(self, output_file_path: str) -> None: sort_keys=False, default_flow_style=False ) + +def check_input_file_for_v3_keys(input_dict) -> None: + """ + Checks if any FLORIS v3 keys are present in the input file and raises special errors if + the extra keys belong to a v3 definition of the input_dct. + and raises special errors if the extra arguments belong to a v3 definition of the class. + + Args: + input_dict (dict): The input dictionary to be checked for v3 keys. + """ + v3_deprecation_msg = ( + "Consider using the convert_floris_input_v3_to_v4.py utility in floris/tools " + + "to convert from a FLORIS v3 input file to FLORIS v4. " + "See https://nrel.github.io/floris/upgrade_guides/v3_to_v4.html for more information." + ) + if "turbulence_intensity" in input_dict["flow_field"]: + raise AttributeError( + "turbulence_intensity has been updated to turbulence_intensities in FLORIS v4. " + + v3_deprecation_msg + ) + elif not hasattr(input_dict["flow_field"]["turbulence_intensities"], "__len__"): + raise AttributeError( + "turbulence_intensities must be a list of floats in FLORIS v4. " + + v3_deprecation_msg + ) + + if input_dict["wake"]["model_strings"]["velocity_model"] == "multidim_cp_ct": + raise AttributeError( + "Dedicated 'multidim_cp_ct' velocity model has been removed in FLORIS v4 in favor of " + + "supporting all available wake models. To recover previous operation, set " + + "velocity_model to gauss. " + + v3_deprecation_msg + ) diff --git a/floris/tools/convert_floris_input_v3_to_v4.py b/floris/tools/convert_floris_input_v3_to_v4.py new file mode 100644 index 000000000..36415e1d2 --- /dev/null +++ b/floris/tools/convert_floris_input_v3_to_v4.py @@ -0,0 +1,70 @@ + +import sys +from pathlib import Path + +import yaml + +from floris.utilities import load_yaml + + +""" +This script is intended to be called with an argument and converts a floris input +yaml file specified for FLORIS v3 to one specified for FLORIS v4. + +Usage: +python convert_floris_input_v3_to_v4.py .yaml + +The resulting floris input file is placed in the same directory as the original yaml, +and is appended _v4. +""" + + +if __name__ == "__main__": + if len(sys.argv) != 2: + raise Exception( + "Usage: python convert_floris_input_v3_to_v4.py .yaml" + ) + + input_yaml = sys.argv[1] + + # Handling the path and new filename + input_path = Path(input_yaml) + split_input = input_path.parts + [filename_v3, extension] = split_input[-1].split(".") + filename_v4 = filename_v3 + "_v4" + split_output = list(split_input[:-1]) + [filename_v4+"."+extension] + output_path = Path(*split_output) + + # Load existing v3 model + v3_floris_input_dict = load_yaml(input_yaml) + v4_floris_input_dict = v3_floris_input_dict.copy() + + # Change turbulence_intensity field to turbulence_intensities as list + if "turbulence_intensities" in v3_floris_input_dict["flow_field"]: + if "turbulence_intensity" in v3_floris_input_dict["flow_field"]: + del v4_floris_input_dict["flow_field"]["turbulence_intensity"] + elif "turbulence_intensity" in v3_floris_input_dict["flow_field"]: + v4_floris_input_dict["flow_field"]["turbulence_intensities"] = ( + [v3_floris_input_dict["flow_field"]["turbulence_intensity"]] + ) + del v4_floris_input_dict["flow_field"]["turbulence_intensity"] + + # Change multidim_cp_ct velocity model to gauss + if v3_floris_input_dict["wake"]["model_strings"]["velocity_model"] == "multidim_cp_ct": + print( + "multidim_cp_ct velocity model specified. Changing to gauss, " + + "but note that other velocity models are also compatible with multidimensional " + + "turbines in FLORIS v4. " + + "You will also need to convert your multidimensional turbine yaml files and their " + + "corresponding power/thrust csv files to be compatible with FLORIS v4 and to reflect " + + " the absolute power curve, rather than the power coefficient curve." + ) + v4_floris_input_dict["wake"]["model_strings"]["velocity_model"] = "gauss" + + yaml.dump( + v4_floris_input_dict, + open(output_path, "w"), + sort_keys=False + ) + + print(output_path, "created.") diff --git a/floris/tools/convert_turbine_v3_to_v4.py b/floris/tools/convert_turbine_v3_to_v4.py index 7e5b9e123..5cf55f3d5 100644 --- a/floris/tools/convert_turbine_v3_to_v4.py +++ b/floris/tools/convert_turbine_v3_to_v4.py @@ -11,7 +11,7 @@ yaml file specified for FLORIS v3 to one specified for FLORIS v4. Usage: -python convert_turbine_yaml_v3_to_v4.py .yaml +python convert_turbine_v3_to_v4.py .yaml The resulting turbine is placed in the same directory as the original yaml, and is appended _v4. @@ -20,7 +20,7 @@ if __name__ == "__main__": if len(sys.argv) != 2: - raise Exception("Usage: python convert_turbine_yaml_v3_to_v4.py .yaml") + raise Exception("Usage: python convert_turbine_v3_to_v4.py .yaml") input_yaml = sys.argv[1] @@ -37,6 +37,13 @@ # Split into components expected by build_turbine_dict power_thrust_table = v3_turbine_dict["power_thrust_table"] + if "power_thrust_data_file" in power_thrust_table: + raise ValueError( + "Cannot convert multidimensional turbine model. Please manually update your " + + "turbine yaml. Note that the power_thrust_data_file csv needs to be updated to " + + "reflect the absolute power curve, rather than the power coefficient curve," + + "and that `thrust` has been replaced by `thrust_coefficient`." + ) power_thrust_table["power_coefficient"] = power_thrust_table["power"] power_thrust_table["thrust_coefficient"] = power_thrust_table["thrust"] power_thrust_table.pop("power") diff --git a/tests/data/nrel_5MW_custom.yaml b/tests/data/nrel_5MW_custom.yaml index 9e3ef6735..b7d3d8e5b 100644 --- a/tests/data/nrel_5MW_custom.yaml +++ b/tests/data/nrel_5MW_custom.yaml @@ -1,166 +1,174 @@ turbine_type: 'nrel_5MW_custom' -generator_efficiency: 1.0 hub_height: 90.0 -pP: 1.88 -pT: 1.88 rotor_diameter: 126.0 TSR: 8.0 -ref_density_cp_ct: 1.225 -ref_tilt_cp_ct: 5.0 power_thrust_table: + cosine_loss_exponent_yaw: 1.88 + cosine_loss_exponent_tilt: 1.88 + ref_air_density: 1.225 + ref_tilt: 5.0 power: - 0.0 - - 0.000000 - - 0.000000 - - 0.178085 - - 0.289075 - - 0.349022 - - 0.384728 - - 0.406059 - - 0.420228 - - 0.428823 - - 0.433873 - - 0.436223 - - 0.436845 - - 0.436575 - - 0.436511 - - 0.436561 - - 0.436517 - - 0.435903 - - 0.434673 - - 0.433230 - - 0.430466 - - 0.378869 - - 0.335199 - - 0.297991 - - 0.266092 - - 0.238588 - - 0.214748 - - 0.193981 - - 0.175808 - - 0.159835 - - 0.145741 - - 0.133256 - - 0.122157 - - 0.112257 - - 0.103399 - - 0.095449 - - 0.088294 - - 0.081836 - - 0.075993 - - 0.070692 - - 0.065875 - - 0.061484 - - 0.057476 - - 0.053809 - - 0.050447 - - 0.047358 - - 0.044518 - - 0.041900 - - 0.039483 - 0.0 + - 40.518011517569214 + - 177.67162506419703 + - 403.900880943964 + - 737.5889584824021 + - 1187.1774030611875 + - 1239.245945375778 + - 1292.5184293723503 + - 1347.3213147477102 + - 1403.2573725578948 + - 1460.7011898730707 + - 1519.6419125979983 + - 1580.174365096404 + - 1642.1103166918167 + - 1705.758292831 + - 1771.1659528893977 + - 2518.553107505315 + - 3448.381605840943 + - 3552.140809000129 + - 3657.9545431794127 + - 3765.121299313842 + - 3873.928844315059 + - 3984.4800226955504 + - 4096.582833096852 + - 4210.721306623712 + - 4326.154305853405 + - 4443.395565353604 + - 4562.497934188341 + - 4683.419890251577 + - 4806.164748311019 + - 4929.931918769215 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 + - 5000.00 - 0.0 - thrust: - 0.0 + thrust_coefficient: - 0.0 - 0.0 - - 0.99 - - 0.99 - - 0.97373036 - - 0.92826162 - - 0.89210543 - - 0.86100905 - - 0.835423 - - 0.81237673 - - 0.79225789 - - 0.77584769 - - 0.7629228 - - 0.76156073 - - 0.76261984 - - 0.76169723 - - 0.75232027 - - 0.74026851 - - 0.72987175 - - 0.70701647 - - 0.54054532 - - 0.45509459 - - 0.39343381 - - 0.34250785 - - 0.30487242 - - 0.27164979 - - 0.24361964 - - 0.21973831 - - 0.19918151 - - 0.18131868 - - 0.16537679 - - 0.15103727 - - 0.13998636 - - 0.1289037 - - 0.11970413 - - 0.11087113 - - 0.10339901 - - 0.09617888 - - 0.09009926 - - 0.08395078 - - 0.0791188 - - 0.07448356 - - 0.07050731 - - 0.06684119 - - 0.06345518 - - 0.06032267 - - 0.05741999 - - 0.05472609 + - 1.132034888 + - 0.999470963 + - 0.917697381 + - 0.860849503 + - 0.815371198 + - 0.811614904 + - 0.807939328 + - 0.80443352 + - 0.800993851 + - 0.79768116 + - 0.794529244 + - 0.791495834 + - 0.788560434 + - 0.787217182 + - 0.787127977 + - 0.785839257 + - 0.783812219 + - 0.783568108 + - 0.783328285 + - 0.781194418 + - 0.777292539 + - 0.773464375 + - 0.769690236 + - 0.766001924 + - 0.762348072 + - 0.758760824 + - 0.755242872 + - 0.751792927 + - 0.748434131 + - 0.745113997 + - 0.717806682 + - 0.672204789 + - 0.63831272 + - 0.610176496 + - 0.585456847 + - 0.563222111 + - 0.542912273 + - 0.399312061 + - 0.310517829 + - 0.248633226 + - 0.203543725 + - 0.169616419 + - 0.143478955 + - 0.122938861 + - 0.106515296 + - 0.093026095 + - 0.081648606 + - 0.072197368 + - 0.064388275 + - 0.057782745 - 0.0 - 0.0 wind_speed: - 0.0 - - 2.0 - - 2.5 + - 2.9 - 3.0 - - 3.5 - 4.0 - - 4.5 - 5.0 - - 5.5 - 6.0 - - 6.5 - 7.0 + - 7.1 + - 7.2 + - 7.3 + - 7.4 - 7.5 + - 7.6 + - 7.7 + - 7.8 + - 7.9 - 8.0 - - 8.5 - 9.0 - - 9.5 - 10.0 + - 10.1 + - 10.2 + - 10.3 + - 10.4 - 10.5 + - 10.6 + - 10.7 + - 10.8 + - 10.9 - 11.0 + - 11.1 + - 11.2 + - 11.3 + - 11.4 - 11.5 + - 11.6 + - 11.7 + - 11.8 + - 11.9 - 12.0 - - 12.5 - 13.0 - - 13.5 - 14.0 - - 14.5 - 15.0 - - 15.5 - 16.0 - - 16.5 - 17.0 - - 17.5 - 18.0 - - 18.5 - 19.0 - - 19.5 - 20.0 - - 20.5 - 21.0 - - 21.5 - 22.0 - - 22.5 - 23.0 - - 23.5 - 24.0 - - 24.5 - 25.0 - - 25.01 - - 25.02 + - 25.1 - 50.0