Source code for openglider.glider.cells

import math
import numpy
from openglider.airfoil import Profile3D
from openglider.glider.ballooning import arsinc
from openglider.vector import norm, normalize, HashedList
from openglider.glider.ribs import Rib
from openglider.utils.cached_property import cached_property


class BasicCell(object):
[docs] def __init__(self, prof1=None, prof2=None, ballooning=None, name="unnamed_cell"): self.prof1 = prof1 or Profile3D() self.prof2 = prof2 or Profile3D() if not ballooning is None: self.ballooning_phi = ballooning # ballooning arcs -> property in cell self._normvectors = None self.name = name def point_basic_cell(self, y=0, ik=0):
[docs] ##round ballooning return self.midrib_basic_cell(y).point(ik) def midrib_basic_cell(self, y, ballooning=True, arc_argument=True):
[docs] if y == 0: # left side return self.prof1 elif y == 1: # right side return self.prof2 else: # somewhere #self._checkxvals() midrib = [] for i in range(len(self.prof1.data)): # Arc -> phi(bal) -> r # oder so... diff = self.prof1[i] - self.prof2[i] if ballooning and self.ballooning_radius[i] > 0.: if arc_argument: d = 0.5 - math.sin(self.ballooning_phi[i] * (0.5 - y)) / math.sin(self.ballooning_phi[i]) h = math.cos(self.ballooning_phi[i] * (1 - 2 * y)) - self.ballooning_cos_phi[i] #h = math.sqrt(1 - (norm(diff) * (0.5 - d) / self._radius[i]) ** 2) #h -= self._cosphi[i] # cosphi2-cosphi else: d = y h = math.sqrt(1 - (norm(diff) * (0.5 - y) / self.ballooning_radius[i]) ** 2) h -= self.ballooning_cos_phi[i] # cosphi2-cosphi else: # Without ballooning d = y h = 0. midrib.append(self.prof1[i] - diff * d + self.normvectors[i] * h * self.ballooning_radius[i]) return Profile3D(midrib) def recalc(self):
[docs] pass # Clear everything #self._normvectors = None #self._calcballooning() @cached_property('prof1', 'prof2') # todo: fix depends (miniribs)
def normvectors(self, j=None):
[docs] prof1 = self.prof1.data prof2 = self.prof2.data p1 = self.prof1.tangents p2 = self.prof2.tangents # cross differenzvektor, tangentialvektor return [normalize(numpy.cross(p1[i] + p2[i], prof1[i] - prof2[i])) for i in range(len(prof1))] # TODO: raise if len not equal, cache @cached_property('ballooning_phi')
def ballooning_cos_phi(self):
[docs] cos_phi = [] for phi in self.ballooning_phi: if round(phi, 5) > 0: cos_phi.append(numpy.cos(phi)) else: cos_phi.append(0) return cos_phi @cached_property('ballooning_phi', 'prof1', 'prof2')
def ballooning_radius(self):
[docs] radius = [] for i, phi in enumerate(self.ballooning_phi): if round(phi, 5) > 0: radius.append(norm(self.prof1.data[i] - self.prof2.data[i]) / (2*numpy.sin(phi))) else: radius.append(0) return radius # Ballooning is considered to be arcs, following two simple rules: # 1: x1 = x*d # 2: x2 = R*normvekt*(cos(phi2)-cos(phi) # 3: norm(d)/r*(1-x) = 2*sin(phi(2)) class Cell(BasicCell):
[docs] #TODO: cosmetics def __init__(self, rib1=None, rib2=None, miniribs=None): self.rib1 = rib1 or Rib() self.rib2 = rib2 or Rib() self.miniribs = miniribs self._yvalues = [] self._cells = [] BasicCell.__init__(self, self.rib1.profile_3d, self.rib2.profile_3d) def recalc(self):
[docs] if not self.rib2.profile_2d.numpoints == self.rib1.profile_2d.numpoints: raise ValueError("Unequal length of Cell-Profiles") xvalues = self.rib1.profile_2d.x_values BasicCell.recalc(self) self.prof1 = self.rib1.profile_3d self.prof2 = self.rib2.profile_3d #Map Ballooning if not self.miniribs: # In case there is no midrib, The Cell represents itself! self._cells = [self] # The cell itself is its cell, clear? self._yvalues = [0, 1] else: self._cells = [] self._yvalues = [0] + [rib.y_value for rib in self.miniribs] + [1] ballooning = [self.rib1.ballooning[x] + self.rib2.ballooning[x] for x in xvalues] miniribs = sorted(self.miniribs, key=lambda rib: rib.y_value) # sort for cell-wide (y) argument. first = self.rib1.profile_3d for minirib in miniribs: big = self.midrib_basic_cell(minirib.y_value, True).data small = self.midrib_basic_cell(minirib.y_value, False).data points = [] for i in range(len(big)): # Calculate Rib fakt = minirib.function(xvalues[i]) # factor ballooned/unb. (0-1) point = small[i] + fakt * (big[i] - small[i]) points.append(point) minirib.data = points second = minirib self._cells.append(BasicCell(first, second, [])) # leave ballooning empty first = second #Last Sub-Cell self._cells.append(BasicCell(first, self.rib2.profile_3d, [])) # Calculate ballooning for each x-value # Hamilton Principle: # http://en.wikipedia.org/wiki/Hamilton%27s_principle # http://en.wikipedia.org/wiki/Hamilton%E2%80%93Jacobi_equation # b' = b # f' = f*(l/l') [f=b/l] for i in range(len(first.data)): bl = ballooning[i] + 1 # b/l -> *l/lnew l = norm(self.rib2.profile_3d.data[i] - self.rib1.profile_3d.data[i]) # L lnew = sum([norm(c.prof1.data[i] - c.prof2.data[i]) for c in self._cells]) # L-NEW for c in self._cells: newval = lnew / l / bl if newval < 1.: c.ballooning_phi.append(arsinc(newval)) # B/L NEW 1 / (bl * l / lnew) else: c.ballooning_phi.append(arsinc(1.)) #raise ValueError("mull") for cell in self._cells: cell.recalc() def point(self, y=0, i=0, k=0):
[docs] return self.midrib(y).point(i, k) def midrib(self, y, ballooning=True, arc_argument=False):
[docs] if len(self._cells) == 1: return self.midrib_basic_cell(y, ballooning=ballooning) if ballooning: i = 0 while self._yvalues[i + 1] < y: i += 1 cell = self._cells[i] y_new = (y - self._yvalues[i]) / (self._yvalues[i + 1] - self._yvalues[i]) return cell.midrib_basic_cell(y_new, arc_argument=arc_argument) else: return self.midrib_basic_cell(y, ballooning=False) @cached_property('rib1.ballooning', 'rib2.ballooning')
def ballooning_phi(self):
[docs] x_values = self.rib1.profile_2d.x_values balloon = [self.rib1.ballooning[i] + self.rib2.ballooning[i] for i in x_values] return HashedList([arsinc(1. / (1+bal)) for bal in balloon]) @property
def ribs(self):
[docs] return [self.rib1, self.rib2] @property
def span(self): # TODO: Maybe use mean length from (1,0), (0,0)
[docs] return norm((self.rib1.pos - self.rib2.pos) * [0, 1, 1]) @property
def area(self):
[docs] p1_1 = self.rib1.align([0, 0, 0]) p1_2 = self.rib1.align([1, 0, 0]) p2_1 = self.rib2.align([0, 0, 0]) p2_2 = self.rib2.align([1, 0, 0]) return 0.5 * (norm(numpy.cross(p1_2 - p1_1, p2_1 - p1_1)) + norm(numpy.cross(p2_2 - p2_1, p2_2 - p1_2))) @property
def aspect_ratio(self):
[docs] return self.span ** 2 / self.area