Skip to content

Commit

Permalink
Support class parameters for attrs
Browse files Browse the repository at this point in the history
  • Loading branch information
rafmudaf committed Nov 30, 2023
1 parent 6009da1 commit 952d799
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 23 deletions.
15 changes: 11 additions & 4 deletions floris/simulation/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
)

from attrs import (
Attribute,
asdict,
define,
field,
fields,
)

Expand All @@ -47,8 +49,9 @@ class BaseClass(LoggerBase, FromDictMixin):
BaseClass object class. This class does the logging and MixIn class inheritance.
"""

state = State.UNINITIALIZED

# Initialize `state` and ensure it is treated as an attribute rather than a constant parameter.
# See https://www.attrs.org/en/stable/api-attr.html#attr.ib
state = field(init=False, default=State.UNINITIALIZED)

@classmethod
def get_model_defaults(cls) -> Dict[str, Any]:
Expand All @@ -74,13 +77,17 @@ def _get_model_dict(self) -> dict:


@define
class BaseModel(BaseClass, ABC):
class BaseModel(BaseClass):
"""
BaseModel is the generic class for any wake models. It defines the API required to
create a valid model.
"""

NUM_EPS: Final[float] = 0.001 # This is a numerical epsilon to prevent divide by zeros
NUM_EPS: Final[float] = field(init=False, default=0.001) # This is a numerical epsilon to prevent divide by zeros

@NUM_EPS.validator
def lock_num_eps(self, attribute: Attribute, value: Any) -> None:
assert value == 0.001 # Locks NUM_EPS to a fixed value. You shouldn't change this!

@abstractmethod
def prepare_function() -> dict:
Expand Down
25 changes: 13 additions & 12 deletions floris/simulation/wake_deflection/gauss.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import numexpr as ne
import numpy as np
from attrs import define, field
from attrs import define, field, fields
from numpy import pi

from floris.simulation import (
Expand All @@ -28,6 +28,7 @@
)
from floris.utilities import cosd, sind

NUM_EPS = fields(BaseModel).NUM_EPS.default

@define
class GaussVelocityDeflection(BaseModel):
Expand Down Expand Up @@ -309,11 +310,11 @@ def wake_added_yaw(
### compute the spanwise and vertical velocities induced by yaw

# decay = eps ** 2 / (4 * nu * delta_x / Uinf + eps ** 2) # This is the decay downstream
yLocs = delta_y + BaseModel.NUM_EPS
yLocs = delta_y + NUM_EPS

# top vortex
# NOTE: this is the top of the grid, not the top of the rotor
zT = z_i - (HH + D / 2) + BaseModel.NUM_EPS # distance from the top of the grid
zT = z_i - (HH + D / 2) + NUM_EPS # distance from the top of the grid
rT = ne.evaluate("yLocs ** 2 + zT ** 2") # TODO: This is (-) in the paper
# This looks like spanwise decay;
# it defines the vortex profile in the spanwise directions
Expand All @@ -323,15 +324,15 @@ def wake_added_yaw(
# w_top = (-1 * Gamma_top * yLocs) / (2 * pi * rT) * core_shape * decay

# bottom vortex
zB = z_i - (HH - D / 2) + BaseModel.NUM_EPS
zB = z_i - (HH - D / 2) + NUM_EPS
rB = ne.evaluate("yLocs ** 2 + zB ** 2")
core_shape = ne.evaluate("1 - exp(-rB / (eps ** 2))")
v_bottom = ne.evaluate("(Gamma_bottom * zB) / (2 * pi * rB) * core_shape")
v_bottom = np.mean( v_bottom, axis=(3,4) )
# w_bottom = (-1 * Gamma_bottom * yLocs) / (2 * pi * rB) * core_shape * decay

# wake rotation vortex
zC = z_i - HH + BaseModel.NUM_EPS
zC = z_i - HH + NUM_EPS
rC = ne.evaluate("yLocs ** 2 + zC ** 2")
core_shape = ne.evaluate("1 - exp(-rC / (eps ** 2))")
v_core = ne.evaluate("(Gamma_wake_rotation * zC) / (2 * pi * rC) * core_shape")
Expand Down Expand Up @@ -411,10 +412,10 @@ def calculate_transverse_velocity(

# This is the decay downstream
decay = ne.evaluate("eps ** 2 / (4 * nu * delta_x / Uinf + eps ** 2)")
yLocs = delta_y + BaseModel.NUM_EPS
yLocs = delta_y + NUM_EPS

# top vortex
zT = z - (HH + D / 2) + BaseModel.NUM_EPS
zT = z - (HH + D / 2) + NUM_EPS
rT = ne.evaluate("yLocs ** 2 + zT ** 2") # TODO: This is - in the paper
# This looks like spanwise decay;
# it defines the vortex profile in the spanwise directions
Expand All @@ -423,14 +424,14 @@ def calculate_transverse_velocity(
W1 = ne.evaluate("(-1 * Gamma_top * yLocs) / (2 * pi * rT) * core_shape * decay")

# bottom vortex
zB = z - (HH - D / 2) + BaseModel.NUM_EPS
zB = z - (HH - D / 2) + NUM_EPS
rB = ne.evaluate("yLocs ** 2 + zB ** 2")
core_shape = ne.evaluate("1 - exp(-rB / (eps ** 2))")
V2 = ne.evaluate("(Gamma_bottom * zB) / (2 * pi * rB) * core_shape * decay")
W2 = ne.evaluate("(-1 * Gamma_bottom * yLocs) / (2 * pi * rB) * core_shape * decay")

# wake rotation vortex
zC = z - HH + BaseModel.NUM_EPS
zC = z - HH + NUM_EPS
rC = ne.evaluate("yLocs ** 2 + zC ** 2")
core_shape = ne.evaluate("1 - exp(-rC / (eps ** 2))")
V5 = ne.evaluate("(Gamma_wake_rotation * zC) / (2 * pi * rC) * core_shape * decay")
Expand All @@ -439,7 +440,7 @@ def calculate_transverse_velocity(
### Boundary condition - ground mirror vortex

# top vortex - ground
zTb = z + (HH + D / 2) + BaseModel.NUM_EPS
zTb = z + (HH + D / 2) + NUM_EPS
rTb = ne.evaluate("yLocs ** 2 + zTb ** 2")
# This looks like spanwise decay;
# it defines the vortex profile in the spanwise directions
Expand All @@ -448,14 +449,14 @@ def calculate_transverse_velocity(
W3 = ne.evaluate("(Gamma_top * yLocs) / (2 * pi * rTb) * core_shape * decay")

# bottom vortex - ground
zBb = z + (HH - D / 2) + BaseModel.NUM_EPS
zBb = z + (HH - D / 2) + NUM_EPS
rBb = ne.evaluate("yLocs ** 2 + zBb ** 2")
core_shape = ne.evaluate("1 - exp(-rBb / (eps ** 2))")
V4 = ne.evaluate("(-1 * Gamma_bottom * zBb) / (2 * pi * rBb) * core_shape * decay")
W4 = ne.evaluate("(Gamma_bottom * yLocs) / (2 * pi * rBb) * core_shape * decay")

# wake rotation vortex - ground effect
zCb = z + HH + BaseModel.NUM_EPS
zCb = z + HH + NUM_EPS
rCb = ne.evaluate("yLocs ** 2 + zCb ** 2")
core_shape = ne.evaluate("1 - exp(-rCb / (eps ** 2))")
V6 = ne.evaluate("(-1 * Gamma_wake_rotation * zCb) / (2 * pi * rCb) * core_shape * decay")
Expand Down
4 changes: 2 additions & 2 deletions floris/simulation/wake_velocity/jensen.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import numexpr as ne
import numpy as np
from attrs import define, field
from attrs import define, field, fields

from floris.simulation import (
BaseModel,
Expand All @@ -24,6 +24,7 @@
Turbine,
)

NUM_EPS = fields(BaseModel).NUM_EPS.default

@define
class JensenVelocityDeficit(BaseModel):
Expand Down Expand Up @@ -107,7 +108,6 @@ def function(
dz = ne.evaluate("z - z_i")

we = self.we
NUM_EPS = JensenVelocityDeficit.NUM_EPS

# Construct a boolean mask to include all points downstream of the turbine
downstream_mask = ne.evaluate("dx > 0 + NUM_EPS")
Expand Down
19 changes: 14 additions & 5 deletions tests/base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,31 @@ def test_get_model_defaults():
"""
This tests that the default parameter values are set correctly and unchanged if not
explicitly set.
Note that NUM_EPS in BaseModel is treated as a default parameter by attrs.
Note that state and NUM_EPS in BaseClass and BaseModel are treated as default parameters
by attrs.
"""
defaults = ClassTest.get_model_defaults()
assert len(defaults) == 3
assert len(defaults) == 4
assert defaults["x"] == 1
assert defaults["a_string"] == "abc"


def test_get_model_values():
"""
This tests that the parameters are changed when set by the user.
Note that NUM_EPS in BaseModel is treated as a parameter by attrs. It isn't changed
in the instantiation method, but it is still included as the parameter set.
Note that state and NUM_EPS in BaseClass and BaseModel are treated as parameters by attrs.
They aren't changed in the instantiation method, but they are still included in the
parameter set.
"""
cls = ClassTest(x=4, a_string="xyz")
values = cls._get_model_dict()
assert len(values) == 3
assert len(values) == 4
assert values["x"] == 4
assert values["a_string"] == "xyz"

def test_NUM_EPS():
cls = ClassTest(x=4, a_string="xyz")
assert cls.NUM_EPS == 0.001

with pytest.raises(AssertionError):
cls.NUM_EPS = 2

0 comments on commit 952d799

Please sign in to comment.