Source code for openaerostruct.geometry.geometry_multi_join

import numpy as np
import openmdao.api as om

default_vec = np.ones(3)


[docs] def get_section_edge_left(mesh, v=default_vec, edge_cur=0, edges_all_constraints=default_vec): """ Function that gets the coordinates of the leading and trailing edge points of the left edge of a section. The output can be masked to only retreive the x,y, or z coordinate. The function also returns the row and column vectors of non zero entries in the edge coordinate jacobian. Parameters ---------- mesh : numpy array OAS mesh of a given section v : numpy array[3] Numpy array of length three populuted with ones and zeros used to mask the output so that only the specified of the x, y, and z coordinates are returned. edge_cur : int Integer indicating which unique intersection of edges this particular edge is associated with. Required to return the correct jacobian sparsity pattern. edges_all_constraints: list See dim_constr in the GeomMultiJoin component. This array needs to passed into this function from the component in order to return the correct jacobian sparsity pattern. Returns ------- edge points : numpy array Array of points corresponding to the leading and trailing edges of the left section edge. Masked according to input. rows : numpy array Array of the rows of the non-zero jacobian entries cols : numpy array Array of the columns of the non-zero jacobian entries """ nx = mesh.shape[0] le_index = 0 te_index = np.ravel_multi_index((nx - 1, 0, 0), mesh.shape) mask = np.array(v, dtype="bool") rows = np.arange(0, 2 * np.sum(v)) + 2 * int(np.sum(edges_all_constraints[:edge_cur])) cols = np.concatenate([np.arange(le_index, le_index + 3)[mask], np.arange(te_index, te_index + 3)[mask]]) return mesh[[0, -1], 0][:, np.arange(0, 3)[mask]], rows, cols
[docs] def get_section_edge_right(mesh, v=default_vec, edge_cur=0, edges_all_constraints=default_vec): """ Function that gets the coordinates of the leading and trailing edge points of the right edge of a section. The output can be masked to only retreive the x,y, or z coordinate. The function also returns the row and column vectors of non zero entries in the edge coordinate jacobian. Parameters ---------- mesh : numpy array OAS mesh of a given section v : numpy array[3] Numpy array of length three populuted with ones and zeros used to mask the output so that only the specified of the x, y, and z coordinates are returned. edge_cur : int Integer indicating which unique intersection of edges this particular edge is associated with. Required to return the correct jacobian sparsity pattern. edges_all_constraints: list See dim_constr in the GeomMultiJoin component. This array needs to passed into this function from the component in order to return the correct jacobian sparsity pattern. Returns ------- edge points : numpy array Array of points corresponding to the leading and trailing edges of the right section edge. Masked according to input. rows : numpy array Array of the rows of the non-zero jacobian entries cols : numpy array Array of the columns of the non-zero jacobian entries """ nx = mesh.shape[0] ny = mesh.shape[1] le_index = np.ravel_multi_index((0, ny - 1, 0), mesh.shape) te_index = np.ravel_multi_index((nx - 1, ny - 1, 0), mesh.shape) mask = np.array(v, dtype="bool") rows = np.arange(0, 2 * np.sum(v)) + 2 * int(np.sum(edges_all_constraints[:edge_cur])) cols = np.concatenate([np.arange(le_index, le_index + 3)[mask], np.arange(te_index, te_index + 3)[mask]]) return mesh[[0, -1], -1][:, np.arange(0, 3)[mask]], rows, cols
[docs] class GeomMultiJoin(om.ExplicitComponent): """ OpenMDAO component that outputs the distance between the leading and trailing edge corners of each section corresponding to each shared edge. Users can specify along which axes the distance should be computed([x y z]). Parameters ---------- sections : list List of section OpenAeroStruct surface dictionaries dim_constr : list A list of vectors of length three corresponding to each edge. Entries correspond to the dimension([x,y,z]) the user wishes to constraint should be set to 1. Remaining entries should be zero. Returns ------- section_separation[2*count_nonzero(concatenate(dim_constr)] : numpy array Numpy array of distances along each axis between corresponding leading and trailing edge points between subsequent sections. """ def initialize(self): """ Declare options. """ self.options.declare("sections", types=list, desc="A list of section surface dictionaries to be joined.") self.options.declare( "dim_constr", types=list, default=[np.ones(3)], desc="A list of vectors of length three corresponding to each edge. Entries corresponding the dimension([x,y,z]) the user wishes to constraint should be set to 1. Remaining entries should be zero.", ) def setup(self): sections = self.options["sections"] self.num_sections = len(sections) self.dim_constr = self.options["dim_constr"] # Compute total number of unique intersecting edges between each section edge_total = self.num_sections - 1 # Defaults to distane along x-axis for each intersecting edge if len(self.dim_constr) != (edge_total): self.dim_constr = [np.array([1, 0, 0]) for i in range(edge_total)] # Compute size of and add output constr_size = 2 * np.count_nonzero(np.concatenate(self.dim_constr)) self.add_output("section_separation", val=np.zeros(constr_size)) """Generate the Jacobian of the edge seperation distance with respect to the section mesh. Jacobian is just ones, zeros, and negative ones so we can declare here. However the sparsity pattern is complicated and requires two helper functions and the loop below.""" # Counter used to track the current unique edge interection between section being processed. edge_cur = 0 for i_sec, section in enumerate(sections): mesh = section["mesh"] nx = mesh.shape[0] ny = mesh.shape[1] name = section["name"] # Add the input mesh_name = "{}_join_mesh".format(name) self.add_input(mesh_name, shape=(nx, ny, 3), units="m") # Get the sparsity patterns for each section. First and last sections only have one edge intersection. if i_sec == 0: rows, cols = get_section_edge_right(mesh, self.dim_constr[i_sec], edge_cur, self.dim_constr)[1:] vals = -1 * np.ones_like(rows) elif i_sec < len(sections) - 1: rows1, cols1 = get_section_edge_left(mesh, self.dim_constr[i_sec - 1], edge_cur, self.dim_constr)[1:] vals1 = np.ones_like(rows1) edge_cur += 1 rows2, cols2 = get_section_edge_right(mesh, self.dim_constr[i_sec], edge_cur, self.dim_constr)[1:] vals2 = -1 * np.ones_like(rows2) rows = np.concatenate([rows1, rows2]) cols = np.concatenate([cols1, cols2]) vals = np.concatenate([vals1, vals2]) else: rows, cols = get_section_edge_left(mesh, self.dim_constr[i_sec - 1], edge_cur, self.dim_constr)[1:] vals = np.ones_like(rows) # Declare partials for the current section self.declare_partials("section_separation", mesh_name, rows=rows, cols=cols, val=vals) def compute(self, inputs, outputs): # Compute the distances between the corresponding leading and trailing edges along the edge interection between each section sections = self.options["sections"] edges = [] edge_constraints = [] for i_sec, section in enumerate(sections): name = section["name"] mesh_name = "{}_join_mesh".format(name) if i_sec == 0: edges.append(get_section_edge_right(inputs[mesh_name], self.dim_constr[i_sec])[0]) elif i_sec < len(sections) - 1: edges.append(get_section_edge_left(inputs[mesh_name], self.dim_constr[i_sec - 1])[0]) edges.append(get_section_edge_right(inputs[mesh_name], self.dim_constr[i_sec])[0]) else: edges.append(get_section_edge_left(inputs[mesh_name], self.dim_constr[i_sec - 1])[0]) for i in range(self.num_sections - 1): edge_constraints.append((edges[2 * i + 1] - edges[2 * i]).flatten()) outputs["section_separation"] = np.array(edge_constraints).flatten()