using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Azimuth; using Slipstream.StokesFlow; using Annulus; namespace Slipstream.UnitTests.StokesFlow { public class StokesPanelGroupTests : UnitTestSharp.TestFixture { public void XYNotSymmetric() { var panel1 = new StokesPanelGroup(new Vector[] { Vector.Zero }, new Vector[] { Vector.UnitY }, 10); var panel2 = new StokesPanelGroup(new Vector[] { Vector.Zero }, new Vector[] { Vector.UnitX }, 10); panel1.EnforceBoundaryConditions(-Vector.UnitY); panel2.EnforceBoundaryConditions(-Vector.UnitY); var samplePoint = Vector.UnitY + Vector.UnitX; CheckNotEqual(panel1.VelocityAtPoint(samplePoint), panel2.VelocityAtPoint(-samplePoint)); } public void YSymmetry_YVelocity() { var panel = new StokesPanelGroup(new Vector[] { Vector.Zero }, new Vector[] { Vector.UnitY }, 10); panel.EnforceBoundaryConditions(-Vector.UnitY); var samplePoint = Vector.UnitY * 10; CheckEqual(panel.VelocityAtPoint(samplePoint), panel.VelocityAtPoint(-samplePoint)); } public void YSymmetry_XVelocity() { var panel = new StokesPanelGroup(new Vector[] { Vector.Zero }, new Vector[] { Vector.UnitY }, 10); panel.EnforceBoundaryConditions(Vector.UnitX); var samplePoint = Vector.UnitY * 10; CheckEqual(panel.VelocityAtPoint(samplePoint), panel.VelocityAtPoint(-samplePoint)); } public void XSymmetry_YVelocity() { var panel = new StokesPanelGroup(new Vector[] { Vector.Zero }, new Vector[] { Vector.UnitY }, 10); panel.EnforceBoundaryConditions(-Vector.UnitY); var samplePoint = Vector.UnitX * 10; CheckEqual(panel.VelocityAtPoint(samplePoint), panel.VelocityAtPoint(-samplePoint)); } public void XSymmetry_XVelocity() { var panel = new StokesPanelGroup(new Vector[] { Vector.Zero }, new Vector[] { Vector.UnitY }, 10); panel.EnforceBoundaryConditions(Vector.UnitX); var samplePoint = Vector.UnitX * 10; CheckEqual(panel.VelocityAtPoint(samplePoint), panel.VelocityAtPoint(-samplePoint)); } public void Rotation() { var panel1 = new StokesPanelGroup(new Vector[] { Vector.Zero }, new Vector[] { Vector.UnitY }, 10); var panel2 = new StokesPanelGroup(new Vector[] { Vector.Zero }, new Vector[] { Vector.UnitX }, 10); panel1.EnforceBoundaryConditions(-Vector.UnitY); panel2.EnforceBoundaryConditions(-Vector.UnitX); var point1 = new Vector(1, 2); var point2 = point1; point2.Rotate(-Math.PI / 2); var velocity1 = panel1.VelocityAtPoint(point1); var velocity2 = panel2.VelocityAtPoint(point2); velocity2.Rotate(Math.PI / 2); CheckEqual(velocity1, velocity2); } public void Translation() { var panel1 = new StokesPanelGroup(new Vector[] { Vector.Zero }, new Vector[] { Vector.UnitY }, 10); var panel2 = new StokesPanelGroup(new Vector[] { new Vector(10, 5), }, new Vector[] { Vector.UnitY }, 10); panel1.EnforceBoundaryConditions(-Vector.UnitY); panel2.EnforceBoundaryConditions(-Vector.UnitY); var point1 = new Vector(1, 2); var point2 = new Vector(11, 7); CheckEqual(panel1.VelocityAtPoint(point1), panel2.VelocityAtPoint(point2)); } public void TwoColinear() { var panel = new StokesPanelGroup( new Vector[] { Vector.Zero, Vector.UnitX * 10 }, new Vector[] { Vector.UnitY, Vector.UnitY }, 10); panel.EnforceBoundaryConditions(-Vector.UnitY); CheckEqual(Vector.UnitY, panel.VelocityAtPoint(Vector.Zero)); CheckEqual(Vector.UnitY, panel.VelocityAtPoint(Vector.UnitX * 10)); } public void TwoParallelSameNormals() { var panel = new StokesPanelGroup( new Vector[] { Vector.Zero, Vector.UnitX * 10 }, new Vector[] { -Vector.UnitX, -Vector.UnitX }, 10); panel.EnforceBoundaryConditions(-Vector.UnitX); CheckEqual(Vector.UnitX, panel.VelocityAtPoint(Vector.Zero)); CheckEqual(Vector.UnitX, panel.VelocityAtPoint(Vector.UnitX * 10)); } public void TwoParallelAntiparallelNormals() { var panel = new StokesPanelGroup( new Vector[] { Vector.Zero, Vector.UnitX * 10 }, new Vector[] { -Vector.UnitX, Vector.UnitX }, 10); panel.EnforceBoundaryConditions(-Vector.UnitX); CheckEqual(Vector.UnitX, panel.VelocityAtPoint(Vector.Zero)); CheckEqual(Vector.UnitX, panel.VelocityAtPoint(Vector.UnitX * 10)); } public void Square_X() { var panel = new StokesPanelGroup( new Vector[] { new Vector(-15, 0), new Vector(0, 15), new Vector(15, 0), new Vector(0, -15) }, new Vector[] { -Vector.UnitX, Vector.UnitY, Vector.UnitX, -Vector.UnitY }, 10); panel.EnforceBoundaryConditions(Vector.UnitX); foreach(var point in panel.ControlPoints) { CheckEqual(-Vector.UnitX, panel.VelocityAtPoint(point)); } } public void Square_Y() { var panel = new StokesPanelGroup( new Vector[] { new Vector(-15, 0), new Vector(0, 15), new Vector(15, 0), new Vector(0, -15) }, new Vector[] { -Vector.UnitX, Vector.UnitY, Vector.UnitX, -Vector.UnitY }, 10); panel.EnforceBoundaryConditions(Vector.UnitY); foreach (var point in panel.ControlPoints) { CheckEqual(-Vector.UnitY, panel.VelocityAtPoint(point)); } } public void Square_XY() { var panel = new StokesPanelGroup( new Vector[] { new Vector(-15, 0), new Vector(0, 15), new Vector(15, 0), new Vector(0, -15) }, new Vector[] { -Vector.UnitX, Vector.UnitY, Vector.UnitX, -Vector.UnitY }, 10); panel.EnforceBoundaryConditions(Vector.UnitY + Vector.UnitX); foreach (var point in panel.ControlPoints) { CheckEqual(-Vector.UnitX - Vector.UnitY, panel.VelocityAtPoint(point)); } } public void Square_SymmetricAcrossStream() { var panel = new StokesPanelGroup( new Vector[] { new Vector(-15, 0), new Vector(0, 15), new Vector(15, 0), new Vector(0, -15) }, new Vector[] { -Vector.UnitX, Vector.UnitY, Vector.UnitX, -Vector.UnitY }, 10); panel.EnforceBoundaryConditions(Vector.UnitX); CheckEqual(panel.VelocityAtPoint(new Vector(0, 10)), panel.VelocityAtPoint(new Vector(0, -10))); } public void Square_SymmetricAcrossStream_UpstreamDownstream() { var panel = new StokesPanelGroup( new Vector[] { new Vector(-15, 0), new Vector(0, 15), new Vector(15, 0), new Vector(0, -15) }, new Vector[] { -Vector.UnitX, Vector.UnitY, Vector.UnitX, -Vector.UnitY }, 10); panel.EnforceBoundaryConditions(Vector.UnitX); CheckEqual(panel.VelocityAtPoint(new Vector(-1, 10)), panel.VelocityAtPoint(new Vector(1, -10))); } public void Square_SymmetricThroughStream() { var panel = new StokesPanelGroup( new Vector[] { new Vector(-15, 0), new Vector(0, 15), new Vector(15, 0), new Vector(0, -15) }, new Vector[] { -Vector.UnitX, Vector.UnitY, Vector.UnitX, -Vector.UnitY }, 10); panel.EnforceBoundaryConditions(-Vector.UnitX); var velocityUpstream = panel.VelocityAtPoint(new Vector(-10, 1)); var velocityDownstream = panel.VelocityAtPoint(new Vector(10, -1)); CheckEqual(velocityUpstream, velocityDownstream); } public void ApproximatelyCircle() { Scalar radius = 1; int subdivisions = 32; var points = new List(); for (int i = 0; i < subdivisions; ++i) { points.Add(radius * new Vector(Math.Cos(2 * Math.PI * i / subdivisions), Math.Sin(2 * Math.PI * i / subdivisions))); } var panels = new StokesPanelGroup(new SimplePolygon(points), subdivisions); panels.EnforceBoundaryConditions(Vector.UnitX); // Check is zero at control points. foreach(var point in panels.ControlPoints) { // CheckEqual(point, panels.VelocityAtPoint(point * 2) + Vector.UnitX); CheckEqual(Vector.Zero, panels.VelocityAtPoint(point) + Vector.UnitX); } // Check general flow now. for (int theta = 0; theta < 360; theta+= 10) { for (int r = 3; r < 10; ++r) { // For stokes flow past a sphere, // The stream function is C sin (theta) (r log r - 0.5 * r + 0.5 * 1/r) // So the partial derivatives are // dr = C sin(theta) * (-1 / (2r^2) + log(r) + 0.5) // dtheta = C cos(theta) * (-.5 r + 1/(2r) + r log(r)) var radAngle = theta * Math.PI / 180; var pdr = Math.Sin(radAngle) * (-1 / (2.0 * r * r) + Math.Log(r) + 0.5); var pdtheta = Math.Cos(radAngle) * (-.5 * r + 1.0 / (2.0 * r) + r * Math.Log(r)); var dr = 1.0 / r * pdtheta; var dtheta = -pdr; var expectedVelocity = new Vector( dr * Math.Cos(radAngle) - r * dtheta * Math.Sin(radAngle), dr * Math.Sin(radAngle) - r * dtheta * Math.Cos(radAngle)) / 2; var point = r * new Vector(Math.Cos(Math.PI - radAngle), Math.Sin(Math.PI - radAngle)); var velocity = panels.VelocityAtPoint(point); Check(expectedVelocity.X == velocity.X || expectedVelocity.X.Sign == velocity.X.Sign); Check(expectedVelocity.Y == velocity.Y || expectedVelocity.Y.Sign == velocity.Y.Sign); } } } public void MockPanel() { int count = 10; Scalar panelLength = 10; var points = new List(); for (int i = 0; i < count; ++i) { points.Add(new Vector(-panelLength / 2 + panelLength * (i / (Scalar)(count - 1)), 0)); } var freeStreamVelocity = new Vector(0, -1); var group = new StokesletGroup(points); group.EnforceBoundaryConditions(freeStreamVelocity); var samplePoint = new Vector(100, 100); var samplePoint2 = new Vector(1, 1); var pointsVelocity = group.VelocityAtPoint(samplePoint); var panel = new StokesPanelGroup(new Vector[] { Vector.Zero }, new Vector[] { Vector.UnitY }, panelLength); panel.EnforceBoundaryConditions(freeStreamVelocity); var panelVelocity = panel.VelocityAtPoint(samplePoint); var pointsVelocity2 = group.VelocityAtPoint(samplePoint2); var panelVelocity2 = panel.VelocityAtPoint(samplePoint2); // Uniform panels don't match the singularity distribution very well. //CheckEqual(pointsVelocity, panelVelocity); //CheckEqual(group.VelocityAtPoint(samplePoint2), panel.VelocityAtPoint(samplePoint2)); //CheckEqual(pointsVelocity.X / panelVelocity.X, pointsVelocity.Y / panelVelocity.Y); //CheckEqual(pointsVelocity2.X / panelVelocity2.X, pointsVelocity2.Y / panelVelocity2.Y); } } }