-
Антон Кудряшов authored
- deleted PRMLib obj,bin files
Антон Кудряшов authored- deleted PRMLib obj,bin files
PRMLibrary.cs 29.61 KiB
using System;
using System.Collections.Generic;
namespace PRMLibrary
{
/// <summary>
/// Index class for raster lists in list
/// </summary>
public class Index
{
/// <summary>
/// Outer index
/// </summary>
public int Row;
/// <summary>
/// Inner index
/// </summary>
public int Col;
/// <summary>
/// designated constructor
/// </summary>
/// <param name="Row">row index</param>
/// <param name="Col">column index</param>
public Index(int Row, int Col)
{
this.Row = Row;
this.Col = Col;
}
/// <summary>
/// default constructor
/// </summary>
public Index() : this(0, 0) { }
}
/// <summary>
/// Coordinate pair
/// </summary>
public class Coordinate
{
/// <summary>
/// easting coordinate
/// </summary>
public double X;
/// <summary>
/// northing coordinate
/// </summary>
public double Y;
/// <summary>
/// designated constructor
/// </summary>
/// <param name="X"></param>
/// <param name="Y"></param>
public Coordinate(double X, double Y)
{
this.X = X;
this.Y = Y;
}
/// <summary>
/// default constructor
/// </summary>
public Coordinate() : this(0, 0) { }
}
/// <summary>
/// Cell obj for regular prognostic wind field
/// </summary>
public class PrognosticCell
{
/// <summary>
/// U - component of wind velocity
/// </summary>
public double velocityX;
/// <summary>
/// V - component of wind velocity
/// </summary>
public double velocityY;
/// <summary>
/// Cell center coordinates
/// </summary>
public Coordinate coords;
/// <summary>
/// designated constructor
/// </summary>
/// <param name="coord"> Coordinate pair</param>
/// <param name="vX"> U component</param>
/// <param name="vY"> V component</param>
public PrognosticCell(Coordinate coord, double vX, double vY)
{
this.coords = coord;
this.velocityX = vX;
this.velocityY = vY;
}
/// <summary>
/// default constructor
/// </summary>
public PrognosticCell() : this(new Coordinate(), 0, 0) { }
}
/// <summary>
/// Cell obj for climate wind regular data
/// </summary>
public class ClimateCell
{
/// <summary>
/// once-in-5-years frequency wind
/// </summary>
public double wind5;
/// <summary>
/// once-in-10-years frequency wind
/// </summary>
public double wind10;
/// <summary>
/// once-in-15-years frequency wind
/// </summary>
public double wind15;
/// <summary>
/// Cell center coordinate pair
/// </summary>
public Coordinate coords;
/// <summary>
/// designated constructor
/// </summary>
/// <param name="coord"></param>
/// <param name="w5"></param>
/// <param name="w10"></param>
/// <param name="w15"></param>
public ClimateCell(Coordinate coord, double w5, double w10, double w15)
{
this.coords = coord;
this.wind5 = w5;
this.wind10 = w10;
this.wind15 = w15;
}
/// <summary>
/// default constructor
/// </summary>
public ClimateCell() : this(new Coordinate(), 0, 0, 0) { }
}
/// <summary>
/// Cell Size parameters
/// </summary>
public struct CellSize
{
public double width;
public double height;
public CellSize(double wdh, double hgh)
{
this.width = wdh;
this.height = hgh;
}
}
/// <summary>
/// power line object
/// </summary>
public class Powerline
{
/// <summary>
/// unique id
/// </summary>
public int identifier { get; set; }
/// <summary>
/// year of construction
/// </summary>
public int year;
/// <summary>
/// average height of cable span between two poles, meters
/// </summary>
public double height;
/// <summary>
/// power kW for switches
/// </summary>
public int power;
/// <summary>
/// Line vertices coordinate list
/// </summary>
public List<Coordinate> coords { get; set; }
/// <summary>
/// assigned powerstation/pole
/// </summary>
public int pointFromID;
/// <summary>
/// assigned powerstation/pole
/// </summary>
public int pointToID;
/// <summary>
/// broken/not broken switch
/// </summary>
public bool isbroken;
/// <summary>
/// power on switch
/// </summary>
public bool ison;
private int MissingIdValue = -1;
/// <summary>
/// designated constructor
/// </summary>
/// <param name="coord"></param>
/// <param name="id"></param>
/// <param name="yer"></param>
/// <param name="h"></param>
/// <param name="pw"></param>
/// <param name="isbrkn"></param>
/// <param name="ison"></param>
/// <param name="toID"></param>
/// <param name="fromID"></param>
public Powerline(List<Coordinate> coordinate, int id, int year, double height, int power, bool isbroken, bool ison, int toID, int fromID)
{
this.coords = coordinate;
this.identifier = id;
this.year = year;
this.height = height;
this.power = power;
this.isbroken = isbroken;
this.ison = ison;
this.pointFromID = fromID;
this.pointToID = toID;
}
/// <summary>
/// default constructor
/// </summary>
public Powerline() : this(new List<Coordinate>(), -1, 0, 0, 0, false, false, -1, -1) { }
}
/// <summary>
/// powerstation/pole point class
/// </summary>
public class PowerStation
{
/// <summary>
/// unique id
/// </summary>
public int identifier;
/// <summary>
/// Coordinates
/// </summary>
public Coordinate coords;
/// <summary>
/// station name field
/// </summary>
public string name;
/// <summary>
/// power, kW
/// </summary>
public int power;
/// <summary>
/// type of point - trans/pole/endpoint
/// </summary>
public string type;
/// <summary>
/// is point a source?
/// </summary>
public bool issource;
/// <summary>
/// power on switch
/// </summary>
public bool ison;
/// <summary>
/// asigned powerlines list
/// </summary>
public List<Powerline> linelist;
private int MissingIdValue = -1;
/// <summary>
/// designated constructor
/// </summary>
/// <param name="crds"></param>
/// <param name="id"></param>
/// <param name="stname"></param>
/// <param name="stpower"></param>
/// <param name="sttype"></param>
/// <param name="issource"></param>
/// <param name="ison"></param>
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;
}
/// <summary>
/// default constructor
/// </summary>
public PowerStation() : this(new Coordinate(), -1, "", 0, "", false, false) { }
}
enum FunctionType
{
FunctionVelocityX = 0,
FunctionVelocityY = 1,
FunctionClimate5 = 2,
FunctionClimate10 = 3,
FunctionClimate15 = 4
}
/// <summary>
/// DTO for input
/// </summary>
public class Input
{
/// <summary>
/// prognistic raster info
/// </summary>
public List<List<PrognosticCell>> prognosticCells {get; set;}
/// <summary>
/// prognostic raster cell info
/// </summary>
public CellSize prognosticCellSize { get; set; }
/// <summary>
/// affine coefficients from prognostic raster projections
/// </summary>
public double[] prognosticAffineCoefficients { get; set; }
/// <summary>
/// climate raster array
/// </summary>
public List<List<ClimateCell>> climateCells { get; set; }
/// <summary>
/// climate raster cell info
/// </summary>
public CellSize climateCellSize { get; set; }
/// <summary>
/// affine coefficients from climate raster projection
/// </summary>
public double[] climateAffineCoefficients { get; set; }
/// <summary>
/// lines list
/// </summary>
public List<Powerline> powerLines { get; set; }
/// <summary>
/// stations/poles list
/// </summary>
public List<PowerStation> powerStations { get; set; }
/// <summary>
/// maximum distance for line segment, meters
/// </summary>
public double dist_threshold
{
get
{
return 500;
}
}
}
/// <summary>
/// Output
/// </summary>
public class Output
{
/// <summary>
/// stations list without power
/// </summary>
public List<PowerStation> disabledStations { get; set; }
/// <summary>
/// broken lines list
/// </summary>
public List<Powerline> disabledLines { get; set; }
}
/// <summary>
/// main calculations class
/// </summary>
public class Module
{
/// <summary>
/// Input Data
/// </summary>
private Input input;
/// <summary>
/// Main function for power graph algorithm
/// </summary>
public Output CheckPower(Input input)
{
this.input = input;
//Calculate which lines are broken
List<PRMLibrary.Powerline> prmBrokenLines = brokenPowerLinesAfterCheck();
//get the graph
PreparingPowerItems();
//start from source points
foreach (PowerStation pwstation in input.powerStations)
{
if (pwstation.issource)
{
CheckPowerPointsForStation(pwstation);
}
}
//fill output
Output output = new Output();
output.disabledStations = new List<PowerStation>();
output.disabledLines = new List<Powerline>();
foreach (Powerline line in input.powerLines)
{
if (line.isbroken)
{
output.disabledLines.Add(line);
}
}
foreach (PowerStation powerStation in input.powerStations)
{
if (!powerStation.ison && !(powerStation.type.ToUpperInvariant().Trim() == "POLE"))
{
output.disabledStations.Add(powerStation);
}
}
return output;
}
/// <summary>
/// recursive search function for power graph
/// </summary>
/// <param name="sourcepoint"> powered station to search next powered station from</param>
private void CheckPowerPointsForStation(PowerStation sourcepoint)
{
if (!sourcepoint.ison)
{
throw new Exception("CheckPowerPointsForStation is called from disabled sourcepoint");
}
// 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 input.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
}
}
/// <summary>
/// preparing powerstations and lists of lines for them to get a graph-like structure
/// </summary>
private void PreparingPowerItems()
{
//First we make sure that all the sources are ON
//and all non sources are OFF
foreach (PowerStation powerStation in input.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 input.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;
}
}
/// <summary>
/// function to chec if powerline is broken by wind
/// </summary>
/// <returns>list of broken powerlines</returns>
private List<Powerline> brokenPowerLinesAfterCheck()
{
List<Powerline> brokenLines = new List<Powerline>();
// actually there are curves in powerLines
foreach (Powerline powerCurve in input.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)
{
brokenLines.Add(powerCurve);
}
}
return brokenLines;
}
/// <summary>
/// Divides the line between two vertices if needed
/// depending on dist_threshold
/// and checks if line is broken by wind in any of midpoints
/// </summary>
/// <param name="coord1">first vertice coordinate</param>
/// <param name="coord2">second vertice </param>
/// <param name="heightLine">height</param>
/// <param name="power">power for climatology switches</param>
/// <returns>true if line is broken false otherwise</returns>
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 / input.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;
}
private double[] affineCoefficients(FunctionType functionType)
{
switch (functionType)
{
case FunctionType.FunctionVelocityX:
case FunctionType.FunctionVelocityY:
{
return input.prognosticAffineCoefficients;
}
case FunctionType.FunctionClimate5:
case FunctionType.FunctionClimate10:
case FunctionType.FunctionClimate15:
{
return input.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.
private 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);
}
private Coordinate cellToProjection(Index index, FunctionType functionType)
{
switch (functionType)
{
case FunctionType.FunctionVelocityX:
case FunctionType.FunctionVelocityY:
{
return input.prognosticCells[index.Row][index.Col].coords;
}
case FunctionType.FunctionClimate5:
case FunctionType.FunctionClimate10:
case FunctionType.FunctionClimate15:
{
return input.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 ? input.prognosticCells.Count : input.prognosticCells[0].Count;
}
case FunctionType.FunctionClimate5:
case FunctionType.FunctionClimate10:
case FunctionType.FunctionClimate15:
{
return forRows ? input.climateCells.Count : input.climateCells[0].Count;
}
default:
break;
}
return 0;
}
private double valueForFunction(FunctionType functionType, Index index)
{
switch (functionType)
{
case FunctionType.FunctionVelocityX:
{
return input.prognosticCells[index.Row][index.Col].velocityX;
}
case FunctionType.FunctionVelocityY:
{
return input.prognosticCells[index.Row][index.Col].velocityY;
}
case FunctionType.FunctionClimate5:
{
return input.climateCells[index.Row][index.Col].wind5;
}
case FunctionType.FunctionClimate10:
{
return input.climateCells[index.Row][index.Col].wind10;
}
case FunctionType.FunctionClimate15:
{
return input.climateCells[index.Row][index.Col].wind15;
}
default:
break;
}
return 0;
}
private CellSize cellSizeForFunction(FunctionType functionType)
{
switch (functionType)
{
case FunctionType.FunctionVelocityX:
case FunctionType.FunctionVelocityY:
{
return input.prognosticCellSize;
}
case FunctionType.FunctionClimate5:
case FunctionType.FunctionClimate10:
case FunctionType.FunctionClimate15:
{
return input.climateCellSize;
}
default:
break;
}
throw new Exception("There is no cell size");
}
private 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;
}
}
}