Package windSimSuite :: Module rotor
[hide private]

Source Code for Module windSimSuite.rotor

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3  # 
  4  # This file is part of MBDyn sim suite. 
  5  # Copyright (C) 2007 André ESPAZE, as part of a Master thesis supervised by 
  6  # Martin O.L.Hansen (DTU) and Nicolas Chauvat (Logilab) 
  7   
  8  # MBDyn sim suite is free software; you can redistribute it and/or modify 
  9  # it under the terms of the GNU General Public License as published by 
 10  # the Free Software Foundation; either version 2 of the License, or 
 11  # (at your option) any later version. 
 12  # 
 13  # This program is distributed in the hope that it will be useful, 
 14  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 15  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 16  # GNU General Public License for more details. 
 17  # 
 18  # You should have received a copy of the GNU General Public License 
 19  # along with this program; if not, write to the Free Software 
 20  # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
 21  # 
 22  """The rotor and hub definitions. 
 23  """ 
 24  import numpy as N 
 25   
 26  from windSimSuite.references import REFERENCE_FRAME, ReferenceFrames 
 27  from windSimSuite.common import BasicObject, ObjectWithNodesAndElems 
 28  from windSimSuite.common import RotationalSpeed, Angle 
 29  from windSimSuite.blade import Blade 
 30   
31 -class Hub(ObjectWithNodesAndElems):
32 """The hub definition, it concerns only the MBDyn part 33 """ 34
35 - def __init__(self, name="hub"):
37 38
39 -class AerodynamicsRotor:
40 """The unsteady BEM calculations applied on the rotor. 41 """ 42
43 - def __init__(self):
44 self.rotational_speed = RotationalSpeed() 45 self.delta = 2.*N.pi / 100. 46 self.wing_angle = Angle(0., "deg") 47 48 self.tangential_force = 0. 49 self.normal_force = 0. 50 self.torque = 0. 51 self.power = 0. 52 53 self.skew_radius_ratio = 0.7 54 self.prandtl_blade_ratio = 0.9 55 56 self.force = {}
57
58 - def set_wing_angle(self, value, unit="deg"):
59 """Set the wing angle for the rotor and recalculate 60 the corresponding reference frame. 61 62 @type value: a float 63 @param value: the wing value 64 65 @type unit: a string 66 @param unit: the 'rad' or 'deg' value""" 67 self.wing_angle[unit] = value 68 for blade in self.blades: 69 blade.set_wing_angle(self.wing_angle, unit)
70
71 - def update_data(self):
72 """Update the values entered by the user on the blades. 73 The wing angle information passes from the rotor to 74 the blades.""" 75 for blade in self.blades: 76 blade.set_wing_angle(self.wing_angle, "rad")
77
78 - def prepend_references(self, refs):
79 """Add the tower and nacelle reference frames 80 for every blade""" 81 for blade in self.blades: 82 blade.references.prepend(refs)
83
84 - def get_deepest_point_angle(self):
85 """Get the angle of the deepest point into the wake, 86 depend of the yaw and tilt angles. 87 Due to the orientation, all the M{x} values are positive. 88 The deepest point into the wake is the one with 89 the maximum M{x} value. 90 """ 91 rotation_values = N.arange(2.*N.pi, -self.delta, -self.delta) + \ 92 self.wing_angle["rad"] 93 blade = self.blades[0] 94 radius_test = N.matrix([ [0.], 95 [0.], 96 [10.] ]) 97 xlist = [] 98 for wing_angle in rotation_values: 99 self.set_wing_angle(wing_angle, "rad") 100 mat = blade.references.get_matrix_from("cone") 101 absolute_vec = mat * radius_test 102 xlist.append(absolute_vec[0][0]) 103 x_deepest_pt = max(xlist) 104 self.deepest_pt_angle = rotation_values[xlist.index(x_deepest_pt)]
105
106 - def set_rotational_speed(self, value, unit_key="rpm"):
107 """Set the rotor rotational speed. 108 109 @type value: a float 110 @param value: the rotational speed value 111 112 @type unit_key: a string 113 @param unit_key: the 'rad/s' or 'rpm' value""" 114 self.rotational_speed[unit_key] = value
115
116 - def set_wing_angle_at_time(self, time):
117 """Find the wing angle from the time and rotational speed value. 118 The time is supposed to be in seconds.""" 119 wing_angle = self.rotational_speed["rad/s"] * time 120 self.set_wing_angle(wing_angle, "rad")
121
122 - def get_prandtl_factor(self, blade, section):
123 """Return the Prandtl correction factor""" 124 #if section.radial_position < self.prandtl_blade_ratio * blade.radius: 125 blade_radius = blade.radius 126 #else: 127 # blade_radius = blade.correction_radius 128 num = self.nb_blades * (blade_radius - section.radial_position) 129 dem = 2. * section.radial_position * N.sin(section.flow_angle["rad"]) 130 factor = num/dem 131 return 2. * N.arccos(N.exp(-factor)) / N.pi
132 #return 1. 133
135 """The same induced velocity is calculated for the the whole wake""" 136 for sid in range(self.blades[0].nb_sections): 137 velocity_sum = N.zeros((3,1)) 138 for blade in self.blades: 139 velocity_sum += blade.sections[sid].induced_velocity["cu"] 140 averaged_velocity = velocity_sum / self.nb_blades 141 for blade in self.blades: 142 blade.sections[sid].induced_velocity["cu"] = \ 143 averaged_velocity.copy()
144
145 - def _find_skew_section_id(self):
146 """Find the section used to compute the skew angle""" 147 blade_test = self.blades[0] 148 radius_list = [] 149 for section in blade_test.sections: 150 radius_list.append(section.radial_position) 151 skew_section_radius = self.skew_radius_ratio * blade_test.radius 152 indices = N.where(N.asarray(radius_list) <= skew_section_radius) 153 low_id = int(indices[0][-1]) 154 high_id = low_id + 1 155 low_dist = skew_section_radius - \ 156 blade_test.sections[low_id].radial_position 157 high_dist = blade_test.sections[high_id].radial_position - \ 158 skew_section_radius 159 if high_dist < low_dist: 160 return high_id 161 else: 162 return low_id
163
164 - def init_aero_unsteady(self):
165 """Init the unsteady BEM code by finding the deepest point 166 into the wake""" 167 self.get_deepest_point_angle() 168 skew_section_id = self._find_skew_section_id() 169 for blade in self.blades: 170 blade.set_skew_section(skew_section_id)
171
172 - def _apply_yaw_model(self):
173 """Apply the yaw model, a restoring couple when the 174 rotor is not facing the wind""" 175 for blade in self.blades: 176 yaw_model_coeff = blade.get_yaw_model_coeff(self) 177 for section in blade.sections: 178 section.induced_velocity["cu"] *= yaw_model_coeff
179
180 - def update_induced_velocity(self):
181 """Update the induced velocity for the whole wake. 182 There are still problems with the yaw model""" 183 self._average_induced_velocity()
184 #self._apply_yaw_model() 185
186 - def get_velocity_correction(self):
187 """Supposed to return a velocity correction in 188 the tower shadow model but not yet used""" 189 lim = N.pi/18. 190 theta = N.fmod(self.wing_angle["rad"], 2*N.pi) 191 if (N.pi/2.-0.35) <= theta < (N.pi-lim): 192 K=1.2/(N.pi/2.-lim) 193 return K*(theta - N.pi/2.) 194 elif (N.pi-lim) <= theta <= (N.pi+lim): 195 return 1 196 elif (N.pi+lim) < theta <= (3*N.pi/2.+0.35): 197 K = -1.2/(N.pi/2.-lim) 198 return K*(theta - 3*N.pi/2.) 199 else: 200 print "It happens?" 201 return 1
202
203 - def get_forces_and_power(self):
204 """Get the forces, tangential and normal, 205 as well as the torque and power on the rotor. 206 Do not return any values but store them as attributes""" 207 self.power = 0. 208 self.torque = 0. 209 for fkey in ["tangential", "normal"]: 210 self.force[fkey] = 0. 211 for blade in self.blades: 212 self.power += blade.power 213 self.torque += blade.torque 214 for fkey in ["tangential", "normal"]: 215 self.force[fkey] += blade.force[fkey] 216 self.tangential_force = self.force["tangential"] 217 self.normal_force = self.force["normal"]
218 219
220 -class Rotor(AerodynamicsRotor, BasicObject):
221 """The rotor manipulated by the user. It gathers the 222 aerodynamics calculations and the MBDyn part. 223 """ 224
225 - def __init__(self, name="rotor"):
226 AerodynamicsRotor.__init__(self) 227 BasicObject.__init__(self, name) 228 229 self.blades = [] 230 self.blade_paras = [] 231 self.nb_blades = 0 232 233 self.hub = None 234 self.hub_para = None 235 self.has_hub = False 236 237 self.rotational_speed_value = 0. 238 self.will_save("tangential_force", 239 "normal_force", 240 "torque", 241 "power", 242 "rotational_speed_value") 243 244 self.own_para_names += ["blade_paras", 245 "has_hub", 246 "hub_para", 247 "rotational_speed"]
248
249 - def set_hub(self, hub):
250 """Set the C{Hub} instance from L{windSimSuite.rotor}""" 251 self.hub = hub 252 self.has_hub = True
253
254 - def add_blade(self, blade):
255 """Add a C{Blade} instance from L{windSimSuite.blade}""" 256 self.blades.append(blade) 257 self.nb_blades += 1
258
259 - def init_unsteady(self):
260 """Init the unsteady BEM code on the rotor, 261 as well as on the blade sections.""" 262 self.init_aero_unsteady() 263 for blade in self.blades: 264 for section in blade.sections: 265 section.init_unsteady()
266
267 - def add_on_simulation(self, simu):
268 """Add the rotor attributes on the MBDyn simulation""" 269 if self.has_hub: 270 self.hub.add_on_simulation(simu) 271 for blade in self.blades: 272 blade.add_on_simulation(simu)
273
274 - def init_results(self):
275 """Init the results for the rotor as well as for the blades""" 276 self.common_init_results() 277 278 for blade in self.blades: 279 blade.init_results()
280
281 - def save(self):
282 """Save the unsteady values""" 283 self.save_results() 284 for blade in self.blades: 285 blade.save()
286
287 - def collect_parameters(self):
288 """Collect the rotor parameters in a dictionary""" 289 if self.has_hub: 290 self.hub.collect_parameters() 291 self.hub_para = self.hub.para 292 for blade in self.blades: 293 blade.collect_parameters() 294 self.blade_paras.append(blade.para) 295 self.collect_own_parameters()
296
297 - def set_parameters(self, para):
298 """Set the rotor parameters from the C{para} dictionary""" 299 self.set_own_parameters(para) 300 if self.has_hub: 301 hub = Hub() 302 hub.set_parameters(self.hub_para) 303 self.set_hub(hub) 304 for blade_para in self.blade_paras: 305 blade = Blade() 306 blade.set_parameters(blade_para) 307 self.add_blade(blade)
308