Source code for openaerostruct.aerodynamics.mtx_rhs
importnumpyasnpimportopenmdao.apiasom
[docs]classVLMMtxRHSComp(om.ExplicitComponent):""" Compute the total velocities at each of the evaluation points for every panel in the entire system. This is the sum of the freestream and induced velocities caused by the circulations. Parameters ---------- freestream_velocities[system_size, 3] : numpy array The rotated freestream velocities at each evaluation point for all lifting surfaces. system_size is the sum of the count of all panels for all lifting surfaces. vel_mtx[num_eval_points, nx - 1, ny - 1, 3] : numpy array The AIC matrix for the all lifting surfaces representing the aircraft. This has some sparsity pattern, but it is more dense than the FEM matrix and the entries have a wide range of magnitudes. One exists for each combination of surface name and evaluation points name. normals[nx-1, ny-1, 3] : numpy array The normal vector for each panel, computed as the cross of the two diagonals from the mesh points. Returns ------- mtx[system_size, system_size] : numpy array Final fully assembled AIC matrix that is used to solve for the circulations. rhs[system_size] : numpy array Right-hand side of the AIC linear system, constructed from the freestream velocities and panel normals. """definitialize(self):self.options.declare("surfaces",types=list)defsetup(self):surfaces=self.options["surfaces"]system_size=0# Loop through the surfaces to compute the total number of panels;# the system_sizeforsurfaceinsurfaces:mesh=surface["mesh"]nx=mesh.shape[0]ny=mesh.shape[1]system_size+=(nx-1)*(ny-1)self.system_size=system_sizeself.add_input("freestream_velocities",shape=(system_size,3),units="m/s")self.add_output("mtx",shape=(system_size,system_size),units="1/m")self.add_output("rhs",shape=system_size,units="m/s")# Set up indicies arrays for sparse Jacobiansvel_indices=np.arange(system_size*3).reshape((system_size,3))mtx_indices=np.arange(system_size*system_size).reshape((system_size,system_size))rhs_indices=np.arange(system_size)self.declare_partials("rhs","freestream_velocities",rows=np.einsum("i,j->ij",rhs_indices,np.ones(3,int)).flatten(),cols=vel_indices.flatten(),)ind_1=0ind_2=0# Loop through each surface to add inputs and set up derivatives.# We keep track of the surface's indices within the total system's# indices to access the matrix in the correct locations for the derivs.# This is because the AIC linear system has information for all surfaces# together.forsurfaceinsurfaces:mesh=surface["mesh"]nx=mesh.shape[0]ny=mesh.shape[1]name=surface["name"]num=(nx-1)*(ny-1)ind_2+=num# Get the correct names for each vel_mtx and normals, then# add them to the componentvel_mtx_name="{}_{}_vel_mtx".format(name,"coll_pts")normals_name="{}_normals".format(name)self.add_input(vel_mtx_name,shape=(system_size,nx-1,ny-1,3),units="1/m")self.add_input(normals_name,shape=(nx-1,ny-1,3))velocities_indices=np.arange(system_size*num*3).reshape((system_size,nx-1,ny-1,3))normals_indices=np.arange(num*3).reshape((num,3))# Declare each set of partials based on the indices, ind_1 and ind_2self.declare_partials("mtx",vel_mtx_name,rows=np.einsum("ij,k->ijk",mtx_indices[:,ind_1:ind_2],np.ones(3,int)).flatten(),cols=velocities_indices.flatten(),)self.declare_partials("mtx",normals_name,rows=np.einsum("ij,k->ijk",mtx_indices[ind_1:ind_2,:],np.ones(3,int)).flatten(),cols=np.einsum("ik,j->ijk",normals_indices,np.ones(system_size,int)).flatten(),)self.declare_partials("rhs",normals_name,rows=np.outer(rhs_indices[ind_1:ind_2],np.ones(3,int)).flatten(),cols=normals_indices.flatten(),)ind_1+=numself.mtx_n_n_3=np.zeros((system_size,system_size,3))self.normals_n_3=np.zeros((system_size,3))self.set_check_partial_options(wrt="*",method="fd",step=1e-5)defcompute(self,inputs,outputs):surfaces=self.options["surfaces"]system_size=self.system_sizeind_1=0ind_2=0forsurfaceinsurfaces:nx=surface["mesh"].shape[0]ny=surface["mesh"].shape[1]name=surface["name"]num=(nx-1)*(ny-1)ind_2+=numvel_mtx_name="{}_{}_vel_mtx".format(name,"coll_pts")normals_name="{}_normals".format(name)# Construct the full matrix and all of the lifting surfaces# together# TODO: This is not complex-safeself.mtx_n_n_3[:,ind_1:ind_2,:]=inputs[vel_mtx_name].reshape((system_size,num,3))self.normals_n_3[ind_1:ind_2,:]=inputs[normals_name].reshape((num,3))ind_1+=num# Actually obtain the final matrix by multiplying through with the# normals. Also create the rhs based on v dot n.outputs["mtx"]=np.einsum("ijk,ik->ij",self.mtx_n_n_3,self.normals_n_3)outputs["rhs"]=-np.einsum("ij,ij->i",inputs["freestream_velocities"],self.normals_n_3)defcompute_partials(self,inputs,partials):surfaces=self.options["surfaces"]system_size=self.system_sizeind_1=0ind_2=0forsurfaceinsurfaces:nx=surface["mesh"].shape[0]ny=surface["mesh"].shape[1]name=surface["name"]num=(nx-1)*(ny-1)ind_2+=numvel_mtx_name="{}_{}_vel_mtx".format(name,"coll_pts")normals_name="{}_normals".format(name)partials["mtx",vel_mtx_name]=np.einsum("ijk,ik->ijk",np.ones((system_size,num,3)),self.normals_n_3,).flatten()partials["mtx",normals_name]=self.mtx_n_n_3[ind_1:ind_2,:,:].flatten()partials["rhs",normals_name]=-inputs["freestream_velocities"][ind_1:ind_2,:].flatten()ind_1+=numpartials["rhs","freestream_velocities"]=-self.normals_n_3.flatten()