Source code for pymead.core.mea

import itertools
import os
import typing

import numpy as np
from pymead.utils.read_write_files import save_data

from pymead.core.transformation import Transformation2D
from pymead.core.airfoil import Airfoil
from pymead.core.pymead_obj import PymeadObj
from pymead.plugins.IGES.curves import BezierIGES
from pymead.plugins.IGES.iges_generator import IGESGenerator


[docs] class MEA(PymeadObj):
[docs] def __init__(self, airfoils: typing.List[Airfoil], name: str or None = None, geo_col=None): self.airfoils = airfoils self.reference_airfoil = self.airfoils[0] super().__init__(sub_container="mea", geo_col=geo_col) # Name the MEA name = "MEA-1" if name is None else name self._name = None self.set_name(name)
def add_airfoil(self, airfoil: Airfoil): self.airfoils.append(airfoil) def remove_airfoil(self, airfoil: Airfoil): self.airfoils.remove(airfoil) def get_coords_list(self, max_airfoil_points: int = None, curvature_exp: float = 2.0): mea_coords_list = [ airfoil.get_coords_selig_format(max_airfoil_points, curvature_exp) for airfoil in self.airfoils] airfoil_order = self.get_airfoil_order() return [mea_coords_list[a_idx] for a_idx in airfoil_order] def get_coords_list_chord_relative(self, max_airfoil_points: int = None, curvature_exp: float = 2.0): coords_list = self.get_coords_list(max_airfoil_points=max_airfoil_points, curvature_exp=curvature_exp) # Get the transformation object chord_length = self.reference_airfoil.measure_chord() transformation_kwargs = dict( tx=[0.0], ty=[0.0], r=[0.0], sx=[1 / chord_length], sy=[1 / chord_length], rotation_units="rad", order="t,s,r" ) transformation = Transformation2D(**transformation_kwargs) transformation_kwargs["length_unit"] = self.geo_col.units.current_length_unit() return [transformation.transform(coords) for coords in coords_list], transformation_kwargs def get_airfoil_order(self) -> np.ndarray: le_y_vals = [] for airfoil in self.airfoils: le_y_vals.append(airfoil.leading_edge.y().value()) airfoil_order = np.argsort(np.array(le_y_vals))[::-1] return airfoil_order def write_mses_blade_file(self, airfoil_sys_name: str, blade_file_dir: str, mea_coords_list: typing.List[np.ndarray] or None = None, grid_bounds: typing.List[float] or None = None, max_airfoil_points: int = None, curvature_exp: float = 2.0) -> (str, typing.List[str]): # Get the MEA coordinates list if not provided mea_coords_list = self.get_coords_list(max_airfoil_points=max_airfoil_points, curvature_exp=curvature_exp) \ if mea_coords_list is None else mea_coords_list # Set the default grid bounds value if grid_bounds is None: grid_bounds = [-5.0, 5.0, -5.0, 5.0] if len(grid_bounds) != 4: raise ValueError("Grid bounds list must have length 4 (x_min, x_max, y_min, y_max)") # Verify that the grid bounds contain all the airfoils for airfoil_idx, airfoil_coords in enumerate(mea_coords_list): min_x = np.min(airfoil_coords[:, 0]) if min_x < grid_bounds[0]: raise ValueError(f"Minimum x-coordinate of airfoil at index {airfoil_idx} ({min_x:.3f}) is less than " f"the x-coordinate of the left grid boundary ({grid_bounds[0]:.3f})") max_x = np.max(airfoil_coords[:, 0]) if max_x > grid_bounds[1]: raise ValueError(f"Maximum x-coordinate of airfoil at index {airfoil_idx} ({max_x:.3f}) is greater than" f" the x-coordinate of the right grid boundary ({grid_bounds[1]:.3f})") min_y = np.min(airfoil_coords[:, 1]) if min_y < grid_bounds[2]: raise ValueError(f"Minimum y-coordinate of airfoil at index {airfoil_idx} ({min_y:.3f}) is less than " f"the y-coordinate of the bottom grid boundary ({grid_bounds[2]:.3f})") max_y = np.max(airfoil_coords[:, 1]) if max_y > grid_bounds[3]: raise ValueError(f"Maximum y-coordinate of airfoil at index {airfoil_idx} ({max_y:.3f}) is greater than" f" the y-coordinate of the top grid boundary ({grid_bounds[3]:.3f})") # Write the header (line 1: airfoil name, line 2: grid bounds values separated by spaces) header = airfoil_sys_name + "\n" + " ".join([str(gb) for gb in grid_bounds]) # Determine the correct ordering for the airfoils. MSES expects airfoils to be ordered from top to bottom airfoil_order = self.get_airfoil_order() # Loop through the airfoils in the correct order mea_coords = None for airfoil_idx in airfoil_order: airfoil_coords = mea_coords_list[airfoil_idx] # Extract the airfoil coordinates for this airfoil if mea_coords is None: mea_coords = airfoil_coords else: mea_coords = np.vstack((mea_coords, np.array([999.0, 999.0]))) # MSES-specific airfoil delimiter mea_coords = np.vstack((mea_coords, airfoil_coords)) # Append this airfoil's coordinates to the mat. # Generate the full file path blade_file_path = os.path.join(blade_file_dir, f"blade.{airfoil_sys_name}") # Save the coordinates to file np.savetxt(blade_file_path, mea_coords, header=header, comments="") # Get the airfoil name order airfoil_name_order = [airfoil.name() for airfoil in [self.airfoils[idx] for idx in airfoil_order]] return blade_file_path, airfoil_name_order
[docs] def write_to_IGES(self, file_name: str): """ Writes the airfoil system to file using the IGES file format. Parameters ========== file_name: str Path to IGES file """ bez_IGES_entities = [ [BezierIGES(np.column_stack((c.P[:, 0], np.zeros(len(c.P)), c.P[:, 1]))) for c in a.curve_list] for a in self.airfoils] entities_flattened = list(itertools.chain.from_iterable(bez_IGES_entities)) iges_generator = IGESGenerator(entities_flattened) iges_generator.generate(file_name)
def get_max_x_extent(self) -> typing.List[float]: coords_list = self.get_coords_list() max_x_list = [max(coords[:, 0]) for coords in coords_list] return max_x_list
[docs] def get_dict_rep(self): return {"airfoils": [a.name() for a in self.airfoils]}