class DistFromTurbines(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') threshold = Float( iotype='in', desc='The threshold value the wind turbines should not be under', unit='m') dist = Array( iotype='out', desc="""The distance between each turbines ndarray([n_wt]).""", unit='m') scaling = Float(1.0, iotype='in', desc='') min_dist = Float(iotype='out', desc='') mean_dist = Float(iotype='out', desc='') def execute(self): n_wt = self.wt_positions.shape[0] if n_wt > 0: dist = wt_dist(self.wt_positions, diag=100000.) #print self.dist non_diag = np.array([ dist[i, range(i) + range(i + 1, n_wt)].min() for i in range(n_wt) ]) if self.scaling == 0.0: self.scaling = non_diag.min() self.dist = np.array([dist[i, :].min() for i in range(n_wt)]) / self.scaling self.min_dist = non_diag.min() / self.scaling self.mean_dist = non_diag.mean() / self.scaling else: self.min_dist = 0. self.mean_dist = 0. self.dist = array([])
class ConfigOpt(AtlasConfiguration): """ Atlas configuration for single point optimization """ # inputs for optimizer Omega_opt = Float(iotype='in', desc='rotor angular velocity') def execute(self): super(ConfigOpt, self).execute() # use optimizer provided value for Omega self.Omega = self.Omega_opt
class ExprComp2(Component): """Evaluates an expression based on the inputs x & y and assigns it to f_xy""" x = Float(iotype='in') y = Float(iotype='in') f_xy = Float(iotype='out') expr = Str('x', iotype='in') def __init__(self, expr='x'): super(ExprComp2, self).__init__() self.runcount = 0 self.expr = expr def execute(self): global exec_order exec_order.append(self.name) x = self.x y = self.y self.f_xy = eval(self.expr) self.runcount += 1
class TurbineEnvironmentVT(VariableTree): vhub = Float(desc='Hub-height velocity') direction = Float(desc='Incident wind direction') density = Float(1.225, desc='air density') viscosity = Float(1.78405e-5, desc='air viscosity') ti = Float(0., desc='Turbulence intensity in percent') inflow_type = Enum('constant', ('constant', 'log', 'powerlaw', 'linear', 'user'), desc='shear type') shear_exp = Float(0., iotype='in', desc='Shear exponent (when applicaple)') kappa = Float(0.4, iotype='in', desc='Von Karman constant') z0 = Float(0.111, iotype='in', desc='Roughness length')
class Paraboloid_shift(Component): """ Evaluates the equation f(x,y) = (1000*x-3)^2 + (1000*x)*(0.01*(y+1000)) + (0.01*(y+1000)+4)^2 - 3 """ # set up interface to the framework # pylint: disable-msg=E1101 x = Float(0.0, iotype='in', desc='The variable x') y = Float(0.0, iotype='in', desc='The variable y') f_xy = Float(iotype='out', desc='F(x,y)') def execute(self): """f(x,y) = (1000*x-3)^2 + (1000*x)*(0.01*(y+1000)) + (0.01*(y+1000)+4)^2 - 3 Optimal solution (minimum): x = 0.0066666666666666671; y = -1733.33333333333337 """ x = self.x y = self.y self.f_xy = (1000 * x - 3)**2 + (1000 * x) * (0.01 * (y + 1000)) + ( 0.01 * (y + 1000) + 4)**2 - 3
class OptRosenSuzukiComponent(Component): """ From the NEWSUMT User's Manual: EXAMPLE 2 - CONSTRAINED ROSEN-SUZUKI FUNCTION. NO GRADIENT INFORMATION. MINIMIZE OBJ = X(1)**2 - 5*X(1) + X(2)**2 - 5*X(2) + 2*X(3)**2 - 21*X(3) + X(4)**2 + 7*X(4) + 50 Subject to: G(1) = X(1)**2 + X(1) + X(2)**2 - X(2) + X(3)**2 + X(3) + X(4)**2 - X(4) - 8 .LE.0 G(2) = X(1)**2 - X(1) + 2*X(2)**2 + X(3)**2 + 2*X(4)**2 - X(4) - 10 .LE.0 G(3) = 2*X(1)**2 + 2*X(1) + X(2)**2 - X(2) + X(3)**2 - X(4) - 5 .LE.0 This problem is solved beginning with an initial X-vector of X = (1.0, 1.0, 1.0, 1.0) The optimum design is known to be OBJ = 6.000 and the corresponding X-vector is X = (0.0, 1.0, 2.0, -1.0) """ x = Array(iotype='in') g = Array([1., 1., 1.], iotype='out') result = Float(iotype='out') def __init__(self): """Initialize""" super(OptRosenSuzukiComponent, self).__init__() # Initial guess self.x = numpy.array([1., 1., 1., 1.], dtype=float) self.result = 0. self.opt_objective = 6. self.opt_design_vars = [0., 1., 2., -1.] def execute(self): """calculate the new objective value""" x = self.x self.result = (x[0]**2 - 5. * x[0] + x[1]**2 - 5. * x[1] + 2. * x[2]**2 - 21. * x[2] + x[3]**2 + 7. * x[3] + 50) self.g[0] = (x[0]**2 + x[0] + x[1]**2 - x[1] + x[2]**2 + x[2] + x[3]**2 - x[3] - 8) self.g[1] = (x[0]**2 - x[0] + 2 * x[1]**2 + x[2]**2 + 2 * x[3]**2 - x[3] - 10) self.g[2] = (2 * x[0]**2 + 2 * x[0] + x[1]**2 - x[1] + x[2]**2 - x[3] - 5)
class Connectable(Component): b_in = Bool(iotype='in') e_in = Enum(values=(1, 2, 3), iotype='in') f_in = Float(iotype='in') i_in = Int(iotype='in') s_in = Str(iotype='in') b_out = Bool(iotype='out') e_out = Enum(values=(1, 2, 3), iotype='out') f_out = Float(iotype='out') i_out = Int(iotype='out') s_out = Str(iotype='out') def execute(self): self.b_out = self.b_in self.e_out = self.e_in self.f_out = self.f_in self.i_out = self.i_in self.s_out = self.s_in
class MainBody(VariableTree): body_name = Str('body') subsystem = Enum('Tower', ('Rotor', 'Nacelle', 'Tower', 'Foundation')) beam_structure = VarTree(BeamStructureVT(), desc='Structural beam properties of the body') geom = VarTree(BeamGeometryVT(), desc='Beam geometry') mass = Float(desc='mass of the body') damping_posdef = Array(np.zeros(6)) concentrated_mass = List(Array())
class AeroelasticHAWTVT(BasicTurbineVT): tilt_angle = Float(units='deg', desc='Rotor tilt angle') cone_angle = Float(units='deg', desc='Rotor cone angle') hub_radius = Float(units='m', desc='Hub radius') blade_length = Float(units='m', desc='blade length') tower_height = Float(units='m', desc='Tower height') towertop_length = Float(units='m', desc='Nacelle Diameter') shaft_length = Float(units='m', desc='Shaft length') airfoildata = VarTree(AirfoilDatasetVT(), desc='Airfoil Aerodynamic characteristics') drivetrain_performance = VarTree(DrivetrainPerformanceVT(), desc='drivetrain performance VT') bodies = List() def add_main_body(self, name, body=None): if body is None: body = MainBody() if 'blade' in name: body.remove('geom') body.add('geom', VarTree(BladePlanformVT())) if 'tower' in name: body.remove('geom') body.add('geom', VarTree(TubularTowerGeometryVT())) self.add(name, VarTree(body)) self.bodies.append(name) return getattr(self, name) def get_main_body(self, name): return getattr(self, name) def remove_main_body(self, name): self.delete(name) def set_machine_type(self, machine_type): self.remove('controls') if machine_type == 'FixedSpeedFixedPitch': self.add('controls', VarTree(FixedSpeedFixedPitch())) if machine_type == 'FixedSpeedVarPitch': self.add('controls', VarTree(FixedSpeedVarPitch())) if machine_type == 'VarSpeedFixedPitch': self.add('controls', VarTree(VarSpeedFixedPitch())) if machine_type == 'VarSpeedVarPitch': self.add('controls', VarTree(VarSpeedVarPitch())) return self.controls
def test_units(self): top = self.top top.c2.add("velocity", Float(3.0, iotype='in', units='inch/s')) top.c1.add("length", Float(9.0, iotype='out', units='inch')) try: top.connect('c1.c', 'c2.velocity') except Exception as err: self.assertEqual( str(err), ": Can't connect 'c1.c' to 'c2.velocity': velocity: units 'ft' are incompatible with assigning units of 'inch/s'" ) else: self.fail("Exception expected") top.c1.a = 1. top.c1.b = 2. top.c1.length = 24. top.connect('c1.length', 'c2.a') top.run() assert_rel_error(self, top.c2.a, 2., 0.0001)
class BaseTCCAggregator_Example(Component): """ Base turbine capital cost aggregator for doing some auxiliary cost calculations needed to get a full wind turbine cost. """ # Outputs turbine_cost = Float( iotype='out', desc='Overall wind turbine capial costs including transportation costs' ) def execute(self): self.turbine_cost = 9000000.0
class FullTowerCostModel_Example(Assembly): """ Full tower cost sub-assembly for bringing together individual tower component cost models. """ # returns cost = Float(iotype='out', units='USD', desc='component cost') def configure(self): configure_full_twcc(self) self.replace('towerCC', TowerComponentCostModel()) self.replace('twrcc', FullTowerCostAggregator_Example())
class BladeVT(VariableTree): length = Float(desc='blade length') mass = Float(desc='blade mass') I_x = Float(desc='first area moment of inertia') I_y = Float(desc='Second area moment of inertia') root_chord = Float(desc='Blade root chord') max_chord = Float(desc='Blade maximum chord') tip_chord = Float(desc='Blade tip chord') airfoils = List(desc='List of airfoil names used on blade')
class TubeStructural(Component): """Place holder for real structural calculations to size the tube wall Thickness""" #Inputs Ps_tube = Float(99, iotype="in", desc="static pressure in the tube", units="Pa") radius_inner = Float(300, iotype="in", units="cm", desc="inner radius of tube") #Outputs radius_outer = Float(300.6, iotype="out", units="cm", desc="outer radius of tube") def execute(self): thickness = self.radius_inner * THICKNESS_RATIO self.radius_outer = self.radius_inner + thickness
class Fan(Component): hub_to_tip = Float(.4, iotype="in", desc="hub to tip ratio for the fan") flow_area = Float(.4, iotype="in", units="cm**2", desc="required flow area for the fan") tip_radius = Float(.4, iotype="out", units="cm", desc="tip radius for the fan") hub_radius = Float(.4, iotype="out", units="cm", desc="hub radius for the fan") def execute(self): self.tip_radius = (self.flow_area / (pi) * 1 / (1 - self.hub_to_tip**2))**.5 self.hub_radius = self.hub_to_tip * self.tip_radius
class ExtendedFinancialAnalysis(Assembly): """ Extended financial analysis assembly for coupling models to get a full wind plant cost of energy estimate as well as provides a detailed cost breakdown for the plant. """ # Inputs turbine_number = Int(iotype='in', desc='number of turbines at plant') #Outputs turbine_cost = Float(iotype='out', desc='A Wind Turbine Capital _cost') bos_costs = Float(iotype='out', desc='A Wind Plant Balance of Station _cost Model') avg_annual_opex = Float(iotype='out', desc='A Wind Plant Operations Expenditures Model') net_aep = Float(iotype='out', desc='A Wind Plant Annual Energy Production Model', units='kW*h') coe = Float(iotype='out', desc='Levelized cost of energy for the wind plant') opex_breakdown = VarTree(OPEXVarTree(), iotype='out') bos_breakdown = VarTree(BOSVarTree(), iotype='out', desc='BOS cost breakdown')
class PassengerCapsule(Component): """Place holder component for passenger capsule sizing and structural analysis. Currently, just assume the baseline shape from the original proposal""" #Inputs n_rows = Int(14, iotype="in", desc="number of rows of seats in the pod") length_row = Float(150, iotype="in", units="cm", desc="length of each row of seats") #Outputs length_capsule = Float(iotype="out", units="cm", desc="overall length of the passenger capsule") area_cross_section = Float( iotype="out", units="cm**2", desc="cross sectional area of the passenger capsule") def execute(self): self.length_capsule = 1.1 * self.n_rows * self.length_row #10% fudge factor self.area_cross_section = 14000 # page 15 of the original proposal
class BaseAEPModel(Assembly): """ Most basic AEP class which only provides key AEP outputs - flexible for use with any energy production model """ # Outputs gross_aep = Float( 0.0, iotype='out', units='kW*h', desc= 'Gross Annual Energy Production before availability and loss impacts') net_aep = Float( 0.0, 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')
class FullRotorCostModel(Assembly): """ Full rotor cost sub-assembly for aggregating rotor component costs. """ # parameters blade_number = Int(iotype='in', desc='number of rotor blades') # Outputs cost = Float( iotype='out', desc= 'Overall wind sub-assembly capial costs including transportation costs' )
class Comp_Module(NastranComponent): """ Model of a composite model """ def mass(op2): return op2.grid_point_weight.mass[0] weight = Float(0., nastran_func=mass, iotype='out', units='lb', desc='Weight of the structure') def execute(self): super(Comp_Module, self).execute()
class Discipline1(Component): """Component containing Discipline 1""" # pylint: disable-msg=E1101 z1 = Float(0.0, iotype='in', desc='Global Design Variable') z2 = Float(0.0, iotype='in', desc='Global Design Variable') x1 = Float(0.0, iotype='in', desc='Local Design Variable') y2 = Float(0.0, iotype='in', desc='Disciplinary Coupling') y1 = Float(iotype='out', desc='Output of this Discipline') def execute(self): """Evaluates the equation y1 = z1**2 + z2 + x1 - 0.2*y2""" z1 = self.z1 z2 = self.z2 x1 = self.x1 y2 = self.y2 self.y1 = z1**2 + z2 + x1 - 0.2 * y2
class Pump(Component): """Calculate the power requirement for a water pump given flow conditions""" Pt_out = Float(1000, iotype="in", units="kPa", desc="Pump output pressure") Pt_in = Float(100, iotype="in", units="kPa", desc="Pump input pressure") Tt = Float(288, iotype="in", units="K", desc="water temperature at the pump inlet") W = Float(.5, iotype="in", units="kg/s", desc="liquid flow rate") eff = Float(.8, iotype="in", desc="") pwr_req = Float(iotype="out", units="kW", desc="power required to drive the pump") def __init__(self): super(Pump, self).__init__() _temps = [ 273.15, 277.15, 283.15, 293.15, 303.15, 313.15, 323.15, 333.15, 343.15, 353.15, 363.15, 373.15 ] #degrees K _rhos = [ 999.8, 1000, 999.7, 998.2, 995.7, 992.2, 988.1, 983.2, 977.8, 971.8, 965.3, 958.4 ] #kg/m**3 self._rho = interp1d(_temps, _rhos) def execute(self): _rho = self._rho(self.Tt) self.pwr_req = self.W / _rho * (self.Pt_out - self.Pt_in)
class Battery(Component): #Inputs time_mission = Float(2100, iotype="in", units="s", desc="pod travel time") area_cross_section = Float(1.3, iotype="in", units="m**2", desc="available cross section for battery pack") energy = Float(iotype="in", units="kW*h", desc="total energy storage requirements") #Outputs mass = Float(iotype="out", units="kg", desc="total mass of the batteries") volume = Float(iotype="out", units="m**3", desc="total volume of the batteries") length = Float(iotype="out", units="m", desc="required length of battery pack") def execute(self): #gathered from http://en.wikipedia.org/wiki/Lithium-ion_battery specific_energy = .182 #.100-.265 kW*h/kg energy_density = 494 #250-739 kW*h/m**3 self.mass = self.energy / specific_energy self.volume = self.energy / energy_density self.length = self.volume / self.area_cross_section
class Discipline2_WithDerivatives(ComponentWithDerivatives): """Component containing Discipline 2.""" # pylint: disable-msg=E1101 z1 = Float(0.0, iotype='in', desc='Global Design Variable.') z2 = Float(0.0, iotype='in', desc='Global Design Variable.') y1 = Float(1.0, iotype='in', desc='Disciplinary Coupling.') y2 = Float(iotype='out', desc='Output of this Discipline.') def __init__(self): super(Discipline2_WithDerivatives, self).__init__() self.derivatives.declare_first_derivative('y2', 'z1') self.derivatives.declare_first_derivative('y2', 'z2') self.derivatives.declare_first_derivative('y2', 'y1') def calculate_first_derivatives(self): """Analytical first derivatives""" self.derivatives.set_first_derivative('y2', 'z1', 1.0) self.derivatives.set_first_derivative('y2', 'z2', 1.0) # Derivative blows up around y1=0, and is imaginary for y1<0 # y1 should be kept above 0. self.derivatives.set_first_derivative('y2', 'y1', .5 * (abs(self.y1))**-0.5) def execute(self): """Evaluates the equation y2 = y1**(.5) + z1 + z2.""" z1 = self.z1 z2 = self.z2 # Note: this may cause some issues. However, y1 is constrained to be # above 3.16, so lets just let it converge, and the optimizer will # throw it out y1 = abs(self.y1) self.y2 = y1**(.5) + z1 + z2
class FullNacelleCostAggregator(Component): """ Full nacelle cost aggregator to aggregate costs of individual nacelle components. """ # variables lss_cost = Float(iotype='in', units='USD', desc='component cost') bearings_cost = Float(iotype='in', units='USD', desc='component cost') gearbox_cost = Float(iotype='in', units='USD', desc='component cost') hss_cost = Float(iotype='in', units='USD', desc='component cost') generator_cost = Float(iotype='in', units='USD', desc='component cost') bedplate_cost = Float(iotype='in', units='USD', desc='component cost') yaw_system_cost = Float(iotype='in', units='USD', desc='component cost') # returns cost = Float(iotype='out', units='USD', desc='component cost')
class PrescribedLoad(VariableTree): y = Float(9.9999, desc='Point load location') pointZ = Float(0.15*9.8, desc='N') pointM = Float(0, desc='Nm') distributedX = Float(0, desc='N/m') distributedZ = Float(0, desc='N/m') distributedM = Float(0, desc='Nm/m')
class DrivenComponent(Component): """ Just something to be driven and compute results. """ x0 = Float(1., iotype='in') y0 = Float(1., iotype='in') # used just to get ParameterGroup x1 = Float(1., iotype='in') x2 = Float(1., iotype='in') x3 = Float(1., iotype='in') err_event = Event() stop_exec = Bool(False, iotype='in') rosen_suzuki = Float(0., iotype='out') def __init__(self): super(DrivenComponent, self).__init__() self._raise_err = False def _err_event_fired(self): self._raise_err = True def execute(self): """ Compute results from input vector. """ self.rosen_suzuki = rosen_suzuki(self.x0, self.x1, self.x2, self.x3) if self._raise_err: self.raise_exception('Forced error', RuntimeError) if self.stop_exec: self.parent.driver.stop() # Only valid if sequential!
class AEPWindRose(Assembly): """Base class to calculate Annual Energy Production (AEP) of a wind farm. Implement the same interface as `BaseAEPModel` """ wf = InterfaceSlot(GenericWindFarm, desc='A wind farm assembly or component') postprocess_wind_rose = InterfaceSlot( GenericPostProcessWindRose, desc='The component taking care of postprocessing the wind rose') case_gen = InterfaceSlot(GenericWindRoseCaseGenerator, desc='Generate the cases from the inputs') # 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]') # 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')
class SplitterBPR(CycleComponent): """Takes a single incoming air stream and splits it into two separate ones based on a given bypass ratio""" BPR = Float(2.0, iotype="in", desc="ratio of mass flow in Fl_O2 to Fl_O1") MNexit1_des = Float(.4, iotype="in", desc="mach number at the design condition for Fl_O1") MNexit2_des = Float(.4, iotype="in", desc="mach number at the design condition for Fl_O2") BPR_des = Float(iotype="out", desc="bypass ratio of the splitter at the design condition") Fl_I = FlowStationVar(iotype="in", desc="incoming air stream to splitter", copy=None) Fl_O1 = FlowStationVar(iotype="out", desc="outgoing air stream 1", copy=None) Fl_O2 = FlowStationVar(iotype="out", desc="outgoing air stream 2", copy=None) def execute(self): Fl_I = self.Fl_I Fl_O1 = self.Fl_O1 Fl_O2 = self.Fl_O2 Fl_O1.W = Fl_I.W/(self.BPR+1) Fl_O2.W = Fl_O1.W*self.BPR Fl_O1.setTotalTP(Fl_I.Tt, Fl_I.Pt) Fl_O2.setTotalTP(Fl_I.Tt, Fl_I.Pt) if self.run_design: Fl_O1.Mach = self.MNexit1_des Fl_O2.Mach = self.MNexit2_des self._exit_area_1_des = Fl_O1.area self._exit_area_2_des = Fl_O2.area self.BPR_des = self.BPR else: Fl_O1.area = self._exit_area_1_des Fl_O2.area = self._exit_area_2_des
class QuadSparProperties(SparProperties): """ subclass of SparProperties for the QuadCopter-specific spars (needed to dynamically create yN and nCap and provide scalar I/O) """ # inputs dQuad = Float(iotype='in', desc='') thetaQuad = Float(iotype='in', desc='') nTubeQuad = Int(iotype='in', desc='number of tube layers') lBiscuitQuad = Float(iotype='in', desc='') RQuad = Float( iotype='in', desc='distance from centre of helicopter to centre of quad rotors') hQuad = Float(iotype='in', desc='height of quad-rotor truss') # outputs mQuad = Float(iotype='out', desc='mass of Quad spar (scalar') def execute(self): lQuad = sqrt(self.RQuad**2 + self.hQuad**2) self.yN = np.array([0, lQuad]) self.nCap = np.array([0, 0]) self.d = [self.dQuad] self.theta = [self.thetaQuad] self.nTube = [self.nTubeQuad] self.lBiscuit = [self.lBiscuitQuad] super(QuadSparProperties, self).execute() self.mQuad = self.mSpar[0]