def test_constructor_defaults(self): self.hobj.add('arr_nodefault3', Array(iotype='in', units='kg')) self.assertTrue(all(array([]) == self.hobj.arr_nodefault3)) self.hobj.add('arr_nounits', Array(iotype='in')) if hasattr(self.hobj.arr_nounits, 'units'): self.fail("Unitless Array should not have units")
def setUp(self): """this setup function will be called before each test in this class""" self.hobj = Component() self.hobj.add('arr1', Array(array([98.9]), iotype='in', units='ft')) self.hobj.add('arr2', Array(array([13.2]), iotype='out', units='inch')) self.hobj.add('arr3', Array(iotype='in', units='kg', desc='stuff')) self.hobj.arr1 = [1.0, 2.0, 3.0] self.hobj.arr2 = [[1., 2.], [3., 4.]] self.hobj.arr3 = [1.1]
class PlantFromWWF(Component): """Create a Plant information from a .wwf WAsP file""" # Inputs filename = Str(iotype='in', desc='The .wwf file name') wt_desc = VarTree(GenericWindTurbinePowerCurveVT(), iotype='in', desc='The wind turbine power curve') # Outputs wt_layout = VarTree(GenericWindFarmTurbineLayout(), iotype='out', desc='wind turbine properties and layout') wind_rose_array = Array( iotype='out', units='m/s', desc='Windrose array [wind_directions, frequency, weibull_A, weibull_k]' ) def execute(self): # Reading the .wwf file wwf = WWF(self.filename) self.wt_layout.wt_list.append(self.wt_desc) # self.wt_layout.wt_wind_roses.frequency_array = self.wt_layout.configure_single() for wt, wr in self.wwf.windroses.iteritems(): self.wt_layout.wt_positions[i, :] = self.wwf.data[wt][:2] self.wt_layout.wt_wind_roses.append(wr) i += 1 self.wt_layout.configure_single() # For practical reason we also output the first wind rose array outside # the wt_layout self.wind_rose_array = self.wt_layout.wt_wind_roses[0]
class AEPM(AEPMultipleWindRoses): """ Calculate the AEP of a wind farm. Provide the standard AEPMultipleWindRoses interfaces, with in addition the possibility to change the layout of the windfarm using wt_positions as an interface. """ wt_positions = Array( [], unit='m', iotype='in', desc='Array of wind turbines attached to particular positions') def __init__(self, wake_model=TopFGCLarsen(), **kwargs): """ :param wake_model: A GenericWindFarm compatible model """ self.wake_model = wake_model super(TopAEP, self).__init__(**kwargs) def configure(self): """ Configure the assembly. :param wake_model: The wake model :return: """ self.add('wf', self.wake_model) super(TopAEP, self).configure() self.connect('wt_positions', 'wf.wt_positions')
def test_default_value_type(self): try: self.hobj.add('bad_default', Array('bad')) except TypeError, err: self.assertEqual( str(err), "Default value should be an array-like object, not a <type 'str'>." )
class A(Component): f = Float(iotype='in') a1d = Array(array([1.0, 1.0, 2.0, 3.0]), iotype='in') a2d = Array(array([[1.0, 1.0], [2.0, 3.0]]), iotype='in') b1d = Array(array([1.0, 1.0, 2.0, 3.0]), iotype='out') b2d = Array(array([[1.0, 1.0], [2.0, 3.0]]), iotype='out') c1d = Array(array([0.0, 1.0, 2.0, 3.0]), iotype='in') c2d = Array(array([[0.0, 1.0], [2.0, 3.0]]), iotype='in') ext1 = Array(array([eye(3), eye(3), eye(3)])) def execute(self): pass def some_funct(self, a, b, op='add'): if op == 'add': return a + b elif op == 'mult': return a * b elif op == 'sub': return a - b raise RuntimeError("bad input to some_funct") @property def some_prop(self): return 7
def test_shapes(self): self.hobj.add( 'sh1', Array(array([[2.0, 4.5], [3.14, 2.5]]), iotype='in', units='kg', shape=(2, 2))) self.assertEqual(self.hobj.sh1[1][1], 2.5) try: self.hobj.add( 'sh1', Array(array([2.0, 2.5]), iotype='in', units='kg', shape=(2, 2))) except ValueError, err: msg = "Shape of the default value does not match the shape attribute." self.assertEqual(str(err), msg)
class GeomData(VariableTree): points = Array([], desc='nx3 array of point (x,y,z) locations.') facets = Array([], desc='nx3 (or nx4) integer array of triangle or quad' + 'connectivities.', dtype='int') def __init__(self, n_point, n_facet, facet_size=3): super(GeomData, self).__init__() if facet_size not in [3, 4]: msg = "facet size must be either 3 or 4" raise ValueError(msg) self.points = np.zeros((n_point, 3)) self.facets = np.zeros((n_facet, facet_size), dtype='int')
def test_set_to_default(self): self.hobj.add('arr4', Array(iotype='in', units='kg')) self.assertTrue(all(array([]) == self.hobj.arr4)) self.hobj.arr4 = [6.5] self.assertEqual(6.5, self.hobj.arr4[0]) self.hobj.revert_to_defaults() self.assertTrue(all(array([98.9]) == self.hobj.arr1)) self.assertTrue(all(array([]) == self.hobj.arr4))
class TopFGCLarsen(FGCLarsen): """ Calculate the wind turbine power using the GCLarsen model. This version gives the standard FGCLarsen interfaces, but replace the wt_layout.wt_positions by the input wt_positions. This is done to speed-up the variable copy in openMDAO. The idea is that at each iteration, it's not necessary to copy arround wt_layout. """ wt_positions = Array([], unit='m', iotype='in', desc='Array of wind turbines attached to particular positions') def execute(self): self.wt_layout.wt_positions = self.wt_positions super(TopFGCLarsen, self).execute()
def test_unit_conversion(self): self.hobj.arr1 = [1., 2., 3.] self.hobj.arr2 = self.hobj.get_wrapped_attr('arr1') self.assertAlmostEqual(12., self.hobj.arr2[0]) self.assertAlmostEqual(24., self.hobj.arr2[1]) self.assertAlmostEqual(36., self.hobj.arr2[2]) # unit to unitless self.hobj.add('arr5', Array(iotype='in')) self.hobj.arr5 = [1., 2., 4.] self.hobj.arr2 = self.hobj.get_wrapped_attr('arr5') self.assertAlmostEqual(1., self.hobj.arr2[0]) self.assertAlmostEqual(2., self.hobj.arr2[1]) self.assertAlmostEqual(4., self.hobj.arr2[2])
class FoundationLength(TopfarmComponent): wt_positions = Array( [], unit='m', iotype='in', desc='Array of wind turbines attached to particular positions') ##wt_layout = VarTree(GenericWindFarmTurbineLayout(), iotype='in', desc='wind turbine properties and layout') borders = Array(iotype='in', desc='The polygon defining the borders ndarray([n_bor,2])', unit='m') depth = Array(iotype='in', desc='An array of depth ndarray([n_d, 2])', unit='m') foundation_length = Float( iotype='out', desc='The total foundation length of the wind farm') foundations = Array(iotype='out', desc='The foundation length ofeach wind turbine') scaling = Float(1.0, iotype='in', desc='scaling of the foundation') inc = 0 def execute(self): foundation_func = LinearNDInterpolator(self.depth[:, 0:2], self.depth[:, 2]) self.foundations = array([ foundation_func(self.wt_positions[i, 0], self.wt_positions[i, 1]) for i in range(self.wt_positions.shape[0]) ]) #dist = DistFromBorders()(wt_positions=self.wt_positions, borders=self.borders).dist min_depth = self.depth[:, 2].min() self.foundations[isnan(self.foundations)] = min_depth if self.scaling == 0.0: # Using the baseline for scaling self.scaling = sum(self.foundations) self.foundation_length = sum(self.foundations) / self.scaling
class ElNetLayout(TopfarmComponent): wt_positions = Array( [], unit='m', iotype='in', desc='Array of wind turbines attached to particular positions') ##wt_layout = VarTree(GenericWindFarmTurbineLayout(), iotype='in', desc='wind turbine properties and layout') #elnet_layout = VarTree(GenericWindTurbineCableLayout(), iotype='out') elnet_layout = Dict( iotype='out', desc= 'The keys are tuples of connected wind turbine indices, the values are the cable length' ) def execute(self): self.elnet_layout = elnet(self.wt_positions)
class Simple(Component): a = Float(iotype='in') b = Float(iotype='in') c = Float(iotype='out') d = Float(iotype='out') x_array = Array([0, 0, 0], iotype='in') def __init__(self): super(Simple, self).__init__() self.a = 4. self.b = 5. self.c = 7. self.d = 1.5 def execute(self): self.c = self.a + self.b self.d = self.a - self.b self.x_array[1] = self.a * self.b
class ElNetLength(TopfarmComponent): wt_positions = Array( [], unit='m', iotype='in', desc='Array of wind turbines attached to particular positions') #wt_layout = VarTree(GenericWindFarmTurbineLayout(), iotype='in', desc='wind turbine properties and layout') elnet_layout = Dict( iotype='out') #VarTree(GenericWindTurbineCableLayout(), iotype='out') elnet_length = Float(iotype='out', desc='The total inner cable length', unit='m') scaling = Float(1.0, iotype='in', desc='') def execute(self): elnet_layout = elnet(self.wt_positions) if self.scaling == 0.0: #using the first run as a baseline for scaling: self.scaling = sum(elnet_layout.values()) self.elnet_length = sum(elnet_layout.values()) / self.scaling self.elnet_layout = elnet_layout
class AEP(TopfarmComponent): # Inputs wind_speeds = List([], iotype='in', units='m/s', desc='The different wind speeds to run [nWS]') wind_directions = List([], iotype='in', units='deg', desc='The different wind directions to run [nWD]') wt_positions = Array(iotype='in') scaling = Float(1.0, iotype='in', desc='Scaling of the AEP') # Outputs array_aep = Array([], iotype='out', units='kW*h', desc='The energy production per sector [nWD, nWS]') gross_aep = Float(iotype='out', units='kW*h', desc='Gross Annual Energy Production before availability and loss impacts') net_aep = Float(iotype='out', units='kW*h', desc='Net Annual Energy Production after availability and loss impacts') capacity_factor = Float(0.0, iotype='out', desc='Capacity factor for wind plant') def __init__(self, wt_layout, wind_rose, wf, **kwargs): """ :param wt_layout: GenericWindFarmTurbineLayout() :param wind_rose: WeibullWindRoseVT() :param wf: GenericWindFarm() :param scaling: float [default = 1.0] The scaling used to calculate the net_aep. If it is set to 0.0, the scaling will be set to the net_aep the first time the simulation is run. """ self.wf = wf self.wf.wt_layout = wt_layout self.wind_rose = wind_rose super(AEP, self).__init__(**kwargs) def execute(self): # build the cdf vector of the wind speed for each wind rose wind direction sector cdfw = [] for iwd, wd in enumerate(self.wind_rose.wind_directions): cdfw.append(weibullCDF(self.wind_speeds, self.wind_rose.A[iwd], self.wind_rose.k[iwd])) # calculate the probability in each wind direction sector, using the CDF of the wind rose wind direction cdfd0 = [sum(self.wind_rose.frequency[:i]) for i in range(len(self.wind_rose.frequency)+1)] wd = np.hstack([self.wind_rose.wind_directions, [360]]) cdfd1 = interp1d(wd, cdfd0)(self.wind_directions) net_aep = 0.0 gross_aep = 0.0 cwd = 0 net_aeps = np.zeros([len(self.wind_directions)]) gross_aeps = np.zeros([len(self.wind_directions)]) self.powers = np.zeros([len(self.wind_directions), len(self.wind_speeds)]) for iwd, wd in enumerate(self.wind_directions): if cwd < len(self.wind_rose.wind_directions): while wd >= self.wind_rose.wind_directions[cwd+1] and cwd < len(self.wind_rose.wind_directions)-2: # switching wind rose wind direction sector cwd += 1 powers = np.zeros([len(self.wind_speeds)]) for iws, ws in enumerate(self.wind_speeds): self.wf.wt_layout.wt_positions=self.wt_positions self.wf.wind_speed=ws self.wf.wind_direction=wd self.wf.run() powers[iws] = self.wf.power self.powers[iwd,:] = powers # Integrating over the wind speed CDF net_aeps[iwd] = np.trapz(powers, cdfw[cwd]) * 365.0 * 24.0 for i in range(self.wt_positions.shape[0]): power_curve = interp1d(self.wf.wt_layout.wt_list[i].power_curve[:,0], self.wf.wt_layout.wt_list[i].power_curve[:,1])(self.wind_speeds) gross_aeps[iwd] += np.trapz(power_curve, cdfw[cwd]) * 365.0 * 24.0 # Integrating over the wind direction CDF net_aep = np.trapz(net_aeps, cdfd1) gross_aep = np.trapz(gross_aeps, cdfd1) self.capacity_factor = net_aep / gross_aep if self.scaling == 0.0: # The scaling has to be calculated self.scaling = net_aep self.net_aep = net_aep / self.scaling self.gross_aep = gross_aep
def test_intvalues(self): f1 = Array([3.]) d1 = f1.default_value / 2 self.assertAlmostEqual(d1[0], 1.5, places=4)
def test_bogus_units(self): try: uf = Array([0.], iotype='in', units='bogus') except ValueError, err: self.assertEqual(str(err), "Units of 'bogus' are invalid")
class TopFortranGCLarsen(FGCL): wt_positions = Array([], unit='m', iotype='in', desc='Array of wind turbines attached to particular positions') def execute(self): self.wt_layout.wt_positions = self.wt_positions super(TopFGCLarsen, self).execute()
class NEWSUMTdriver(DriverUsesDerivatives): """ Driver wrapper of Fortran version of NEWSUMT. .. todo:: Check to see if this itmax variable is needed. NEWSUMT might handle it for us. """ implements(IHasParameters, IHasIneqConstraints, IHasObjective) itmax = Int(10, iotype='in', desc='Maximum number of iterations before \ termination.') default_fd_stepsize = Float(0.01, iotype='in', desc='Default finite ' \ 'difference stepsize. Parameters with ' \ 'specified values override this.') ilin = Array(dtype=numpy_int, default_value=zeros(0, 'i4'), iotype='in', desc='Array designating whether each constraint is linear.') # Control parameters for NEWSUMT. # NEWSUMT has quite a few parameters to give the user control over aspects # of the solution. epsgsn = Float(0.001, iotype='in', desc='Convergence criteria \ of the golden section algorithm used for the \ one dimensional minimization.') epsodm = Float(0.001, iotype='in', desc='Convergence criteria \ of the unconstrained minimization.') epsrsf = Float(0.001, iotype='in', desc='Convergence criteria \ for the overall process.') g0 = Float(0.1, iotype='in', desc='Initial value of the transition \ parameter.') ra = Float(1.0, iotype='in', desc='Penalty multiplier. Required if mflag=1') racut = Float(0.1, iotype='in', desc='Penalty multiplier decrease ratio. \ Required if mflag=1.') ramin = Float(1.0e-13, iotype='in', desc='Lower bound of \ penalty multiplier. \ Required if mflag=1.') stepmx = Float(2.0, iotype='in', desc='Maximum bound imposed on the \ initial step size of the one-dimensional \ minimization.') jprint = Int(0, iotype='in', desc='Print information during NEWSUMT \ solution. Higher values are more verbose. If 0,\ print initial and final designs only.', high=3, low=-1) lobj = Int(0, iotype='in', desc='Set to 1 if linear objective function.') maxgsn = Int(20, iotype='in', desc='Maximum allowable number of golden \ section iterations used for 1D minimization.') maxodm = Int(6, iotype='in', desc='Maximum allowable number of one \ dimensional minimizations.') maxrsf = Int(15, iotype='in', desc='Maximum allowable number of \ unconstrained minimizations.') mflag = Int(0, iotype='in', desc='Flag for penalty multiplier. \ If 0, initial value computed by NEWSUMT. \ If 1, initial value set by ra.') def __init__(self, *args, **kwargs): super(NEWSUMTdriver, self).__init__(*args, **kwargs) self.iter_count = 0 # Save data from common blocks into the driver self.contrl = _contrl() self.countr = _countr() # define the NEWSUMTdriver's private variables # note, these are all resized in config_newsumt # basic stuff self.design_vals = zeros(0, 'd') self.constraint_vals = [] # temp storage self.__design_vals_tmp = zeros(0, 'd') self._ddobj = zeros(0) self._dg = zeros(0) self._dh = zeros(0) self._dobj = zeros(0) self._g = zeros(0) self._gb = zeros(0) self._g1 = zeros(0) self._g2 = zeros(0) self._g3 = zeros(0) self._s = zeros(0) self._sn = zeros(0) self._x = zeros(0) self._iik = zeros(0, dtype=int) self._lower_bounds = zeros(0) self._upper_bounds = zeros(0) self._iside = zeros(0) self.fdcv = zeros(0) # Just defined here. Set elsewhere self.n1 = self.n2 = self.n3 = self.n4 = 0 # Ready inputs for NEWSUMT self._obj = 0.0 self._objmin = 0.0 self.isdone = False self.resume = False self.uses_Hessians = False def start_iteration(self): """Perform the optimization.""" # Flag used to figure out if we are starting a new finite difference self.baseline_point = True # set newsumt array sizes and more... self._config_newsumt() self.iter_count = 0 # get the values of the parameters # check if any min/max constraints are violated by initial values for i, val in enumerate(self.get_parameters().values()): value = val.evaluate(self.parent) self.design_vals[i] = value # next line is specific to NEWSUMT self.__design_vals_tmp[i] = value # Call the interruptible version of SUMT in a loop that we manage self.isdone = False self.resume = False def continue_iteration(self): """Returns True if iteration should continue.""" return not self.isdone and self.iter_count < self.itmax def pre_iteration(self): """Checks or RunStopped and evaluates objective.""" super(NEWSUMTdriver, self).pre_iteration() if self._stop: self.raise_exception('Stop requested', RunStopped) def run_iteration(self): """ The NEWSUMT driver iteration.""" self._load_common_blocks() try: ( fmin, self._obj, self._objmin, self.design_vals, self.__design_vals_tmp, self.isdone, self.resume) = \ newsumtinterruptible.newsuminterruptible(user_function, self._lower_bounds, self._upper_bounds, self._ddobj, self._dg, self._dh, self._dobj, self.fdcv, self._g, self._gb, self._g1, self._g2, self._g3, self._obj, self._objmin, self._s, self._sn, self.design_vals, self.__design_vals_tmp, self._iik, self.ilin, self._iside, self.n1, self.n2, self.n3, self.n4, self.isdone, self.resume, analys_extra_args = (self,)) except Exception, err: self._logger.error(str(err)) raise self._save_common_blocks() self.iter_count += 1 # Update the parameters and run one final time with what it gave us. # This update is needed because I obeserved that the last callback to # user_function is the final leg of a finite difference, so the model # is not in sync with the final design variables. if not self.continue_iteration(): dvals = [float(val) for val in self.design_vals] self.set_parameters(dvals) super(NEWSUMTdriver, self).run_iteration()
class AEPFortranMultipleWindRoses(TopfarmComponent): # Inputs wind_speeds = List([], iotype='in', units='m/s', desc='The different wind speeds to run [nWS]') wind_directions = List([], iotype='in', units='deg', desc='The different wind directions to run [nWD]') turbulence_intensity = Float(0.1, iotype='in', desc='The turbulence intensity at the site') wt_positions = Array(iotype='in') wind_roses = List(iotype='in', desc='List of weibull wind_rose arrays') scaling = Float(1.0, iotype='in', desc='Scaling of the AEP') # Outputs array_aep = Array([], iotype='out', units='kW*h', desc='The energy production per sector [nWD, nWS]') gross_aep = Float(iotype='out', units='kW*h', desc='Gross Annual Energy Production before availability and loss impacts') net_aep = Float(iotype='out', units='kW*h', desc='Net Annual Energy Production after availability and loss impacts') capacity_factor = Float(0.0, iotype='out', desc='Capacity factor for wind plant') def __init__(self, wt_layout, wf, **kwargs): """ :param wt_layout: GenericWindFarmTurbineLayout() :param wf: GenericWindFarm() :param scaling: float [default = 1.0] The scaling used to calculate the net_aep. If it is set to 0.0, the scaling will be set to the net_aep the first time the simulation is run. """ self.wf = wf self.wf.wt_layout = wt_layout #self.wind_rose = wind_rose super(AEPFortranMultipleWindRoses, self).__init__(**kwargs) def execute(self): wt_net_aep = np.zeros([self.wt_positions.shape[0]]) wt_gross_aep = np.zeros([self.wt_positions.shape[0]]) cwd = 0 wind_speeds, wind_directions = np.meshgrid(self.wind_speeds, self.wind_directions) # Runnning all the wind speeds and wind directions at the same time self.wf.wt_layout.wt_positions=self.wt_positions self.wf.wind_speeds = wind_speeds.flatten() self.wf.wind_directions = wind_directions.flatten() self.wf.turbulence_intensities = self.turbulence_intensity * ones_like(self.wf.wind_directions) self.wf.run() powers = self.wf.wt_power.reshape([len(self.wind_directions), len(self.wind_speeds), self.wt_positions.shape[0]]) self.powers = powers for iwt, wt in enumerate(self.wf.wt_layout.wt_list): net_aeps = np.zeros([len(self.wind_directions)]) gross_aeps = np.zeros([len(self.wind_directions)]) # build the cdf vector of the wind speed for each wind rose wind direction sector cdfw = [] for iwd, wd in enumerate(self.wind_roses[iwt].wind_directions): cdfw.append(weibullCDF(self.wind_speeds, self.wind_roses[iwt].A[iwd], self.wind_roses[iwt].k[iwd])) # calculate the probability in each wind direction sector, using the CDF of the wind rose wind direction cdfd0 = [sum(self.wind_roses[iwt].frequency[:i]) for i in range(len(self.wind_roses[iwt].frequency)+1)] wd = np.hstack([self.wind_roses[iwt].wind_directions, [360]]) cdfd1 = interp1d(wd, cdfd0)(self.wind_directions) cwd = 0 for iwd, wd in enumerate(self.wind_directions): # self.wind_directions is not the same as self.wind_roses[iwt].wind_directions, so we have to match both sectors if cwd < len(self.wind_roses[iwt].wind_directions): while wd >= self.wind_roses[iwt].wind_directions[cwd+1] and \ cwd < len(self.wind_roses[iwt].wind_directions)-2: # switching wind rose wind direction sector cwd += 1 # Integrating over the wind speed CDF net_aeps[iwd] = np.trapz(powers[iwd,:,iwt], cdfw[cwd]) * 365.0 * 24.0 for i in range(self.wt_positions.shape[0]): power_curve = interp1d(wt.power_curve[:,0], wt.power_curve[:,1])(self.wind_speeds) gross_aeps[iwd] += np.trapz(power_curve, cdfw[cwd]) * 365.0 * 24.0 # Integrating over the wind direction CDF wt_net_aep[iwt] = np.trapz(net_aeps, cdfd1) wt_gross_aep[iwt] = np.trapz(gross_aeps, cdfd1) net_aep = wt_net_aep.sum() gross_aep = wt_gross_aep.sum() self.capacity_factor = net_aep / gross_aep if self.scaling == 0.0: # The scaling has to be calculated self.scaling = net_aep self.net_aep = net_aep / self.scaling self.gross_aep = gross_aep