Does anyone have a script to generate an Airfoil ?
Landon Mitchell Kanner
Member, Employee, GitHub-issue-creator Posts: 319
✭✭✭✭
in 3D Design
Has anyone developed a script to generate the Airfoil based on input parameters like Max camber, Camber position etc. and worked on parameterization of the Airfoil? (e.g.: NACA 4-digit airfoil generator)
Tagged:
0
Answers
-
Solution from @Adam Anderson :
#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright Ansys Inc 2024 class NACA_Profile: """ Generate NACA 4-digit profiles (refer to https://en.wikipedia.org/wiki/NACA_airfoil for details) """ def __init__(self, thickness=15, chord_length=1.0, num_pts=50, camber=0, pos_max_camber=30, # Percent values closed_trailing_edge=False): import math self.profile = [(0.0, 0.0)] self.num_pts = num_pts # Percent values self.chord_length = chord_length self.thickness = thickness / 100 self.camber = camber / 100 if camber: self.pos_max_camber = max(min(pos_max_camber,99),1)/100 else: self.pos_max_camber = 0 self.closed_trailing_edge = closed_trailing_edge delta_x = 1.0/(self.num_pts-1) x = 0.0 if self.closed_trailing_edge: coeffs = (0.2969, -0.1260, -0.3516, +0.2843, -0.1036) else: coeffs = (0.2969, -0.1260, -0.3516, +0.2843, -0.1015) for i in range(1,self.num_pts): x += delta_x y = coeffs[0] * math.sqrt(x) y += coeffs[1] * x x2 = x*x y += coeffs[2] * x2 x3 = x2*x y += coeffs[3] * x3 x4 = x3*x y += coeffs[4] * x4 self.profile.append((x, 5*y*self.thickness)) # Add camber self.upper_profile = [(0.0, 0.0)] self.lower_profile = [(0.0, 0.0)] self.camber_line = [(0.0, 0.0)] for x,y in self.profile[1:]: yc, dyc_dx = self.get_camber_and_gradient(x) cost = 1/math.sqrt(1 + dyc_dx*dyc_dx) sint = dyc_dx * cost y_sint = y * sint y_cost = y * cost self.upper_profile.append((x - y_sint, yc + y_cost)) self.lower_profile.append((x + y_sint, yc - y_cost)) self.camber_line.append((x, yc)) def get_camber_and_gradient(self, x): if x < self.pos_max_camber: xc = x mx = self.pos_max_camber mx2 = mx*mx else: xc = 1 - x mx = 1 - self.pos_max_camber mx2 = -mx*mx yc = self.camber*(2*mx*xc - xc*xc)/(mx*mx) dyc_dx = 2*self.camber*(mx-xc)/mx2 return yc, dyc_dx def get_points_upper_profile(self): for x,y in self.upper_profile: yield x*self.chord_length, y*self.chord_length def get_points_lower_profile(self): for x,y in self.lower_profile: yield x*self.chord_length, y*self.chord_length def get_points_te_to_te(self, upper_profile_first=False): # Points from trailing edge to leading edge. profile = self.upper_profile if upper_profile_first else self.lower_profile for x,y in profile[:0:-1]: # Excluding the leading edge point yield x*self.chord_length, y*self.chord_length # Points from leading edge to trailing edge profile = self.lower_profile if upper_profile_first else self.upper_profile for x,y in profile: yield x*self.chord_length, y*self.chord_length def get_camberline_circles(self, limit_circles=False): # Returns x, y of camberline and circle radii to fill profile. import math for (x,y),r in zip(self.camber_line, [p[1] for p in self.profile]): if limit_circles: # r could make circle go outside of chord, so limit r here if it looks better rl = math.sqrt(x*x+y*y) rt = math.sqrt((1-x)*(1-x)+(1-y)*(1-y)) r = min(r,rl,rt) yield x*self.chord_length, y*self.chord_length, r*self.chord_length def get_profile_name(self, basename="NACA"): # Return NACA standard name return "{0}{1:1d}{2:1d}{3:02d}".format(basename, round(self.camber*100), round(self.pos_max_camber*10), round(self.thickness*100)) def write_te_to_te(self, filename, upper_profile_first=False): with open(filename, "w") as f: for x,y in self.get_points_te_to_te(upper_profile_first=upper_profile_first): f.write(f"{x} {y}\n") def write_upper_lower(self, filename): # Leading edge to trailing edge with open(filename, "w") as f: for x,y in self.get_points_upper_profile(): f.write(f"{x} {y}\n") f.write("\n") for x,y in self.get_points_lower_profile(): f.write(f"{x} {y}\n") def write_lower_upper(self, filename): # Leading edge to trailing edge with open(filename, "w") as f: for x,y in self.get_points_lower_profile(): f.write(f"{x} {y}\n") f.write("\n") for x,y in self.get_points_upper_profile(): f.write(f"{x} {y}\n") def write_circles(self, filename, limit_circles=True): with open(filename, "w") as f: # Points on the camber line with profile as radius for x,y,r in self.get_camberline_circles(limit_circles=limit_circles): f.write(f"{x} {y} {r}\n")
0