From ca3397b9f0fc742cc47a3d75567e1a089fbc6ed8 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 12 May 2022 16:31:59 -0600 Subject: [PATCH 1/7] first pass --- examples/make_error.py | 15 +++++++++++++++ floris/tools/floris_interface.py | 8 ++++++++ 2 files changed, 23 insertions(+) create mode 100644 examples/make_error.py diff --git a/examples/make_error.py b/examples/make_error.py new file mode 100644 index 000000000..55553e9c5 --- /dev/null +++ b/examples/make_error.py @@ -0,0 +1,15 @@ + + +from floris.tools import FlorisInterface + + +fi = FlorisInterface("inputs/gch.yaml") + +# # Convert to a simple two turbine layout +# fi.reinitialize( layout=( [0, 500.], [0., 0.] ) ) + + +# fi.calculate_wake() + +# Get the turbine powers +turbine_powers = fi.get_turbine_powers()/1000. diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index ab3cc5dd7..40df37e9e 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -558,12 +558,20 @@ def check_wind_condition_for_viz(self, wd=None, ws=None): if len(ws) > 1 or len(ws) < 1: raise ValueError("Wind speed input must be of length 1 for visualization. Current length is {}.".format(len(ws))) + def check_calc_wake_run(self, func_name): + + if not hasattr('self.floris.farm', 'turbine_type_map'): + raise AttributeError('Cant call function %s without first calling calculate_wake' % func_name) + def get_turbine_powers(self) -> NDArrayFloat: """Calculates the power at each turbine in the windfarm. Returns: NDArrayFloat: [description] """ + + self.check_calc_wake_run('get_turbine_powers') + turbine_powers = power( air_density=self.floris.flow_field.air_density, velocities=self.floris.flow_field.u, From f1836068e39b3d5711cac70107e73f96707ad2dd Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 24 May 2022 14:53:48 -0600 Subject: [PATCH 2/7] switch to private and check on get_farm_power --- floris/tools/floris_interface.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index 40df37e9e..351a47526 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -558,7 +558,7 @@ def check_wind_condition_for_viz(self, wd=None, ws=None): if len(ws) > 1 or len(ws) < 1: raise ValueError("Wind speed input must be of length 1 for visualization. Current length is {}.".format(len(ws))) - def check_calc_wake_run(self, func_name): + def _check_calc_wake_run(self, func_name): if not hasattr('self.floris.farm', 'turbine_type_map'): raise AttributeError('Cant call function %s without first calling calculate_wake' % func_name) @@ -570,7 +570,8 @@ def get_turbine_powers(self) -> NDArrayFloat: NDArrayFloat: [description] """ - self.check_calc_wake_run('get_turbine_powers') + # Confirm calculate wake has been run + self._check_calc_wake_run('get_turbine_powers') turbine_powers = power( air_density=self.floris.flow_field.air_density, @@ -632,6 +633,9 @@ def get_farm_power( # for turbine in self.floris.farm.turbines: # turbine.use_turbulence_correction = use_turbulence_correction + # Confirm calculate wake has been run + self._check_calc_wake_run('get_farm_power') + turbine_powers = self.get_turbine_powers() return np.sum(turbine_powers, axis=2) From bb5281239aa5e2295150b398f6be718b7a72b180 Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 24 May 2022 15:00:24 -0600 Subject: [PATCH 3/7] bugfix --- floris/tools/floris_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index 351a47526..0dd9974fd 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -560,7 +560,7 @@ def check_wind_condition_for_viz(self, wd=None, ws=None): def _check_calc_wake_run(self, func_name): - if not hasattr('self.floris.farm', 'turbine_type_map'): + if not hasattr(self.floris.farm, 'turbine_type_map'): raise AttributeError('Cant call function %s without first calling calculate_wake' % func_name) def get_turbine_powers(self) -> NDArrayFloat: From f6410487dd1af2ac5d2d69ba214f6500b313bf3f Mon Sep 17 00:00:00 2001 From: Rafael M Mudafort Date: Fri, 27 May 2022 17:03:37 -0500 Subject: [PATCH 4/7] Check BaseClass state for flow control in FI --- floris/simulation/__init__.py | 2 +- floris/simulation/base.py | 11 +++++++++++ floris/simulation/farm.py | 4 +++- floris/simulation/floris.py | 7 +++++-- floris/tools/floris_interface.py | 11 ++++------- 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/floris/simulation/__init__.py b/floris/simulation/__init__.py index ae17a7dfb..3233b27cb 100644 --- a/floris/simulation/__init__.py +++ b/floris/simulation/__init__.py @@ -33,7 +33,7 @@ # that should be included in the simulation package. # Since some of these depend on each other, the order # that they are listed here does matter. -from .base import BaseClass, BaseModel +from .base import BaseClass, BaseModel, State from .turbine import Turbine, Ct, power, axial_induction, average_velocity from .farm import Farm from .grid import Grid, TurbineGrid, FlowFieldGrid, FlowFieldPlanarGrid diff --git a/floris/simulation/base.py b/floris/simulation/base.py index dbd36eab1..7964bc501 100644 --- a/floris/simulation/base.py +++ b/floris/simulation/base.py @@ -18,6 +18,7 @@ """ from abc import ABC, abstractmethod +from enum import Enum from typing import Any, Dict, Final import attrs @@ -27,11 +28,21 @@ from floris.logging_manager import LoggerBase +class State(Enum): + UNINITIALIZED = 0 + INITIALIZED = 1 + USED = 2 + EXPIRED = 3 + + class BaseClass(LoggerBase, FromDictMixin): """ BaseClass object class. This class does the logging and MixIn class inheritance. """ + state = State.UNINITIALIZED + + @classmethod def get_model_defaults(cls) -> Dict[str, Any]: """Produces a dictionary of the keyword arguments and their defaults. diff --git a/floris/simulation/farm.py b/floris/simulation/farm.py index 186ed0d77..97f8573b4 100644 --- a/floris/simulation/farm.py +++ b/floris/simulation/farm.py @@ -26,7 +26,7 @@ NDArrayFloat ) from floris.utilities import Vec3, load_yaml -from floris.simulation import BaseClass +from floris.simulation import BaseClass, State from floris.simulation import Turbine @@ -94,6 +94,7 @@ def initialize(self, sorted_indices): sorted_indices[:, :, :, 0, 0], axis=2, ) + self.state = State.INITIALIZED def construct_hub_heights(self): self.hub_heights = np.array([turb['hub_height'] for turb in self.turbine_definitions]) @@ -149,6 +150,7 @@ def finalize(self, unsorted_indices): self.TSRs = np.take_along_axis(self.TSRs_sorted, unsorted_indices[:,:,:,0,0], axis=2) self.pPs = np.take_along_axis(self.pPs_sorted, unsorted_indices[:,:,:,0,0], axis=2) self.turbine_type_map = np.take_along_axis(self.turbine_type_map_sorted, unsorted_indices[:,:,:,0,0], axis=2) + self.state.USED @property def n_turbines(self): diff --git a/floris/simulation/floris.py b/floris/simulation/floris.py index cb0c89c71..a652d59cd 100644 --- a/floris/simulation/floris.py +++ b/floris/simulation/floris.py @@ -20,8 +20,8 @@ from floris.utilities import load_yaml import floris.logging_manager as logging_manager -from floris.type_dec import FromDictMixin from floris.simulation import ( + BaseClass, Farm, WakeModelManager, FlowField, @@ -30,6 +30,7 @@ TurbineGrid, FlowFieldGrid, FlowFieldPlanarGrid, + State, sequential_solver, cc_solver, turbopark_solver, @@ -41,7 +42,7 @@ @define -class Floris(logging_manager.LoggerBase, FromDictMixin): +class Floris(BaseClass): """ Top-level class that describes a Floris model and initializes the simulation. Use the :py:class:`~.simulation.farm.Farm` attribute to @@ -136,6 +137,7 @@ def initialize_domain(self): # Initialize farm quantities self.farm.initialize(self.grid.sorted_indices) + self.state.INITIALIZED def steady_state_atmospheric_condition(self): """Perform the steady-state wind farm wake calculations. Note that @@ -196,6 +198,7 @@ def finalize(self): # the user-supplied order of things. self.flow_field.finalize(self.grid.unsorted_indices) self.farm.finalize(self.grid.unsorted_indices) + self.state = State.USED ## I/O diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index 0dd9974fd..33580a59a 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -558,11 +558,6 @@ def check_wind_condition_for_viz(self, wd=None, ws=None): if len(ws) > 1 or len(ws) < 1: raise ValueError("Wind speed input must be of length 1 for visualization. Current length is {}.".format(len(ws))) - def _check_calc_wake_run(self, func_name): - - if not hasattr(self.floris.farm, 'turbine_type_map'): - raise AttributeError('Cant call function %s without first calling calculate_wake' % func_name) - def get_turbine_powers(self) -> NDArrayFloat: """Calculates the power at each turbine in the windfarm. @@ -571,7 +566,8 @@ def get_turbine_powers(self) -> NDArrayFloat: """ # Confirm calculate wake has been run - self._check_calc_wake_run('get_turbine_powers') + if self.floris.state is not State.USED: + raise RuntimeError(f"Can't run function `FlorisInterface.get_turbine_powers` without first running `FlorisInterface.calculate_wake`.") turbine_powers = power( air_density=self.floris.flow_field.air_density, @@ -634,7 +630,8 @@ def get_farm_power( # turbine.use_turbulence_correction = use_turbulence_correction # Confirm calculate wake has been run - self._check_calc_wake_run('get_farm_power') + if self.floris.state is not State.USED: + raise RuntimeError(f"Can't run function `FlorisInterface.get_turbine_powers` without running `FlorisInterface.calculate_wake`.") turbine_powers = self.get_turbine_powers() return np.sum(turbine_powers, axis=2) From 220e76a709cc66006da96ae9a3a5f19c03c1effb Mon Sep 17 00:00:00 2001 From: Rafael M Mudafort Date: Fri, 27 May 2022 17:06:54 -0500 Subject: [PATCH 5/7] Remove failing example --- examples/make_error.py | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 examples/make_error.py diff --git a/examples/make_error.py b/examples/make_error.py deleted file mode 100644 index 55553e9c5..000000000 --- a/examples/make_error.py +++ /dev/null @@ -1,15 +0,0 @@ - - -from floris.tools import FlorisInterface - - -fi = FlorisInterface("inputs/gch.yaml") - -# # Convert to a simple two turbine layout -# fi.reinitialize( layout=( [0, 500.], [0., 0.] ) ) - - -# fi.calculate_wake() - -# Get the turbine powers -turbine_powers = fi.get_turbine_powers()/1000. From 6e1ef0c8497e9779301271b4d56454e4082e0419 Mon Sep 17 00:00:00 2001 From: Rafael M Mudafort Date: Fri, 27 May 2022 17:39:29 -0500 Subject: [PATCH 6/7] FIx missing import --- floris/tools/floris_interface.py | 1 + 1 file changed, 1 insertion(+) diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index 33580a59a..09a38d311 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -38,6 +38,7 @@ from floris.simulation.turbine import Ct, power, axial_induction, average_velocity from floris.tools.interface_utilities import get_params, set_params, show_params from floris.tools.cut_plane import CutPlane, change_resolution, get_plane_from_flow_data +from floris.simulation import State # from .visualization import visualize_cut_plane # from .layout_functions import visualize_layout, build_turbine_loc From e069888c8b9945335543e682ea5f7ff6fb6df709 Mon Sep 17 00:00:00 2001 From: Rafael M Mudafort Date: Thu, 21 Jul 2022 23:07:55 -0500 Subject: [PATCH 7/7] Reduce state-enum to three states --- floris/simulation/base.py | 1 - 1 file changed, 1 deletion(-) diff --git a/floris/simulation/base.py b/floris/simulation/base.py index 9ff3ee510..8967c3e58 100644 --- a/floris/simulation/base.py +++ b/floris/simulation/base.py @@ -31,7 +31,6 @@ class State(Enum): UNINITIALIZED = 0 INITIALIZED = 1 USED = 2 - EXPIRED = 3 class BaseClass(LoggerBase, FromDictMixin):