/// <summary> /// Creates an n by n + 1 (where n is the length of the provided array /// of source panels) matrix representing the linear equations to be /// solved for the strengths of the source panels. /// </summary> /// <param name="sourcePanels"> /// The array of source panels. /// </param> /// <param name="vPanels"> /// A vector representing the velocity vector of the panels (the /// negative far-field flow velocity). /// </param> /// <returns>The equation matrix.</returns> public static double[,] CreateEquationMatrix( UnitStrengthRectangularSourcePanel[] sourcePanels, Vector3 vPanels) { int rows = sourcePanels.Length; int columns = rows + 1; double[,] equationMatrix = new double[rows, columns]; for (int i = 0; i < rows; i++) { Vector3 surfaceNormal = Vector3.Cross(sourcePanels[i].U, sourcePanels[i].V); for (int j = 0; j < columns - 1; j++) { Vector3 unscaledVelocity = Velocity.ComputeSteadyState( sourcePanels[i].Position, sourcePanels[j]); equationMatrix[i, j] = Vector3.Dot(unscaledVelocity, surfaceNormal); } equationMatrix[i, columns - 1] = Vector3.Dot(vPanels, surfaceNormal); } return(equationMatrix); }
public void ComputeSteadyState_WithDrInTheUVPlane_ReturnsTheCorrectValue() { // Arrange float aOver2 = 1; float bOver2 = 2; Vector3 dr = new(3, 4, 0); Vector3 u = Vector3.UnitX; Vector3 v = Vector3.UnitY; float d11 = (dr - aOver2 * u - bOver2 * v).Length(); float d12 = (dr - aOver2 * u + bOver2 * v).Length(); float d21 = (dr + aOver2 * u - bOver2 * v).Length(); float d22 = (dr + aOver2 * u + bOver2 * v).Length(); float uDotDr = Vector3.Dot(u, dr); float vDotDr = Vector3.Dot(v, dr); float ln1 = MathF.Log(MathF.Abs( (-bOver2 - vDotDr + d11) * (bOver2 - vDotDr + d22) / ((-bOver2 - vDotDr + d21) * (bOver2 - vDotDr + d12)))); float ln2 = MathF.Log(MathF.Abs( (-aOver2 - uDotDr + d11) * (aOver2 - uDotDr + d22) / ((aOver2 - uDotDr + d21) * (-aOver2 - uDotDr + d12)))); Vector3 correctResult = 1 / (4 * MathF.PI) * (ln1 * u + ln2 * v); // Act Vector3 result = Velocity.ComputeSteadyState(aOver2, bOver2, dr, u, v); // Assert Assert.AreEqual(correctResult, result); }
public void SolveForSourcePanelStrengths_WithUnitCubeInput_SatisfiesTheTangentialFlowConstraint() { // Arrange UnitStrengthRectangularSourcePanel[] sourcePanels = new UnitStrengthRectangularSourcePanel[6] { new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(0.5F, 0, 0), U = Vector3.UnitY, V = Vector3.UnitZ }, new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(-0.5F, 0, 0), U = Vector3.UnitY, V = Vector3.UnitZ }, new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(0, 0.5F, 0), U = Vector3.UnitZ, V = Vector3.UnitX }, new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(0, -0.5F, 0), U = Vector3.UnitZ, V = Vector3.UnitX }, new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(0, 0, 0.5F), U = Vector3.UnitX, V = Vector3.UnitY }, new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(0, 0, -0.5F), U = Vector3.UnitX, V = Vector3.UnitY } }; Vector3 vPanels = new Vector3(10, 0, 0); // Act double[] strengths = Solver.SolveForSourcePanelStrengths(sourcePanels, vPanels); bool flowIsTangentialAtEachControlPoint = true; for (int i = 0; i < sourcePanels.Length; i++) { Vector3 surfaceNormal = Vector3.Cross(sourcePanels[i].U, sourcePanels[i].V); Vector3 velocity = vPanels; for (int j = 0; j < sourcePanels.Length; j++) { velocity += (float)strengths[j] * Velocity.ComputeSteadyState(sourcePanels[i].Position, sourcePanels[j]); } if (Vector3.Dot(velocity, surfaceNormal) != 0) { flowIsTangentialAtEachControlPoint = false; } } // Assert Assert.IsTrue(flowIsTangentialAtEachControlPoint); }
public void ComputeSteadyState_WithDrEqualToTheZeroVector_ReturnsTheZeroVector() { // Arrange float aOver2 = 1; float bOver2 = 2; Vector3 dr = Vector3.Zero; Vector3 u = Vector3.UnitX; Vector3 v = Vector3.UnitY; // Act Vector3 result = Velocity.ComputeSteadyState(aOver2, bOver2, dr, u, v); // Assert Assert.AreEqual(Vector3.Zero, result); }
public void ComputeSteadyState_WithDrOutsideTheUVPlane_ReturnsTheCorrectValue() { // Arrange float aOver2 = 1; float bOver2 = 2; Vector3 dr = new(3, 4, 5); Vector3 u = Vector3.UnitX; Vector3 v = Vector3.UnitY; float d11 = (dr - aOver2 * u - bOver2 * v).Length(); float d12 = (dr - aOver2 * u + bOver2 * v).Length(); float d21 = (dr + aOver2 * u - bOver2 * v).Length(); float d22 = (dr + aOver2 * u + bOver2 * v).Length(); float uDotDr = Vector3.Dot(u, dr); float vDotDr = Vector3.Dot(v, dr); float ln1 = MathF.Log(MathF.Abs( (-bOver2 - vDotDr + d11) * (bOver2 - vDotDr + d22) / ((-bOver2 - vDotDr + d21) * (bOver2 - vDotDr + d12)))); float ln2 = MathF.Log(MathF.Abs( (-aOver2 - uDotDr + d11) * (aOver2 - uDotDr + d22) / ((aOver2 - uDotDr + d21) * (-aOver2 - uDotDr + d12)))); Vector3 c = dr - uDotDr * u - vDotDr * v; float sqrtK = c.Length(); float atan11 = MathF.Atan2( (-aOver2 - uDotDr) * (-bOver2 - vDotDr), sqrtK * d11); float atan12 = MathF.Atan2( (-aOver2 - uDotDr) * (bOver2 - vDotDr), sqrtK * d12); float atan21 = MathF.Atan2( (aOver2 - uDotDr) * (-bOver2 - vDotDr), sqrtK * d21); float atan22 = MathF.Atan2( (aOver2 - uDotDr) * (bOver2 - vDotDr), sqrtK * d22); float atanSum = atan22 - atan21 - atan12 + atan11; Vector3 correctResult = 1 / (4 * MathF.PI) * (ln1 * u + ln2 * v + (atanSum / sqrtK) * c); // Act Vector3 result = Velocity.ComputeSteadyState(aOver2, bOver2, dr, u, v); // Assert Assert.AreEqual(correctResult, result); }
public void CreateEquationMatrix_WithUnitCubeInput_CreatesTheCorrectLHS() { // Arrange UnitStrengthRectangularSourcePanel[] sourcePanels = new UnitStrengthRectangularSourcePanel[6] { new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(0.5F, 0, 0), U = Vector3.UnitY, V = Vector3.UnitZ }, new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(-0.5F, 0, 0), U = Vector3.UnitY, V = Vector3.UnitZ }, new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(0, 0.5F, 0), U = Vector3.UnitZ, V = Vector3.UnitX }, new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(0, -0.5F, 0), U = Vector3.UnitZ, V = Vector3.UnitX }, new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(0, 0, 0.5F), U = Vector3.UnitX, V = Vector3.UnitY }, new UnitStrengthRectangularSourcePanel { AOver2 = 0.5F, BOver2 = 0.5F, Position = new Vector3(0, 0, -0.5F), U = Vector3.UnitX, V = Vector3.UnitY } }; Vector3 vPanels = new Vector3(10, 0, 0); double[,] correctLHS = new double[6, 6] { { Velocity.ComputeSteadyState(sourcePanels[0].Position, sourcePanels[0]).X, Velocity.ComputeSteadyState(sourcePanels[0].Position, sourcePanels[1]).X, Velocity.ComputeSteadyState(sourcePanels[0].Position, sourcePanels[2]).X, Velocity.ComputeSteadyState(sourcePanels[0].Position, sourcePanels[3]).X, Velocity.ComputeSteadyState(sourcePanels[0].Position, sourcePanels[4]).X, Velocity.ComputeSteadyState(sourcePanels[0].Position, sourcePanels[5]).X }, { Velocity.ComputeSteadyState(sourcePanels[1].Position, sourcePanels[0]).X, Velocity.ComputeSteadyState(sourcePanels[1].Position, sourcePanels[1]).X, Velocity.ComputeSteadyState(sourcePanels[1].Position, sourcePanels[2]).X, Velocity.ComputeSteadyState(sourcePanels[1].Position, sourcePanels[3]).X, Velocity.ComputeSteadyState(sourcePanels[1].Position, sourcePanels[4]).X, Velocity.ComputeSteadyState(sourcePanels[1].Position, sourcePanels[5]).X }, { Velocity.ComputeSteadyState(sourcePanels[2].Position, sourcePanels[0]).Y, Velocity.ComputeSteadyState(sourcePanels[2].Position, sourcePanels[1]).Y, Velocity.ComputeSteadyState(sourcePanels[2].Position, sourcePanels[2]).Y, Velocity.ComputeSteadyState(sourcePanels[2].Position, sourcePanels[3]).Y, Velocity.ComputeSteadyState(sourcePanels[2].Position, sourcePanels[4]).Y, Velocity.ComputeSteadyState(sourcePanels[2].Position, sourcePanels[5]).Y }, { Velocity.ComputeSteadyState(sourcePanels[3].Position, sourcePanels[0]).Y, Velocity.ComputeSteadyState(sourcePanels[3].Position, sourcePanels[1]).Y, Velocity.ComputeSteadyState(sourcePanels[3].Position, sourcePanels[2]).Y, Velocity.ComputeSteadyState(sourcePanels[3].Position, sourcePanels[3]).Y, Velocity.ComputeSteadyState(sourcePanels[3].Position, sourcePanels[4]).Y, Velocity.ComputeSteadyState(sourcePanels[3].Position, sourcePanels[5]).Y }, { Velocity.ComputeSteadyState(sourcePanels[4].Position, sourcePanels[0]).Z, Velocity.ComputeSteadyState(sourcePanels[4].Position, sourcePanels[1]).Z, Velocity.ComputeSteadyState(sourcePanels[4].Position, sourcePanels[2]).Z, Velocity.ComputeSteadyState(sourcePanels[4].Position, sourcePanels[3]).Z, Velocity.ComputeSteadyState(sourcePanels[4].Position, sourcePanels[4]).Z, Velocity.ComputeSteadyState(sourcePanels[4].Position, sourcePanels[5]).Z }, { Velocity.ComputeSteadyState(sourcePanels[5].Position, sourcePanels[0]).Z, Velocity.ComputeSteadyState(sourcePanels[5].Position, sourcePanels[1]).Z, Velocity.ComputeSteadyState(sourcePanels[5].Position, sourcePanels[2]).Z, Velocity.ComputeSteadyState(sourcePanels[5].Position, sourcePanels[3]).Z, Velocity.ComputeSteadyState(sourcePanels[5].Position, sourcePanels[4]).Z, Velocity.ComputeSteadyState(sourcePanels[5].Position, sourcePanels[5]).Z } }; // Act double[,] result = Solver.CreateEquationMatrix(sourcePanels, vPanels); bool lhsIsCorrect = true; for (int i = 0; i < 6; i++) { for (int j = 0; j < 6; j++) { if (result[i, j] != correctLHS[i, j]) { lhsIsCorrect = false; } } } // Assert Assert.IsTrue(lhsIsCorrect); }
public void ComputeSteadyState() { Vector3 _ = Velocity.ComputeSteadyState(_r, _sourcePanel); }