Repository: NREL/floris

Issue:
Jensen model has odd power value
**Bug description**
Working on an example script I noticed that some turbines in a farm returned oddly low power levels

**To Reproduce**
Here is an example script to reproduce the behavior, notice one of the turbines produces much less power than the rest.  This is not consistent with the other wake models:
```
import numpy as np
import matplotlib.pyplot as plt
from floris.tools import FlorisInterface

fi = FlorisInterface("inputs/jensen.yaml")

# Make a 25 turbine farm
# Get number of turbines and make 0 yaw angle matrix
N = 5
yaw_angles = np.zeros((1, 1, N**2)) # 1 wd/ 1ws/ N*N turbines


# Define layout
X, Y = np.meshgrid(
    6 * fi.floris.turbine.rotor_diameter * np.arange(0, N, 1),
    6 * fi.floris.turbine.rotor_diameter * np.arange(0, N, 1),
)
X = X.flatten()
Y = Y.flatten()

fi.reinitialize( layout=( X, Y ), wind_speeds=[8.0], wind_directions=[285.], turbulence_intensity=0.06 )
fi.calculate_wake(yaw_angles)
print(fi.get_turbine_powers()/1000.)

plt.plot(fi.get_turbine_powers().flatten()/1000.)
plt.show()
```

**Expected behavior**
This pattern of turbine outputs doesn't make sense to me

**Screenshots, if applicable**
![image](https://user-images.githubusercontent.com/6117702/153516724-7590a49f-3075-45e9-b8aa-1c3d8dfd1102.png)


**Floris Version**
v3.0

**System Information (please complete the following information):** 
 - OS: mac




Discussion:

Patch:
diff --git a/src/floris/simulation/wake_velocity/jensen.py b/src/floris/simulation/wake_velocity/jensen.py
index 294c4ac7..c7cdb251 100644
--- a/src/floris/simulation/wake_velocity/jensen.py
+++ b/src/floris/simulation/wake_velocity/jensen.py
@@ -114,7 +114,7 @@ class JensenVelocityDeficit(BaseModel):
         # np.nan_to_num
 
         # C should be 0 at the current turbine and everywhere in front of it
-        upstream_mask = np.array(dx <= 0.0, dtype=bool)
+        upstream_mask = np.array(dx <= 0.1, dtype=bool)
         # C should be 0 everywhere outside of the lateral and vertical bounds defined by the wake expansion parameter
         boundary_mask = np.array( np.sqrt(dy ** 2 + dz ** 2) > boundary_line, dtype=bool)
 


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index a0cc9214..a59df6f9 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -61,7 +61,7 @@ def print_test_values(average_velocities: list, thrusts: list, powers: list, axi
 WIND_DIRECTIONS = [
     270.0,
     360.0,
-    315.0, #293.0,
+    285.0,
     315.0,
 ]
 N_WIND_DIRECTIONS = len(WIND_DIRECTIONS)
@@ -88,6 +88,7 @@ Z_COORDS = [
     90.0
 ]
 N_TURBINES = len(X_COORDS)
+ROTOR_DIAMETER = 126.0
 TURBINE_GRID_RESOLUTION = 2
 
 
diff --git a/tests/reg_tests/gauss_regression_test.py b/tests/reg_tests/gauss_regression_test.py
index 5a14960a..83c39dec 100644
--- a/tests/reg_tests/gauss_regression_test.py
+++ b/tests/reg_tests/gauss_regression_test.py
@@ -689,3 +689,57 @@ def test_regression_secondary_steering(sample_inputs_fixture):
         )
 
     assert_results_arrays(test_results[0], secondary_steering_baseline)
+
+
+def test_regression_small_grid_rotation(sample_inputs_fixture):
+    """
+    Where wake models are masked based on the x-location of a turbine, numerical precision
+    can cause masking to fail unexpectedly. For example, in the configuration here one of
+    the turbines has these delta x values;
+
+    [[4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]
+     [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]
+     [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]
+     [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]
+     [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]]
+
+    and therefore the masking statement is False when it should be True. This causes the current
+    turbine to be affected by its own wake. This test requires that at least in this particular
+    configuration the masking correctly filters grid points.
+    """
+    sample_inputs_fixture.floris["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL
+    sample_inputs_fixture.floris["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL
+    X, Y = np.meshgrid(
+        6.0 * 126.0 * np.arange(0, 5, 1),
+        6.0 * 126.0 * np.arange(0, 5, 1)
+    )
+    X = X.flatten()
+    Y = Y.flatten()
+
+    sample_inputs_fixture.floris["farm"]["layout_x"] = X
+    sample_inputs_fixture.floris["farm"]["layout_y"] = Y
+
+    floris = Floris.from_dict(sample_inputs_fixture.floris)
+    floris.steady_state_atmospheric_condition()
+
+    # farm_avg_velocities = average_velocity(floris.flow_field.u)
+    velocities = floris.flow_field.u
+    yaw_angles = floris.farm.yaw_angles
+
+    farm_powers = power(
+        floris.flow_field.air_density,
+        velocities,
+        yaw_angles,
+        floris.turbine.pP,
+        floris.turbine.power_interp,
+    )
+
+    # A "column" is oriented parallel to the wind direction
+    # Columns 1 - 4 should have the same power profile
+    # Column 5 leading turbine is completely unwaked
+    # and the rest of the turbines have a partial wake from their immediate upstream turbine
+    assert np.allclose(farm_powers[2,0,0:5], farm_powers[2,0,5:10])
+    assert np.allclose(farm_powers[2,0,0:5], farm_powers[2,0,10:15])
+    assert np.allclose(farm_powers[2,0,0:5], farm_powers[2,0,15:20])
+    assert np.allclose(farm_powers[2,0,20], farm_powers[2,0,0])
+    assert np.allclose(farm_powers[2,0,21], farm_powers[2,0,21:25])
diff --git a/tests/reg_tests/jensen_jimenez_regression_test.py b/tests/reg_tests/jensen_jimenez_regression_test.py
index 5c5864f3..5aa11d93 100644
--- a/tests/reg_tests/jensen_jimenez_regression_test.py
+++ b/tests/reg_tests/jensen_jimenez_regression_test.py
@@ -14,7 +14,8 @@
 
 import numpy as np
 
-from tests.conftest import N_TURBINES, N_WIND_DIRECTIONS, N_WIND_SPEEDS, print_test_values, assert_results_arrays
+from tests.conftest import N_TURBINES, N_WIND_DIRECTIONS, N_WIND_SPEEDS, ROTOR_DIAMETER
+from tests.conftest import print_test_values, assert_results_arrays
 from floris.simulation import Ct, Floris, power, axial_induction, average_velocity
 
 
@@ -280,3 +281,55 @@ def test_regression_yaw(sample_inputs_fixture):
         )
 
     assert_results_arrays(test_results[0], yawed_baseline)
+
+
+def test_regression_small_grid_rotation(sample_inputs_fixture):
+    """
+    Where wake models are masked based on the x-location of a turbine, numerical precision
+    can cause masking to fail unexpectedly. For example, in the configuration here one of
+    the turbines has these delta x values;
+
+    [[4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]
+     [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]
+     [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]
+     [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]
+     [4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13 4.54747351e-13]]
+
+    and therefore the masking statement is False when it should be True. This causes the current
+    turbine to be affected by its own wake. This test requires that at least in this particular
+    configuration the masking correctly filters grid points.
+    """
+    sample_inputs_fixture.floris["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL
+    sample_inputs_fixture.floris["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL
+    X, Y = np.meshgrid(
+        6.0 * 126.0 * np.arange(0, 5, 1),
+        6.0 * 126.0 * np.arange(0, 5, 1)
+    )
+    X = X.flatten()
+    Y = Y.flatten()
+
+    sample_inputs_fixture.floris["farm"]["layout_x"] = X
+    sample_inputs_fixture.floris["farm"]["layout_y"] = Y
+
+    floris = Floris.from_dict(sample_inputs_fixture.floris)
+    floris.steady_state_atmospheric_condition()
+
+    # farm_avg_velocities = average_velocity(floris.flow_field.u)
+    velocities = floris.flow_field.u
+    yaw_angles = floris.farm.yaw_angles
+
+    farm_powers = power(
+        floris.flow_field.air_density,
+        velocities,
+        yaw_angles,
+        floris.turbine.pP,
+        floris.turbine.power_interp,
+    )
+
+    # A "column" is oriented parallel to the wind direction
+    # Columns 1 - 4 should have the same power profile
+    # Column 5 is completely unwaked in this model
+    assert np.allclose(farm_powers[2,0,0:5], farm_powers[2,0,5:10])
+    assert np.allclose(farm_powers[2,0,0:5], farm_powers[2,0,10:15])
+    assert np.allclose(farm_powers[2,0,0:5], farm_powers[2,0,15:20])
+    assert np.allclose(farm_powers[2,0,20], farm_powers[2,0,20:25])