├── IMAGES
├── Jensen's wake model.png
└── j.enconman.2019.06.082.svg
├── LICENSE
├── README.md
├── WindFarmGenetic.py
└── main.py
/IMAGES/Jensen's wake model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JuXinglong/WFLOP_SUGGA_Python/27ae25a40be7fe2258894a741013aa46e9d1e4b3/IMAGES/Jensen's wake model.png
--------------------------------------------------------------------------------
/IMAGES/j.enconman.2019.06.082.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 JuXinglong
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Energy Conversion and Management - Wind farm layout optimization based on support vector regression guided genetic algorithm with consideration of participation among landowners
2 |
3 | # Wind farm layout optimization problem (WFLOP) SUGGA Python toolbox
4 |
5 | ## Reference
6 | [1] Xinglong Ju, Feng Liu, Li Wang, and Wei-Jen Lee. "Wind farm layout optimization based on support vector regression guided genetic algorithm with consideration of participation among landowners." *Energy Conversion and Management* 196 (2019): 1267-1281. [](https://doi.org/10.1016/j.enconman.2019.06.082)
7 |
8 | If you have any questions, please email Feng Liu (*fliu0@mgh.harvard.edu*), or Xinglong Ju (*xinglong.ju@mavs.uta.edu*).
9 |
10 |
11 |
12 | Jensen's wake model
13 |
14 |
15 | ## SUGGA - WFLOP Python toolbox guide
16 | ### Import necessary libraries
17 | ```python
18 | import numpy as np
19 | import pandas as pd
20 | import WindFarmGenetic # wind farm layout optimization using genetic algorithms classes
21 | from datetime import datetime
22 | import os
23 | from sklearn.svm import SVR
24 | import pickle
25 | ```
26 |
27 | ### Wind farm settings and algorithm settings
28 | ```python
29 | # parameters for the genetic algorithm
30 | elite_rate = 0.2
31 | cross_rate = 0.6
32 | random_rate = 0.5
33 | mutate_rate = 0.1
34 |
35 | wt_N = 25 # number of wind turbines 15, 20, or 25
36 | # NA_loc_array : not available location array, the index starting from 1
37 |
38 | # L1 : wind farm, cells 121(inclusive) to 144(inclusive)
39 | NA_loc_array = np.arange(121, 145, 1)
40 | #
41 | # # L2
42 | # NA_loc_array = np.arange(61, 85, 1)
43 | #
44 | # # L3
45 | # NA_loc_array = np.concatenate((np.arange(11, 144, 12), np.arange(12, 145, 12)))
46 | #
47 | # # L4
48 | # NA_loc_array = np.concatenate((np.arange(6, 144, 12), np.arange(7, 145, 12)))
49 | #
50 | # # L5
51 | # NA_loc_array = np.concatenate((np.arange(41, 105, 12), np.arange(42, 105, 12),
52 | # np.arange(43, 105, 12),
53 | # np.arange(44, 105, 12)))
54 | #
55 | # # L6
56 | # NA_loc_array = np.concatenate((np.arange(1, 28, 12), np.arange(2, 28, 12),
57 | # np.arange(12, 37, 12),
58 | # np.arange(11, 37, 12),
59 | # np.arange(109, 145, 12), np.arange(119, 145, 12),
60 | # np.arange(110, 145, 12),
61 | # np.arange(120, 145, 12),
62 | # ))
63 | #
64 | # # L7
65 | # NA_loc_array = np.arange(133, 145, 1)
66 | #
67 | # # L8
68 | # NA_loc_array = np.arange(61, 73, 1)
69 | #
70 | # # L9
71 | # NA_loc_array = np.arange(12, 145, 12)
72 | #
73 | # # L10
74 | # NA_loc_array = np.arange(6, 145, 12)
75 | #
76 | # # L11
77 | # NA_loc_array = np.concatenate((np.arange(42, 105, 12),
78 | # np.arange(43, 105, 12)))
79 | #
80 | # # L12
81 | # NA_loc_array = np.array((1, 2, 11, 12, 13, 24, 121, 132, 133, 134, 143, 144))
82 |
83 | # convert numpy array to list, datatype convert
84 | NA_loc = NA_loc_array.tolist()
85 |
86 | # L0
87 | # NA_loc = []
88 |
89 |
90 | population_size = 120 # how many layouts in a population
91 | iteration_times = 200 # how many iterations in a genetic algorithm run
92 |
93 | n_inits = 100 # number of initial populations n_inits >= run_times
94 | run_times = 100 # number of different initial populations
95 |
96 |
97 |
98 | # wind farm size, cells
99 | cols_cells = 12 # number of cells each row
100 | rows_cells = 12 # number of cells each column
101 | cell_width = 77.0 * 3 # unit : m
102 |
103 | # all data will be save in data folder
104 | data_folder = "data"
105 | if not os.path.exists(data_folder):
106 | os.makedirs(data_folder)
107 | ```
108 |
109 |
110 | ### Create a WindFarmGenetic object
111 | ```python
112 | # create an WindFarmGenetic object. Specify the number of rows and the number columns of the wind farm land. N is the number of wind turbines.
113 | # NA_loc is the not available locations on the wind farm land. Landowners does not want to participate in the wind farm.
114 | # pop_size: how many individuals in the population
115 | # iteration: iteration times of the genetic algorithm
116 | wfg = WindFarmGenetic.WindFarmGenetic(rows=rows_cells, cols=cols_cells, N=wt_N, NA_loc=NA_loc, pop_size=population_size,
117 | iteration=iteration_times,cell_width=cell_width, elite_rate=elite_rate,
118 | cross_rate=cross_rate, random_rate=random_rate, mutate_rate=mutate_rate)
119 | # Specify the wind distribution
120 | # wind distribution is discrete (number of wind speeds) by (number of wind directions)
121 | # wfg.init_1_direction_1_N_speed_13()
122 | # file name to store the wind power distribution SVR model
123 | # svr_model_filename = 'svr_1s1d_N_13.svr'
124 |
125 | # wfg.init_4_direction_1_speed_13()
126 | # svr_model_filename = 'svr_1s4d_13.svr'
127 |
128 | wfg.init_6_direction_1_speed_13()
129 | # svr_model_filename = 'svr_1s6d_13.svr'
130 | ```
131 |
132 | ### Generate initial populations
133 | ```python
134 | # initial population saved folder
135 | init_pops_data_folder = "{}/init_data".format(data_folder)
136 | if not os.path.exists(init_pops_data_folder):
137 | os.makedirs(init_pops_data_folder)
138 | # generate initial populations to start with and store them
139 | # in order to start from the same initial population for different methods
140 | # so it is fair to compare the final results
141 |
142 | for i in range(n_inits):
143 | wfg.gen_init_pop_NA()
144 | wfg.save_init_pop_NA("{}/init_{}.dat".format(init_pops_data_folder,i), "{}/init_{}_NA.dat".format(init_pops_data_folder,i))
145 | ```
146 |
147 | ### Create results folder
148 | ```python
149 | # results folder
150 | # adaptive_best_layouts_N60_9_20190422213718.dat : best layout for AGA of run index 9
151 | # result_CGA_20190422213715.dat : run time and best eta for CGA method
152 | results_data_folder = "data/results"
153 | if not os.path.exists(results_data_folder):
154 | os.makedirs(results_data_folder)
155 | # if cg,ag,sg folder does not exist, create these folders. Folders to store the running results
156 | # cg: convertional genetic algorithm
157 | # ag: adaptive genetic algorithm
158 | # sg: support vector regression guided genetic algorithm
159 | cg_result_folder = "{}/cg".format(results_data_folder)
160 | if not os.path.exists(cg_result_folder):
161 | os.makedirs(cg_result_folder)
162 |
163 | ag_result_folder = "{}/ag".format(results_data_folder)
164 | if not os.path.exists(ag_result_folder):
165 | os.makedirs(ag_result_folder)
166 |
167 | sg_result_folder = "{}/sg".format(results_data_folder)
168 | if not os.path.exists(sg_result_folder):
169 | os.makedirs(sg_result_folder)
170 |
171 | # resul_arr: run_times by 2 , the first column is the run time in seconds for each run and the second column is the conversion efficiency for the run
172 | result_arr = np.zeros((run_times, 2), dtype=np.float32)
173 | ```
174 |
175 | ### Run conventional genetic algorithm (CGA)
176 | ```python
177 | # CGA: Conventional genetic algorithm
178 | for i in range(0, run_times): # run times
179 | print("run times {} ...".format(i))
180 | # load initial population
181 | wfg.load_init_pop_NA("{}/init_{}.dat".format(init_pops_data_folder,i), "{}/init_{}_NA.dat".format(init_pops_data_folder,i))
182 | # run the conventional genetic algorithm and return run time and conversion efficiency
183 | run_time, eta = wfg.conventional_genetic_alg(i,result_folder=cg_result_folder)
184 | result_arr[i, 0] = run_time
185 | result_arr[i, 1] = eta
186 | time_stamp = datetime.now().strftime("%Y%m%d%H%M%S")
187 | # save the run time and etas to a file
188 | filename = "{}/result_conventional_{}.dat".format(cg_result_folder,time_stamp)
189 | np.savetxt(filename, result_arr, fmt='%f', delimiter=" ")
190 | ```
191 |
192 | ### Run adaptive genetic algorithm (AGA)
193 | ```python
194 | # AGA: adaptive genetic algorithm
195 | for i in range(0,run_times): # run times
196 | print("run times {} ...".format(i))
197 | wfg.load_init_pop_NA("{}/init_{}.dat".format(init_pops_data_folder,i),"{}/init_{}_NA.dat".format(init_pops_data_folder,i))
198 | run_time, eta = wfg.adaptive_genetic_alg(i,result_folder=ag_result_folder)
199 | result_arr[i, 0] = run_time
200 | result_arr[i, 1] = eta
201 | time_stamp = datetime.now().strftime("%Y%m%d%H%M%S")
202 | filename = "{}/result_adaptive_{}.dat".format(ag_result_folder,time_stamp)
203 | np.savetxt(filename, result_arr, fmt='%f', delimiter=" ")
204 | ```
205 |
206 | ### Run support vector regression guided genetic algorithm (SUGGA)
207 | #### Generate wind distribution surface
208 | ```python
209 | n_mc_samples = 10000 # svr train data, number of layouts to average
210 |
211 | wds_data_folder = "{}/wds".format(data_folder)
212 | if not os.path.exists(wds_data_folder):
213 | os.makedirs(wds_data_folder)
214 | # mc : monte-carlo
215 |
216 | # number of layouts to generate as the training data for regression
217 | # to build the power distribution surface
218 |
219 | # mc_layout.dat file stores layouts only with 0s and 1s. 0 means no turbine here. 1 means one turbine here.
220 | # mc_layout_NA.dat file stores layouts with 0s, 1s and 2s. 2 means no turbine and not available for turbine.
221 | # These two files are used to generate wind power distribution.
222 | # Each file has 10000 lines. Each line is layout.
223 | # gen_mc_grid_with_NA_loc function generates these two files.
224 | train_mc_layouts, train_mc_layouts_NA = WindFarmGenetic.LayoutGridMCGenerator.gen_mc_grid_with_NA_loc(rows_cells,
225 | cols_cells,
226 | n_mc_samples,
227 | wt_N, NA_loc,
228 | "{}/mc_layout.dat".format(wds_data_folder),
229 | "{}/mc_layout_NA.dat".format(wds_data_folder))
230 |
231 |
232 | # wfg.init_1_direction_1_N_speed_13()
233 | # file name to store the wind power distribution SVR model
234 | # svr_model_filename = 'svr_1s1d_N_13.svr'
235 |
236 | # wfg.init_4_direction_1_speed_13()
237 | # svr_model_filename = 'svr_1s4d_13.svr'
238 |
239 | # wfg.init_6_direction_1_speed_13()
240 | svr_model_filename = 'svr_1s6d_13.svr'
241 |
242 |
243 | # load Monte-Carlo layouts from a text file. 10000 random layouts
244 | layouts = np.genfromtxt("{}/mc_layout.dat".format(wds_data_folder), delimiter=" ", dtype=np.int32)
245 | # generate the location index coordinate and average power output at each location index coordinate
246 | # location index coordinate : in the cells, the cell with index 1 has location index (0,0) and the cell 2 has (1,0)
247 | # store the location index coordinate in x.dat and average power in y.dat
248 | wfg.mc_gen_xy_NA(rows=rows_cells, cols=cols_cells, layouts=layouts, n=n_mc_samples, N=wt_N, xfname="{}/x.dat".format(wds_data_folder),
249 | yfname="{}/y.dat".format(wds_data_folder))
250 |
251 |
252 | # read index location coordinates
253 | x_original = pd.read_csv("{}/x.dat".format(wds_data_folder), header=None, nrows=rows_cells * cols_cells, delim_whitespace=True, dtype=np.float32)
254 | x_original = x_original.values
255 |
256 | # read the power output of each index location coordinate
257 | y_original = pd.read_csv("{}/y.dat".format(wds_data_folder), header=None, nrows=rows_cells * cols_cells, delim_whitespace=True, dtype=np.float32)
258 | y_original = y_original.values.flatten()
259 |
260 | # create a SVR object and specify the kernal and other parameters
261 | svr_model = SVR(kernel='rbf', C=2000.0, gamma=0.3, epsilon=.1)
262 | # build the SVR power distribution model
263 | svr_model.fit(x_original, y_original)
264 |
265 | # save the SVR model to a file
266 | pickle.dump(svr_model, open("{}/{}".format(wds_data_folder,svr_model_filename), 'wb'))
267 |
268 | # This is how to load SVR model from a file
269 | # svr_model = pickle.load(open("{}/{}".format(wds_data_folder,svr_model_filename), 'rb'))
270 | ```
271 | #### Run SUGGA method
272 | ```python
273 | #SUGGA: support vector regression guided genetic algorithm
274 | for i in range(0, run_times): # run times
275 | print("run times {} ...".format(i))
276 | wfg.load_init_pop_NA("{}/init_{}.dat".format(init_pops_data_folder,i),"{}/init_{}_NA.dat".format(init_pops_data_folder,i))
277 | run_time, eta = wfg.sugga_genetic_alg(i,svr_model=svr_model,result_folder=sg_result_folder)
278 | result_arr[i, 0] = run_time
279 | result_arr[i, 1] = eta
280 | time_stamp = datetime.now().strftime("%Y%m%d%H%M%S")
281 | filename = "{}/result_sugga_{}.dat".format(sg_result_folder,time_stamp)
282 | np.savetxt(filename, result_arr, fmt='%f', delimiter=" ")
283 | ```
284 |
--------------------------------------------------------------------------------
/WindFarmGenetic.py:
--------------------------------------------------------------------------------
1 | import math
2 | import numpy as np
3 | import matplotlib.pyplot as plt
4 | import matplotlib.patches as patches
5 | import time
6 | from datetime import datetime
7 |
8 | __version__ = "1.0.0"
9 |
10 |
11 | class WindFarmGenetic:
12 | elite_rate = 0.2 # elite rate: parameter for genetic algorithm
13 | cross_rate = 0.6 # crossover rate: parameter for genetic algorithm
14 | random_rate = 0.5 # random rate: parameter for genetic algorithm
15 | mutate_rate = 0.1 # mutation rate: parameter for genetic algorithm
16 | turbine = None
17 | pop_size = 0 # population size : how many individuals in a population
18 | N = 0 # number of wind turbines
19 | rows = 0 # how many cell rows the wind farm are divided into
20 | cols = 0 # how many colus the wind farm land are divided into
21 | iteration = 0 # how many iterations the genetic algorithm run
22 | NA_loc=None # not available, not usable locations index list (the index starts from 1)
23 | cell_width = 0 # cell width
24 | cell_width_half = 0 # half cell width
25 |
26 | # constructor of the class
27 | def __init__(self, rows=21, cols=21, N=0,NA_loc=None, pop_size=100, iteration=200,cell_width=0, elite_rate=0.2,
28 | cross_rate=0.6, random_rate=0.5, mutate_rate=0.1):
29 | self.turbine = GE_1_5_sleTurbine()
30 | self.rows = rows
31 | self.cols = cols
32 | self.N = N
33 | self.pop_size = pop_size
34 | self.iteration = iteration
35 |
36 | self.cell_width = cell_width
37 | self.cell_width_half = cell_width * 0.5
38 |
39 | self.elite_rate = elite_rate
40 | self.cross_rate = cross_rate
41 | self.random_rate = random_rate
42 | self.mutate_rate = mutate_rate
43 |
44 | self.init_pop = None
45 | self.init_pop_NA = None
46 | self.init_pop_nonezero_indices = None
47 | self.NA_loc=NA_loc
48 | return
49 |
50 |
51 | # choose wind distribution : 1 direction, 1 speed
52 | def init_1_direction_1_N_speed_13(self):
53 | self.theta = np.array([0], dtype=np.float32)
54 | self.velocity = np.array([13.0], dtype=np.float32)
55 | self.f_theta_v = np.array([[1.0]], dtype=np.float32)
56 | return
57 |
58 | # choose wind distribution : 4 directions, 1 speed
59 | def init_4_direction_1_speed_13(self):
60 | self.theta = np.array(
61 | [0, 3 * np.pi / 6.0, 6 * np.pi / 6.0, 9 * np.pi / 6.0], dtype=np.float32) # 1.0/4
62 | self.velocity = np.array([13.0], dtype=np.float32) # 1
63 | self.f_theta_v = np.array([[0.25], [0.25], [0.25], [0.25]], dtype=np.float32)
64 | return
65 |
66 | # choose wind distribution : 6 direction, 1 speed
67 | def init_6_direction_1_speed_13(self):
68 | self.theta = np.array([0, np.pi / 3.0, 2 * np.pi / 3.0, 3 * np.pi / 3.0, 4 * np.pi / 3.0, 5 * np.pi / 3.0],
69 | dtype=np.float32) # 0.2, 0,3 0.2 0. 1 0.1 0.1
70 | self.velocity = np.array([13.0], dtype=np.float32) # 1
71 | self.f_theta_v = np.array([[0.2], [0.3], [0.2], [0.1], [0.1], [0.1]], dtype=np.float32)
72 | return
73 |
74 |
75 | # the total cost
76 | def cost(self, N):
77 | return 1.0 * N * (2.0 / 3.0 + 1.0 / 3.0 * math.exp(-0.00174 * N ** 2))
78 |
79 | # generate initial population
80 | def gen_init_pop_NA(self):
81 | self.init_pop,self.init_pop_NA = LayoutGridMCGenerator.gen_pop_with_NA_loc(rows=self.rows, cols=self.cols,NA_loc=self.NA_loc, n=self.pop_size, N=self.N)
82 | self.init_pop_nonezero_indices = np.zeros((self.pop_size, self.N), dtype=np.int32)
83 | for ind_init_pop in range(self.pop_size):
84 | ind_indices = 0
85 | for ind in range(self.rows * self.cols):
86 | if self.init_pop[ind_init_pop, ind] == 1:
87 | self.init_pop_nonezero_indices[ind_init_pop, ind_indices] = ind
88 | ind_indices += 1
89 | return
90 |
91 | # save initial population
92 | def save_init_pop_NA(self, fname,fname_NA):
93 | np.savetxt(fname, self.init_pop, fmt='%d', delimiter=" ")
94 | np.savetxt(fname_NA, self.init_pop_NA, fmt='%d', delimiter=" ")
95 | return
96 |
97 |
98 | # load initial population
99 | def load_init_pop_NA(self, fname,fname_NA):
100 | self.init_pop = np.genfromtxt(fname, delimiter=" ", dtype=np.int32)
101 | self.init_pop_NA = np.genfromtxt(fname_NA, delimiter=" ", dtype=np.int32)
102 | self.init_pop_nonezero_indices = np.zeros((self.pop_size, self.N), dtype=np.int32)
103 | for ind_init_pop in range(self.pop_size):
104 | ind_indices = 0
105 | for ind in range(self.rows * self.cols):
106 | if self.init_pop[ind_init_pop, ind] == 1:
107 | self.init_pop_nonezero_indices[ind_init_pop, ind_indices] = ind
108 | ind_indices += 1
109 | return
110 |
111 | # calculate total rate power
112 | def cal_P_rate_total(self):
113 | f_p = 0.0
114 | for ind_t in range(len(self.theta)):
115 | for ind_v in range(len(self.velocity)):
116 | f_p += self.f_theta_v[ind_t, ind_v] * self.turbine.P_i_X(self.velocity[ind_v])
117 | return self.N * f_p
118 |
119 |
120 | # generate the location index coordinate and average power output at each location index coordinate
121 | # location index coordinate : in the cells, the cell with index 1 has location index (0,0) and the cell 2 has (1,0)
122 | # store the location index coordinate in x.dat and average power in y.dat
123 | def mc_gen_xy_NA(self, rows, cols, layouts, n, N, xfname, yfname):
124 | layouts_cr = np.zeros((rows * cols, 2), dtype=np.int32) # layouts column row index
125 | n_copies = np.sum(layouts, axis=0)
126 | layouts_power = np.zeros((n, rows * cols), dtype=np.float32)
127 | self.mc_fitness(pop=layouts, rows=rows, cols=cols, pop_size=n, N=N, lp=layouts_power)
128 | sum_layout_power = np.sum(layouts_power, axis=0)
129 | mean_power = np.zeros(rows * cols, dtype=np.float32)
130 | for i in range(rows * cols):
131 | if n_copies[i]>0:
132 | mean_power[i] = sum_layout_power[i] / n_copies[i]
133 | for ind in range(rows * cols):
134 | r_i = np.floor(ind / cols)
135 | c_i = np.floor(ind - r_i * cols)
136 | layouts_cr[ind, 0] = c_i
137 | layouts_cr[ind, 1] = r_i
138 | np.savetxt(xfname, layouts_cr, fmt='%d', delimiter=" ")
139 | np.savetxt(yfname, mean_power, fmt='%f', delimiter=" ")
140 | return
141 |
142 | # calculate fitness value of the population
143 | def mc_fitness(self, pop, rows, cols, pop_size, N, lp):
144 | for i in range(pop_size):
145 | print("layout {}...".format(i))
146 | xy_position = np.zeros((2, N), dtype=np.float32) # x y position
147 | cr_position = np.zeros((2, N), dtype=np.int32) # column row position
148 | ind_position = np.zeros(N, dtype=np.int32)
149 | ind_pos = 0
150 | for ind in range(rows * cols):
151 | if pop[i, ind] == 1:
152 | r_i = np.floor(ind / cols)
153 | c_i = np.floor(ind - r_i * cols)
154 | cr_position[0, ind_pos] = c_i
155 | cr_position[1, ind_pos] = r_i
156 | xy_position[0, ind_pos] = c_i * self.cell_width + self.cell_width_half
157 | xy_position[1, ind_pos] = r_i * self.cell_width + self.cell_width_half
158 | ind_position[ind_pos] = ind
159 | ind_pos += 1
160 | lp_power_accum = np.zeros(N, dtype=np.float32) # a specific layout power accumulate
161 | for ind_t in range(len(self.theta)):
162 | for ind_v in range(len(self.velocity)):
163 | trans_matrix = np.array(
164 | [[np.cos(self.theta[ind_t]), -np.sin(self.theta[ind_t])],
165 | [np.sin(self.theta[ind_t]), np.cos(self.theta[ind_t])]],
166 | np.float32)
167 | trans_xy_position = np.matmul(trans_matrix, xy_position)
168 | speed_deficiency = self.wake_calculate(trans_xy_position, N)
169 |
170 | actual_velocity = (1 - speed_deficiency) * self.velocity[ind_v]
171 | lp_power = self.layout_power(actual_velocity,
172 | N) # total power of a specific layout specific wind speed specific theta
173 | lp_power = lp_power * self.f_theta_v[ind_t, ind_v]
174 | lp_power_accum += lp_power
175 |
176 | lp[i, ind_position] = lp_power_accum
177 |
178 | return
179 |
180 |
181 | # calculate wake effect
182 | def wake_calculate(self, trans_xy_position, N):
183 | sorted_index = np.argsort(-trans_xy_position[1, :]) # y value descending
184 | wake_deficiency = np.zeros(N, dtype=np.float32)
185 | wake_deficiency[sorted_index[0]] = 0
186 | for i in range(1, N):
187 | for j in range(i):
188 | xdis = np.absolute(trans_xy_position[0, sorted_index[i]] - trans_xy_position[0, sorted_index[j]])
189 | ydis = np.absolute(trans_xy_position[1, sorted_index[i]] - trans_xy_position[1, sorted_index[j]])
190 | d = self.cal_deficiency(dx=xdis, dy=ydis, r=self.turbine.rator_radius,
191 | ec=self.turbine.entrainment_const)
192 | wake_deficiency[sorted_index[i]] += d ** 2
193 |
194 | wake_deficiency[sorted_index[i]] = np.sqrt(wake_deficiency[sorted_index[i]])
195 | return wake_deficiency
196 |
197 | # ec : entrainment_const
198 | def cal_deficiency(self, dx, dy, r, ec):
199 | if dy == 0:
200 | return 0
201 | R = r + ec * dy
202 | inter_area = self.cal_interaction_area(dx=dx, dy=dy, r=r, R=R)
203 | d = 2.0 / 3.0 * (r ** 2) / (R ** 2) * inter_area / (np.pi * r ** 2)
204 | return d
205 |
206 | #calculate ineraction area
207 | def cal_interaction_area(self, dx, dy, r, R):
208 | if dx >= r + R:
209 | return 0
210 | elif dx >= np.sqrt(R ** 2 - r ** 2):
211 | alpha = np.arccos((R ** 2 + dx ** 2 - r ** 2) / (2 * R * dx))
212 | beta = np.arccos((r ** 2 + dx ** 2 - R ** 2) / (2 * r * dx))
213 | A1 = alpha * R ** 2
214 | A2 = beta * r ** 2
215 | A3 = R * dx * np.sin(alpha)
216 | return A1 + A2 - A3
217 | elif dx >= R - r:
218 | alpha = np.arccos((R ** 2 + dx ** 2 - r ** 2) / (2 * R * dx))
219 | beta = np.pi - np.arccos((r ** 2 + dx ** 2 - R ** 2) / (2 * r * dx))
220 | A1 = alpha * R ** 2
221 | A2 = beta * r ** 2
222 | A3 = R * dx * np.sin(alpha)
223 | return np.pi * r ** 2 - (A2 + A3 - A1)
224 | else:
225 | return np.pi * r ** 2
226 |
227 | def layout_power(self, velocity, N):
228 | power = np.zeros(N, dtype=np.float32)
229 | for i in range(N):
230 | power[i] = self.turbine.P_i_X(velocity[i])
231 | return power
232 |
233 | # conventional genetic algorithm
234 | def conventional_genetic_alg(self, ind_time=0,result_folder=None): # conventional genetic algorithm
235 | P_rate_total = self.cal_P_rate_total()
236 | start_time = datetime.now()
237 | print("conventional genetic algorithm starts....")
238 | fitness_generations = np.zeros(self.iteration, dtype=np.float32) # best fitness value in each generation
239 | best_layout_generations = np.zeros((self.iteration, self.rows * self.cols),
240 | dtype=np.int32) # best layout in each generation
241 | best_layout_NA_generations = np.zeros((self.iteration, self.rows * self.cols),
242 | dtype=np.int32) # best layout in each generation
243 | power_order = np.zeros((self.pop_size, self.N),
244 | dtype=np.int32) # each row is a layout cell indices. in each layout, order turbine power from least to largest
245 | pop = np.copy(self.init_pop)
246 | pop_NA = np.copy(self.init_pop_NA)
247 | pop_indices = np.copy(self.init_pop_nonezero_indices) # each row is a layout cell indices.
248 |
249 | eN = int(np.floor(self.pop_size * self.elite_rate)) # elite number
250 | rN = int(int(np.floor(self.pop_size * self.mutate_rate)) / eN) * eN # reproduce number
251 | mN = rN # mutation number
252 | cN = self.pop_size - eN - mN # crossover number
253 |
254 | for gen in range(self.iteration):
255 | print("generation {}...".format(gen))
256 | fitness_value = self.conventional_fitness(pop=pop, rows=self.rows, cols=self.cols, pop_size=self.pop_size,
257 | N=self.N,
258 | po=power_order)
259 | sorted_index = np.argsort(-fitness_value) # fitness value descending from largest to least
260 |
261 | pop = pop[sorted_index, :]
262 | pop_NA=pop_NA[sorted_index, :]
263 |
264 | power_order = power_order[sorted_index, :]
265 | pop_indices = pop_indices[sorted_index, :]
266 |
267 | if gen == 0:
268 | fitness_generations[gen] = fitness_value[sorted_index[0]]
269 | best_layout_generations[gen, :] = pop[0, :]
270 | best_layout_NA_generations[gen, :] = pop_NA[0, :]
271 | else:
272 | if fitness_value[sorted_index[0]] > fitness_generations[gen - 1]:
273 | fitness_generations[gen] = fitness_value[sorted_index[0]]
274 | best_layout_generations[gen, :] = pop[0, :]
275 | best_layout_NA_generations[gen, :] = pop_NA[0, :]
276 | else:
277 | fitness_generations[gen] = fitness_generations[gen - 1]
278 | best_layout_generations[gen, :] = best_layout_generations[gen - 1, :]
279 | best_layout_NA_generations[gen, :] = best_layout_NA_generations[gen - 1, :]
280 | n_parents, parent_layouts,parent_layouts_NA, parent_pop_indices = self.conventional_select(pop=pop,pop_NA=pop_NA, pop_indices=pop_indices,
281 | pop_size=self.pop_size,
282 | elite_rate=self.elite_rate,
283 | random_rate=self.random_rate)
284 | self.conventional_crossover(N=self.N, pop=pop,pop_NA=pop_NA, pop_indices=pop_indices, pop_size=self.pop_size,
285 | n_parents=n_parents,
286 | parent_layouts=parent_layouts,parent_layouts_NA=parent_layouts_NA, parent_pop_indices=parent_pop_indices)
287 |
288 |
289 | self.conventional_mutation(rows=self.rows, cols=self.cols, N=self.N, pop=pop,pop_NA= pop_NA,pop_indices=pop_indices,
290 | pop_size=self.pop_size,
291 | mutation_rate=self.mutate_rate)
292 | end_time = datetime.now()
293 | run_time = (end_time - start_time).total_seconds()
294 | eta_generations = np.copy(fitness_generations)
295 | eta_generations = eta_generations * (1.0 / P_rate_total)
296 | time_stamp = datetime.now().strftime("%Y%m%d%H%M%S")
297 | filename = "{}/conventional_eta_N{}_{}_{}.dat".format(result_folder,self.N, ind_time, time_stamp)
298 | np.savetxt(filename, eta_generations, fmt='%f', delimiter=" ")
299 | filename = "{}/conventional_best_layouts_N{}_{}_{}.dat".format(result_folder,self.N, ind_time, time_stamp)
300 | np.savetxt(filename, best_layout_generations, fmt='%d', delimiter=" ")
301 | filename = "{}/conventional_best_layouts_NA_N{}_{}_{}.dat".format(result_folder,self.N, ind_time, time_stamp)
302 | np.savetxt(filename, best_layout_NA_generations, fmt='%d', delimiter=" ")
303 | print("conventional genetic algorithm ends.")
304 | filename = "{}/conventional_runtime.txt".format(result_folder)
305 | f = open(filename, "a+")
306 | f.write("{}\n".format(run_time))
307 | f.close()
308 |
309 | filename = "{}/conventional_eta.txt".format(result_folder)
310 | f = open(filename, "a+")
311 | f.write("{}\n".format(eta_generations[self.iteration - 1]))
312 | f.close()
313 | return run_time, eta_generations[self.iteration - 1]
314 |
315 | def conventional_mutation(self, rows, cols, N, pop,pop_NA, pop_indices, pop_size, mutation_rate):
316 | np.random.seed(seed=int(time.time()))
317 | for i in range(pop_size):
318 | if np.random.randn() > mutation_rate:
319 | continue
320 | while True:
321 | turbine_pos = np.random.randint(0, cols * rows)
322 | if pop_NA[i, turbine_pos] == 1:
323 | break
324 | while True:
325 | null_turbine_pos = np.random.randint(0, cols * rows)
326 | if pop_NA[i, null_turbine_pos] == 0:
327 | break
328 | pop[i, turbine_pos] = 0
329 | pop[i, null_turbine_pos] = 1
330 |
331 | pop_NA[i, turbine_pos] = 0
332 | pop_NA[i, null_turbine_pos] = 1
333 |
334 | for j in range(N):
335 | if pop_indices[i, j] == turbine_pos:
336 | pop_indices[i, j] = null_turbine_pos
337 | break
338 | pop_indices[i, :] = np.sort(pop_indices[i, :])
339 | return
340 |
341 | def conventional_crossover(self, N, pop,pop_NA, pop_indices, pop_size, n_parents,
342 | parent_layouts,parent_layouts_NA, parent_pop_indices):
343 | n_counter = 0
344 | np.random.seed(seed=int(time.time())) # init random seed
345 | while n_counter < pop_size:
346 | male = np.random.randint(0, n_parents)
347 | female = np.random.randint(0, n_parents)
348 | if male != female:
349 | cross_point = np.random.randint(1, N)
350 | if parent_pop_indices[male, cross_point - 1] < parent_pop_indices[female, cross_point]:
351 | pop[n_counter, :] = 0
352 | pop[n_counter, :parent_pop_indices[male, cross_point - 1] + 1] = parent_layouts[male,
353 | :parent_pop_indices[
354 | male, cross_point - 1] + 1]
355 | pop[n_counter, parent_pop_indices[female, cross_point]:] = parent_layouts[female,
356 | parent_pop_indices[female, cross_point]:]
357 |
358 | pop_NA[n_counter, :] = pop[n_counter, :]
359 | for i in self.NA_loc:
360 | pop_NA[n_counter,i-1]=2
361 | pop_indices[n_counter, :cross_point] = parent_pop_indices[male, :cross_point]
362 | pop_indices[n_counter, cross_point:] = parent_pop_indices[female, cross_point:]
363 | n_counter += 1
364 | return
365 |
366 | def conventional_select(self, pop,pop_NA, pop_indices, pop_size, elite_rate, random_rate):
367 | n_elite = int(pop_size * elite_rate)
368 | parents_ind = [i for i in range(n_elite)]
369 | np.random.seed(seed=int(time.time())) # init random seed
370 | for i in range(n_elite, pop_size):
371 | if np.random.randn() < random_rate:
372 | parents_ind.append(i)
373 | parent_layouts = pop[parents_ind, :]
374 | parent_layouts_NA = pop_NA[parents_ind, :]
375 | parent_pop_indices = pop_indices[parents_ind, :]
376 | return len(parent_pop_indices), parent_layouts,parent_layouts_NA, parent_pop_indices
377 |
378 | def conventional_fitness(self, pop, rows, cols, pop_size, N, po):
379 | fitness_val = np.zeros(pop_size, dtype=np.float32)
380 | for i in range(pop_size):
381 |
382 | # layout = np.reshape(pop[i, :], newshape=(rows, cols))
383 | xy_position = np.zeros((2, N), dtype=np.float32) # x y position
384 | cr_position = np.zeros((2, N), dtype=np.int32) # column row position
385 | ind_position = np.zeros(N, dtype=np.int32)
386 | ind_pos = 0
387 | for ind in range(rows * cols):
388 | if pop[i, ind] == 1:
389 | r_i = np.floor(ind / cols)
390 | c_i = np.floor(ind - r_i * cols)
391 | cr_position[0, ind_pos] = c_i
392 | cr_position[1, ind_pos] = r_i
393 | xy_position[0, ind_pos] = c_i * self.cell_width + self.cell_width_half
394 | xy_position[1, ind_pos] = r_i * self.cell_width + self.cell_width_half
395 | ind_position[ind_pos] = ind
396 | ind_pos += 1
397 | lp_power_accum = np.zeros(N, dtype=np.float32) # a specific layout power accumulate
398 | for ind_t in range(len(self.theta)):
399 | for ind_v in range(len(self.velocity)):
400 | trans_matrix = np.array(
401 | [[np.cos(self.theta[ind_t]), -np.sin(self.theta[ind_t])],
402 | [np.sin(self.theta[ind_t]), np.cos(self.theta[ind_t])]],
403 | np.float32)
404 |
405 | trans_xy_position = np.matmul(trans_matrix, xy_position)
406 | speed_deficiency = self.wake_calculate(trans_xy_position, N)
407 |
408 | actual_velocity = (1 - speed_deficiency) * self.velocity[ind_v]
409 | lp_power = self.layout_power(actual_velocity,
410 | N) # total power of a specific layout specific wind speed specific theta
411 | lp_power = lp_power * self.f_theta_v[ind_t, ind_v]
412 | lp_power_accum += lp_power
413 |
414 | sorted_index = np.argsort(lp_power_accum) # power from least to largest
415 | po[i, :] = ind_position[sorted_index]
416 | fitness_val[i] = np.sum(lp_power_accum)
417 | return fitness_val
418 |
419 | # AGA: adaptive genetic algorithm
420 | def adaptive_genetic_alg(self, ind_time=0,result_folder=None): # adaptive genetic algorithm
421 | P_rate_total = self.cal_P_rate_total()
422 | start_time = datetime.now()
423 | print("adaptive genetic algorithm starts....")
424 | fitness_generations = np.zeros(self.iteration, dtype=np.float32) # best fitness value in each generation
425 | best_layout_generations = np.zeros((self.iteration, self.rows * self.cols),
426 | dtype=np.int32) # best layout in each generation
427 | best_layout_NA_generations = np.zeros((self.iteration, self.rows * self.cols),
428 | dtype=np.int32) # best layout in each generation
429 |
430 | power_order = np.zeros((self.pop_size, self.N),
431 | dtype=np.int32) # each row is a layout cell indices. in each layout, order turbine power from least to largest
432 | pop = np.copy(self.init_pop)
433 | pop_NA = np.copy(self.init_pop_NA)
434 | pop_indices = np.copy(self.init_pop_nonezero_indices) # each row is a layout cell indices.
435 |
436 | eN = int(np.floor(self.pop_size * self.elite_rate)) # elite number
437 | rN = int(int(np.floor(self.pop_size * self.mutate_rate)) / eN) * eN # reproduce number
438 | mN = rN # mutation number
439 | cN = self.pop_size - eN - mN # crossover number
440 |
441 | for gen in range(self.iteration):
442 | print("generation {}...".format(gen))
443 | fitness_value = self.adaptive_fitness(pop=pop, rows=self.rows, cols=self.cols, pop_size=self.pop_size,
444 | N=self.N,
445 | po=power_order)
446 | sorted_index = np.argsort(-fitness_value) # fitness value descending from largest to least
447 |
448 | pop = pop[sorted_index, :]
449 | pop_NA = pop_NA[sorted_index, :]
450 | power_order = power_order[sorted_index, :]
451 | pop_indices = pop_indices[sorted_index, :]
452 | if gen == 0:
453 | fitness_generations[gen] = fitness_value[sorted_index[0]]
454 | best_layout_generations[gen, :] = pop[0, :]
455 | best_layout_NA_generations[gen, :] = pop_NA[0, :]
456 | else:
457 | if fitness_value[sorted_index[0]] > fitness_generations[gen - 1]:
458 | fitness_generations[gen] = fitness_value[sorted_index[0]]
459 | best_layout_generations[gen, :] = pop[0, :]
460 | best_layout_NA_generations[gen, :] = pop_NA[0, :]
461 | else:
462 | fitness_generations[gen] = fitness_generations[gen - 1]
463 | best_layout_generations[gen, :] = best_layout_generations[gen - 1, :]
464 | best_layout_NA_generations[gen, :] = best_layout_NA_generations[gen - 1, :]
465 | self.adaptive_move_worst(rows=self.rows, cols=self.cols, pop=pop,pop_NA=pop_NA, pop_indices=pop_indices,
466 | pop_size=self.pop_size, power_order=power_order)
467 |
468 |
469 |
470 | n_parents, parent_layouts,parent_layouts_NA, parent_pop_indices = self.adaptive_select(pop=pop,pop_NA=pop_NA, pop_indices=pop_indices,
471 | pop_size=self.pop_size,
472 | elite_rate=self.elite_rate,
473 | random_rate=self.random_rate)
474 |
475 |
476 | self.adaptive_crossover(N=self.N, pop=pop,pop_NA=pop_NA, pop_indices=pop_indices, pop_size=self.pop_size,
477 | n_parents=n_parents,
478 | parent_layouts=parent_layouts,parent_layouts_NA=parent_layouts_NA, parent_pop_indices=parent_pop_indices)
479 |
480 |
481 | self.adaptive_mutation(rows=self.rows, cols=self.cols, N=self.N, pop=pop,pop_NA=pop_NA, pop_indices=pop_indices,
482 | pop_size=self.pop_size,
483 | mutation_rate=self.mutate_rate)
484 |
485 |
486 | end_time = datetime.now()
487 | run_time = (end_time - start_time).total_seconds()
488 | eta_generations = np.copy(fitness_generations)
489 | eta_generations = eta_generations * (1.0 / P_rate_total)
490 | time_stamp = datetime.now().strftime("%Y%m%d%H%M%S")
491 |
492 | filename = "{}/adaptive_eta_N{}_{}_{}.dat".format(result_folder,self.N, ind_time, time_stamp)
493 | np.savetxt(filename, eta_generations, fmt='%f', delimiter=" ")
494 | filename = "{}/adaptive_best_layouts_N{}_{}_{}.dat".format(result_folder,self.N, ind_time, time_stamp)
495 | np.savetxt(filename, best_layout_generations, fmt='%d', delimiter=" ")
496 | filename = "{}/adaptive_best_layouts_NA_N{}_{}_{}.dat".format(result_folder,self.N, ind_time, time_stamp)
497 | np.savetxt(filename, best_layout_NA_generations, fmt='%d', delimiter=" ")
498 | print("adaptive genetic algorithm ends.")
499 | filename = "{}/adaptive_runtime.txt".format(result_folder)
500 | f = open(filename, "a+")
501 | f.write("{}\n".format(run_time))
502 | f.close()
503 |
504 | filename = "{}/adaptive_eta.txt".format(result_folder)
505 | f = open(filename, "a+")
506 | f.write("{}\n".format(eta_generations[self.iteration - 1]))
507 | f.close()
508 |
509 | return run_time, eta_generations[self.iteration - 1]
510 |
511 | def adaptive_move_worst(self, rows, cols, pop,pop_NA, pop_indices, pop_size, power_order):
512 | np.random.seed(seed=int(time.time()))
513 | for i in range(pop_size):
514 | turbine_pos = power_order[i, 0]
515 | while True:
516 | null_turbine_pos = np.random.randint(0, cols * rows)
517 | if pop_NA[i, null_turbine_pos] == 0:
518 | break
519 | pop[i, turbine_pos] = 0
520 | pop[i, null_turbine_pos] = 1
521 | pop_NA[i, turbine_pos] = 0
522 | pop_NA[i, null_turbine_pos] = 1
523 | power_order[i, 0] = null_turbine_pos
524 | pop_indices[i, :] = np.sort(power_order[i, :])
525 | return
526 |
527 | def adaptive_mutation(self, rows, cols, N, pop,pop_NA, pop_indices, pop_size, mutation_rate):
528 | np.random.seed(seed=int(time.time()))
529 | for i in range(pop_size):
530 | if np.random.randn() > mutation_rate:
531 | continue
532 | while True:
533 | turbine_pos = np.random.randint(0, cols * rows)
534 | if pop_NA[i, turbine_pos] == 1:
535 | break
536 | while True:
537 | null_turbine_pos = np.random.randint(0, cols * rows)
538 | if pop_NA[i, null_turbine_pos] == 0:
539 | break
540 | pop[i, turbine_pos] = 0
541 | pop[i, null_turbine_pos] = 1
542 |
543 | pop_NA[i, turbine_pos] = 0
544 | pop_NA[i, null_turbine_pos] = 1
545 |
546 | for j in range(N):
547 | if pop_indices[i, j] == turbine_pos:
548 | pop_indices[i, j] = null_turbine_pos
549 | break
550 | pop_indices[i, :] = np.sort(pop_indices[i, :])
551 | return
552 |
553 | def adaptive_crossover(self, N, pop,pop_NA, pop_indices, pop_size, n_parents,
554 | parent_layouts,parent_layouts_NA, parent_pop_indices):
555 | n_counter = 0
556 | np.random.seed(seed=int(time.time())) # init random seed
557 | while n_counter < pop_size:
558 | male = np.random.randint(0, n_parents)
559 | female = np.random.randint(0, n_parents)
560 | if male != female:
561 | cross_point = np.random.randint(1, N)
562 | if parent_pop_indices[male, cross_point - 1] < parent_pop_indices[female, cross_point]:
563 | pop[n_counter, :] = 0
564 | pop[n_counter, :parent_pop_indices[male, cross_point - 1] + 1] = parent_layouts[male,
565 | :parent_pop_indices[
566 | male, cross_point - 1] + 1]
567 | pop[n_counter, parent_pop_indices[female, cross_point]:] = parent_layouts[female,
568 | parent_pop_indices[female, cross_point]:]
569 |
570 | pop_NA[n_counter, :] = pop[n_counter, :]
571 | for i in self.NA_loc:
572 | pop_NA[n_counter, i - 1] = 2
573 |
574 | pop_indices[n_counter, :cross_point] = parent_pop_indices[male, :cross_point]
575 | pop_indices[n_counter, cross_point:] = parent_pop_indices[female, cross_point:]
576 | n_counter += 1
577 | return
578 |
579 | def adaptive_select(self, pop,pop_NA, pop_indices, pop_size, elite_rate, random_rate):
580 | n_elite = int(pop_size * elite_rate)
581 | parents_ind = [i for i in range(n_elite)]
582 | np.random.seed(seed=int(time.time())) # init random seed
583 | for i in range(n_elite, pop_size):
584 | if np.random.randn() < random_rate:
585 | parents_ind.append(i)
586 | parent_layouts = pop[parents_ind, :]
587 | parent_layouts_NA = pop_NA[parents_ind, :]
588 | parent_pop_indices = pop_indices[parents_ind, :]
589 | return len(parent_pop_indices), parent_layouts, parent_layouts_NA, parent_pop_indices
590 |
591 | def adaptive_fitness(self, pop, rows, cols, pop_size, N, po):
592 | fitness_val = np.zeros(pop_size, dtype=np.float32)
593 | for i in range(pop_size):
594 |
595 | # layout = np.reshape(pop[i, :], newshape=(rows, cols))
596 | xy_position = np.zeros((2, N), dtype=np.float32) # x y position
597 | cr_position = np.zeros((2, N), dtype=np.int32) # column row position
598 | ind_position = np.zeros(N, dtype=np.int32)
599 | ind_pos = 0
600 | for ind in range(rows * cols):
601 | if pop[i, ind] == 1:
602 | r_i = np.floor(ind / cols)
603 | c_i = np.floor(ind - r_i * cols)
604 | cr_position[0, ind_pos] = c_i
605 | cr_position[1, ind_pos] = r_i
606 | xy_position[0, ind_pos] = c_i * self.cell_width + self.cell_width_half
607 | xy_position[1, ind_pos] = r_i * self.cell_width + self.cell_width_half
608 | ind_position[ind_pos] = ind
609 | ind_pos += 1
610 | lp_power_accum = np.zeros(N, dtype=np.float32) # a specific layout power accumulate
611 | for ind_t in range(len(self.theta)):
612 | for ind_v in range(len(self.velocity)):
613 | trans_matrix = np.array(
614 | [[np.cos(self.theta[ind_t]), -np.sin(self.theta[ind_t])],
615 | [np.sin(self.theta[ind_t]), np.cos(self.theta[ind_t])]],
616 | np.float32)
617 |
618 | trans_xy_position = np.matmul(trans_matrix, xy_position)
619 | speed_deficiency = self.wake_calculate(trans_xy_position, N)
620 |
621 | actual_velocity = (1 - speed_deficiency) * self.velocity[ind_v]
622 | lp_power = self.layout_power(actual_velocity,
623 | N) # total power of a specific layout specific wind speed specific theta
624 | lp_power = lp_power * self.f_theta_v[ind_t, ind_v]
625 | lp_power_accum += lp_power
626 |
627 | sorted_index = np.argsort(lp_power_accum) # power from least to largest
628 | po[i, :] = ind_position[sorted_index]
629 |
630 | fitness_val[i] = np.sum(lp_power_accum)
631 |
632 | return fitness_val
633 |
634 | # SUGGA: support vector regression guided genetic algorithm
635 | def sugga_genetic_alg(self, ind_time=0,svr_model=None,result_folder=None):
636 |
637 | P_rate_total = self.cal_P_rate_total()
638 | start_time = datetime.now()
639 | print("Support vector regression guided genetic algorithm starts....")
640 | fitness_generations = np.zeros(self.iteration, dtype=np.float32) # best fitness value in each generation
641 | best_layout_generations = np.zeros((self.iteration, self.rows * self.cols),
642 | dtype=np.int32) # best layout in each generation
643 | best_layout_NA_generations = np.zeros((self.iteration, self.rows * self.cols),
644 | dtype=np.int32) # best layout in each generation
645 |
646 | power_order = np.zeros((self.pop_size, self.N),
647 | dtype=np.int32) # each row is a layout cell indices. in each layout, order turbine power from least to largest
648 | pop = np.copy(self.init_pop)
649 | pop_NA = np.copy(self.init_pop_NA)
650 | pop_indices = np.copy(self.init_pop_nonezero_indices) # each row is a layout cell indices.
651 |
652 | eN = int(np.floor(self.pop_size * self.elite_rate)) # elite number
653 | rN = int(int(np.floor(self.pop_size * self.mutate_rate)) / eN) * eN # reproduce number
654 | mN = rN # mutation number
655 | cN = self.pop_size - eN - mN # crossover number
656 |
657 | for gen in range(self.iteration):
658 | print("generation {}...".format(gen))
659 | fitness_value = self.sugga_fitness(pop=pop, rows=self.rows, cols=self.cols, pop_size=self.pop_size,
660 | N=self.N,
661 | po=power_order)
662 | sorted_index = np.argsort(-fitness_value) # fitness value descending from largest to least
663 |
664 | pop = pop[sorted_index, :]
665 | pop_NA = pop_NA[sorted_index, :]
666 | power_order = power_order[sorted_index, :]
667 | pop_indices = pop_indices[sorted_index, :]
668 | if gen == 0:
669 | fitness_generations[gen] = fitness_value[sorted_index[0]]
670 | best_layout_generations[gen, :] = pop[0, :]
671 | best_layout_NA_generations[gen, :] = pop_NA[0, :]
672 | else:
673 | if fitness_value[sorted_index[0]] > fitness_generations[gen - 1]:
674 | fitness_generations[gen] = fitness_value[sorted_index[0]]
675 | best_layout_generations[gen, :] = pop[0, :]
676 | best_layout_NA_generations[gen, :] = pop_NA[0, :]
677 | else:
678 | fitness_generations[gen] = fitness_generations[gen - 1]
679 | best_layout_generations[gen, :] = best_layout_generations[gen - 1, :]
680 | best_layout_NA_generations[gen, :] = best_layout_NA_generations[gen - 1, :]
681 | self.sugga_move_worst(rows=self.rows, cols=self.cols, pop=pop,pop_NA=pop_NA, pop_indices=pop_indices,
682 | pop_size=self.pop_size, power_order=power_order, svr_model=svr_model)
683 |
684 |
685 |
686 | n_parents, parent_layouts,parent_layouts_NA, parent_pop_indices = self.sugga_select(pop=pop,pop_NA=pop_NA, pop_indices=pop_indices,
687 | pop_size=self.pop_size,
688 | elite_rate=self.elite_rate,
689 | random_rate=self.random_rate)
690 |
691 |
692 | self.sugga_crossover(N=self.N, pop=pop,pop_NA=pop_NA, pop_indices=pop_indices, pop_size=self.pop_size,
693 | n_parents=n_parents,
694 | parent_layouts=parent_layouts,parent_layouts_NA=parent_layouts_NA, parent_pop_indices=parent_pop_indices)
695 |
696 |
697 |
698 | self.sugga_mutation(rows=self.rows, cols=self.cols, N=self.N, pop=pop,pop_NA=pop_NA, pop_indices=pop_indices,
699 | pop_size=self.pop_size,
700 | mutation_rate=self.mutate_rate)
701 |
702 | end_time = datetime.now()
703 | run_time = (end_time - start_time).total_seconds()
704 | eta_generations = np.copy(fitness_generations)
705 | eta_generations = eta_generations * (1.0 / P_rate_total)
706 | time_stamp = datetime.now().strftime("%Y%m%d%H%M%S")
707 |
708 | filename = "{}/sugga_eta_N{}_{}_{}.dat".format(result_folder,self.N, ind_time, time_stamp)
709 | np.savetxt(filename, eta_generations, fmt='%f', delimiter=" ")
710 | filename = "{}/sugga_best_layouts_N{}_{}_{}.dat".format(result_folder,self.N, ind_time, time_stamp)
711 | np.savetxt(filename, best_layout_generations, fmt='%d', delimiter=" ")
712 | filename = "{}/sugga_best_layouts_NA_N{}_{}_{}.dat".format(result_folder,self.N, ind_time, time_stamp)
713 | np.savetxt(filename, best_layout_NA_generations, fmt='%d', delimiter=" ")
714 | print("Support vector regression guided genetic algorithm ends.")
715 | filename = "{}/sugga_runtime.txt".format(result_folder)
716 | f = open(filename, "a+")
717 | f.write("{}\n".format(run_time))
718 | f.close()
719 | filename = "{}/sugga_eta.txt".format(result_folder)
720 | f = open(filename, "a+")
721 | f.write("{}\n".format(eta_generations[self.iteration - 1]))
722 | f.close()
723 | return run_time, eta_generations[self.iteration - 1]
724 |
725 | def sugga_move_worst(self, rows, cols, pop,pop_NA, pop_indices, pop_size, power_order, mars=None,svr_model=None):
726 | np.random.seed(seed=int(time.time()))
727 | for i in range(pop_size):
728 | r = np.random.randn()
729 | if r < 0.5:
730 | self.sugga_move_worst_case_random(i=i, rows=rows, cols=cols, pop=pop,pop_NA=pop_NA, pop_indices=pop_indices,
731 | pop_size=pop_size, power_order=power_order)
732 | else:
733 | self.sugga_move_worst_case_best(i=i, rows=rows, cols=cols, pop=pop,pop_NA=pop_NA, pop_indices=pop_indices,
734 | pop_size=pop_size, power_order=power_order, mars=mars,svr_model=svr_model)
735 |
736 | return
737 |
738 | def sugga_move_worst_case_random(self, i, rows, cols, pop,pop_NA, pop_indices, pop_size, power_order):
739 | np.random.seed(seed=int(time.time()))
740 | turbine_pos = power_order[i, 0]
741 | while True:
742 | null_turbine_pos = np.random.randint(0, cols * rows)
743 | if pop_NA[i, null_turbine_pos] == 0:
744 | break
745 | pop[i, turbine_pos] = 0
746 | pop[i, null_turbine_pos] = 1
747 | pop_NA[i, turbine_pos] = 0
748 | pop_NA[i, null_turbine_pos] = 1
749 |
750 | power_order[i, 0] = null_turbine_pos
751 | pop_indices[i, :] = np.sort(power_order[i, :])
752 | return
753 |
754 | def sugga_move_worst_case_best(self, i, rows, cols, pop,pop_NA, pop_indices, pop_size, power_order, mars,svr_model):
755 | np.random.seed(seed=int(time.time()))
756 | n_candiate = 5
757 | pos_candidate = np.zeros((n_candiate, 2), dtype=np.int32)
758 | ind_pos_candidate = np.zeros(n_candiate, dtype=np.int32)
759 | turbine_pos = power_order[i, 0]
760 | ind_can = 0
761 | while True:
762 | null_turbine_pos = np.random.randint(0, cols * rows)
763 | if pop_NA[i, null_turbine_pos] == 0:
764 | pos_candidate[ind_can, 1] = int(np.floor(null_turbine_pos / cols))
765 | pos_candidate[ind_can, 0] = int(np.floor(null_turbine_pos - pos_candidate[ind_can, 1] * cols))
766 | ind_pos_candidate[ind_can] = null_turbine_pos
767 | ind_can += 1
768 | if ind_can == n_candiate:
769 | break
770 | svr_val = svr_model.predict(pos_candidate)
771 | sorted_index = np.argsort(-svr_val) # fitness value descending from largest to least
772 | null_turbine_pos = ind_pos_candidate[sorted_index[0]]
773 |
774 | pop[i, turbine_pos] = 0
775 | pop[i, null_turbine_pos] = 1
776 |
777 | pop_NA[i, turbine_pos] = 0
778 | pop_NA[i, null_turbine_pos] = 1
779 |
780 | power_order[i, 0] = null_turbine_pos
781 | pop_indices[i, :] = np.sort(power_order[i, :])
782 | return
783 |
784 | def sugga_move_worst_case_worst(self, i, rows, cols, pop, pop_indices, pop_size, power_order, mars):
785 | np.random.seed(seed=int(time.time()))
786 | n_candiate = 11
787 | pos_candidate = np.zeros((n_candiate, 2), dtype=np.int32)
788 | ind_pos_candidate = np.zeros(n_candiate, dtype=np.int32)
789 | turbine_pos = power_order[i, 0]
790 | ind_can = 0
791 | while True:
792 | null_turbine_pos = np.random.randint(0, cols * rows)
793 | if pop[i, null_turbine_pos] == 0:
794 | pos_candidate[ind_can, 1] = int(np.floor(null_turbine_pos / cols))
795 | pos_candidate[ind_can, 0] = int(np.floor(null_turbine_pos - pos_candidate[ind_can, 1] * cols))
796 | ind_pos_candidate[ind_can] = null_turbine_pos
797 | ind_can += 1
798 | if ind_can == n_candiate:
799 | break
800 | mars_val = mars.predict(pos_candidate)
801 | mars_val = mars_val[:, 0]
802 | sorted_index = np.argsort(mars_val) # fitness value descending from least to largest
803 | null_turbine_pos = ind_pos_candidate[sorted_index[0]]
804 | pop[i, turbine_pos] = 0
805 | pop[i, null_turbine_pos] = 1
806 | power_order[i, 0] = null_turbine_pos
807 | pop_indices[i, :] = np.sort(power_order[i, :])
808 | return
809 | # SUGGA move worst
810 | def sugga_move_worst_case_middle(self, i, rows, cols, pop, pop_indices, pop_size, power_order, mars):
811 | np.random.seed(seed=int(time.time()))
812 | n_candiate = 11
813 | pos_candidate = np.zeros((n_candiate, 2), dtype=np.int32)
814 | ind_pos_candidate = np.zeros(n_candiate, dtype=np.int32)
815 | turbine_pos = power_order[i, 0]
816 | ind_can = 0
817 | while True:
818 | null_turbine_pos = np.random.randint(0, cols * rows)
819 | if pop[i, null_turbine_pos] == 0:
820 | pos_candidate[ind_can, 1] = int(np.floor(null_turbine_pos / cols))
821 | pos_candidate[ind_can, 0] = int(np.floor(null_turbine_pos - pos_candidate[ind_can, 1] * cols))
822 | ind_pos_candidate[ind_can] = null_turbine_pos
823 | ind_can += 1
824 | if ind_can == n_candiate:
825 | break
826 | mars_val = mars.predict(pos_candidate)
827 | mars_val = mars_val[:, 0]
828 | sorted_index = np.argsort(-mars_val) # fitness value descending from largest to least
829 | null_turbine_pos = ind_pos_candidate[sorted_index[5]]
830 | pop[i, turbine_pos] = 0
831 | pop[i, null_turbine_pos] = 1
832 | power_order[i, 0] = null_turbine_pos
833 | pop_indices[i, :] = np.sort(power_order[i, :])
834 | return
835 | # SUGGA mutation
836 | def sugga_mutation(self, rows, cols, N, pop,pop_NA, pop_indices, pop_size, mutation_rate):
837 | np.random.seed(seed=int(time.time()))
838 | for i in range(pop_size):
839 | if np.random.randn() > mutation_rate:
840 | continue
841 | while True:
842 | turbine_pos = np.random.randint(0, cols * rows)
843 | if pop_NA[i, turbine_pos] == 1:
844 | break
845 | while True:
846 | null_turbine_pos = np.random.randint(0, cols * rows)
847 | if pop_NA[i, null_turbine_pos] == 0:
848 | break
849 | pop[i, turbine_pos] = 0
850 | pop[i, null_turbine_pos] = 1
851 |
852 | pop_NA[i, turbine_pos] = 0
853 | pop_NA[i, null_turbine_pos] = 1
854 |
855 | for j in range(N):
856 | if pop_indices[i, j] == turbine_pos:
857 | pop_indices[i, j] = null_turbine_pos
858 | break
859 | pop_indices[i, :] = np.sort(pop_indices[i, :])
860 | return
861 |
862 | # SUGGA crossover
863 | def sugga_crossover(self, N, pop,pop_NA, pop_indices, pop_size, n_parents,
864 | parent_layouts,parent_layouts_NA, parent_pop_indices):
865 | n_counter = 0
866 | np.random.seed(seed=int(time.time())) # init random seed
867 | while n_counter < pop_size:
868 | male = np.random.randint(0, n_parents)
869 | female = np.random.randint(0, n_parents)
870 | if male != female:
871 | cross_point = np.random.randint(1, N)
872 | if parent_pop_indices[male, cross_point - 1] < parent_pop_indices[female, cross_point]:
873 | pop[n_counter, :] = 0
874 | pop[n_counter, :parent_pop_indices[male, cross_point - 1] + 1] = parent_layouts[male,
875 | :parent_pop_indices[
876 | male, cross_point - 1] + 1]
877 | pop[n_counter, parent_pop_indices[female, cross_point]:] = parent_layouts[female,
878 | parent_pop_indices[female, cross_point]:]
879 |
880 | pop_NA[n_counter, :] = pop[n_counter, :]
881 | for i in self.NA_loc:
882 | pop_NA[n_counter, i - 1] = 2
883 |
884 | pop_indices[n_counter, :cross_point] = parent_pop_indices[male, :cross_point]
885 | pop_indices[n_counter, cross_point:] = parent_pop_indices[female, cross_point:]
886 | n_counter += 1
887 | return
888 | # SUGGA select
889 | def sugga_select(self, pop,pop_NA, pop_indices, pop_size, elite_rate, random_rate):
890 | n_elite = int(pop_size * elite_rate)
891 | parents_ind = [i for i in range(n_elite)]
892 | np.random.seed(seed=int(time.time())) # init random seed
893 | for i in range(n_elite, pop_size):
894 | if np.random.randn() < random_rate:
895 | parents_ind.append(i)
896 | parent_layouts = pop[parents_ind, :]
897 | parent_layouts_NA = pop_NA[parents_ind, :]
898 | parent_pop_indices = pop_indices[parents_ind, :]
899 | return len(parent_pop_indices), parent_layouts, parent_layouts_NA, parent_pop_indices
900 | #calculate fitness value
901 | def sugga_fitness(self, pop, rows, cols, pop_size, N, po):
902 | fitness_val = np.zeros(pop_size, dtype=np.float32)
903 | for i in range(pop_size):
904 |
905 | # layout = np.reshape(pop[i, :], newshape=(rows, cols))
906 | xy_position = np.zeros((2, N), dtype=np.float32) # x y position
907 | cr_position = np.zeros((2, N), dtype=np.int32) # column row position
908 | ind_position = np.zeros(N, dtype=np.int32)
909 | ind_pos = 0
910 | for ind in range(rows * cols):
911 | if pop[i, ind] == 1:
912 | r_i = np.floor(ind / cols)
913 | c_i = np.floor(ind - r_i * cols)
914 | cr_position[0, ind_pos] = c_i
915 | cr_position[1, ind_pos] = r_i
916 | xy_position[0, ind_pos] = c_i * self.cell_width + self.cell_width_half
917 | xy_position[1, ind_pos] = r_i * self.cell_width + self.cell_width_half
918 | ind_position[ind_pos] = ind
919 | ind_pos += 1
920 | lp_power_accum = np.zeros(N, dtype=np.float32) # a specific layout power accumulate
921 | for ind_t in range(len(self.theta)):
922 | for ind_v in range(len(self.velocity)):
923 | # print(theta[ind_t])
924 | # print(np.cos(theta[ind_t]))
925 | trans_matrix = np.array(
926 | [[np.cos(self.theta[ind_t]), -np.sin(self.theta[ind_t])],
927 | [np.sin(self.theta[ind_t]), np.cos(self.theta[ind_t])]],
928 | np.float32)
929 |
930 | trans_xy_position = np.matmul(trans_matrix, xy_position)
931 | speed_deficiency = self.wake_calculate(trans_xy_position, N)
932 |
933 | actual_velocity = (1 - speed_deficiency) * self.velocity[ind_v]
934 | lp_power = self.layout_power(actual_velocity,
935 | N) # total power of a specific layout specific wind speed specific theta
936 | lp_power = lp_power * self.f_theta_v[ind_t, ind_v]
937 | lp_power_accum += lp_power
938 |
939 | sorted_index = np.argsort(lp_power_accum) # power from least to largest
940 | po[i, :] = ind_position[sorted_index]
941 |
942 | fitness_val[i] = np.sum(lp_power_accum)
943 | #
944 | return fitness_val
945 |
946 |
947 |
948 |
949 | class GE_1_5_sleTurbine:
950 | hub_height = 80.0 # unit (m)
951 | rator_diameter = 77.0 # unit m
952 | surface_roughness = 0.25 * 0.001 # unit mm surface roughness
953 | # surface_roughness = 0.25 # unit mm surface roughness
954 | rator_radius = 0
955 |
956 | entrainment_const = 0
957 |
958 | def __init__(self):
959 | self.rator_radius = self.rator_diameter / 2
960 | self.entrainment_const = 0.5 / np.log(self.hub_height / self.surface_roughness)
961 | return
962 |
963 | # power curve
964 | def P_i_X(self, v):
965 | if v < 2.0:
966 | return 0
967 | elif v < 12.8:
968 | return 0.3 * v ** 3
969 | elif v < 18:
970 | return 629.1
971 | else:
972 | return 0
973 |
974 |
975 |
976 |
977 | class LayoutGridMCGenerator:
978 | def __init__(self):
979 | return
980 |
981 | # rows : number of rows in wind farm
982 | # cols : number of columns in wind farm
983 | # n : number of layouts
984 | # N : number of turbines
985 | def gen_mc_grid(rows, cols, n, N, lofname): # , xfname): generate monte carlo wind farm layout grids
986 | np.random.seed(seed=int(time.time())) # init random seed
987 | layouts = np.zeros((n, rows * cols), dtype=np.int32) # one row is a layout
988 | # layouts_cr = np.zeros((n*, 2), dtype=np.float32) # layouts column row index
989 | positionX = np.random.randint(0, cols, size=(N * n * 2))
990 | positionY = np.random.randint(0, rows, size=(N * n * 2))
991 | ind_rows = 0 # index of layouts from 0 to n-1
992 | ind_pos = 0 # index of positionX, positionY from 0 to N*n*2-1
993 | # ind_crs = 0
994 | while ind_rows < n:
995 | layouts[ind_rows, positionX[ind_pos] + positionY[ind_pos] * cols] = 1
996 | if np.sum(layouts[ind_rows, :]) == N:
997 | # for ind in range(rows * cols):
998 | # if layouts[ind_rows, ind] == 1:
999 | # r_i = np.floor(ind / cols)
1000 | # c_i = np.floor(ind - r_i * cols)
1001 | # layouts_cr[ind_crs, 0] = c_i
1002 | # layouts_cr[ind_crs, 1] = r_i
1003 | # ind_crs += 1
1004 | ind_rows += 1
1005 | ind_pos += 1
1006 | if ind_pos >= N * n * 2:
1007 | print("Not enough positions")
1008 | break
1009 | # filename = "positions{}by{}by{}N{}.dat".format(rows, cols, n, N)
1010 | np.savetxt(lofname, layouts, fmt='%d', delimiter=" ")
1011 | # np.savetxt(xfname, layouts_cr, fmt='%d', delimiter=" ")
1012 | return layouts
1013 |
1014 | # rows : number of rows in wind farm
1015 | # cols : number of columns in wind farm
1016 | # n : number of layouts
1017 | # N : number of turbines
1018 | # NA_loc : not usable locations
1019 | # generate layouts with not usable locations
1020 | def gen_mc_grid_with_NA_loc(rows, cols, n, N,NA_loc, lofname,loNAfname): # , xfname): generate monte carlo wind farm layout grids
1021 | np.random.seed(seed=int(time.time())) # init random seed
1022 | layouts = np.zeros((n, rows * cols), dtype=np.int32) # one row is a layout, NA loc is 0
1023 |
1024 | layouts_NA= np.zeros((n, rows * cols), dtype=np.int32) # one row is a layout, NA loc is 2
1025 | for i in NA_loc:
1026 | layouts_NA[:,i-1]=2
1027 |
1028 | # layouts_cr = np.zeros((n*, 2), dtype=np.float32) # layouts column row index
1029 | positionX = np.random.randint(0, cols, size=(N * n * 2))
1030 | positionY = np.random.randint(0, rows, size=(N * n * 2))
1031 | ind_rows = 0 # index of layouts from 0 to n-1
1032 | ind_pos = 0 # index of positionX, positionY from 0 to N*n*2-1
1033 | # ind_crs = 0
1034 | N_count=0
1035 | while ind_rows < n:
1036 | cur_state=layouts_NA[ind_rows, positionX[ind_pos] + positionY[ind_pos] * cols]
1037 | if cur_state!=1 and cur_state!=2:
1038 | layouts[ind_rows, positionX[ind_pos] + positionY[ind_pos] * cols]=1
1039 | layouts_NA[ind_rows, positionX[ind_pos] + positionY[ind_pos] * cols] = 1
1040 | N_count+=1
1041 | if np.sum(layouts[ind_rows, :]) == N:
1042 | ind_rows += 1
1043 | N_count=0
1044 | ind_pos += 1
1045 | if ind_pos >= N * n * 2:
1046 | print("Not enough positions")
1047 | break
1048 | # filename = "positions{}by{}by{}N{}.dat".format(rows, cols, n, N)
1049 | np.savetxt(lofname, layouts, fmt='%d', delimiter=" ")
1050 | np.savetxt(loNAfname, layouts_NA, fmt='%d', delimiter=" ")
1051 | # np.savetxt(xfname, layouts_cr, fmt='%d', delimiter=" ")
1052 | return layouts,layouts_NA
1053 |
1054 | # generate population
1055 | def gen_pop(rows, cols, n,
1056 | N): # generate population very similar to gen_mc_grid, just without saving layouts to a file
1057 | np.random.seed(seed=int(time.time()))
1058 | layouts = np.zeros((n, rows * cols), dtype=np.int32)
1059 | positionX = np.random.randint(0, cols, size=(N * n * 2))
1060 | positionY = np.random.randint(0, rows, size=(N * n * 2))
1061 | ind_rows = 0
1062 | ind_pos = 0
1063 |
1064 | while ind_rows < n:
1065 | layouts[ind_rows, positionX[ind_pos] + positionY[ind_pos] * cols] = 1
1066 | if np.sum(layouts[ind_rows, :]) == N:
1067 | ind_rows += 1
1068 | ind_pos += 1
1069 | if ind_pos >= N * n * 2:
1070 | print("Not enough positions")
1071 | break
1072 | return layouts
1073 |
1074 | # rows : number of rows in wind farm
1075 | # cols : number of columns in wind farm
1076 | # n : number of layouts
1077 | # N : number of turbines
1078 | # NA_loc : not usable locations
1079 | # generate layouts with not usable locations
1080 | def gen_pop_with_NA_loc(rows, cols, n, N, NA_loc):
1081 | np.random.seed(seed=int(time.time())) # init random seed
1082 | layouts = np.zeros((n, rows * cols), dtype=np.int32) # one row is a layout, NA loc is 0
1083 |
1084 | layouts_NA = np.zeros((n, rows * cols), dtype=np.int32) # one row is a layout, NA loc is 2
1085 | for i in NA_loc:
1086 | layouts_NA[:, i - 1] = 2
1087 |
1088 | # layouts_cr = np.zeros((n*, 2), dtype=np.float32) # layouts column row index
1089 | positionX = np.random.randint(0, cols, size=(N * n * 2))
1090 | positionY = np.random.randint(0, rows, size=(N * n * 2))
1091 | ind_rows = 0 # index of layouts from 0 to n-1
1092 | ind_pos = 0 # index of positionX, positionY from 0 to N*n*2-1
1093 | # ind_crs = 0
1094 | N_count = 0
1095 | while ind_rows < n:
1096 | cur_state = layouts_NA[ind_rows, positionX[ind_pos] + positionY[ind_pos] * cols]
1097 | if cur_state != 1 and cur_state != 2:
1098 | layouts[ind_rows, positionX[ind_pos] + positionY[ind_pos] * cols] = 1
1099 | layouts_NA[ind_rows, positionX[ind_pos] + positionY[ind_pos] * cols] = 1
1100 | N_count += 1
1101 | if np.sum(layouts[ind_rows, :]) == N:
1102 | ind_rows += 1
1103 | N_count = 0
1104 | ind_pos += 1
1105 | if ind_pos >= N * n * 2:
1106 | print("Not enough positions")
1107 | break
1108 | return layouts, layouts_NA
1109 |
1110 |
1111 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pandas as pd
3 | import WindFarmGenetic # wind farm layout optimization using genetic algorithms classes
4 | from datetime import datetime
5 | import os
6 | from sklearn.svm import SVR
7 | import pickle
8 |
9 | # Wind farm settings and algorithm settings
10 | # parameters for the genetic algorithm
11 | elite_rate = 0.2
12 | cross_rate = 0.6
13 | random_rate = 0.5
14 | mutate_rate = 0.1
15 |
16 | wt_N = 25 # number of wind turbines 15, 20, or 25
17 | # NA_loc_array : not available location array, the index starting from 1
18 |
19 | # L1 : wind farm, cells 121(inclusive) to 144(inclusive)
20 | NA_loc_array = np.arange(121, 145, 1)
21 | #
22 | # # L2
23 | # NA_loc_array = np.arange(61, 85, 1)
24 | #
25 | # # L3
26 | # NA_loc_array = np.concatenate((np.arange(11, 144, 12), np.arange(12, 145, 12)))
27 | #
28 | # # L4
29 | # NA_loc_array = np.concatenate((np.arange(6, 144, 12), np.arange(7, 145, 12)))
30 | #
31 | # # L5
32 | # NA_loc_array = np.concatenate((np.arange(41, 105, 12), np.arange(42, 105, 12),
33 | # np.arange(43, 105, 12),
34 | # np.arange(44, 105, 12)))
35 | #
36 | # # L6
37 | # NA_loc_array = np.concatenate((np.arange(1, 28, 12), np.arange(2, 28, 12),
38 | # np.arange(12, 37, 12),
39 | # np.arange(11, 37, 12),
40 | # np.arange(109, 145, 12), np.arange(119, 145, 12),
41 | # np.arange(110, 145, 12),
42 | # np.arange(120, 145, 12),
43 | # ))
44 | #
45 | # # L7
46 | # NA_loc_array = np.arange(133, 145, 1)
47 | #
48 | # # L8
49 | # NA_loc_array = np.arange(61, 73, 1)
50 | #
51 | # # L9
52 | # NA_loc_array = np.arange(12, 145, 12)
53 | #
54 | # # L10
55 | # NA_loc_array = np.arange(6, 145, 12)
56 | #
57 | # # L11
58 | # NA_loc_array = np.concatenate((np.arange(42, 105, 12),
59 | # np.arange(43, 105, 12)))
60 | #
61 | # # L12
62 | # NA_loc_array = np.array((1, 2, 11, 12, 13, 24, 121, 132, 133, 134, 143, 144))
63 |
64 | # convert numpy array to list, datatype convert
65 | NA_loc = NA_loc_array.tolist()
66 |
67 | # L0
68 | # NA_loc = []
69 |
70 |
71 | population_size = 120 # how many layouts in a population
72 | iteration_times = 200 # how many iterations in a genetic algorithm run
73 |
74 | n_inits = 100 # number of initial populations n_inits >= run_times
75 | run_times = 100 # number of different initial populations
76 |
77 | # wind farm size, cells
78 | cols_cells = 12 # number of cells each row
79 | rows_cells = 12 # number of cells each column
80 | cell_width = 77.0 * 3 # unit : m
81 |
82 | # all data will be save in data folder
83 | data_folder = "data"
84 | if not os.path.exists(data_folder):
85 | os.makedirs(data_folder)
86 |
87 | # Create a WindFarmGenetic object
88 | # create an WindFarmGenetic object. Specify the number of rows and the number columns of the wind farm land. N is the number of wind turbines.
89 | # NA_loc is the not available locations on the wind farm land. Landowners does not want to participate in the wind farm.
90 | # pop_size: how many individuals in the population
91 | # iteration: iteration times of the genetic algorithm
92 | wfg = WindFarmGenetic.WindFarmGenetic(rows=rows_cells, cols=cols_cells, N=wt_N, NA_loc=NA_loc, pop_size=population_size,
93 | iteration=iteration_times, cell_width=cell_width, elite_rate=elite_rate,
94 | cross_rate=cross_rate, random_rate=random_rate, mutate_rate=mutate_rate)
95 | # Specify the wind distribution
96 | # wind distribution is discrete (number of wind speeds) by (number of wind directions)
97 | # wfg.init_1_direction_1_N_speed_13()
98 | # file name to store the wind power distribution SVR model
99 | # svr_model_filename = 'svr_1s1d_N_13.svr'
100 |
101 | # wfg.init_4_direction_1_speed_13()
102 | # svr_model_filename = 'svr_1s4d_13.svr'
103 |
104 | wfg.init_6_direction_1_speed_13()
105 | # svr_model_filename = 'svr_1s6d_13.svr'
106 |
107 | ################################################
108 | # generate initial populations
109 | ################################################
110 | # initial population saved folder
111 | init_pops_data_folder = "{}/init_data".format(data_folder)
112 | if not os.path.exists(init_pops_data_folder):
113 | os.makedirs(init_pops_data_folder)
114 | # generate initial populations to start with and store them
115 | # in order to start from the same initial population for different methods
116 | # so it is fair to compare the final results
117 |
118 | for i in range(n_inits):
119 | wfg.gen_init_pop_NA()
120 | wfg.save_init_pop_NA("{}/init_{}.dat".format(init_pops_data_folder, i),
121 | "{}/init_{}_NA.dat".format(init_pops_data_folder, i))
122 |
123 | # Create results folder
124 | # results folder
125 | # adaptive_best_layouts_N60_9_20190422213718.dat : best layout for AGA of run index 9
126 | # result_CGA_20190422213715.dat : run time and best eta for CGA method
127 | results_data_folder = "data/results"
128 | if not os.path.exists(results_data_folder):
129 | os.makedirs(results_data_folder)
130 | # if cg,ag,sg folder does not exist, create these folders. Folders to store the running results
131 | # cg: convertional genetic algorithm
132 | # ag: adaptive genetic algorithm
133 | # sg: support vector regression guided genetic algorithm
134 | cg_result_folder = "{}/cg".format(results_data_folder)
135 | if not os.path.exists(cg_result_folder):
136 | os.makedirs(cg_result_folder)
137 |
138 | ag_result_folder = "{}/ag".format(results_data_folder)
139 | if not os.path.exists(ag_result_folder):
140 | os.makedirs(ag_result_folder)
141 |
142 | sg_result_folder = "{}/sg".format(results_data_folder)
143 | if not os.path.exists(sg_result_folder):
144 | os.makedirs(sg_result_folder)
145 | # resul_arr: run_times by 2 , the first column is the run time in seconds for each run and the second column is the conversion efficiency for the run
146 | result_arr = np.zeros((run_times, 2), dtype=np.float32)
147 |
148 | # Run adaptive genetic algorithm (AGA)
149 | # CGA: Conventional genetic algorithm
150 | for i in range(0, run_times): # run times
151 | print("run times {} ...".format(i))
152 | # load initial population
153 | wfg.load_init_pop_NA("{}/init_{}.dat".format(init_pops_data_folder, i),
154 | "{}/init_{}_NA.dat".format(init_pops_data_folder, i))
155 | # run the conventional genetic algorithm and return run time and conversion efficiency
156 | run_time, eta = wfg.conventional_genetic_alg(i, result_folder=cg_result_folder)
157 | result_arr[i, 0] = run_time
158 | result_arr[i, 1] = eta
159 | time_stamp = datetime.now().strftime("%Y%m%d%H%M%S")
160 | # save the run time and etas to a file
161 | filename = "{}/result_conventional_{}.dat".format(cg_result_folder, time_stamp)
162 | np.savetxt(filename, result_arr, fmt='%f', delimiter=" ")
163 |
164 | # Run adaptive genetic algorithm (AGA)
165 | # AGA: adaptive genetic algorithm
166 | for i in range(0, run_times): # run times
167 | print("run times {} ...".format(i))
168 | wfg.load_init_pop_NA("{}/init_{}.dat".format(init_pops_data_folder, i),
169 | "{}/init_{}_NA.dat".format(init_pops_data_folder, i))
170 | run_time, eta = wfg.adaptive_genetic_alg(i, result_folder=ag_result_folder)
171 | result_arr[i, 0] = run_time
172 | result_arr[i, 1] = eta
173 | time_stamp = datetime.now().strftime("%Y%m%d%H%M%S")
174 | filename = "{}/result_adaptive_{}.dat".format(ag_result_folder, time_stamp)
175 | np.savetxt(filename, result_arr, fmt='%f', delimiter=" ")
176 |
177 | # Run support vector regression guided genetic algorithm (SUGGA)
178 | # Generate wind distribution surface
179 |
180 | #############################################
181 | # generate wind distribution surface
182 | #############################################
183 | n_mc_samples = 10000 # svr train data, number of layouts to average
184 |
185 | wds_data_folder = "{}/wds".format(data_folder)
186 | if not os.path.exists(wds_data_folder):
187 | os.makedirs(wds_data_folder)
188 | # mc : monte-carlo
189 |
190 | # number of layouts to generate as the training data for regression
191 | # to build the power distribution surface
192 |
193 | # mc_layout.dat file stores layouts only with 0s and 1s. 0 means no turbine here. 1 means one turbine here.
194 | # mc_layout_NA.dat file stores layouts with 0s, 1s and 2s. 2 means no turbine and not available for turbine.
195 | # These two files are used to generate wind power distribution.
196 | # Each file has 10000 lines. Each line is layout.
197 | # gen_mc_grid_with_NA_loc function generates these two files.
198 | train_mc_layouts, train_mc_layouts_NA = WindFarmGenetic.LayoutGridMCGenerator.gen_mc_grid_with_NA_loc(rows_cells,
199 | cols_cells,
200 | n_mc_samples,
201 | wt_N, NA_loc,
202 | "{}/mc_layout.dat".format(
203 | wds_data_folder),
204 | "{}/mc_layout_NA.dat".format(
205 | wds_data_folder))
206 |
207 | # wfg.init_1_direction_1_N_speed_13()
208 | # file name to store the wind power distribution SVR model
209 | # svr_model_filename = 'svr_1s1d_N_13.svr'
210 |
211 | # wfg.init_4_direction_1_speed_13()
212 | # svr_model_filename = 'svr_1s4d_13.svr'
213 |
214 | # wfg.init_6_direction_1_speed_13()
215 | svr_model_filename = 'svr_1s6d_13.svr'
216 |
217 | # load Monte-Carlo layouts from a text file. 10000 random layouts
218 | layouts = np.genfromtxt("{}/mc_layout.dat".format(wds_data_folder), delimiter=" ", dtype=np.int32)
219 | # generate the location index coordinate and average power output at each location index coordinate
220 | # location index coordinate : in the cells, the cell with index 1 has location index (0,0) and the cell 2 has (1,0)
221 | # store the location index coordinate in x.dat and average power in y.dat
222 | wfg.mc_gen_xy_NA(rows=rows_cells, cols=cols_cells, layouts=layouts, n=n_mc_samples, N=wt_N,
223 | xfname="{}/x.dat".format(wds_data_folder),
224 | yfname="{}/y.dat".format(wds_data_folder))
225 |
226 | # read index location coordinates
227 | x_original = pd.read_csv("{}/x.dat".format(wds_data_folder), header=None, nrows=rows_cells * cols_cells,
228 | delim_whitespace=True, dtype=np.float32)
229 | x_original = x_original.values
230 |
231 | # read the power output of each index location coordinate
232 | y_original = pd.read_csv("{}/y.dat".format(wds_data_folder), header=None, nrows=rows_cells * cols_cells,
233 | delim_whitespace=True, dtype=np.float32)
234 | y_original = y_original.values.flatten()
235 |
236 | # create a SVR object and specify the kernal and other parameters
237 | svr_model = SVR(kernel='rbf', C=2000.0, gamma=0.3, epsilon=.1)
238 | # build the SVR power distribution model
239 | svr_model.fit(x_original, y_original)
240 |
241 | # save the SVR model to a file
242 | pickle.dump(svr_model, open("{}/{}".format(wds_data_folder, svr_model_filename), 'wb'))
243 |
244 | # This is how to load SVR model from a file
245 | # svr_model = pickle.load(open("{}/{}".format(wds_data_folder,svr_model_filename), 'rb'))
246 |
247 |
248 | # SUGGA: support vector regression guided genetic algorithm
249 | for i in range(0, run_times): # run times
250 | print("run times {} ...".format(i))
251 | wfg.load_init_pop_NA("{}/init_{}.dat".format(init_pops_data_folder, i),
252 | "{}/init_{}_NA.dat".format(init_pops_data_folder, i))
253 | run_time, eta = wfg.sugga_genetic_alg(i, svr_model=svr_model, result_folder=sg_result_folder)
254 | result_arr[i, 0] = run_time
255 | result_arr[i, 1] = eta
256 | time_stamp = datetime.now().strftime("%Y%m%d%H%M%S")
257 | filename = "{}/result_sugga_{}.dat".format(sg_result_folder, time_stamp)
258 | np.savetxt(filename, result_arr, fmt='%f', delimiter=" ")
259 |
--------------------------------------------------------------------------------