Repository: NREL/HOPP (language: python)

Issue:
Update to latest floris



Discussion / hints:

Fix patch:
diff --git a/pyproject.toml b/pyproject.toml
index 48d25c760..42891e974 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,7 +17,7 @@ dependencies = [
     "Pyomo>=6.1.2",
     "diskcache",
     "fastkml",
-    "floris",
+    "floris<=3.6",
     "future",
     "global_land_mask",
     "humpday",


Test patch:
diff --git a/tests/hopp/inputs/floris_config.yaml b/tests/hopp/inputs/floris_config.yaml
new file mode 100644
index 000000000..26d5d58f3
--- /dev/null
+++ b/tests/hopp/inputs/floris_config.yaml
@@ -0,0 +1,92 @@
+name: Gauss
+description: Three turbines using Gauss model
+floris_version: v3.0.0
+
+logging:
+  console:
+    enable: false
+    level: WARNING
+  file:
+    enable: false
+    level: WARNING
+
+solver:
+  type: turbine_grid
+  turbine_grid_points: 1
+
+farm:
+  layout_x:
+  - 0.0
+  - 1841.0
+  - 3682.0
+  - 5523.0
+
+  layout_y:
+  - 0.0
+  - 0.0
+  - 0.0
+  - 0.0
+
+  turbine_type:
+  - nrel_5MW
+
+flow_field:
+  air_density: 1.225
+  reference_wind_height: -1 # -1 is code for use the hub height
+  turbulence_intensity: 0.06
+  wind_directions:
+  - 270.0
+  wind_shear: 0.33
+  wind_speeds:
+  - 8.0
+  wind_veer: 0.0
+
+wake:
+  model_strings:
+    combination_model: sosfs # TODO followup on why not using linear free-stream superposition? - ask Chris Bay and Gen S.
+    deflection_model: gauss
+    turbulence_model: crespo_hernandez
+    velocity_model: gauss
+
+  enable_secondary_steering: false
+  enable_yaw_added_recovery: false
+  enable_transverse_velocities: false
+
+  wake_deflection_parameters:
+    gauss:
+      ad: 0.0
+      alpha: 0.58
+      bd: 0.0
+      beta: 0.077
+      dm: 1.0
+      ka: 0.38
+      kb: 0.004
+    jimenez:
+      ad: 0.0
+      bd: 0.0
+      kd: 0.05
+
+  wake_velocity_parameters:
+    cc:
+      a_s: 0.179367259
+      b_s: 0.0118889215
+      c_s1: 0.0563691592
+      c_s2: 0.13290157
+      a_f: 3.11
+      b_f: -0.68
+      c_f: 2.41
+      alpha_mod: 1.0
+    gauss:
+      alpha: 0.58
+      beta: 0.077
+      ka: 0.38
+      kb: 0.004
+    jensen:
+      we: 0.05
+
+  wake_turbulence_parameters:
+    crespo_hernandez:
+      initial: 0.1
+      constant: 0.5
+      ai: 0.8
+      downstream: -0.32
\ No newline at end of file
diff --git a/tests/hopp/test_hybrid.py b/tests/hopp/test_hybrid.py
index f304acdbc..6a4a5486b 100644
--- a/tests/hopp/test_hybrid.py
+++ b/tests/hopp/test_hybrid.py
@@ -385,7 +385,7 @@ def test_hybrid_wave_battery(hybrid_config, wavesite, subtests):
         assert aeps.battery == approx(87.84, 1e3)
 
 
-def test_hybrid_wind_only(hybrid_config):
+def test_hybrid_wind_only(hybrid_config, subtests):
     technologies = hybrid_config["technologies"]
     wind_only = {key: technologies[key] for key in ("wind", "grid")}
     hybrid_config["technologies"] = wind_only
@@ -398,12 +398,45 @@ def test_hybrid_wind_only(hybrid_config):
     npvs = hybrid_plant.net_present_values
     cf = hybrid_plant.capacity_factors
 
-    assert aeps.wind == approx(33615479, 1e3)
-    assert aeps.hybrid == approx(33615479, 1e3)
+    with subtests.test("wind aep"):
+        assert aeps.wind == approx(31977778, 1e-3)
+    with subtests.test("hybrid aep"):
+        assert aeps.hybrid == approx(31977778, 1e-3)
+    with subtests.test("wind npv"):
+        assert npvs.wind == approx(-7256658, 1e-3)
+    with subtests.test("hybrid npv"):
+        assert npvs.hybrid == approx(-7256658, 1e-3)
 
-    assert npvs.wind == approx(-13692784, 1e3)
-    assert npvs.hybrid == approx(-13692784, 1e3)
+def test_hybrid_wind_only_floris(hybrid_config, subtests):
 
+    floris_config_path = (
+        ROOT_DIR.parent / "tests" / "hopp" / "inputs" / "floris_config.yaml"
+    )
+    technologies = hybrid_config["technologies"]
+    wind_only = {key: technologies[key] for key in ("wind", "grid")}
+
+    wind_only["wind"]["model_name"] = "floris"
+    wind_only["wind"]["floris_config"] = floris_config_path
+    wind_only["wind"]["timestep"] = [0, 8760]
+
+    hybrid_config["technologies"] = wind_only
+    hi = HoppInterface(hybrid_config)
+    hybrid_plant = hi.system
+
+    hi.simulate(25)
+
+    aeps = hybrid_plant.annual_energies
+    npvs = hybrid_plant.net_present_values
+    cf = hybrid_plant.capacity_factors
+
+    with subtests.test("wind aep"):
+        assert aeps.wind == approx(72776380, 1e-3)
+    with subtests.test("hybrid aep"):
+        assert aeps.hybrid == approx(67118441, 1e-3)
+    with subtests.test("wind npv"):
+        assert npvs.wind == approx(3231853, 1e-3)
+    with subtests.test("hybrid npv"):
+        assert npvs.hybrid == approx(1804831, 1e-3)
 
 def test_hybrid_pv_only(hybrid_config):
     technologies = hybrid_config["technologies"]
diff --git a/tests/hopp/test_wind.py b/tests/hopp/test_wind.py
index e0bd7baf2..ec9cbc9b5 100644
--- a/tests/hopp/test_wind.py
+++ b/tests/hopp/test_wind.py
@@ -7,6 +7,8 @@
 from hopp.simulation.technologies.wind.wind_plant import WindPlant, WindConfig
 from tests.hopp.utils import create_default_site_info
 
+from hopp import ROOT_DIR
+
 @fixture
 def site():
     return create_default_site_info()
@@ -128,5 +130,49 @@ def test_changing_system_capacity(site):
         model.system_capacity_by_rating(n)
         assert model.system_capacity_kw == pytest.approx(n)
 
+#################### FLORIS tests ################
+
+def test_changing_rotor_diam_recalc_floris(site):
+    floris_config_path = (
+        ROOT_DIR.parent / "tests" / "hopp" / "inputs" / "floris_config.yaml"
+    )
+    
+    config = WindConfig.from_dict({'num_turbines': 20, "turbine_rating_kw": 1000, "model_name": "floris", "timestep": [1, 8760], "floris_config": floris_config_path})
+    model = WindPlant(site, config=config)
+    assert model.system_capacity_kw == 20000
+    diams = range(50, 70, 140)
+    for d in diams:
+        model.rotor_diameter = d
+        assert model.rotor_diameter == d, "rotor diameter should be " + str(d)
+
+def test_changing_turbine_rating_floris(site):
+    
+    floris_config_path = (
+        ROOT_DIR.parent / "tests" / "hopp" / "inputs" / "floris_config.yaml"
+    )
+    config = WindConfig.from_dict({'num_turbines': 20, "turbine_rating_kw": 1000, "model_name": "floris", "timestep": [1, 8760], "floris_config": floris_config_path})
+    model = WindPlant(site, config=config)
+    n_turbs = model.num_turbines
+    for n in range(1000, 3000, 150):
+        model.turb_rating = n
+        assert model.system_capacity_kw == model.turb_rating * n_turbs, "system size error when rating is " + str(n)
+
+def test_changing_system_capacity_floris(site):
+    floris_config_path = (
+        ROOT_DIR.parent / "tests" / "hopp" / "inputs" / "floris_config.yaml"
+    )
+    config = WindConfig.from_dict({'num_turbines': 20, "turbine_rating_kw": 1000, "model_name": "floris", "timestep": [1, 8760], "floris_config": floris_config_path})
+    model = WindPlant(site, config=config)
+    rating = model.turb_rating
+    for n in range(1000, 20000, 1000):
+        model.system_capacity_by_num_turbines(n)
+        assert model.turb_rating == rating, str(n)
+        assert model.system_capacity_kw == rating * round(n/rating)
+
+    # adjust turbine rating first, system capacity will be exact
+    model = WindPlant(site, config=config)
+    for n in range(40000, 60000, 1000):
+        model.system_capacity_by_rating(n)
+        assert model.system_capacity_kw == pytest.approx(n)