Newer
Older
using System;
using System.Collections.Generic;
/// <summary>
/// Index class for raster lists in list
/// </summary>

Антон Кудряшов
committed
{
/// <summary>
/// Outer index
/// </summary>

Антон Кудряшов
committed
public int Row;
/// <summary>
/// Inner index
/// </summary>

Антон Кудряшов
committed
public int Col;
/// <summary>
/// designated constructor
/// </summary>
/// <param name="Row">row index</param>
/// <param name="Col">column index</param>

Антон Кудряшов
committed
{
if (Row <= -1 || Col <= -1) {
throw new Exception("Index must be initialized with nonegative integer value");
}

Антон Кудряшов
committed
this.Row = Row;
this.Col = Col;
}
}
/// <summary>
/// Coordinate pair
/// </summary>
/// <summary>
/// easting coordinate
/// </summary>
/// <summary>
/// northing coordinate
/// </summary>
/// <summary>
/// designated constructor
/// </summary>
/// <param name="X"></param>
/// <param name="Y"></param>

Debolskiy Andrey
committed
if (!(this.CheckValue()))
{
throw new System.ArgumentException("Passed coordinates are not valid!");
}
else
{
this.X = X;
this.Y = Y;
}
}
public bool CheckValue()
{
bool checker = true;
if (X != null) { checker = true; }
else { checker = false; }
if (Y != null) { checker = true; }
else { checker = false; }
return checker;
/// <summary>
/// Cell obj for regular prognostic wind field
/// </summary>
/// <summary>
/// U - component of wind velocity
/// </summary>
/// <summary>
/// V - component of wind velocity
/// </summary>
/// <summary>
/// Cell center coordinates
/// </summary>
/// <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)

Debolskiy Andrey
committed
if (!(this.CheckValue()))
{
throw new System.ArgumentException("Prognostic wind velocities are incorrect");
}
else
{
this.coords = coord;
this.velocityX = vX;
this.velocityY = vY;
}
}
public bool CheckValue()
{
bool checker = false;
if (Math.Abs(velocityX) < 70) { checker = true; }
else { checker = false; }
if (Math.Abs(velocityY) < 70) { checker = true; }
else { checker = false; }
return checker;
/// <summary>
/// Cell obj for climate wind regular data
/// </summary>
/// <summary>
/// once-in-5-years frequency wind
/// </summary>
/// <summary>
/// once-in-10-years frequency wind
/// </summary>
/// <summary>
/// once-in-15-years frequency wind
/// </summary>
/// <summary>
/// Cell center coordinate pair
/// </summary>
/// <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)

Debolskiy Andrey
committed
if (!(this.CheckValue()))
{
throw new System.ArgumentException("Climate wind value is not correct");
}
else
{
this.coords = coord;
this.wind5 = w5;
this.wind10 = w10;
this.wind15 = w15;
}
}
public bool CheckValue()
{
bool checker = true;
if (Math.Abs(wind5) < 70 && Math.Abs(wind10) < 70 && Math.Abs(wind15) < 70) { checker = true; }
else { checker = false; }
return checker;
/// <summary>
/// Cell Size parameters
/// </summary>
{
public double width;
public double height;
public CellSize(double wdh, double hgh)

Debolskiy Andrey
committed
{

Debolskiy Andrey
committed
this.height = hgh;

Debolskiy Andrey
committed
if (!(this.CheckValue()))
{
throw new System.ArgumentException("Cell width or height values are incorrect!");
}
}
public bool CheckValue()
{
bool checker = true;
if (width > 0 && height > 0) { checker = true; }
else { checker = false; }
return checker;

Debolskiy Andrey
committed
}
/// <summary>
/// power line object
/// </summary>
/// <summary>
/// unique id
/// </summary>

Debolskiy Andrey
committed
public int identifier { get; set; }
/// <summary>
/// year of construction
/// </summary>
/// <summary>
/// average height of cable span between two poles, meters
/// </summary>
/// <summary>
/// power kW for switches
/// </summary>
/// <summary>
/// Line vertices coordinate list
/// </summary>
public List<Coordinate> coords { get; set; }
/// <summary>
/// assigned powerstation/pole
/// </summary>

Debolskiy Andrey
committed
public int pointFromID;
/// <summary>
/// assigned powerstation/pole
/// </summary>

Debolskiy Andrey
committed
public int pointToID;
/// <summary>
/// broken/not broken switch
/// </summary>
/// <summary>
/// power on switch
/// </summary>

Debolskiy Andrey
committed
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> coordinates, int id, int year, double height, int power, int toID, int fromID)
this.year = year;
this.height = height;
this.power = power;

Debolskiy Andrey
committed
this.pointFromID = fromID;
this.pointToID = toID;
/// <summary>
/// default constructor
/// </summary>
public Powerline() : this(new List<Coordinate>(), -1, 0, 0, 0, -1, -1) { }
/// <summary>
/// powerstation/pole point class
/// </summary>
/// <summary>
/// unique id
/// </summary>

Debolskiy Andrey
committed
public int identifier;
/// <summary>
/// Coordinates
/// </summary>
/// <summary>
/// station name field
/// </summary>

Debolskiy Andrey
committed
public string name;
/// <summary>
/// power, kW
/// </summary>

Debolskiy Andrey
committed
public int power;
/// <summary>
/// type of point - trans/pole/endpoint
/// </summary>

Debolskiy Andrey
committed
public string type;
/// <summary>
/// is point a source?
/// </summary>

Debolskiy Andrey
committed
public bool issource;
/// <summary>
/// power on switch
/// </summary>

Debolskiy Andrey
committed
public bool ison;
/// <summary>
/// asigned powerlines list
/// </summary>
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)

Debolskiy Andrey
committed
{
this.coords = crds;
this.identifier = id;
this.name = stname;
this.power = stpower;
this.type = sttype;
this.issource = issource;

Debolskiy Andrey
committed
}
/// <summary>
/// default constructor
/// </summary>
public PowerStation() : this(new Coordinate(0, 0), -1, "", 0, "", false) { }

Антон Кудряшов
committed
enum FunctionType
{
FunctionVelocityX = 0,
FunctionVelocityY = 1,
FunctionClimate5 = 2,
FunctionClimate10 = 3,
FunctionClimate15 = 4

Антон Кудряшов
committed
}
/// <summary>
/// DTO for input
/// </summary>
/// <summary>
/// prognistic raster info
/// </summary>
public List<List<PrognosticCell>> prognosticCells {get; set;}
/// <summary>
/// prognostic raster cell info
/// </summary>
/// <summary>
/// affine coefficients from prognostic raster projections
/// </summary>
public double[] prognosticAffineCoefficients { get; set; }

Debolskiy Andrey
committed
/// <summary>
/// climate raster array
/// </summary>
public List<List<ClimateCell>> climateCells { get; set; }
/// <summary>
/// climate raster cell info
/// </summary>
/// <summary>
/// affine coefficients from climate raster projection
/// </summary>
public double[] climateAffineCoefficients { get; set; }
/// <summary>
/// lines list
/// </summary>
/// <summary>
/// stations/poles list
/// </summary>
public List<PowerStation> powerStations { get; set; }
public double dist_threshold
{
get
{
return 500;
}
}
/// <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 StressPowerChecker
/// <summary>
/// Input Data
/// </summary>
/// <summary>
/// Main function for power graph algorithm
/// </summary>

Debolskiy Andrey
committed
{
this.input = input;
//Calculate which lines are broken
List<WindStressPRM.Powerline> prmBrokenLines = brokenPowerLinesAfterCheck();

Debolskiy Andrey
committed
//get the graph

Debolskiy Andrey
committed
//start from source points

Debolskiy Andrey
committed
{
if (pwstation.issource)
{
CheckPowerPointsForStation(pwstation);

Debolskiy Andrey
committed
}
Output output = new Output();
output.disabledStations = new List<PowerStation>();
output.disabledLines = new List<Powerline>();

Debolskiy Andrey
committed
{
if (line.isbroken)
{
output.disabledLines.Add(line);

Debolskiy Andrey
committed
}

Debolskiy Andrey
committed
{
if (!powerStation.ison && !(powerStation.type.ToUpperInvariant().Trim() == "POLE"))
{

Debolskiy Andrey
committed
}

Debolskiy Andrey
committed
}
/// <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)

Debolskiy Andrey
committed
if (!sourcepoint.ison)
{
throw new Exception("CheckPowerPointsForStation is called from disabled sourcepoint");

Debolskiy Andrey
committed
}
// 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)

Debolskiy Andrey
committed
{

Debolskiy Andrey
committed
{
if (powerStation.identifier != sourcepoint.identifier && (powerStation.identifier == line.pointFromID || powerStation.identifier == line.pointToID))

Debolskiy Andrey
committed
{
if (!(sourcepoint.type.Trim().ToUpperInvariant() == "POLE")) {
powerStation.ison = true;
CheckPowerPointsForStation(powerStation);

Debolskiy Andrey
committed
}

Debolskiy Andrey
committed
// 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)

Debolskiy Andrey
committed
{
if (powerline.isbroken) {
powerLineCheck = true;
}

Debolskiy Andrey
committed
}

Debolskiy Andrey
committed
{
powerStation.ison = true;
CheckPowerPointsForStation(powerStation);

Debolskiy Andrey
committed
}
}
}
}
}
//else we have broken line or already switched ON powerpoint
}

Debolskiy Andrey
committed
}
/// <summary>
/// preparing powerstations and lists of lines for them to get a graph-like structure
/// </summary>

Debolskiy Andrey
committed
{
//First we make sure that all the sources are ON
//and all non sources are OFF

Debolskiy Andrey
committed
{
if (powerStation.issource == true) {
powerStation.ison = true;
}
else {
powerStation.ison = false;
}

Debolskiy Andrey
committed
// for each power station we create a list of powerlines it is attached to
List<Powerline> lines = new List<Powerline>();

Debolskiy Andrey
committed
{
//we also switch OFF all lines
line.ison = false;
if (line.pointFromID == powerStation.identifier || line.pointToID == powerStation.identifier) {

Debolskiy Andrey
committed
lines.Add(line);
}
}

Debolskiy Andrey
committed
}
}
/// <summary>
/// function to chec if powerline is broken by wind
/// </summary>
/// <returns>list of broken powerlines</returns>
private List<Powerline> brokenPowerLinesAfterCheck()

Debolskiy Andrey
committed
{
List<Powerline> brokenLines = new List<Powerline>();
// actually there are curves in powerLines
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)
{

Debolskiy Andrey
committed
{
brokenLines.Add(powerCurve);

Debolskiy Andrey
committed
}

Debolskiy Andrey
committed
/// <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));
List<Coordinate> pointlist = new List<Coordinate>();
Coordinate midpoint = new Coordinate(0, 0);
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++)
{

Debolskiy Andrey
committed
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;

Debolskiy Andrey
committed
midpoint.X = (subCoord1.X + subCoord2.X) / 2;
midpoint.Y = (subCoord1.Y + subCoord2.Y) / 2;
pointlist.Add(midpoint);
midpoint.X = (coord1.X + coord2.X) / 2;
midpoint.Y = (coord1.Y + coord2.Y) / 2;

Debolskiy Andrey
committed
pointlist.Add(midpoint);
}
FunctionType climateType;
if (power > 5 && power < 330)
{
climateType = FunctionType.FunctionClimate10;
}
else
{
if (power <= 5) {
climateType = FunctionType.FunctionClimate5;

Debolskiy Andrey
committed
}
else {
climateType = FunctionType.FunctionClimate15;

Debolskiy Andrey
committed
}

Debolskiy Andrey
committed
List<bool> checkbool = new List<bool>();
foreach (Coordinate coords in pointlist)

Debolskiy Andrey
committed
{
bool res = false;
double uwind = interpol(coords, FunctionType.FunctionVelocityX);
double vwind = interpol(coords, FunctionType.FunctionVelocityY);

Антон Кудряшов
committed
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

Debolskiy Andrey
committed
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

Debolskiy Andrey
committed
checkbool.Add(res);
}
bool result = false;
foreach (bool lastcheck in checkbool)
{
if (lastcheck == true) { result = true; }

Debolskiy Andrey
committed
return result;
private double[] affineCoefficients(FunctionType functionType)

Антон Кудряшов
committed
{
switch (functionType)
{
case FunctionType.FunctionVelocityX:
case FunctionType.FunctionVelocityY:

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}
case FunctionType.FunctionClimate5:
case FunctionType.FunctionClimate10:
case FunctionType.FunctionClimate15:

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}
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)

Антон Кудряшов
committed
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
{
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))
{
throw new Exception("projectionToCell method trying to find uncorrect index");

Антон Кудряшов
committed
}

Антон Кудряшов
committed
}
private Coordinate cellToProjection(Index index, FunctionType functionType)

Антон Кудряшов
committed
{
switch (functionType)
{
case FunctionType.FunctionVelocityX:
case FunctionType.FunctionVelocityY:

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}
case FunctionType.FunctionClimate5:
case FunctionType.FunctionClimate10:
case FunctionType.FunctionClimate15:

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}
default:
break;
}
return null;
}
int countInList(FunctionType functionType, bool forRows)

Антон Кудряшов
committed
{
switch (functionType)
{
case FunctionType.FunctionVelocityX:
case FunctionType.FunctionVelocityY:

Антон Кудряшов
committed
{
return forRows ? input.prognosticCells.Count : input.prognosticCells[0].Count;

Антон Кудряшов
committed
}
case FunctionType.FunctionClimate5:
case FunctionType.FunctionClimate10:
case FunctionType.FunctionClimate15:

Антон Кудряшов
committed
{
return forRows ? input.climateCells.Count : input.climateCells[0].Count;

Антон Кудряшов
committed
}
default:
break;
}
return 0;
}
private double valueForFunction(FunctionType functionType, Index index)

Антон Кудряшов
committed
{
switch (functionType)
{

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}
default:
break;
}
return 0;
}
private CellSize cellSizeForFunction(FunctionType functionType)

Антон Кудряшов
committed
{
switch (functionType)
{
case FunctionType.FunctionVelocityX:
case FunctionType.FunctionVelocityY:

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}
case FunctionType.FunctionClimate5:
case FunctionType.FunctionClimate10:
case FunctionType.FunctionClimate15:

Антон Кудряшов
committed
{

Антон Кудряшов
committed
}
default:
break;
}
throw new Exception("There is no cell size");
}
private double interpol(Coordinate coords, FunctionType functionType)

Антон Кудряшов
committed
{
// 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);

Антон Кудряшов
committed
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));

Антон Кудряшов
committed
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);

Антон Кудряшов
committed
//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;
}