using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace PRMLibrary { public class Index { public int Row; public int Col; public Index(int Row, int Col) { this.Row = Row; this.Col = Col; } public Index() : this(0, 0) { } } public class Coordinate { public double X; public double Y; public Coordinate(double X, double Y) { this.X = X; this.Y = Y; } public Coordinate() : this(0, 0) { } } public class PrognosticCell { public double velocityX; public double velocityY; public Coordinate coords; public PrognosticCell(Coordinate coord, double vX, double vY) { this.coords = coord; this.velocityX = vX; this.velocityY = vY; } } public class ClimateCell { public double wind5; public double wind10; public double wind15; public Coordinate coords; public ClimateCell(Coordinate coord, double w5, double w10, double w15) { this.coords = coord; this.wind5 = w5; this.wind10 = w10; this.wind15 = w15; } } public struct CellSize { public double width; public double height; public CellSize(double wdh, double hgh) { this.width = wdh; this.height = hgh; } } public class Powerline { public int identifier { get; set; } public int year; public double height; public int power; public List<Coordinate> coords { get; set; } public int pointFromID; public int pointToID; public bool isbroken; public bool ison; public Powerline(List<Coordinate> coord, int id, int yer, double h, int pw, bool isbrkn, bool ison, int toID, int fromID) { this.coords = coord; this.identifier = id; this.year = yer; this.height = h; this.power = pw; this.isbroken = isbrkn; this.ison = ison; this.pointFromID = fromID; this.pointToID = toID; } public Powerline() : base() { } } public class PowerStation { public int identifier; public Coordinate coords; public string name; public int power; public string type; public bool issource; public bool ison; public List<Powerline> linelist; public PowerStation(Coordinate crds, int id, string stname, int stpower, string sttype, bool issource, bool ison) { this.coords = crds; this.identifier = id; this.name = stname; this.power = stpower; this.type = sttype; this.issource = issource; this.ison = ison; } public PowerStation() : base() { } } enum FunctionType { FunctionVelocityX = 0, FunctionVelocityY = 1, FunctionClimate5 = 2, FunctionClimate10 = 3, FunctionClimate15 = 4 } public class Module { //prognistic raster info public List<List<PrognosticCell>> prognosticCells; public CellSize prognosticCellSize; public double[] prognosticAffineCoefficients; //climate raster info public List<List<ClimateCell>> climateCells; public CellSize climateCellSize; public double[] climateAffineCoefficients; //lines collection public List<Powerline> powerLines; //station collection public List<PowerStation> powerStations; //broken stations and lines public List<PowerStation> disabledStations = new List<PowerStation>(); public List<Powerline> disabledLines = new List<Powerline>(); public double dist_threshold = 500; //Main function for power graph algorithm public void checkPower() { //get the graph PreparingPowerItems(); //start from source points foreach (PowerStation pwstation in powerStations) { if (pwstation.issource) { CheckPowerPointsForStation(pwstation); } } foreach (Powerline line in powerLines) { if (line.isbroken) { disabledLines.Add(line); } } foreach (PowerStation powerStation in powerStations) { if (!powerStation.ison && !(powerStation.type.ToUpperInvariant().Trim() == "POLE")){ disabledStations.Add(powerStation); } } return; } //search function for power graph private void CheckPowerPointsForStation(PowerStation sourcepoint) { if (!sourcepoint.ison) { throw new Exception("CheckPowerPointsForStation is called from disabled sourcepoint"); return; } // if the point is not a pole - i.e. we know // it can redistribute power within connected lines // we turn it ON if any of the connected lines are powered foreach (Powerline line in sourcepoint.linelist) { if (!line.isbroken && !line.ison) { line.ison = true; foreach (PowerStation powerStation in powerStations) { if (powerStation.identifier != sourcepoint.identifier && (powerStation.identifier == line.pointFromID || powerStation.identifier == line.pointToID)) { if (!(sourcepoint.type.Trim().ToUpperInvariant() == "POLE")) { powerStation.ison = true; CheckPowerPointsForStation(powerStation); } else { // if line is a pole we have to check if it's actually able to // get electricity to other points i.e. no connected lines are broken bool powerLineCheck = false; foreach (Powerline powerline in powerStation.linelist) { if (powerline.isbroken) { powerLineCheck = true; } } if (!powerLineCheck) { powerStation.ison = true; CheckPowerPointsForStation(powerStation); } } } } } //else we have broken line or already switched ON powerpoint } } //preparing powerstations and lists of lines for them to get a graph-like structure private void PreparingPowerItems() { //First we make sure that all the sources are ON //and all non sources are OFF foreach (PowerStation powerStation in powerStations) { if (powerStation.issource == true) { powerStation.ison = true; } else { powerStation.ison = false; } // for each power station we create a list of powerlines it is attached to List<Powerline> lines = new List<Powerline>(); foreach (Powerline line in powerLines) { //we also switch OFF all lines line.ison = false; if (line.pointFromID == powerStation.identifier || line.pointToID == powerStation.identifier) { lines.Add(line); } } powerStation.linelist = lines; } } public List<Powerline> brokenPowerLinesAfterCheck() { List<Powerline> brklines = new List<Powerline>(); // actually there are curves in powerLines foreach (Powerline powerCurve in powerLines) { // get coordinates list List<Coordinate> points = powerCurve.coords; List<bool> checkList = new List<bool>(); // cycle throw all points in line for (int i = 1; i < points.Count; i++) { double x1 = points[i - 1].X; double y1 = points[i - 1].Y; double x2 = points[i].X; double y2 = points[i].Y; bool result = linearLineIsBroken(points[i - 1], points[i], powerCurve.height, powerCurve.power); checkList.Add(result); } foreach (bool chkpnt in checkList) { if (chkpnt == true) { powerCurve.isbroken = true; } } if (powerCurve.isbroken) { brklines.Add(powerCurve); } } return brklines; } private bool linearLineIsBroken(Coordinate coord1, Coordinate coord2, double heightLine, int power) { double distance = Math.Sqrt((coord2.X - coord1.X) * (coord2.X - coord1.X) + (coord2.Y - coord1.Y) * (coord2.Y - coord1.Y)); double distpropD = distance / dist_threshold; List<Coordinate> pointlist = new List<Coordinate>(); Coordinate midpoint = new Coordinate(); int distpropI = Convert.ToInt32(distpropD); if (distpropI > 1) { double constXdiff = (coord2.X - coord1.X) / distpropI; double constYdiff = (coord2.Y - coord1.Y) / distpropI; Coordinate subCoord1 = new Coordinate(coord1.X, coord1.Y); Coordinate subCoord2 = new Coordinate(coord1.X + constXdiff, coord1.Y + constXdiff); for (int j = 0; j < distpropI; j++) { if (j == 0) { midpoint.X = (subCoord1.X + subCoord2.X) / 2; midpoint.Y = (subCoord1.Y + subCoord2.Y) / 2; pointlist.Add(midpoint); } else { // move to next center subCoord1.X = subCoord2.X; subCoord1.Y = subCoord2.Y; subCoord2.X = subCoord1.X + constXdiff; subCoord2.Y = subCoord1.Y + constYdiff; midpoint.X = (subCoord1.X + subCoord2.X) / 2; midpoint.Y = (subCoord1.Y + subCoord2.Y) / 2; pointlist.Add(midpoint); } } } else { midpoint.X = (coord1.X + coord2.X) / 2; midpoint.Y = (coord1.Y + coord2.Y) / 2; pointlist.Add(midpoint); } FunctionType climateType; if (power > 5 && power < 330) { climateType = FunctionType.FunctionClimate10; } else { if (power <= 5) { climateType = FunctionType.FunctionClimate5; } else { climateType = FunctionType.FunctionClimate15; } } List<bool> checkbool = new List<bool>(); foreach (Coordinate coords in pointlist) { bool res = false; double uwind = interpol(coords, FunctionType.FunctionVelocityX); double vwind = interpol(coords, FunctionType.FunctionVelocityY); double climwind = interpol(coords, climateType); double umod = Math.Sqrt(uwind * uwind + vwind * vwind); ; double angleline = Math.Atan2((coord2.Y - coord1.Y), (coord2.X - coord1.X)); double anglewind = Math.Atan2(vwind, uwind) - angleline; double sinwind = Math.Sin(anglewind); double C_height = 1.0; if (umod < 20) { //wind is too low res = false; } else { // calculate prognostic and threshold windstress double p1 = -0.00078501; double p2 = 0.13431; double p3 = -2.11112; double p4 = 19.548; double qpr = p1 * umod * umod * umod + p2 * umod * umod + p3 * umod + p4; double qcl = p1 * climwind * climwind * climwind + p2 * climwind * climwind + p3 * climwind + p4; double Ppr = qpr * C_height * sinwind * sinwind; double Pcl = qcl * C_height * 1.0; if (Ppr >= Pcl) { // here line is broken res = true; } } checkbool.Add(res); } bool result = false; foreach (bool lastcheck in checkbool) { if (lastcheck == true) { result = true; } } return result; } double[] affineCoefficients(FunctionType functionType) { switch (functionType) { case FunctionType.FunctionVelocityX: case FunctionType.FunctionVelocityY: { return prognosticAffineCoefficients; } case FunctionType.FunctionClimate5: case FunctionType.FunctionClimate10: case FunctionType.FunctionClimate15: { return climateAffineCoefficients; } default: break; } return null; } // Taken from DotSpatial https://github.com/ViceIce/DotSpatial/blob/22c156c7646b1595d88d2523c066a9c6ab4d3a53/DotSpatial.Data/RasterBoundsExt.cs // RasterBoundsExt.cs. Define AffineCoefficients like this. Index projectionToCell(Coordinate coordinate, FunctionType functionType) { double[] c = affineCoefficients(functionType); double rw, cl; if (c[2] == 0 && c[4] == 0) { // If we don't have skew terms, then the x and y terms are independent. // also both of the other tests will have divide by zero errors and fail. cl = (coordinate.X - c[0]) / c[1]; rw = (coordinate.Y - c[3]) / c[5]; } else if (c[2] != 0) { // this equation will have divide by zero if c[2] is zero, but works if c[4] is zero double div = (c[5] * c[1]) / c[2] - c[4]; cl = (c[3] + (c[5] * coordinate.X) / c[2] - (c[5] * c[0]) / c[2] - coordinate.Y) / div; rw = (coordinate.X - c[0] - c[1] * cl) / c[2]; } else { double div = (c[4] * c[2] / c[1] - c[5]); rw = (c[3] + c[4] * coordinate.X / c[1] - c[4] * c[0] / c[1] - coordinate.Y) / div; cl = (coordinate.X - c[2] * rw - c[0]) / c[1]; } int iRow = (int)Math.Round(rw); int iCol = (int)Math.Round(cl); if (iRow < 0 || iCol < 0 || iRow >= countInList(functionType, true) || iCol >= countInList(functionType, false)) { return new Index(); } return new Index(iRow, iCol); } Coordinate cellToProjection(Index index, FunctionType functionType) { switch (functionType) { case FunctionType.FunctionVelocityX: case FunctionType.FunctionVelocityY: { return prognosticCells[index.Row][index.Col].coords; } case FunctionType.FunctionClimate5: case FunctionType.FunctionClimate10: case FunctionType.FunctionClimate15: { return climateCells[index.Row][index.Col].coords; } default: break; } return null; } int countInList(FunctionType functionType, bool forRows) { switch (functionType) { case FunctionType.FunctionVelocityX: case FunctionType.FunctionVelocityY: { return forRows ? prognosticCells.Count : prognosticCells[0].Count; } case FunctionType.FunctionClimate5: case FunctionType.FunctionClimate10: case FunctionType.FunctionClimate15: { return forRows ? climateCells.Count : climateCells[0].Count; } default: break; } return 0; } double valueForFunction(FunctionType functionType, Index index) { switch (functionType) { case FunctionType.FunctionVelocityX: { return prognosticCells[index.Row][index.Col].velocityX; } case FunctionType.FunctionVelocityY: { return prognosticCells[index.Row][index.Col].velocityY; } case FunctionType.FunctionClimate5: { return climateCells[index.Row][index.Col].wind5; } case FunctionType.FunctionClimate10: { return climateCells[index.Row][index.Col].wind10; } case FunctionType.FunctionClimate15: { return climateCells[index.Row][index.Col].wind15; } default: break; } return 0; } CellSize cellSizeForFunction(FunctionType functionType) { switch (functionType) { case FunctionType.FunctionVelocityX: case FunctionType.FunctionVelocityY: { return prognosticCellSize; } case FunctionType.FunctionClimate5: case FunctionType.FunctionClimate10: case FunctionType.FunctionClimate15: { return climateCellSize; } default: break; } throw new Exception("There is no cell size"); } double interpol(Coordinate coords, FunctionType functionType) { // select directions for projections const bool normalX = true;// true - East, false West const bool normalY = false;// true - North, false South Index rc = projectionToCell(coords, functionType); Coordinate center = cellToProjection(rc, functionType); double xDiff = coords.X - center.X; double yDiff = coords.Y - center.Y; //calculate second index int row2, col2; if ((xDiff >= 0 && normalX) || (!normalX && xDiff < 0)) { row2 = rc.Row >= countInList(functionType, true) - 1 ? rc.Row - 1 : rc.Row + 1; } else { row2 = rc.Row > 0 ? rc.Row - 1 : rc.Row + 1; } if ((yDiff >= 0 && normalY) || (!normalY && yDiff < 0)) { col2 = rc.Col >= countInList(functionType, false) - 1 ? rc.Col - 1 : rc.Col + 1; } else { col2 = rc.Col > 0 ? rc.Col - 1 : rc.Col + 1; } // indexes and values at bounds Index rcBotLeft = new Index(Math.Min(row2, rc.Row), Math.Min(col2, rc.Col)); Index rcBotRight = new Index(Math.Max(row2, rc.Row), Math.Min(col2, rc.Col)); Index rcTopLeft = new Index(Math.Min(row2, rc.Row), Math.Max(col2, rc.Col)); Index rcTopRight = new Index(Math.Max(row2, rc.Row), Math.Max(col2, rc.Col)); double valBotLeft = valueForFunction(functionType, rcBotLeft); double valBotRight = valueForFunction(functionType, rcBotRight); double valTopLeft = valueForFunction(functionType, rcTopLeft); double valTopRight = valueForFunction(functionType, rcTopRight); Coordinate origin = cellToProjection(rcBotLeft, functionType); //PRM_coordinate last = cellToProjection(rcTopRight, functionType);//test only // sizes for cell double hx = cellSizeForFunction(functionType).width; double hy = cellSizeForFunction(functionType).height; // coefficients double px = (coords.X - origin.X) / hx; double py = (coords.Y - origin.Y) / hy; // inverse directions px *= normalX ? 1 : -1; py *= normalY ? 1 : -1; // interpolation double top = (1 - px) * valTopLeft + px * valTopRight; double bot = (1 - px) * valBotLeft + px * valBotRight; double rval = (1 - py) * bot + py * top; return rval; } } }