Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to more flexible turbine operation model definitions #770

Merged
merged 111 commits into from
Jan 10, 2024

Conversation

misi9170
Copy link
Collaborator

@misi9170 misi9170 commented Dec 28, 2023

Description

This pull request makes significant changes to the Turbine() class and surrounding code (most heavily located in turbine.py, but spread throughout the FLORIS source code) to enable specification of more advanced functionality for computing the thrust coefficient and power of a turbine off of "nominal" operation. In doing so, I've added two turbine operation models, CosineLossTurbine (which is the default, and matches the previous behavior of FLORIS v3) and SimpleTurbine, which is an actuator disk without handling for rotor misalignment with the flow.

We envisage that other operation models will be added in FLORIS v4.1.

In doing so, I've also incorporated the multidimensional Cp/Ct turbine code. There are likely still some minor updates needed here to get all naming conventions aligned (e.g. dropping "cp" in favor of "power", see #765 ); however, the necessary functional changes have been made and multidimensional turbine tests pass.


Previous discussion

Modified from the FLORIS v4 project board
Currently, the turbine model is essentially implemented using interpolation functions, where each turbine_type has its own interpolation function and a mapping for which turbine is which type is created before the solver is run to mask the outputs of the interpolators to the correct turbine positions.

Proposed changes for FLORIS v4 would not change this underlying structure, but the interpolators would be replaced by more general callable function definitions to implement different ways of computing power and thrust coefficient given turbine inputs. Now referred to as different "operation models"

A turbine model operation model is then a set of two three functions:

  • power(), which returns the turbine power at the given atmospheric conditions and control settings
  • thrust_coefficient() (or possibly Ct()), which returns the turbine thrust coefficient at the given atmospheric conditions and control settings.
  • axial_induction(), returns the axial induction factor

For flexibility, there may also be a prepare function (similar to that of the wake models) that establishes a dictionary of keyword arguments needed for each turbine type; this will be unpacked in the call to the turbine functions.

We imagine that power(), thrust_coefficient(), may call from a library of lower level functions that implement, for example, sliding along the power curve for yaw angle or air density changes.

We may represent a turbine model as a "static class", which has now memory but has various @staticmethods for inheritance reasons and to enforce implementation of certain methods.

The reason for not making them class objects is to save memory, but this could possibly be revisited if necessary as only one instantiated object would be needed for each turbine_type (not for each turbine).

It is not yet clear exactly how the selection of different subturbine operation models will be configured in the inputs files. However, the subturbine operation models could be specified within the turbine yamls, as an option.

How are we going to deal with multidimensional turbines? Should be able to be handled in this.

Steps

  • Move to form where turbine.py is essentially a masker; SimpleTurbine implements main functionality, CosinePpTurbine implements results of yaw misalignment. Check tests pass.
  • Add a base class for turbine operation models to inherit from, add tests to check inheritance.
  • Update examples as needed.

Completion criteria

  • Existing tests pass
  • All examples run
  • Add tests for each operation model

Still to do:

  • Multidimensional model
  • axial induction models
  • Base class to inherit from (?)
  • New tests
  • clean up (names, filenames, check for any remaining TODOs)
  • Handling for rotor averaging method
  • Docstrings update for simulation/turbine/operation_models.py
  • Get all examples running
  • Make sure all checks pass

Architecture

Architecture of new turbine module within floris.simulation (and main connections to other parts of code). Arrows indicate where a function, method, or class is used by something upstream of it (i.e., if b points to a, then a calls/uses b)
turbine_module_layout

Further discussion needed (not included in this PR)

To discuss further:

  • Best location for axial_induction model (on turbine.py (applies to all turbine models) vs on specified turbine submodels) This has now been placed on the operation models
  • Current use of pT (seems very limited and somewhat inconsistent) This was my misunderstanding---pT refers to the power loss exponent for tilt---I will create a separate PR clarifying this
  • Should the "multidim_cp_ct" velocity model be removed to allow the user to use any velocity deficit model? Likely yes, but will be it's own PR
  • Should the multidimensional Power and Ct data structure be the default with the one-entry form being a special case? It would require a change to most users' input file, but it would simplify the code a bit.
  • Should the averaging method be user-definable? Could live in the turbine yaml, or the main floris input yaml.
  • Has this slowed down execution? Visualization runs seem slower, but I'm not sure about "normal" solves? Approximately 10% slower on normal solves
  • I've been inconsistent about actually removing code vs just commenting things out, but once we are happy I can remove all commented out code. These have now been removed
  • I've removed pP, pT, and ref_air_density from the farm object (mostly commenting out lines). Were they needed for anything, or am I breaking a convention here? No

…MW turbine only, will remove prior to merge into v4 branch.
self.turbine_map.append(TurbineMultiDimensional.from_dict(_turb))
else:
self.turbine_map = [Turbine.from_dict(turb) for turb in self.turbine_definitions]
self.turbine_map = [Turbine.from_dict(turb) for turb in self.turbine_definitions]

def construct_turbine_fCts(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change function name to reflect that it isn't fCt anymore

@misi9170 misi9170 changed the title Update to more flexible turbine model definitions Update to more flexible turbine operation model definitions Jan 10, 2024
This was previously used for the multidimension turbine, but it has since been consolidated and flatten_dict isn't used
@@ -81,8 +81,11 @@ class Farm(BaseClass):

turbine_definitions: list = field(init=False, validator=iter_validator(list, dict))

turbine_fCts: Dict[str, interp1d] | List[interp1d] = field(init=False, factory=list)
turbine_fCts_sorted: NDArrayFloat = field(init=False, factory=list)
turbine_thrust_coefficient_functions: Dict[str, Callable] | List[Callable] = \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were previously either a dict or a list depending whether it was a multidimensional turbine. Now I believe they're only dict. Is that true?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe I've fixed these in the last commit @misi9170 but I'll let you hit the resolve button

turbine_thrust_coefficient_functions: Dict[str, Callable] | List[Callable] = \
field(init=False, factory=list)

turbine_axial_induction_functions: Dict[str, Callable] | List[Callable] = \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were previously either a dict or a list depending whether it was a multidimensional turbine. Now I believe they're only dict. Is that true?


turbine_type_map: NDArrayObject = field(init=False, factory=list)
turbine_type_map_sorted: NDArrayObject = field(init=False, factory=list)

turbine_power_interps: Dict[str, interp1d] | List[interp1d] = field(init=False, factory=list)
turbine_power_interps_sorted: NDArrayFloat = field(init=False, factory=list)
turbine_power_functions: Dict[str, Callable] | List[Callable] = field(init=False, factory=list)
Copy link
Collaborator

@rafmudaf rafmudaf Jan 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were previously either a dict or a list depending whether it was a multidimensional turbine. Now I believe they're only dict. Is that true?

@misi9170
Copy link
Collaborator Author

We decided to use the name "operation model" to describe these models. Now, "turbine model" refers to the turbine physical properties, such as rotor diameter and hub height, which are normally specified in the turbine yamls. We chose the name "operation model" to highlight that these models describe how the turbine performs in different operating modes. For example, intentional yaw misalignment is an operating mode, and the manifestation of how yaw misaligment affects the turbine's thrust and power is specified in an operation model. Similarly, the Helix wake mixing method is a "mode" that the turbine may operate in, and we plan to implement an operation model to represent the effects on thrust, axial induction, and power.

@rafmudaf rafmudaf merged commit 8e6fb6b into NREL:v4 Jan 10, 2024
8 checks passed
@misi9170 misi9170 deleted the v4-ms/turbine-submodels branch January 10, 2024 21:54
@misi9170 misi9170 mentioned this pull request Apr 8, 2024
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement An improvement of an existing feature v4 Focus of FLORIS v4
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

2 participants