floris 4.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- floris/__init__.py +24 -0
- floris/convert_floris_input_v3_to_v4.py +93 -0
- floris/convert_turbine_v3_to_v4.py +86 -0
- floris/core/__init__.py +61 -0
- floris/core/base.py +65 -0
- floris/core/core.py +411 -0
- floris/core/farm.py +523 -0
- floris/core/flow_field.py +347 -0
- floris/core/grid.py +671 -0
- floris/core/rotor_velocity.py +241 -0
- floris/core/solver.py +1528 -0
- floris/core/turbine/__init__.py +9 -0
- floris/core/turbine/operation_models.py +730 -0
- floris/core/turbine/turbine.py +673 -0
- floris/core/wake.py +164 -0
- floris/core/wake_combination/__init__.py +4 -0
- floris/core/wake_combination/fls.py +32 -0
- floris/core/wake_combination/max.py +38 -0
- floris/core/wake_combination/sosfs.py +31 -0
- floris/core/wake_deflection/__init__.py +5 -0
- floris/core/wake_deflection/empirical_gauss.py +141 -0
- floris/core/wake_deflection/gauss.py +503 -0
- floris/core/wake_deflection/jimenez.py +130 -0
- floris/core/wake_deflection/none.py +54 -0
- floris/core/wake_turbulence/__init__.py +4 -0
- floris/core/wake_turbulence/crespo_hernandez.py +87 -0
- floris/core/wake_turbulence/none.py +32 -0
- floris/core/wake_turbulence/wake_induced_mixing.py +76 -0
- floris/core/wake_velocity/__init__.py +8 -0
- floris/core/wake_velocity/cumulative_gauss_curl.py +239 -0
- floris/core/wake_velocity/empirical_gauss.py +305 -0
- floris/core/wake_velocity/gauss.py +237 -0
- floris/core/wake_velocity/jensen.py +127 -0
- floris/core/wake_velocity/none.py +50 -0
- floris/core/wake_velocity/turbopark.py +181 -0
- floris/core/wake_velocity/turbopark_lookup_table.mat +0 -0
- floris/core/wake_velocity/turboparkgauss.py +142 -0
- floris/cut_plane.py +485 -0
- floris/floris_model.py +1812 -0
- floris/flow_visualization.py +768 -0
- floris/heterogeneous_map.py +533 -0
- floris/layout_visualization.py +591 -0
- floris/logging_manager.py +144 -0
- floris/optimization/__init__.py +5 -0
- floris/optimization/layout_optimization/__init__.py +0 -0
- floris/optimization/layout_optimization/layout_optimization_base.py +289 -0
- floris/optimization/layout_optimization/layout_optimization_boundary_grid.py +648 -0
- floris/optimization/layout_optimization/layout_optimization_gridded.py +212 -0
- floris/optimization/layout_optimization/layout_optimization_pyoptsparse.py +231 -0
- floris/optimization/layout_optimization/layout_optimization_pyoptsparse_spread.py +212 -0
- floris/optimization/layout_optimization/layout_optimization_random_search.py +760 -0
- floris/optimization/layout_optimization/layout_optimization_scipy.py +263 -0
- floris/optimization/other/__init__.py +1 -0
- floris/optimization/other/boundary_grid.py +448 -0
- floris/optimization/yaw_optimization/__init__.py +0 -0
- floris/optimization/yaw_optimization/yaw_optimization_base.py +627 -0
- floris/optimization/yaw_optimization/yaw_optimization_tools.py +130 -0
- floris/optimization/yaw_optimization/yaw_optimizer_geometric.py +255 -0
- floris/optimization/yaw_optimization/yaw_optimizer_scipy.py +148 -0
- floris/optimization/yaw_optimization/yaw_optimizer_sr.py +314 -0
- floris/par_floris_model.py +366 -0
- floris/parallel_floris_model.py +690 -0
- floris/turbine_library/__init__.py +5 -0
- floris/turbine_library/iea_10MW.yaml +93 -0
- floris/turbine_library/iea_15MW.yaml +185 -0
- floris/turbine_library/iea_15MW_floating_multi_dim_cp_ct.yaml +29 -0
- floris/turbine_library/iea_15MW_multi_dim_cp_ct.yaml +11 -0
- floris/turbine_library/nrel_5MW.yaml +240 -0
- floris/turbine_library/turbine_previewer.py +839 -0
- floris/turbine_library/turbine_utilities.py +200 -0
- floris/type_dec.py +274 -0
- floris/uncertain_floris_model.py +1214 -0
- floris/utilities.py +441 -0
- floris/wind_data.py +3380 -0
- floris-4.2.1.dist-info/LICENSE.txt +26 -0
- floris-4.2.1.dist-info/METADATA +243 -0
- floris-4.2.1.dist-info/RECORD +79 -0
- floris-4.2.1.dist-info/WHEEL +5 -0
- floris-4.2.1.dist-info/top_level.txt +1 -0
floris/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
from importlib.metadata import version
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
__version__ = version("floris")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from .floris_model import FlorisModel
|
|
10
|
+
from .flow_visualization import (
|
|
11
|
+
plot_rotor_values,
|
|
12
|
+
visualize_cut_plane,
|
|
13
|
+
visualize_quiver,
|
|
14
|
+
)
|
|
15
|
+
from .heterogeneous_map import HeterogeneousMap
|
|
16
|
+
from .par_floris_model import ParFlorisModel
|
|
17
|
+
from .parallel_floris_model import ParallelFlorisModel
|
|
18
|
+
from .uncertain_floris_model import ApproxFlorisModel, UncertainFlorisModel
|
|
19
|
+
from .wind_data import (
|
|
20
|
+
TimeSeries,
|
|
21
|
+
WindRose,
|
|
22
|
+
WindRoseWRG,
|
|
23
|
+
WindTIRose,
|
|
24
|
+
)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
This script is intended to be called with an argument and converts a floris input
|
|
9
|
+
yaml file specified for FLORIS v3 to one specified for FLORIS v4.
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
python convert_floris_input_v3_to_v4.py <path/to/floris_input>.yaml
|
|
13
|
+
|
|
14
|
+
The resulting floris input file is placed in the same directory as the original yaml,
|
|
15
|
+
and is appended _v4.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def ignore_include(loader, node):
|
|
20
|
+
# Parrot back the !include tag
|
|
21
|
+
return node.tag + " " + node.value
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
if len(sys.argv) != 2:
|
|
26
|
+
raise Exception(
|
|
27
|
+
"Usage: python convert_floris_input_v3_to_v4.py <path/to/floris_input>.yaml"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Set the yaml loader to ignore the !include tag
|
|
31
|
+
yaml.SafeLoader.add_constructor("!include", ignore_include)
|
|
32
|
+
|
|
33
|
+
input_yaml = sys.argv[1]
|
|
34
|
+
|
|
35
|
+
# Handling the path and new filename
|
|
36
|
+
input_path = Path(input_yaml)
|
|
37
|
+
split_input = input_path.parts
|
|
38
|
+
[filename_v3, extension] = split_input[-1].split(".")
|
|
39
|
+
filename_v4 = filename_v3 + "_v4"
|
|
40
|
+
split_output = list(split_input[:-1]) + [filename_v4 + "." + extension]
|
|
41
|
+
output_path = Path(*split_output)
|
|
42
|
+
|
|
43
|
+
# Load existing v3 model
|
|
44
|
+
with open(input_yaml, "r") as file:
|
|
45
|
+
v3_floris_input_dict = yaml.safe_load(file)
|
|
46
|
+
v4_floris_input_dict = v3_floris_input_dict.copy()
|
|
47
|
+
|
|
48
|
+
# Change turbulence_intensity field to turbulence_intensities as list
|
|
49
|
+
if "turbulence_intensities" in v3_floris_input_dict["flow_field"]:
|
|
50
|
+
if "turbulence_intensity" in v3_floris_input_dict["flow_field"]:
|
|
51
|
+
del v4_floris_input_dict["flow_field"]["turbulence_intensity"]
|
|
52
|
+
elif "turbulence_intensity" in v3_floris_input_dict["flow_field"]:
|
|
53
|
+
v4_floris_input_dict["flow_field"]["turbulence_intensities"] = [
|
|
54
|
+
v3_floris_input_dict["flow_field"]["turbulence_intensity"]
|
|
55
|
+
]
|
|
56
|
+
del v4_floris_input_dict["flow_field"]["turbulence_intensity"]
|
|
57
|
+
|
|
58
|
+
# Change multidim_cp_ct velocity model to gauss
|
|
59
|
+
if v3_floris_input_dict["wake"]["model_strings"]["velocity_model"] == "multidim_cp_ct":
|
|
60
|
+
print(
|
|
61
|
+
"multidim_cp_ct velocity model specified. Changing to gauss, "
|
|
62
|
+
+ "but note that other velocity models are also compatible with multidimensional "
|
|
63
|
+
+ "turbines in FLORIS v4. "
|
|
64
|
+
+ "You will also need to convert your multidimensional turbine yaml files and their "
|
|
65
|
+
+ "corresponding power/thrust csv files to be compatible with FLORIS v4 and to reflect "
|
|
66
|
+
+ " the absolute power curve, rather than the power coefficient curve."
|
|
67
|
+
)
|
|
68
|
+
v4_floris_input_dict["wake"]["model_strings"]["velocity_model"] = "gauss"
|
|
69
|
+
|
|
70
|
+
# Add enable_active_wake_mixing field
|
|
71
|
+
v4_floris_input_dict["wake"]["enable_active_wake_mixing"] = False
|
|
72
|
+
|
|
73
|
+
# Write the new v4 model to a new file, note that the in order to ignore the !include tag
|
|
74
|
+
# it is wrapped in single quotes by the ignore include/load/dump sequence and these will
|
|
75
|
+
# need to be removed in the next block of code
|
|
76
|
+
yaml.dump(v4_floris_input_dict, open(output_path, "w"), sort_keys=False)
|
|
77
|
+
|
|
78
|
+
# Open the output file and loop through line by line
|
|
79
|
+
# if a line contains the substring !include, then strip all
|
|
80
|
+
# occurrences of ' from the line to remove the extra single quotes
|
|
81
|
+
# added by the ignore include/load/dump sequence
|
|
82
|
+
temp_output_path = output_path.with_name("temp.yaml")
|
|
83
|
+
with open(temp_output_path, "w") as file:
|
|
84
|
+
with open(output_path, "r") as f:
|
|
85
|
+
for line in f:
|
|
86
|
+
if "!include" in line:
|
|
87
|
+
line = line.replace("'", "")
|
|
88
|
+
file.write(line)
|
|
89
|
+
|
|
90
|
+
# Move the temp file to the output file
|
|
91
|
+
temp_output_path.replace(output_path)
|
|
92
|
+
|
|
93
|
+
print(output_path, "created.")
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from floris.turbine_library import build_cosine_loss_turbine_dict, check_smooth_power_curve
|
|
6
|
+
from floris.utilities import load_yaml
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
This script is intended to be called with an argument and converts a turbine
|
|
11
|
+
yaml file specified for FLORIS v3 to one specified for FLORIS v4.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
python convert_turbine_v3_to_v4.py <path/to/turbine>.yaml
|
|
15
|
+
|
|
16
|
+
The resulting turbine is placed in the same directory as the original yaml,
|
|
17
|
+
and is appended _v4.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
if len(sys.argv) != 2:
|
|
23
|
+
raise Exception("Usage: python convert_turbine_v3_to_v4.py <path/to/turbine>.yaml")
|
|
24
|
+
|
|
25
|
+
input_yaml = sys.argv[1]
|
|
26
|
+
|
|
27
|
+
# Handling the path and new filename
|
|
28
|
+
input_path = Path(input_yaml)
|
|
29
|
+
split_input = input_path.parts
|
|
30
|
+
[filename_v3, extension] = split_input[-1].split(".")
|
|
31
|
+
filename_v4 = filename_v3 + "_v4"
|
|
32
|
+
split_output = list(split_input[:-1]) + [filename_v4+"."+extension]
|
|
33
|
+
output_path = Path(*split_output)
|
|
34
|
+
|
|
35
|
+
# Load existing v3 model
|
|
36
|
+
v3_turbine_dict = load_yaml(input_yaml)
|
|
37
|
+
|
|
38
|
+
# Split into components expected by build_turbine_dict
|
|
39
|
+
power_thrust_table = v3_turbine_dict["power_thrust_table"]
|
|
40
|
+
if "power_thrust_data_file" in power_thrust_table:
|
|
41
|
+
raise ValueError(
|
|
42
|
+
"Cannot convert multidimensional turbine model. Please manually update your "
|
|
43
|
+
+ "turbine yaml. Note that the power_thrust_data_file csv needs to be updated to "
|
|
44
|
+
+ "reflect the absolute power curve, rather than the power coefficient curve,"
|
|
45
|
+
+ "and that `thrust` has been replaced by `thrust_coefficient`."
|
|
46
|
+
)
|
|
47
|
+
power_thrust_table["power_coefficient"] = power_thrust_table["power"]
|
|
48
|
+
power_thrust_table["thrust_coefficient"] = power_thrust_table["thrust"]
|
|
49
|
+
power_thrust_table.pop("power")
|
|
50
|
+
power_thrust_table.pop("thrust")
|
|
51
|
+
|
|
52
|
+
valid_properties = [
|
|
53
|
+
"generator_efficiency",
|
|
54
|
+
"hub_height",
|
|
55
|
+
"cosine_loss_exponent_yaw",
|
|
56
|
+
"cosine_loss_exponent_tilt",
|
|
57
|
+
"rotor_diameter",
|
|
58
|
+
"TSR",
|
|
59
|
+
"ref_air_density",
|
|
60
|
+
"ref_tilt"
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
turbine_properties = {k:v for k,v in v3_turbine_dict.items() if k in valid_properties}
|
|
64
|
+
turbine_properties["ref_air_density"] = v3_turbine_dict["ref_density_cp_ct"]
|
|
65
|
+
turbine_properties["cosine_loss_exponent_yaw"] = v3_turbine_dict["pP"]
|
|
66
|
+
if "ref_tilt_cp_ct" in v3_turbine_dict:
|
|
67
|
+
turbine_properties["ref_tilt"] = v3_turbine_dict["ref_tilt_cp_ct"]
|
|
68
|
+
if "pT" in v3_turbine_dict:
|
|
69
|
+
turbine_properties["cosine_loss_exponent_tilt"] = v3_turbine_dict["pT"]
|
|
70
|
+
|
|
71
|
+
# Convert to v4 and print new yaml
|
|
72
|
+
v4_turbine_dict = build_cosine_loss_turbine_dict(
|
|
73
|
+
power_thrust_table,
|
|
74
|
+
v3_turbine_dict["turbine_type"],
|
|
75
|
+
output_path,
|
|
76
|
+
**turbine_properties
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if not check_smooth_power_curve(
|
|
80
|
+
v4_turbine_dict["power_thrust_table"]["power"],
|
|
81
|
+
tolerance=0.001
|
|
82
|
+
):
|
|
83
|
+
print(
|
|
84
|
+
"Non-smoothness detected in output power curve. ",
|
|
85
|
+
"Check above-rated power in generated v4 yaml file."
|
|
86
|
+
)
|
floris/core/__init__.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
|
|
2
|
+
"""
|
|
3
|
+
The :py:obj:`floris` package contains :py:obj:`floris.utilities` module
|
|
4
|
+
and the modules that make up the FLORIS software. The floris simulation
|
|
5
|
+
modules are used to complete a wake simulation for a given wind farm
|
|
6
|
+
and turbine configuration.
|
|
7
|
+
|
|
8
|
+
All modules and package can be imported with
|
|
9
|
+
|
|
10
|
+
>>> import floris
|
|
11
|
+
|
|
12
|
+
The ``__init__.py`` file enables the import of all modules in this
|
|
13
|
+
package so any additional modules should be included there.
|
|
14
|
+
|
|
15
|
+
isort:skip_file
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# Provide full-path imports here for all modules
|
|
19
|
+
# that should be included in the simulation package.
|
|
20
|
+
# Since some of these depend on each other, the order
|
|
21
|
+
# that they are listed here does matter.
|
|
22
|
+
|
|
23
|
+
import floris.logging_manager
|
|
24
|
+
|
|
25
|
+
from .base import BaseClass, BaseModel, State
|
|
26
|
+
from .turbine.turbine import (
|
|
27
|
+
axial_induction,
|
|
28
|
+
power,
|
|
29
|
+
thrust_coefficient,
|
|
30
|
+
Turbine
|
|
31
|
+
)
|
|
32
|
+
from .rotor_velocity import (
|
|
33
|
+
average_velocity,
|
|
34
|
+
rotor_effective_velocity,
|
|
35
|
+
compute_tilt_angles_for_floating_turbines,
|
|
36
|
+
)
|
|
37
|
+
from .farm import Farm
|
|
38
|
+
from .grid import (
|
|
39
|
+
FlowFieldGrid,
|
|
40
|
+
FlowFieldPlanarGrid,
|
|
41
|
+
Grid,
|
|
42
|
+
PointsGrid,
|
|
43
|
+
TurbineGrid,
|
|
44
|
+
TurbineCubatureGrid
|
|
45
|
+
)
|
|
46
|
+
from .flow_field import FlowField
|
|
47
|
+
from .wake import WakeModelManager
|
|
48
|
+
from .solver import (
|
|
49
|
+
cc_solver,
|
|
50
|
+
empirical_gauss_solver,
|
|
51
|
+
full_flow_cc_solver,
|
|
52
|
+
full_flow_empirical_gauss_solver,
|
|
53
|
+
full_flow_sequential_solver,
|
|
54
|
+
full_flow_turbopark_solver,
|
|
55
|
+
sequential_solver,
|
|
56
|
+
turbopark_solver,
|
|
57
|
+
)
|
|
58
|
+
from .core import Core
|
|
59
|
+
|
|
60
|
+
# initialize the logger
|
|
61
|
+
floris.logging_manager._setup_logger()
|
floris/core/base.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
|
|
2
|
+
from abc import abstractmethod
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import (
|
|
5
|
+
Any,
|
|
6
|
+
Dict,
|
|
7
|
+
Final,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from attrs import (
|
|
11
|
+
Attribute,
|
|
12
|
+
define,
|
|
13
|
+
field,
|
|
14
|
+
fields,
|
|
15
|
+
setters,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from floris.logging_manager import LoggingManager
|
|
19
|
+
from floris.type_dec import FromDictMixin
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
Defines the BaseClass parent class for all models to be based upon.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class State(Enum):
|
|
28
|
+
UNINITIALIZED = 0
|
|
29
|
+
INITIALIZED = 1
|
|
30
|
+
USED = 2
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@define
|
|
34
|
+
class BaseClass(FromDictMixin):
|
|
35
|
+
"""
|
|
36
|
+
BaseClass object class. This class does the logging and MixIn class inheritance.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# Initialize `state` and ensure it is treated as an attribute rather than a constant parameter.
|
|
40
|
+
# See https://www.attrs.org/en/stable/api-attr.html#attr.ib
|
|
41
|
+
state = field(init=False, default=State.UNINITIALIZED)
|
|
42
|
+
_logging_manager: LoggingManager = field(init=False, default=LoggingManager())
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def logger(self):
|
|
46
|
+
"""Returns the logger manager object."""
|
|
47
|
+
return self._logging_manager.logger
|
|
48
|
+
|
|
49
|
+
@define
|
|
50
|
+
class BaseModel(BaseClass):
|
|
51
|
+
"""
|
|
52
|
+
BaseModel is the generic class for any wake models. It defines the API required to
|
|
53
|
+
create a valid model.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
# This is a numerical epsilon to prevent divide by zeros
|
|
57
|
+
NUM_EPS: Final[float] = field(init=False, default=0.001, on_setattr=setters.frozen)
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
def prepare_function() -> dict:
|
|
61
|
+
raise NotImplementedError("BaseModel.prepare_function")
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def function() -> None:
|
|
65
|
+
raise NotImplementedError("BaseModel.function")
|