[docs]classRegularLayoutOptProblem(FarmVarsProblem):""" Places turbines on a regular grid and optimizes its parameters. Attributes ---------- min_spacing: float The minimal turbine spacing :group: opt.problems.layout """SPACING_X="spacing_x"SPACING_Y="spacing_y"OFFSET_X="offset_X"OFFSET_Y="offset_Y"ANGLE="angle"
[docs]def__init__(self,name,algo,min_spacing,**kwargs,):""" Constructor. Parameters ---------- name: str The problem's name algo: foxes.core.Algorithm The algorithm min_spacing: float The minimal turbine spacing kwargs: dict, optional Additional parameters for `FarmVarsProblem` """super().__init__(name,algo,**kwargs)self.min_spacing=min_spacing
[docs]definitialize(self,verbosity=1,**kwargs):""" Initialize the object. Parameters ---------- verbosity: int The verbosity level, 0 = silent kwargs: dict, optional Additional parameters for super class init """self._mname=self.name+"_calc"fortinself.algo.farm.turbines:ifself._mnamenotint.models:t.models.append(self._mname)self._turbine=deepcopy(self.farm.turbines[-1])self.algo.mbook.turbine_models[self._mname]=Calculator(in_vars=[FC.VALID,FV.P,FV.CT],out_vars=[FC.VALID,FV.P,FV.CT],func=lambdavalid,P,ct,st_sel:(valid,P*valid,ct*valid),pre_rotor=False,)b=self.farm.boundaryassertbisnotNone,f"Problem '{self.name}': Missing wind farm boundary."pmax=b.p_max()pmin=b.p_min()self._pmin=pminself._xy0=0.5*(pmin+pmax)self._halfspan=(pmax-pmin)/2self._halflen=np.linalg.norm(self._halfspan)self.max_spacing=2*(self._halflen+self.min_spacing)self._halfn=int(self._halflen/self.min_spacing)ifself._halfn*self.min_spacing<self._halflen:self._halfn+=1self._nrow=2*self._halfn+1self._nturb=self._nrow**2ifverbosity>0:print(f"Problem '{self.name}':")print(f" xy0 = {self._xy0}")print(f" span = {np.linalg.norm(self._halfspan*2):.2f}")print(f" min spacing = {self.min_spacing:.2f}")print(f" max spacing = {self.max_spacing:.2f}")print(f" n row turbns = {self._nrow}")print(f" n turbines = {self._nturb}")print(f" turbine mdls = {self._turbine.models}")ifself.farm.n_turbines<self._nturb:foriinrange(self._nturb-self.farm.n_turbines):ti=len(self.farm.turbines)self.farm.turbines.append(deepcopy(self._turbine))self.farm.turbines[-1].index=tiself.farm.turbines[-1].name=f"T{ti}"elifself.farm.n_turbines>self._nturb:self.farm.turbines=self.farm.turbines[:self._nturb]self.algo.n_turbines=self._nturbsuper().initialize(pre_rotor_vars=[FV.X,FV.Y,FC.VALID],post_rotor_vars=[],verbosity=verbosity,**kwargs,)
[docs]defvar_names_float(self):""" The names of float variables. Returns ------- names: list of str The names of the float variables """return[self.SPACING_X,self.SPACING_Y,self.OFFSET_X,self.OFFSET_Y,self.ANGLE,]
[docs]definitial_values_float(self):""" The initial values of the float variables. Returns ------- values: numpy.ndarray Initial float values, shape: (n_vars_float,) """return[self.min_spacing,self.min_spacing,0.0,0.0,0.0]
[docs]defmin_values_float(self):""" The minimal values of the float variables. Use -numpy.inf for unbounded. Returns ------- values: numpy.ndarray Minimal float values, shape: (n_vars_float,) """return[self.min_spacing,self.min_spacing,-self._halfspan[0]-self.min_spacing,-self._halfspan[1]-self.min_spacing,0.0,]
[docs]defmax_values_float(self):""" The maximal values of the float variables. Use numpy.inf for unbounded. Returns ------- values: numpy.ndarray Maximal float values, shape: (n_vars_float,) """return[self.max_spacing,self.max_spacing,self._halfspan[0]+self.min_spacing,self._halfspan[1]+self.min_spacing,90.0,]
[docs]defopt2farm_vars_individual(self,vars_int,vars_float):""" Translates optimization variables to farm variables Parameters ---------- vars_int: numpy.ndarray The integer optimization variable values, shape: (n_vars_int,) vars_float: numpy.ndarray The float optimization variable values, shape: (n_vars_float,) Returns ------- farm_vars: dict The foxes farm variables. Key: var name, value: numpy.ndarray with values, shape: (n_states, n_sel_turbines) """dx,dy,ox,oy,a=vars_floatn_states=self.algo.n_statesnx=self._nrowny=self._nrowa=np.deg2rad(a)nax=np.array([np.cos(a),np.sin(a),0.0],dtype=config.dtype_double)nay=np.cross(np.array([0.0,0.0,1.0],dtype=config.dtype_double),nax)x0=self._xy0+np.array([ox,oy],dtype=config.dtype_double)pts=np.zeros((n_states,nx,ny,2),dtype=config.dtype_double)pts[:]=(x0[None,None,None,:]+np.arange(nx)[None,:,None,None]*dx*nax[None,None,None,:2]+np.arange(ny)[None,None,:,None]*dy*nay[None,None,None,:2])pts=pts.reshape(n_states,nx*ny,2)valid=self.farm.boundary.points_inside(pts.reshape(n_states*nx*ny,2))farm_vars={FV.X:pts[:,:,0],FV.Y:pts[:,:,1],FC.VALID:valid.reshape(n_states,nx*ny).astype(config.dtype_double),}returnfarm_vars
[docs]defopt2farm_vars_population(self,vars_int,vars_float,n_states):""" Translates optimization variables to farm variables Parameters ---------- vars_int: numpy.ndarray The integer optimization variable values, shape: (n_pop, n_vars_int) vars_float: numpy.ndarray The float optimization variable values, shape: (n_pop, n_vars_float) n_states: int The number of original (non-pop) states Returns ------- farm_vars: dict The foxes farm variables. Key: var name, value: numpy.ndarray with values, shape: (n_pop, n_states, n_sel_turbines) """n_pop=len(vars_float)n_turbines=self.farm.n_turbinesdx=vars_float[:,0]dy=vars_float[:,1]ox=vars_float[:,2]oy=vars_float[:,3]nx=self._nrowny=self._nrowa=vars_float[:,4]N=self._nturba=np.deg2rad(a)nax=np.stack([np.cos(a),np.sin(a),np.zeros_like(a)],axis=-1)naz=np.zeros_like(nax)naz[...,2]=1nay=np.cross(naz,nax)pts=np.zeros((n_pop,n_states,nx,ny,2),dtype=config.dtype_double)pts[:]=self._xy0[None,None,None,None,:]pts[...,0]+=ox[:,None,None,None]pts[...,1]+=oy[:,None,None,None]pts[:]+=(np.arange(nx)[None,None,:,None,None]*dx[:,None,None,None,None]*nax[:,None,None,None,:2]+np.arange(ny)[None,None,None,:,None]*dy[:,None,None,None,None]*nay[:,None,None,None,:2])qts=np.zeros((n_pop,n_states,n_turbines,2))qts[:,:N]=pts.reshape(n_pop,n_states,N,2)delptsvalid=self.farm.boundary.points_inside(qts.reshape(n_pop*n_states*n_turbines,2))farm_vars={FV.X:qts[:,:,:,0],FV.Y:qts[:,:,:,1],FC.VALID:valid.reshape(n_pop,n_states,n_turbines).astype(config.dtype_double),}returnfarm_vars
[docs]deffinalize_individual(self,vars_int,vars_float,verbosity=1):""" Finalization, given the champion data. Parameters ---------- vars_int: np.array The optimal integer variable values, shape: (n_vars_int,) vars_float: np.array The optimal float variable values, shape: (n_vars_float,) verbosity: int The verbosity level, 0 = silent Returns ------- problem_results: Any The results of the variable application to the problem objs: np.array The objective function values, shape: (n_objectives,) cons: np.array The constraints values, shape: (n_constraints,) """farm_vars=self.opt2farm_vars_individual(vars_int,vars_float)sel=np.where(farm_vars[FC.VALID][0])[0]x=farm_vars[FV.X][0,sel]y=farm_vars[FV.Y][0,sel]self.farm.turbines=[tfori,tinenumerate(self.farm.turbines)ifiinsel]fori,tinenumerate(self.farm.turbines):t.xy=np.array([x[i],y[i]],dtype=config.dtype_double)t.models=[mformint.modelsifmnotin[self.name,self._mname]]t.index=it.name=f"T{i}"self.algo.update_n_turbines()returnFarmOptProblem.finalize_individual(self,vars_int,vars_float,verbosity=1)