예제 #1
0
        internal static void turbinesCalculations(out double dTurb, out int nTurb, out double kWake, out ILArray<double> x, out int gridX, out int gridY, out ILArray<double> yOrder, out double dy, out ILArray<int> xTurbC, out ILArray<int> yTurbC, WindTurbineParameters parm, SimParm simParm)
        {
            #region "Used variables declaration"
            ILArray<double> data;
            double rotA;
            int gridRes;
            int endSize;
            ILArray<double> y;
            ILArray<double> xCoor;
            ILArray<double> yCoor;
            ILArray<double> xTurb;
            ILArray<double> yTurb;
            ILArray<double> xOrder;
            int ppp;
            ILArray<double> xGrid;
            ILArray<double> yGrid;
            #endregion

            data = parm.wf.C;
            dTurb = 2 * parm.radius._(1);
            nTurb = parm.N;
            rotA = parm.rotA;
            kWake = parm.kWake;
            gridRes = simParm.gridRes; // Grid Resolution, the lower the number, the higher the amount of points computed.
            endSize = simParm.grid;

            //if (Ct < 0) // !ILMath.isreal(Ct) | 
            {
                //disp('Ct is negative or complex');
            }

            x = _c(1.0, gridRes, endSize);// x-grid.
            y = _c(1.0, gridRes, endSize);// y-grid.
            gridX = length(x); // Number of grid points.
            gridY = length(y); // Number of grid points.

            xCoor = data[_(':'), _(1)]; // Coordiante of turbine, x-position
            yCoor = data[_(':'), _(2)]; // Coordinate of turbine, y-position

            ROTATE_corrd(out xTurb, out yTurb, xCoor, yCoor, rotA); // Rotated (and scaled) coordinates
            WT_order(out xOrder, out yOrder, xTurb, yTurb); // Ordered turbines. 

            ppp = 2; // This parameter is also a bit weird.. But it changes the grid.
            DOMAIN_pt(out xGrid, out _double, gridX, dTurb, xOrder, ppp); // 
            ppp = 5;
            DOMAIN_pt(out yGrid, out dy, gridY, dTurb, yOrder, ppp);

            Turb_centr_coord(out xTurbC, nTurb, gridX, xGrid, xOrder, gridRes); // Determines the grid point closest to the turbine.
            Turb_centr_coord(out yTurbC, nTurb, gridY, yGrid, yOrder, gridRes); // Determines the grid point closest to the turbine. 
        }
예제 #2
0
        //% v_nac = WAKECALCULATION(Ct,i,wind)
        // RLC, Aalborg
        // The below is based on the .F90 code developed by ?, and will give a
        // better estimate of the actual wake the individual turbines experience. 
        #endregion
        internal static void wakeCalculationsRLC(out ILArray<double> vNac, double dTurb, int nTurb, double kWake, ILArray<double> x, int gridX, int gridY, ILArray<double> yOrder, double dy, ILArray<int> xTurbC, ILArray<int> yTurbC, ILArray<double> Ct, ILArray<double> wField, double[] vHub, WindTurbineParameters parm, SimParm simParm)
        {
            #region "Used variables declaration"
            double[,] Velocity;
            int j;
            #endregion

            // Velocity Computation
            Compute_Vell(out Velocity, yOrder, xTurbC, yTurbC, x, wField, vHub, kWake, gridX, gridY, nTurb, dTurb, Ct, dy);

            // Extracting the individual Nacelle Wind Speeds from the wind velocity matrix.
            //Velocity = Velocity';
            vNac = zeros(nTurb, 1);

            for (j = 1; j <= length(xTurbC); j++)
            {
                vNac._(j, '=', Velocity[yTurbC._(j) - 1, xTurbC._(j) - 1]);
            }
        }
        //P_ref is a vector of power refenreces for tehe wind turbine with dimension 1xN
        //v_nac is a vector of wind speed at each wind turbine with dimension 1xN
        //P_demand is a scale of the wind farm power demand.
        //parm is a struct of wind turbine parameters e.g. NREL5MW
        #endregion
        internal static void powerDistributionControl(out ILArray<double> P_ref, out ILArray<double> P_a, double[] v_nac, double P_demand, WindTurbineParameters parm)
        {
            #region "Used variables declaration"
            double rho;
            ILArray<double> R;
            ILArray<double> rated;
            ILArray<double> Cp;
            int i;
            #endregion

            rho = parm.rho;                 //air density for each wind turbine(probably the same for all)
            R = parm.radius.C;              //rotor radius for each wind turbine(NREL.r=63m)
            rated = parm.rated.C;           //Rated power for each wind turbine(NREL.Prated=5MW)
            Cp = parm.Cp.C;                 // Max cp of the turbines for each wind turbine(NREL.Cp.max=0.45)

            P_a = zeros(parm.N, 1);
            P_ref = zeros(parm.N, 1);

            // Compute available power at each turbine
            for (i = 1; i <= parm.N; i++)
            {
                P_a._(i, '=', min_(__[ rated._(i), (pi / 2) * rho * _p(R._(i), 2) * _p(v_nac[i - 1], 3) * Cp._(i) ]));
            }

            var sum_P_a_ = sum_(P_a);

            //Distribute power according to availibility
            for (i = 1; i <= parm.N; i++)
            {
                if (P_demand < sum_P_a_)
                {
                    P_ref._(i, '=', max_(__[ 0, min_(__[ rated._(i), P_demand * P_a._(i) / sum_P_a_ ]) ]));
                }
                else
                {
                    P_ref._(i, '=', P_a._(i));
                }
            }
        }
예제 #4
0
        //% v_nac = WAKECALCULATION(Ct,i,wind)
        // RLC, Aalborg
        // The below is based on the .F90 code developed by ?, and will give a
        // better estimate of the actual wake the individual turbines experience.
        #endregion
        internal static void wakeCalculationsRLC(out ILArray <double> vNac, double dTurb, int nTurb, double kWake, ILArray <double> x, int gridX, int gridY, ILArray <double> yOrder, double dy, ILArray <int> xTurbC, ILArray <int> yTurbC, ILArray <double> Ct, ILArray <double> wField, double[] vHub, WindTurbineParameters parm, SimParm simParm)
        {
            #region "Used variables declaration"
            double[,] Velocity;
            int j;
            #endregion

            // Velocity Computation
            Compute_Vell(out Velocity, yOrder, xTurbC, yTurbC, x, wField, vHub, kWake, gridX, gridY, nTurb, dTurb, Ct, dy);

            // Extracting the individual Nacelle Wind Speeds from the wind velocity matrix.
            //Velocity = Velocity';
            vNac = zeros(nTurb, 1);

            for (j = 1; j <= length(xTurbC); j++)
            {
                vNac._(j, '=', Velocity[yTurbC._(j) - 1, xTurbC._(j) - 1]);
            }
        }
예제 #5
0
        //P_ref is a vector of power refenreces for tehe wind turbine with dimension 1xN
        //v_nac is a vector of wind speed at each wind turbine with dimension 1xN
        //P_demand is a scale of the wind farm power demand.
        //parm is a struct of wind turbine parameters e.g. NREL5MW
        #endregion
        internal static void powerDistributionControl(out ILArray <double> P_ref, out ILArray <double> P_a, double[] v_nac, double P_demand, WindTurbineParameters parm)
        {
            #region "Used variables declaration"
            double           rho;
            ILArray <double> R;
            ILArray <double> rated;
            ILArray <double> Cp;
            int i;
            #endregion

            rho   = parm.rho;               //air density for each wind turbine(probably the same for all)
            R     = parm.radius.C;          //rotor radius for each wind turbine(NREL.r=63m)
            rated = parm.rated.C;           //Rated power for each wind turbine(NREL.Prated=5MW)
            Cp    = parm.Cp.C;              // Max cp of the turbines for each wind turbine(NREL.Cp.max=0.45)

            P_a   = zeros(parm.N, 1);
            P_ref = zeros(parm.N, 1);

            // Compute available power at each turbine
            for (i = 1; i <= parm.N; i++)
            {
                P_a._(i, '=', min_(__[rated._(i), (pi / 2) * rho * _p(R._(i), 2) * _p(v_nac[i - 1], 3) * Cp._(i)]));
            }

            var sum_P_a_ = sum_(P_a);

            //Distribute power according to availibility
            for (i = 1; i <= parm.N; i++)
            {
                if (P_demand < sum_P_a_)
                {
                    P_ref._(i, '=', max_(__[0, min_(__[rated._(i), P_demand * P_a._(i) / sum_P_a_])]));
                }
                else
                {
                    P_ref._(i, '=', P_a._(i));
                }
            }
        }
예제 #6
0
        public static double[][] FarmControl(WakeFarmControlConfig config, out double[][] dataOut)
        {
            #region "Used variables declaration"
            bool saveData;
            bool enablePowerDistribution;
            bool enableTurbineDynamics;
            bool powerRefInterpolation;
            bool enableVaryingDemand;
            SimParm simParm;

            ILArray<double> wind;
            WindTurbineParameters parm;
            EnvMatFileDataStructure env;
            WtMatFileDataStructure wt;
            ILArray<int> idx;
            double Mg_max_rate;
            double Ki, Kp, Umax, Umin;
            double VS_CtInSp, VS_RtGnSp, VS_Rgn2K;
            double omega0, beta0, power0;
            ILArray<double> initMatrix;
            ILArray<double> sumPower;
            ILArray<double> sumRef;
            ILArray<double> sumAvai;
            ILArray<double> P_ref_new;
            ILArray<double> P_demand;
            ILArray<double> v_nac;
            ILArray<double> P_ref;
            ILArray<double> Pa;
            ILArray<double> Power;
            ILArray<double> beta;
            ILArray<double> Omega;
            ILArray<double> initVector;
            ILArray<double> Mg___i_;
            ILArray<double> wField;
            double dZ;
            ILArray<double> du;
            double[] x___1_;
            double[] x___2_;
            double[] u___1_;
            double[] u___2_;
            double alpha;
            int j;
            ILArray<double> dx;
            ILArray<double> time;
            #endregion

            //% Initialization
            //General settings to be changed
            saveData                = config.saveData; // Save all the simulated data to a .mat file?
            enablePowerDistribution = config.enablePowerDistribution; // Enable wind farm control and not only constant power
            enableTurbineDynamics   = config.enableTurbineDynamics; // Enable dynamical turbine model. Disabling this will increase the speed significantly, but also lower the fidelity of the results (setting to false does not work properly yet)
            powerRefInterpolation   = config.powerRefInterpolation; // Power Reference table interpolation.
            enableVaryingDemand     = config.enableVaryingDemand; // Varying Reference

            // Simulation Properties:
            simParm = new SimParm();
            simParm.tStart      = config.SimParm.tStart; // time start
            simParm.timeStep    = config.SimParm.timeStep; // time step, 8Hz - the NREL model is 80Hz (for reasons unknown)
            simParm.tEnd        = config.SimParm.tEnd; // time end
            int _simParm_tEnd_simParm_tStart__simParm_timeStep = (int)((simParm.tEnd - simParm.tStart) / simParm.timeStep);
            simParm.gridRes     = config.SimParm.gridRes; // Grid Resolution
            simParm.grid        = config.SimParm.grid; // Grid Size
            simParm.ctrlUpdate  = config.SimParm.ctrlUpdate;  // Update inverval for farm controller
            simParm.powerUpdate = config.SimParm.powerUpdate; // How often the control algorithm should update!
            load(config.Wind_MatFile, out wind); // Load Wind Data

            // Wind farm and Turbine Properties properties
            parm = new WindTurbineParameters();
            parm.wf = load(config.Turbines); // Loads the Wind Farm Layout.
            parm.N = length(parm.wf); // number of turbines in farm
            parm.rotA = -48.80; // Angle of Attack
            parm.kWake = 0.06;

            //% Turbine properties - Loaded from the NREL5MW.mat file
            load(config.NREL5MW_MatFile, out env, out wt); // Load parameters from the NREL 5MW Reference turbine struct.
            parm.rho = env.rho; // air density
            parm.radius = wt.rotor.radius * ones(1, parm.N); // rotor radius (NREL5MW)
            parm.rated = wt.ctrl.p_rated * wt.gen.effeciency * ones(1, parm.N); //rated power (NREL5MW)
            parm.ratedSpeed = wt.rotor.ratedspeed; //rated rotor speed
            max(out idx, wt.cp.table[_(':')]); // Find index for max Cp
            parm.Ct = 0.0 * wt.ct.table[_(idx)] * ones(parm.N, (_simParm_tEnd_simParm_tStart__simParm_timeStep)); // Define initial Ct as the optimal Ct. 
            parm.Cp = wt.cp.table[_(idx)] * ones(parm.N, (_simParm_tEnd_simParm_tStart__simParm_timeStep)); // Define initial Cp as the optimal Cp. 
            Mg_max_rate = wt.ctrl.torq.ratelim; // Rate-limit on Torque Change.

            //Pitch control
            Ki = wt.ctrl.pitch.Igain; // 0.008068634*360/2/pi; % integral gain (NREL5MW).
            Kp = wt.ctrl.pitch.Pgain; // 0.01882681*360/2/pi; % proportional gain (NREL5MW).
            Umax = wt.ctrl.pitch.ulim; // Upper limit of the pitch controller
            Umin = wt.ctrl.pitch.llim; // Lower limit of the pitch controller.

            // NREL Regional Control - extracted from the NREL report. 
            VS_CtInSp = 70.162240;
            VS_RtGnSp = 121.680500;
            VS_Rgn2K = 2.332287;

            //% Set initial conditions
            omega0 = wt.rotor.ratedspeed; // Desired Rotation speed
            beta0 = 0; // wt.ctrl.pitch.llim; % Initial pitch at zero.
            power0 = 0; // Power Production

            //% Memory Allocation and Memory Initialization
            initMatrix = zeros(parm.N, _simParm_tEnd_simParm_tStart__simParm_timeStep);
            sumPower = initMatrix[_(1), _(':')]; // Initialize produced power vector
            sumRef = initMatrix[_(1), _(':')]; // Initialize reference power vector
            sumAvai = initMatrix[_(1), _(':')]; // Initialize available power vector
            P_ref_new = initMatrix[_(1), _(':')]; // Initialize new reference vector
            P_demand = initMatrix[_(1), _(':')]; // Initialize power demand vector
            v_nac = initMatrix.C; // Initialize hub velocity matrix.
            P_ref = initMatrix.C; // Initialize matrix to save the power production history for each turbine.
            Pa = initMatrix.C; // Initialize available power matrix.
            Power = initMatrix.C; // Initialize individual WT power production matrix.
            beta = initMatrix.C; // Initialize pitch matrix.
            Omega = initMatrix.C; // Initialize revolutional velocity matrix.

            initVector = ones(parm.N, 1);
            Mg___i_ = 0 * initVector;
            wField = zeros(simParm.grid / simParm.gridRes, simParm.grid / simParm.gridRes); // Wind field matrix

            //% Controller Initizliation
            dZ = 0; // Integrator Initialization.
            du = zeros(parm.N, 1); // Integration variable. 
            x___1_ = (omega0 * initVector).GetArrayForRead();
            x___2_ = (0 * initVector).GetArrayForRead();
            u___1_ = (beta0 * initVector).GetArrayForRead(); // u0
            u___2_ = (power0 * initVector).GetArrayForRead(); // u0
            P_demand._(1, '=', config.InitialPowerDemand); // Power Demand.

            double turbinesDTurb;
            int turbinesNTurb;
            double turbinesKWake;
            ILArray<double> turbinesX;
            int turbinesGridX;
            int turbinesGridY;
            ILArray<double> turbinesYOrder;
            double turbinesDy;
            ILArray<int> turbinesXTurbC;
            ILArray<int> turbinesYTurbC;
            turbinesCalculations(out turbinesDTurb, out turbinesNTurb, out turbinesKWake, out turbinesX, out turbinesGridX, out turbinesGridY, out turbinesYOrder, out turbinesDy, out turbinesXTurbC, out turbinesYTurbC, parm, simParm);

            //% Simulate wind farm operation
            for (var i = 2; i <= _simParm_tEnd_simParm_tStart__simParm_timeStep; i++) // At each sample time (DT) from Tstart to Tend
            {
                //clc;
                //fprintf('Iteration Counter: %i out of %i \n', i, config.SimParm.tEnd * (1 / config.SimParm.timeStep));

                //%%%%%%%%%%%%%%% WIND FIELD FIFO MATRIX %%%%%%%%%%%%%%%%%%%
                wField[_(':'), _(2, ':', end)]  = wField[_(':'), _(1, ':', (end - 1))];
                wField[_(':'), _(1)]            = wind._(i, 2) * ones(simParm.grid / simParm.gridRes, 1) + randn(simParm.grid / simParm.gridRes, 1) * 0.5;
                //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  

                // Calculate the wake using the last Ct values
                ILArray<double> v_nac___i_;
                wakeCalculationsRLC(out v_nac___i_, turbinesDTurb, turbinesNTurb, turbinesKWake, turbinesX, turbinesGridX, turbinesGridY, turbinesYOrder, turbinesDy, turbinesXTurbC, turbinesYTurbC, parm.Ct[_(':'), _(i - 1)], transpose(wField), x___2_, parm, simParm);
                v_nac[_(':'), _(i)] = v_nac___i_;
                x___2_          = (v_nac[_(':'), _(i)]).GetArrayForRead();


                if (enableVaryingDemand) // A random walk to simulate fluctuations in the power demand.
                {
                    P_demand._(i, '=', P_demand._(i - 1) + randn() * 50000);
                }
                else
                {
                    P_demand._(i, '=', P_demand._(i - 1));
                }

                // Farm control
                // Calculate the power distribution references for each turbine
                if (enablePowerDistribution)
                {
                    ILArray<double> Pa___i_;
                    powerDistributionControl(out P_ref_new, out Pa___i_, x___2_, P_demand._(i), parm);
                    Pa[_(':'), _(i)] = Pa___i_;
                }

                //Hold  the demand for some seconds
                if (mod(i, round(simParm.ctrlUpdate / simParm.timeStep)) == simParm.powerUpdate)
                {
                    P_ref[_(':'), _(i)] = P_ref_new;
                }
                else
                {
                    if (powerRefInterpolation)
                    {
                        alpha = 0.01;
                        P_ref[_(':'), _(i)] = (1 - alpha) * P_ref[_(':'), _(i - 1)] + (alpha) * P_ref_new;
                    }
                    else
                    {
                        P_ref[_(':'), _(i)] = P_ref_new;
                    }
                }
                //Torque controller
                for (j = 1; j <= parm.N; j++)
                {
                    if ((x___1_[j - 1] * 97 >= VS_RtGnSp) || (u___1_[j - 1] >= 1))      //! We are in region 3 - power is constant
                    {
                        u___2_[j - 1]   = P_ref._(j, i) / x___1_[j - 1];
                    }
                    else if (x___1_[j - 1] * 97 <= VS_CtInSp)                     //! We are in region 1 - torque is zero
                    {
                        u___2_[j - 1]   = 0.0;
                    }
                    else                                                //! We are in region 2 - optimal torque is proportional to the square of the generator speed
                    {
                        u___2_[j - 1]   = 97 * VS_Rgn2K * x___1_[j - 1] * x___1_[j - 1] * _p(97, 2);
                    }
                }

                dx = (omega0 - ((ILArray<double>)x___1_)) - (omega0 - Omega[_(':'), _(i - 1)]);
                du = Kp * dx + Ki * simParm.timeStep * (omega0 - ((ILArray<double>)(x___1_)));
                du = min(max(du, -wt.ctrl.pitch.ratelim), wt.ctrl.pitch.ratelim);
                u___1_ = (min(max(((ILArray<double>)u___1_) + du * simParm.timeStep, Umin), Umax)).GetArrayForRead();


                Mg___i_ = u___2_; // Torque Input
                beta[_(':'), _(i)] = u___1_; // Pitch Input

                // Turbine dynamics - can be simplified:
                if (enableTurbineDynamics)
                {
                    for (j = 1; j <= parm.N; j++)
                    {
                        double x_j_1_; double parm_Ct_j_i_; double parm_Cp_j_i_;
                        turbineDrivetrainModel(out x_j_1_, out parm_Ct_j_i_, out parm_Cp_j_i_, x___1_[j - 1], x___2_[j - 1], u___1_[j - 1], u___2_[j - 1], wt, env, simParm.timeStep);
                        x___1_[j - 1] = x_j_1_; parm.Ct._(j, i, '=', parm_Ct_j_i_); parm.Cp._(j, i, '=', parm_Cp_j_i_);
                    }
                }
                else
                {
                    x___1_.Fill(parm.ratedSpeed); // Rotational speed
                }

                Omega[_(':'), _(i)] = x___1_;
                Power[_(':'), _(i)] = Omega[_(':'), _(i)] * Mg___i_;

                // Power Summations
                sumPower._(i,   '=', sum_(Power[_(':'), _(i)]) * _p(10, -6));
                sumRef._(i,     '=', sum_(P_ref[_(':'), _(i)]) * _p(10, -6));
                sumAvai._(i,    '=', sum_(Pa[_(':'), _(i)]) * _p(10, -6));

                // NOWCASTING FUNKTION HER
                // powerPrediction(i) = powerPrediction(i,sumPower(i:-1:i-10)) % or something
                // similar.
            }

            //%
            //time = (_c((decimal)simParm.tStart, (decimal)simParm.timeStep, (decimal)simParm.tEnd - (decimal)simParm.timeStep)).T;
            time = (_c(0.0, 1.0, _simParm_tEnd_simParm_tStart__simParm_timeStep - 1) * simParm.timeStep + simParm.tStart).T;


            if (saveData)
            {
                //throw new ApplicationException(string.Format("{0}\t{1}\t{2}\t{3}", time.Dimensions, sumPower.T.Dimensions, sumRef.T.Dimensions, sumAvai.T.Dimensions));
                dataOut = (__[ time, sumPower.T, sumRef.T, sumAvai.T ]).ToDoubleArray();
                //save dataOut;
            }
            else
            {
                dataOut = new double[][] { new double[] { 0, 0, 0, 0 } };
            }
            //% Plotting
            //Below a number of different plots are made. Most of them for test purposes
            ILArray<double> plotsData;
            plotsData = time.C;
            plotsData = __[plotsData, P_ref.T * (_p(10, -6))];              // f1 = figure(1); xlabel('Time [s]'); ylabel('Power Reference [MW]'); title('Individual Power Reference');
            plotsData = __[ plotsData, v_nac.T ];                           // f2 = figure(2); xlabel('Time [s]'); ylabel('Wind Speed [m/s]'); title('Wind Speed @ individual turbine');
            plotsData = __[ plotsData, beta.T ];                            // f4 = figure(4); xlabel('Time [s]'); ylabel('Pitch Angle [deg]'); title('Evolution of Pitch angle over time');
            plotsData = __[ plotsData, Omega.T ];                           // f5 = figure(5); xlabel('Time [s]'); ylabel('Revolutional Velocity [rpm]'); title('Evolution of Revolutional Velocity over time');
            plotsData = __[ plotsData, sumRef.T, sumAvai.T, sumPower.T ];   // f3 = figure(3); xlabel('Time [s]'); ylabel('Power [MW]'); title('Power Plot'); legend('Reference','Available','Produced');

            return plotsData.ToDoubleArray();
        }
예제 #7
0
        internal static void turbinesCalculations(out double dTurb, out int nTurb, out double kWake, out ILArray <double> x, out int gridX, out int gridY, out ILArray <double> yOrder, out double dy, out ILArray <int> xTurbC, out ILArray <int> yTurbC, WindTurbineParameters parm, SimParm simParm)
        {
            #region "Used variables declaration"
            ILArray <double> data;
            double           rotA;
            int gridRes;
            int endSize;
            ILArray <double> y;
            ILArray <double> xCoor;
            ILArray <double> yCoor;
            ILArray <double> xTurb;
            ILArray <double> yTurb;
            ILArray <double> xOrder;
            int ppp;
            ILArray <double> xGrid;
            ILArray <double> yGrid;
            #endregion

            data    = parm.wf.C;
            dTurb   = 2 * parm.radius._(1);
            nTurb   = parm.N;
            rotA    = parm.rotA;
            kWake   = parm.kWake;
            gridRes = simParm.gridRes; // Grid Resolution, the lower the number, the higher the amount of points computed.
            endSize = simParm.grid;

            //if (Ct < 0) // !ILMath.isreal(Ct) |
            {
                //disp('Ct is negative or complex');
            }

            x     = _c(1.0, gridRes, endSize);                            // x-grid.
            y     = _c(1.0, gridRes, endSize);                            // y-grid.
            gridX = length(x);                                            // Number of grid points.
            gridY = length(y);                                            // Number of grid points.

            xCoor = data[_(':'), _(1)];                                   // Coordiante of turbine, x-position
            yCoor = data[_(':'), _(2)];                                   // Coordinate of turbine, y-position

            ROTATE_corrd(out xTurb, out yTurb, xCoor, yCoor, rotA);       // Rotated (and scaled) coordinates
            WT_order(out xOrder, out yOrder, xTurb, yTurb);               // Ordered turbines.

            ppp = 2;                                                      // This parameter is also a bit weird.. But it changes the grid.
            DOMAIN_pt(out xGrid, out _double, gridX, dTurb, xOrder, ppp); //
            ppp = 5;
            DOMAIN_pt(out yGrid, out dy, gridY, dTurb, yOrder, ppp);

            Turb_centr_coord(out xTurbC, nTurb, gridX, xGrid, xOrder, gridRes); // Determines the grid point closest to the turbine.
            Turb_centr_coord(out yTurbC, nTurb, gridY, yGrid, yOrder, gridRes); // Determines the grid point closest to the turbine.
        }
예제 #8
0
        public static double[][] FarmControl(WakeFarmControlConfig config, out double[][] dataOut)
        {
            #region "Used variables declaration"
            bool    saveData;
            bool    enablePowerDistribution;
            bool    enableTurbineDynamics;
            bool    powerRefInterpolation;
            bool    enableVaryingDemand;
            SimParm simParm;

            ILArray <double>        wind;
            WindTurbineParameters   parm;
            EnvMatFileDataStructure env;
            WtMatFileDataStructure  wt;
            ILArray <int>           idx;
            double           Mg_max_rate;
            double           Ki, Kp, Umax, Umin;
            double           VS_CtInSp, VS_RtGnSp, VS_Rgn2K;
            double           omega0, beta0, power0;
            ILArray <double> initMatrix;
            ILArray <double> sumPower;
            ILArray <double> sumRef;
            ILArray <double> sumAvai;
            ILArray <double> P_ref_new;
            ILArray <double> P_demand;
            ILArray <double> v_nac;
            ILArray <double> P_ref;
            ILArray <double> Pa;
            ILArray <double> Power;
            ILArray <double> beta;
            ILArray <double> Omega;
            ILArray <double> initVector;
            ILArray <double> Mg___i_;
            ILArray <double> wField;
            double           dZ;
            ILArray <double> du;
            double[]         x___1_;
            double[]         x___2_;
            double[]         u___1_;
            double[]         u___2_;
            double           alpha;
            int j;
            ILArray <double> dx;
            ILArray <double> time;
            #endregion

            //% Initialization
            //General settings to be changed
            saveData = config.saveData;                               // Save all the simulated data to a .mat file?
            enablePowerDistribution = config.enablePowerDistribution; // Enable wind farm control and not only constant power
            enableTurbineDynamics   = config.enableTurbineDynamics;   // Enable dynamical turbine model. Disabling this will increase the speed significantly, but also lower the fidelity of the results (setting to false does not work properly yet)
            powerRefInterpolation   = config.powerRefInterpolation;   // Power Reference table interpolation.
            enableVaryingDemand     = config.enableVaryingDemand;     // Varying Reference

            // Simulation Properties:
            simParm          = new SimParm();
            simParm.tStart   = config.SimParm.tStart;         // time start
            simParm.timeStep = config.SimParm.timeStep;       // time step, 8Hz - the NREL model is 80Hz (for reasons unknown)
            simParm.tEnd     = config.SimParm.tEnd;           // time end
            int _simParm_tEnd_simParm_tStart__simParm_timeStep = (int)((simParm.tEnd - simParm.tStart) / simParm.timeStep);
            simParm.gridRes     = config.SimParm.gridRes;     // Grid Resolution
            simParm.grid        = config.SimParm.grid;        // Grid Size
            simParm.ctrlUpdate  = config.SimParm.ctrlUpdate;  // Update inverval for farm controller
            simParm.powerUpdate = config.SimParm.powerUpdate; // How often the control algorithm should update!
            load(config.Wind_MatFile, out wind);              // Load Wind Data

            // Wind farm and Turbine Properties properties
            parm       = new WindTurbineParameters();
            parm.wf    = load(config.Turbines); // Loads the Wind Farm Layout.
            parm.N     = length(parm.wf);       // number of turbines in farm
            parm.rotA  = -48.80;                // Angle of Attack
            parm.kWake = 0.06;

            //% Turbine properties - Loaded from the NREL5MW.mat file
            load(config.NREL5MW_MatFile, out env, out wt);                                                            // Load parameters from the NREL 5MW Reference turbine struct.
            parm.rho        = env.rho;                                                                                // air density
            parm.radius     = wt.rotor.radius * ones(1, parm.N);                                                      // rotor radius (NREL5MW)
            parm.rated      = wt.ctrl.p_rated * wt.gen.effeciency * ones(1, parm.N);                                  //rated power (NREL5MW)
            parm.ratedSpeed = wt.rotor.ratedspeed;                                                                    //rated rotor speed
            max(out idx, wt.cp.table[_(':')]);                                                                        // Find index for max Cp
            parm.Ct     = 0.0 * wt.ct.table[_(idx)] * ones(parm.N, (_simParm_tEnd_simParm_tStart__simParm_timeStep)); // Define initial Ct as the optimal Ct.
            parm.Cp     = wt.cp.table[_(idx)] * ones(parm.N, (_simParm_tEnd_simParm_tStart__simParm_timeStep));       // Define initial Cp as the optimal Cp.
            Mg_max_rate = wt.ctrl.torq.ratelim;                                                                       // Rate-limit on Torque Change.

            //Pitch control
            Ki   = wt.ctrl.pitch.Igain; // 0.008068634*360/2/pi; % integral gain (NREL5MW).
            Kp   = wt.ctrl.pitch.Pgain; // 0.01882681*360/2/pi; % proportional gain (NREL5MW).
            Umax = wt.ctrl.pitch.ulim;  // Upper limit of the pitch controller
            Umin = wt.ctrl.pitch.llim;  // Lower limit of the pitch controller.

            // NREL Regional Control - extracted from the NREL report.
            VS_CtInSp = 70.162240;
            VS_RtGnSp = 121.680500;
            VS_Rgn2K  = 2.332287;

            //% Set initial conditions
            omega0 = wt.rotor.ratedspeed; // Desired Rotation speed
            beta0  = 0;                   // wt.ctrl.pitch.llim; % Initial pitch at zero.
            power0 = 0;                   // Power Production

            //% Memory Allocation and Memory Initialization
            initMatrix = zeros(parm.N, _simParm_tEnd_simParm_tStart__simParm_timeStep);
            sumPower   = initMatrix[_(1), _(':')]; // Initialize produced power vector
            sumRef     = initMatrix[_(1), _(':')]; // Initialize reference power vector
            sumAvai    = initMatrix[_(1), _(':')]; // Initialize available power vector
            P_ref_new  = initMatrix[_(1), _(':')]; // Initialize new reference vector
            P_demand   = initMatrix[_(1), _(':')]; // Initialize power demand vector
            v_nac      = initMatrix.C;             // Initialize hub velocity matrix.
            P_ref      = initMatrix.C;             // Initialize matrix to save the power production history for each turbine.
            Pa         = initMatrix.C;             // Initialize available power matrix.
            Power      = initMatrix.C;             // Initialize individual WT power production matrix.
            beta       = initMatrix.C;             // Initialize pitch matrix.
            Omega      = initMatrix.C;             // Initialize revolutional velocity matrix.

            initVector = ones(parm.N, 1);
            Mg___i_    = 0 * initVector;
            wField     = zeros(simParm.grid / simParm.gridRes, simParm.grid / simParm.gridRes); // Wind field matrix

            //% Controller Initizliation
            dZ     = 0;                // Integrator Initialization.
            du     = zeros(parm.N, 1); // Integration variable.
            x___1_ = (omega0 * initVector).GetArrayForRead();
            x___2_ = (0 * initVector).GetArrayForRead();
            u___1_ = (beta0 * initVector).GetArrayForRead();  // u0
            u___2_ = (power0 * initVector).GetArrayForRead(); // u0
            P_demand._(1, '=', config.InitialPowerDemand);    // Power Demand.

            double           turbinesDTurb;
            int              turbinesNTurb;
            double           turbinesKWake;
            ILArray <double> turbinesX;
            int              turbinesGridX;
            int              turbinesGridY;
            ILArray <double> turbinesYOrder;
            double           turbinesDy;
            ILArray <int>    turbinesXTurbC;
            ILArray <int>    turbinesYTurbC;
            turbinesCalculations(out turbinesDTurb, out turbinesNTurb, out turbinesKWake, out turbinesX, out turbinesGridX, out turbinesGridY, out turbinesYOrder, out turbinesDy, out turbinesXTurbC, out turbinesYTurbC, parm, simParm);

            //% Simulate wind farm operation
            for (var i = 2; i <= _simParm_tEnd_simParm_tStart__simParm_timeStep; i++) // At each sample time (DT) from Tstart to Tend
            {
                //clc;
                //fprintf('Iteration Counter: %i out of %i \n', i, config.SimParm.tEnd * (1 / config.SimParm.timeStep));

                //%%%%%%%%%%%%%%% WIND FIELD FIFO MATRIX %%%%%%%%%%%%%%%%%%%
                wField[_(':'), _(2, ':', end)] = wField[_(':'), _(1, ':', (end - 1))];
                wField[_(':'), _(1)]           = wind._(i, 2) * ones(simParm.grid / simParm.gridRes, 1) + randn(simParm.grid / simParm.gridRes, 1) * 0.5;
                //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

                // Calculate the wake using the last Ct values
                ILArray <double> v_nac___i_;
                wakeCalculationsRLC(out v_nac___i_, turbinesDTurb, turbinesNTurb, turbinesKWake, turbinesX, turbinesGridX, turbinesGridY, turbinesYOrder, turbinesDy, turbinesXTurbC, turbinesYTurbC, parm.Ct[_(':'), _(i - 1)], transpose(wField), x___2_, parm, simParm);
                v_nac[_(':'), _(i)] = v_nac___i_;
                x___2_ = (v_nac[_(':'), _(i)]).GetArrayForRead();


                if (enableVaryingDemand) // A random walk to simulate fluctuations in the power demand.
                {
                    P_demand._(i, '=', P_demand._(i - 1) + randn() * 50000);
                }
                else
                {
                    P_demand._(i, '=', P_demand._(i - 1));
                }

                // Farm control
                // Calculate the power distribution references for each turbine
                if (enablePowerDistribution)
                {
                    ILArray <double> Pa___i_;
                    powerDistributionControl(out P_ref_new, out Pa___i_, x___2_, P_demand._(i), parm);
                    Pa[_(':'), _(i)] = Pa___i_;
                }

                //Hold  the demand for some seconds
                if (mod(i, round(simParm.ctrlUpdate / simParm.timeStep)) == simParm.powerUpdate)
                {
                    P_ref[_(':'), _(i)] = P_ref_new;
                }
                else
                {
                    if (powerRefInterpolation)
                    {
                        alpha = 0.01;
                        P_ref[_(':'), _(i)] = (1 - alpha) * P_ref[_(':'), _(i - 1)] + (alpha) * P_ref_new;
                    }
                    else
                    {
                        P_ref[_(':'), _(i)] = P_ref_new;
                    }
                }
                //Torque controller
                for (j = 1; j <= parm.N; j++)
                {
                    if ((x___1_[j - 1] * 97 >= VS_RtGnSp) || (u___1_[j - 1] >= 1))      //! We are in region 3 - power is constant
                    {
                        u___2_[j - 1] = P_ref._(j, i) / x___1_[j - 1];
                    }
                    else if (x___1_[j - 1] * 97 <= VS_CtInSp)                     //! We are in region 1 - torque is zero
                    {
                        u___2_[j - 1] = 0.0;
                    }
                    else                                                //! We are in region 2 - optimal torque is proportional to the square of the generator speed
                    {
                        u___2_[j - 1] = 97 * VS_Rgn2K * x___1_[j - 1] * x___1_[j - 1] * _p(97, 2);
                    }
                }

                dx     = (omega0 - ((ILArray <double>)x___1_)) - (omega0 - Omega[_(':'), _(i - 1)]);
                du     = Kp * dx + Ki * simParm.timeStep * (omega0 - ((ILArray <double>)(x___1_)));
                du     = min(max(du, -wt.ctrl.pitch.ratelim), wt.ctrl.pitch.ratelim);
                u___1_ = (min(max(((ILArray <double>)u___1_) + du * simParm.timeStep, Umin), Umax)).GetArrayForRead();


                Mg___i_            = u___2_; // Torque Input
                beta[_(':'), _(i)] = u___1_; // Pitch Input

                // Turbine dynamics - can be simplified:
                if (enableTurbineDynamics)
                {
                    for (j = 1; j <= parm.N; j++)
                    {
                        double x_j_1_; double parm_Ct_j_i_; double parm_Cp_j_i_;
                        turbineDrivetrainModel(out x_j_1_, out parm_Ct_j_i_, out parm_Cp_j_i_, x___1_[j - 1], x___2_[j - 1], u___1_[j - 1], u___2_[j - 1], wt, env, simParm.timeStep);
                        x___1_[j - 1] = x_j_1_; parm.Ct._(j, i, '=', parm_Ct_j_i_); parm.Cp._(j, i, '=', parm_Cp_j_i_);
                    }
                }
                else
                {
                    x___1_.Fill(parm.ratedSpeed); // Rotational speed
                }

                Omega[_(':'), _(i)] = x___1_;
                Power[_(':'), _(i)] = Omega[_(':'), _(i)] * Mg___i_;

                // Power Summations
                sumPower._(i, '=', sum_(Power[_(':'), _(i)]) * _p(10, -6));
                sumRef._(i, '=', sum_(P_ref[_(':'), _(i)]) * _p(10, -6));
                sumAvai._(i, '=', sum_(Pa[_(':'), _(i)]) * _p(10, -6));

                // NOWCASTING FUNKTION HER
                // powerPrediction(i) = powerPrediction(i,sumPower(i:-1:i-10)) % or something
                // similar.
            }

            //%
            //time = (_c((decimal)simParm.tStart, (decimal)simParm.timeStep, (decimal)simParm.tEnd - (decimal)simParm.timeStep)).T;
            time = (_c(0.0, 1.0, _simParm_tEnd_simParm_tStart__simParm_timeStep - 1) * simParm.timeStep + simParm.tStart).T;


            if (saveData)
            {
                //throw new ApplicationException(string.Format("{0}\t{1}\t{2}\t{3}", time.Dimensions, sumPower.T.Dimensions, sumRef.T.Dimensions, sumAvai.T.Dimensions));
                dataOut = (__[time, sumPower.T, sumRef.T, sumAvai.T]).ToDoubleArray();
                //save dataOut;
            }
            else
            {
                dataOut = new double[][] { new double[] { 0, 0, 0, 0 } };
            }
            //% Plotting
            //Below a number of different plots are made. Most of them for test purposes
            ILArray <double> plotsData;
            plotsData = time.C;
            plotsData = __[plotsData, P_ref.T *(_p(10, -6))];               // f1 = figure(1); xlabel('Time [s]'); ylabel('Power Reference [MW]'); title('Individual Power Reference');
            plotsData = __[plotsData, v_nac.T];                             // f2 = figure(2); xlabel('Time [s]'); ylabel('Wind Speed [m/s]'); title('Wind Speed @ individual turbine');
            plotsData = __[plotsData, beta.T];                              // f4 = figure(4); xlabel('Time [s]'); ylabel('Pitch Angle [deg]'); title('Evolution of Pitch angle over time');
            plotsData = __[plotsData, Omega.T];                             // f5 = figure(5); xlabel('Time [s]'); ylabel('Revolutional Velocity [rpm]'); title('Evolution of Revolutional Velocity over time');
            plotsData = __[plotsData, sumRef.T, sumAvai.T, sumPower.T];     // f3 = figure(3); xlabel('Time [s]'); ylabel('Power [MW]'); title('Power Plot'); legend('Reference','Available','Produced');

            return(plotsData.ToDoubleArray());
        }