Skip to content
Snippets Groups Projects
WindStressPRM.cs 38.1 KiB
Newer Older
using System;
using System.Collections.Generic;

namespace WindStressPRM
    /// <summary>
    /// Index class for raster lists in list
    /// Индекс-класс для растровых массивов
    public class Index
        /// Outer index
        /// Внешний индекс
        /// <summary>
        /// Inner index
        /// <summary>
        /// designated constructor
        /// </summary>
        /// <param name="Row">row index</param>
        /// <param name="Col">column index</param>
        public Index(int Row, int Col)
            if (Row <= -1 || Col <= -1) {
                throw new Exception("Index must be initialized with nonegative integer value");
            }
    /// <summary>
    /// Coordinate pair 
    /// </summary>
    public class Coordinate
        /// <summary>
        /// easting coordinate
        /// Координата по широте
        public double X;
        /// <summary>
        /// northing coordinate
        /// Координата по долготе
        public double Y;
        /// <summary>
        /// designated constructor
        /// Основной конструктор
        /// </summary>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        public Coordinate(double X, double Y)
            if (!(this.CheckValue()))
                {
                throw new System.ArgumentException("Passed coordinates are not valid!");
            }
            else
            {
                this.X = X;
                this.Y = Y;
            }
        }
        /// <summary>
        /// Проверка на валидность значений
        /// </summary>
        /// <returns></returns>
        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
    /// Объект ячейки для поля прогностического ветра на высоте 10м на регулярной сетке
    public class PrognosticCell
        /// U - component of wind velocity, m/s
        /// U - компонента скорости ветра, м/с
        public double velocityX;
        /// V - component of wind velocity, m/s
        /// V - компонента скорости ветра, м/с 
        public double velocityY;
        /// <summary>
        /// Cell center coordinates
        /// Координаты центра ячейки
        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)
            if (!(this.CheckValue()))
            {
                throw new System.ArgumentException("Prognostic wind velocities are incorrect");
            }
            else
            {
                this.coords = coord;
                this.velocityX = vX;
                this.velocityY = vY;
            }
        }
        /// <summary>
        /// Проверка полей на валидность
        /// </summary>
        /// <returns></returns>
            ///Скорость ветра на высоте 10м от поверхности на Земле не может превышать 70м/c
            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
    /// Объект - ячейка полей ветра определенной повторяемости на 10м на регулярной сетке
    public class ClimateCell
        /// once-in-5-years frequency wind, m/s 
        /// скорость ветра повторяемости один раз в 5 лет, м/с
        public double wind5;
        /// once-in-10-years frequency wind, m/s
        /// скорость ветра повторяемости один раз в 10 лет, м/с
        public double wind10;
        /// once-in-15-years frequency wind, m/s
        /// скорость ветра повторяемости один раз в 15 лет, м/с
        public double wind15;
        /// <summary>
        /// Cell center coordinate pair 
        /// Координаты центра ячейки
        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)
            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;
            }
        }
        /// <summary>
        /// Провекра валидности полей класса
        /// </summary>
        /// <returns></returns>
        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
    /// Параметры ячейки (регулярной сетки)
    public struct CellSize
        /// <summary>
        /// ширина ячейки (расстояние между соседними по широте центрами ячеек)
        /// </summary>
        public double width;
        /// <summary>
        /// высота ячейки (расстояние между соседними по долготе центрами ячеек)
        /// </summary>
        public double height;
        /// <summary>
        /// designated constructor
        /// </summary>
        /// <param name="wdh">Cell Width</param>
        /// <param name="hgh">Cell Height</param>
        public CellSize(double wdh, double hgh)
            if (!(this.CheckValue()))
            {
                throw new System.ArgumentException("Cell width or height values are incorrect!");
            }
        }
        /// <summary>
        /// Проверка валидности полей
        /// </summary>
        /// <returns></returns>
        public bool CheckValue()
        {
            bool checker = true;
            if (width > 0 && height > 0) { checker = true; }
            else { checker = false; }
            return checker;
    /// <summary>
    /// power line object
    public class Powerline
        /// <summary>
        ///  unique id
        ///  уникальный идентификатор 
        /// <summary>
        /// year of construction
        public int year;
        /// average height of cable span between two poles, meters
        /// средняя высота пролета, м
        public double height;
        /// <summary>
        /// power kW for switches
        public int power;
        /// <summary>
        /// Line vertices coordinate list
        /// список координат вершин ЛЭП как линейного объекта
        public List<Coordinate> coords { get; set; }
        /// <summary>
        /// assigned powerstation/pole
        /// идентификатор соответсвующего конца/начала линии (столб, трансформаторная подстанция, понижающая подстанция)
        /// <summary>
        /// assigned powerstation/pole
        /// идентификатор соответсвующего конца/начала линии (столб, трансформаторная подстанция, понижающая подстанция)
        /// <summary>
        /// broken/not broken switch
        /// сломана (true) или нет (false) линяя
        public bool isbroken;
        /// <summary>
        /// power on switch
        /// получает (true) или нет (false) линия питание
        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.coords = coordinates;
            this.identifier = id;
            this.year = year;
            this.height = height;
            this.power = power;
            this.isbroken = false;
            this.ison = false;
            this.pointFromID = fromID;
            this.pointToID = toID;
            if (!(this.CheckValue()))
            { throw new System.ArgumentException("Powerline object wasn't initialized correctly"); }
        /// проверка валидности полей
        /// <returns></returns>
        public bool CheckValue()
        {
            bool checker = true;
            if ( identifier>= 0 && Math.Abs(year - 1985) < 45 && Math.Abs(height - 15)<15 && power > 0 && power < 1000 && pointFromID >=0 && pointToID >=0)
            { checker = true; }
            else { checker = false; }
            return checker;
        }
    /// <summary>
    /// powerstation/pole point class
    /// класс для трансформаторных подстанций/столбов/понижающих(конечных) подстанций
    public class PowerStation
        /// <summary>
        /// unique id  
        /// уникальный идентификатор подстанции/столба
        /// <summary>
        /// Coordinates
        /// </summary>
        public Coordinate coords;
        /// <summary>
        /// station name field
        /// название подстанции
        /// <summary>
        /// power, kW
        /// <summary>
        /// type of point - trans/pole/endpoint
        /// тип станции - трансформаторная подстанция/столб/понижающая(конечная)подстанция
        public enum stationtype {pole, trans, endstat};
        /// <summary>
        /// тип подстанции
        /// </summary>
        public stationtype type;
        /// <summary>
        /// is point a source?
        /// является ли подстаниция источником питания (в случае ТЭЦ или питания от внешних для рассматриваемой цепи ЛЭП)
        /// <summary>
        /// power on switch
        /// поступает (true) или нет (false) на подстанцию питание
        /// <summary>
        /// asigned powerlines list
        /// список оканчивающихся/начинающихся на подстанции ЛЭП
        public List<Powerline> linelist;
        /// <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, stationtype sttype, bool issource)
        {
            this.coords = crds;
            this.identifier = id;
            this.name = stname;
            this.power = stpower;
            this.type = sttype;
            this.issource = issource;
            this.ison = false;
        public bool CheckValue()
        {
            bool checker = true;
            if (identifier >=0 && power >0 && power < 1000 )
            { checker = true; }
            else { checker = false; }
            return checker;

        }
    enum FunctionType
    {
        FunctionVelocityX = 0,
        FunctionVelocityY = 1,
        FunctionClimate5 = 2,
        FunctionClimate10 = 3,
        FunctionClimate15 = 4
    /// <summary>
    /// DTO for input
    /// </summary>
    public class Input
        /// <summary>
        /// prognistic raster info
        /// массив прогностического ветра
        public List<List<PrognosticCell>> prognosticCells {get; set;}
        /// <summary>
        /// prognostic raster cell info
        /// параметры ячеек регулярной сетки прогностического ветра
        public CellSize prognosticCellSize { get; set; }
        /// <summary>
        /// affine coefficients from prognostic raster projections
        /// коэффициенты аффиного проеобразования из проекции массива прогностического ветра
        public double[] prognosticAffineCoefficients { get; set; }
        /// <summary>
        /// climate raster array
        /// массив климатических полей скорости ветра заданной повторяемости
        public List<List<ClimateCell>> climateCells { get; set; }
        /// <summary>
        /// climate raster cell info
        /// параметры ячеек регулярной сетки климатических полей скорости ветра заданной повторяемости 
        public CellSize climateCellSize { get; set; }
        /// <summary>
        /// affine coefficients from climate raster projection
        /// коэффициенты аффинного преобразования из проекции массива климатических полей скорости ветра заданной повторяемости
        public double[] climateAffineCoefficients { get; set; }
        /// <summary>
        /// lines list
        public List<Powerline> powerLines { get; set; }
        /// <summary>
        /// stations/poles list
        /// список точечных объектов  - трансформаторных подстанций/столбов/понижающих(конечных) подстанций 
        public List<PowerStation> powerStations { get; set; }
        /// maximum distance for line segment, meters
        /// максимальное расстояние между точками ЛЭП, для которых проверяется сломаются/несломаются под действием ветра
        public double dist_threshold
        {
            get
            {
                return 500;
            }
        }
Антон Кудряшов's avatar
Антон Кудряшов committed
    }
    /// Output
    public class Output
Антон Кудряшов's avatar
Антон Кудряшов committed
    {
        /// <summary>
        /// stations list without power
        /// Список подстанций на которые не поступит питание в результате предсказанных поломок ЛЭП в сети
        public List<PowerStation> disabledStations { get; set; }
        /// <summary>
        /// broken lines list
        /// Список прогнозируемых сломанных ЛЭП в результате ветрового воздействия
        public List<Powerline> disabledLines { get; set; }
Антон Кудряшов's avatar
Антон Кудряшов committed
    }
    /// <summary>
    /// main calculations class
    /// </summary>
    public class StressPowerChecker
Антон Кудряшов's avatar
Антон Кудряшов committed
    {
        /// <summary>
        /// Input Data
        /// </summary>
        private Input input;
        /// <summary>
        /// Main function for power graph algorithm
        /// Основая функция для вычисления распределения питания по графу ЛЭП-подстанции
        public Output CheckPower(Input input)
            this.input = input;
            //Calculate which lines are broken
            List<WindStressPRM.Powerline> prmBrokenLines = brokenPowerLinesAfterCheck();
            PreparingPowerItems();
Антон Кудряшов's avatar
Антон Кудряшов committed
            foreach (PowerStation pwstation in input.powerStations)
                if (pwstation.issource)
                {
                    CheckPowerPointsForStation(pwstation);
            Output output = new Output();
            output.disabledStations = new List<PowerStation>();
            output.disabledLines = new List<Powerline>();
Антон Кудряшов's avatar
Антон Кудряшов committed
            foreach (Powerline line in input.powerLines)
                if (line.isbroken)
                {
                    output.disabledLines.Add(line);
Антон Кудряшов's avatar
Антон Кудряшов committed
            foreach (PowerStation powerStation in input.powerStations)
                //stations of type pole can be disabled if the line is broken 
                if (!powerStation.ison && !(powerStation.type == PowerStation.stationtype.pole))
Антон Кудряшов's avatar
Антон Кудряшов committed
                    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)
                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)
Антон Кудряшов's avatar
Антон Кудряшов committed
                    foreach (PowerStation powerStation in input.powerStations)
                        if (powerStation.identifier != sourcepoint.identifier && (powerStation.identifier == line.pointFromID || powerStation.identifier == line.pointToID))
                            if (!(sourcepoint.type == PowerStation.stationtype.pole)) {
                                powerStation.ison = true;
                                CheckPowerPointsForStation(powerStation);
                                // 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
Антон Кудряшов's avatar
Антон Кудряшов committed
            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>();

Антон Кудряшов's avatar
Антон Кудряшов committed
                foreach (Powerline line in input.powerLines)
                {
                    //we also switch OFF all lines
                    line.ison = false;
                    if (line.pointFromID == powerStation.identifier || line.pointToID == powerStation.identifier) {
                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
Антон Кудряшов's avatar
Антон Кудряшов committed
            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);
        /// <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));
Антон Кудряшов's avatar
Антон Кудряшов committed
            double distpropD = distance / input.dist_threshold;
            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++)
                {
                    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);
                midpoint.X = (coord1.X + coord2.X) / 2;
                midpoint.Y = (coord1.Y + coord2.Y) / 2;
            FunctionType climateType;
            if (power > 5 && power < 330)
            {
                climateType = FunctionType.FunctionClimate10;
            }
            else
            {
                if (power <= 5) { 
                    climateType = FunctionType.FunctionClimate5; 
                else { 
                    climateType = FunctionType.FunctionClimate15;
            foreach (Coordinate coords in pointlist)
                double uwind = interpol(coords, FunctionType.FunctionVelocityX);
                double vwind = interpol(coords, FunctionType.FunctionVelocityY);
                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 
                }
                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
                checkbool.Add(res);
            }
            bool result = false;
            foreach (bool lastcheck in checkbool)
            {
                if (lastcheck == true) { result = true; }
        private double[] affineCoefficients(FunctionType functionType)
                case FunctionType.FunctionVelocityX:
                case FunctionType.FunctionVelocityY:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return input.prognosticAffineCoefficients;
                case FunctionType.FunctionClimate5:
                case FunctionType.FunctionClimate10:
                case FunctionType.FunctionClimate15:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        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))
            {
                throw new Exception("projectionToCell method trying to find uncorrect index");
            return new Index(iRow, iCol);
        private Coordinate cellToProjection(Index index, FunctionType functionType)
            switch (functionType)
            {
                case FunctionType.FunctionVelocityX:
                case FunctionType.FunctionVelocityY:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return input.prognosticCells[index.Row][index.Col].coords;
                case FunctionType.FunctionClimate5:
                case FunctionType.FunctionClimate10:
                case FunctionType.FunctionClimate15:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return input.climateCells[index.Row][index.Col].coords;
        int countInList(FunctionType functionType, bool forRows)
                case FunctionType.FunctionVelocityX:
                case FunctionType.FunctionVelocityY:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return forRows ? input.prognosticCells.Count : input.prognosticCells[0].Count;
                case FunctionType.FunctionClimate5:
                case FunctionType.FunctionClimate10:
                case FunctionType.FunctionClimate15:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return forRows ? input.climateCells.Count : input.climateCells[0].Count;
        private double valueForFunction(FunctionType functionType, Index index)
                case FunctionType.FunctionVelocityX:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return input.prognosticCells[index.Row][index.Col].velocityX;
                case FunctionType.FunctionVelocityY:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return input.prognosticCells[index.Row][index.Col].velocityY;
                case FunctionType.FunctionClimate5:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return input.climateCells[index.Row][index.Col].wind5;
                case FunctionType.FunctionClimate10:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return input.climateCells[index.Row][index.Col].wind10;
                case FunctionType.FunctionClimate15:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return input.climateCells[index.Row][index.Col].wind15;
        private CellSize cellSizeForFunction(FunctionType functionType)
                case FunctionType.FunctionVelocityX:
                case FunctionType.FunctionVelocityY:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return input.prognosticCellSize;
                case FunctionType.FunctionClimate5:
                case FunctionType.FunctionClimate10:
                case FunctionType.FunctionClimate15:
Антон Кудряшов's avatar
Антон Кудряшов committed
                        return input.climateCellSize;
        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;
        }