This tutorial is intended for developers who wish to implement their own specific turbomachine components. For a Users Guide read Users’ Guide.
This step by step tutorial aims to address the question: How do I implement a an analytical camber line? This question is answered with an examplary simplified NACA 6 series camber line. This chapter is divided into four steps:
The equation for the simplified 6-series camber line is the following:
To build an arbitrary turbomachine component you have the perform these four steps. In the second tutorial we will show you how to build a custom profile.
The BladeDesigner provides a base class for every component. This tutorial covers how to implement a simplified naca 6-series camber line, so we choose an AnalyticalCamberLine class as base class (line 3). It’s located in the bladedesigner.baseclasses package (line 1).
The AnalyticalCamberLine and its base classes provides some basic functionality like:
For more information about the functionality of the AnalyticalCamberLine, CamberLine and Component class see here, here and here.
1 2 3 4 | import bladedesigner.baseclasses as bcls
class N6SCamberLine(bcls.AnalyticalCamberLine):
pass
|
The simplified naca 6-series camber has one parameter the lift coefficient.
But first things first. We have to initialize the base classes (line 7) because the child class overwrites the inherited __init__ method. For that purpose super accesses all parent classes and initializes their attributes via .__init__().
In the previous section we mentioned that the base class provides a method which checks if the user attributes are initialized. We want to check the new user attribute self._lift_coefficient as well. To do this two steps are necessary:
Assign to self._lift_coefficient an Uninitialized instance (line 8).
Uninitialized is a class without logic. We use them to categorize initialized and uninitialized attributes. If you call an Uninitialized instance it returns the name of the attribute. Uninitialized is located in the bladedesigner.foundation package (line 2).
Append ‘lift_coefficient’ to self._properties (line 9).
All attributes contained in self._properties are categorized in initialized or uninitialized attributes. The convention is to append the attribute name without leading underscore. The idea behind the missing underscore is: We want to refer to a property and not a variable. More about properties in Implementation of properties.
1 2 3 4 5 6 7 8 9 | import bladedesigner.baseclasses as bcls
import bladedesigner.foundation as fdn
class N6SCamberLine(bcls.AnalyticalCamberLine):
def __init__(self):
super(N6SCamberLine, self).__init__()
self._lift_coefficient = fdn.Uninitialized('lift_coefficient')
self._properties.append('lift_coefficient')
|
If an attribute changes the state of it’s instance, the attribute must have a getter and a setter method. Why is that? Because we have to do some clean up if the state changes. We are hiding the getter and setter method with a decorator named property [1]. The python documentation recommends properties instead of the direct use of getter and setter methods. The advantage is that attributes with and without getter and setter methods look the same.
The naming convention for properties and attributes are:
Back to our camber line example.
In Initialization of attributes we identified the attribute lift coefficient. It’s a state changing attribute (as opposed to an attribute which does not change the state of the turbomachine, such as a name of an attribute), hence we need a property. In agreement with our naming convention we named the lift coefficient attribute _lift_coefficient and the property lift_coefficient (line 16, 17, 20 and 22)
The only task of the getter method (line 16 and 17) is to return the value of _lift_coefficient (line 18).
The setter method (line 20 and 22) has three main tasks:
checking if the current and new value are the same (line 23)
If so the hole body of the if statement will be skipped.
applying the the new value to _lift_coefficient (line 24)
text
doing some cleaning work (line 25)
text
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import bladedesigner as bd
import bladedesigner.baseclasses as bcls
import bladedesigner.foundation as fdn
rigor = bd.config.get('supervisor', 'rigor')
class N6SCamberLine(bcls.AnalyticalCamberLine):
def __init__(self):
super(N6SCamberLine, self).__init__()
self._lift_coefficient = fdn.Uninitialized('lift_coefficient')
self._properties.append('lift_coefficient')
@property
def lift_coefficient(self):
return self._lift_coefficient
@lift_coefficient.setter
@fdn.requires(lift_coefficient=(int, float), rigor=rigor)
def lift_coefficient(self, lift_coefficient):
if self._lift_coefficient != lift_coefficient:
self._lift_coefficient = lift_coefficient
self.update()
|
Footnotes
[1] | If you’re not familiar with python decorators have a look in PEP 318. For more information about the property decorater click here. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | import bladedesigner as bd
import bladedesigner.baseclasses as bcls
import bladedesigner.foundation as fdn
rigor = bd.config.get('supervisor', 'rigor')
class N6SCamberLine(bcls.AnalyticalCamberLine):
def __init__(self):
super(N6SCamberLine, self).__init__()
self._lift_coefficient = fdn.Uninitialized('lift_coefficient')
self._properties.append('lift_coefficient')
@property
def lift_coefficient(self):
return self._lift_coefficient
@lift_coefficient.setter
@fdn.requires(lift_coefficient=(int, float), rigor=rigor)
def lift_coefficient(self, lift_coefficient):
if self._lift_coefficient != lift_coefficient:
self._lift_coefficient = lift_coefficient
self.update()
@fdn.cached_property
def slopes(self):
self._check_initialization()
self._cached = True
x = self.distribution(self.sample_rate)
c_l = self.lift_coefficient
return -c_l / 4. / np.pi * (np.log(x) - np.log(1 - x))
@fdn.memoized
def as_array(self):
self._check_initialization()
self._cached = True
x = self.distribution(self.sample_rate)
c_l = self.lift_coefficient
y = -c_l / 4. / np.pi * ((1 - x) * np.log(1 - x) + x * np.log(x))
y[0] = y[-1] = 0
return np.reshape(np.append(x, y), (-1, 2), "F")
|