diff --git a/bladex/blade.py b/bladex/blade.py index 74f52f5..fac2567 100644 --- a/bladex/blade.py +++ b/bladex/blade.py @@ -84,6 +84,10 @@ class Blade(object): radial section of the blade. :param array_like skew_angles: 1D array, contains the skew angles (in degrees) for each radial section of the blade. + :param array_like thickness: 1D array, contains the value of the + thickness of each section, specified if sections have not the desired thickness. + :param array_like camber: 1D array, contains the value of the + camber of each section, specified if sections have note the desired camber. Note that, each of the previous array_like parameters must be consistent with the other parameters in terms of the radial ordering of the blade @@ -149,7 +153,7 @@ class Blade(object): """ def __init__(self, sections, radii, chord_lengths, pitch, rake, - skew_angles): + skew_angles, thickness=None, camber=None): # Data are given in absolute values self.sections = sections self.n_sections = len(sections) @@ -158,6 +162,8 @@ def __init__(self, sections, radii, chord_lengths, pitch, rake, self.pitch = pitch self.rake = rake self.skew_angles = skew_angles + self.thickness = thickness + self.camber = camber self._check_params() self.conversion_factor = 1000 # to convert units if necessary @@ -328,12 +334,20 @@ def apply_transformations(self, reflect=True): # Translate reference point into origin self.sections[i].translate(-self.sections[i].reference_point) - if reflect: - self.sections[i].reflect() - # Scale the unit chord to actual length. self.sections[i].scale(self.chord_lengths[i]) + # Setting thickness max is required + if self.thickness is not None: + self.sections[i].set_thickness_max(self.thickness[i]) + + # Setting camber max is required + if self.camber is not None and i < self.n_sections-1: + self.sections[i].set_camber_line_max(self.camber[i]) + + if reflect: + self.sections[i].reflect() + # Rotate according to the pitch angle. # Since the current orientation system is not standard (It is # left-handed Cartesian orientation system, where Y-axis points diff --git a/bladex/profile/baseprofile.py b/bladex/profile/baseprofile.py index 1d08637..b307d8d 100644 --- a/bladex/profile/baseprofile.py +++ b/bladex/profile/baseprofile.py @@ -379,9 +379,6 @@ def deform_camber_line(self, percent_change, n_interpolated_points=None): :param float percent_change: percentage of change of the maximum camber. Default value is None - :param bool interpolate: if True, the interpolated coordinates are - used to compute the camber line and foil's thickness, otherwise - the original discrete coordinates are used. Default value is False. :param int n_interpolated_points: number of points to be used for the equally-spaced sample computations. If None then there is no interpolation, unless the arrays x_up != x_down elementwise which @@ -415,6 +412,50 @@ def deform_camber_line(self, percent_change, n_interpolated_points=None): self.yup_coordinates = self.camber_line[1] + half_thickness self.ydown_coordinates = self.camber_line[1] - half_thickness + def set_camber_line_max(self, camber_max): + """ + Deform camber line according to a given maximum value. + The percentage of camber wrt to the x/c coordinate does not change, i.e., + we rescale the camber value by a scalar + Also reconstructs the deformed airfoil's coordinates. + + Thus, the percentage of change is defined as follows: + + .. math:: + \\frac{\\text{new magnitude of max camber - old magnitude of + maximum \ + camber}}{\\text{old magnitude of maximum camber}} * 100 + + A positive percentage means the new camber is larger than the max + camber value, while a negative percentage indicates the new value + is smaller. + + We note that the method works only for airfoils in the reference + position, i.e. chord line lies on the X-axis and the foil is not + rotated, since the measurements are based on the Y-values of the + airfoil coordinates, hence any measurements or scalings will be + inaccurate for the foils not in their reference position. + + :param float camber_max: maximum camber to be set. + """ + old_camber_max = self.max_camber() + percent_scaling_factor = 100*(camber_max - old_camber_max) / (old_camber_max) + # print(percent_scaling_factor) + self.deform_camber_line(percent_scaling_factor) + + + def set_thickness_max(self, thickness_max): + """ + Deform y_up and y_down coordinates to have the desired thickness max value. + To do so, we compute the ratio between olt thickness and new one + + :param float thickness_max: maximum thickness to be set. + """ + old_thickness = self.max_thickness() + ratio_thickness = thickness_max / old_thickness if old_thickness != 0. else 0. + self.yup_coordinates *= ratio_thickness + self.ydown_coordinates *= ratio_thickness + @property def yup_curve(self): """ diff --git a/bladex/profile/customprofile.py b/bladex/profile/customprofile.py index 72285c3..0ea9fbe 100644 --- a/bladex/profile/customprofile.py +++ b/bladex/profile/customprofile.py @@ -74,8 +74,8 @@ def __init__(self, **kwargs): raise RuntimeError( """Input arguments should be the section coordinates (xup, yup, xdown, ydown) or the section parameters - (camber_perc, thickness_perc, - camber_max, thickness_max, chord_perc).""") + (camber_perc, thickness_perc, camber_max, + thickness_max, chord_perc, chord_len).""") def generate_parameters(self, convention='british'): return super().generate_parameters(convention) diff --git a/tests/test_profiles.py b/tests/test_profiles.py index 1b590c8..3fb309b 100644 --- a/tests/test_profiles.py +++ b/tests/test_profiles.py @@ -14,6 +14,25 @@ def create_custom_profile(): return CustomProfile(xup=xup, yup=yup, xdown=xdown, ydown=ydown) +class TestBaseProfileMethods(TestCase): + def test_max_thickness(self): + profile = NacaProfile(digits='2412') + self.assertAlmostEqual(profile.max_thickness(), .12, delta=1e-4) + + def test_set_thickness_max(self): + profile = NacaProfile(digits='2412') + profile.set_thickness_max(.24) + self.assertAlmostEqual(profile.max_thickness(), .24, delta=1e-4) + + def test_max_camber(self): + profile = NacaProfile(digits='3412') + self.assertAlmostEqual(profile.max_camber(), .03, delta=1e-4) + + def test_set_camber_max(self): + profile = NacaProfile(digits='3412') + profile.set_camber_line_max(.05) + self.assertAlmostEqual(profile.max_camber(), .05, delta=1e-4) + class TestCustomProfile(TestCase): def test_inheritance_custom(self): self.assertTrue(issubclass(CustomProfile, ProfileInterface))