def compute_jacvec_product(self, inputs, outputs, d_inputs, d_outputs, mode): circ = inputs['circulations'] alpha = inputs['alpha'] * np.pi / 180. rho = inputs['rho'] cosa = np.cos(alpha) sina = np.sin(alpha) # Assemble a different matrix here than the AIC_mtx from above; Note # that the collocation points used here are the midpoints of each # bound vortex filament, not the collocation points from above _assemble_AIC_mtx(self.mtx, inputs, self.surfaces, skip=True) # Compute the induced velocities at the midpoints of the # bound vortex filaments for ind in range(3): self.v[:, ind] = self.mtx[:, :, ind].dot(circ) # Add the freestream velocity to the induced velocity so that # self.v is the total velocity seen at the point self.v[:, 0] += cosa * inputs['v'] self.v[:, 2] += sina * inputs['v'] if mode == 'fwd': circ = inputs['circulations'] alpha = inputs['alpha'] * np.pi / 180. if 'alpha' in d_inputs: alphad = d_inputs['alpha'] * np.pi / 180. else: alphad = 0. if 'circulations' in d_inputs: circ_d = d_inputs['circulations'] else: circ_d = np.zeros(circ.shape) cosa = np.cos(alpha) sina = np.sin(alpha) cosad = -sina * alphad sinad = cosa * alphad rho = inputs['rho'] v = inputs['v'] mtxd = np.zeros(self.mtx.shape) # Actually assemble the AIC matrix _assemble_AIC_mtx_d(mtxd, inputs, d_inputs, self.surfaces, skip=True) vd = np.zeros(self.v.shape) # Compute the induced velocities at the midpoints of the # bound vortex filaments for ind in range(3): vd[:, ind] += mtxd[:, :, ind].dot(circ) vd[:, ind] += self.mtx[:, :, ind].real.dot(circ_d) # Add the freestream velocity to the induced velocity so that # self.v is the total velocity seen at the point if 'v' in d_inputs: v_d = d_inputs['v'] else: v_d = 0. vd[:, 0] += cosa * v_d vd[:, 2] += sina * v_d vd[:, 0] += cosad * v vd[:, 2] += sinad * v if 'rho' in d_inputs: rho_d = d_inputs['rho'] else: rho_d = 0. i = 0 rho = inputs['rho'].real for surface in self.surfaces: name = surface['name'] nx = surface['num_x'] ny = surface['num_y'] num_panels = (nx - 1) * (ny - 1) b_pts = inputs[name + '_b_pts'] if name + '_b_pts' in d_inputs: b_pts_d = d_inputs[name + '_b_pts'] else: b_pts_d = np.zeros(b_pts.shape) self.compute(inputs, outputs) sec_forces = outputs[name + '_sec_forces'].real sec_forces, sec_forcesd = OAS_API.oas_api.forcecalc_d( self.v[i:i + num_panels, :], vd[i:i + num_panels], circ[i:i + num_panels], circ_d[i:i + num_panels], rho, rho_d, b_pts, b_pts_d) d_outputs[name + '_sec_forces'] += sec_forcesd.reshape( (nx - 1, ny - 1, 3), order='F') i += num_panels if mode == 'rev': circ = inputs['circulations'] alpha = inputs['alpha'] * np.pi / 180. cosa = np.cos(alpha) sina = np.sin(alpha) i = 0 rho = inputs['rho'].real v = inputs['v'] vb = np.zeros(self.v.shape) for surface in self.surfaces: name = surface['name'] nx = surface['num_x'] ny = surface['num_y'] num_panels = (nx - 1) * (ny - 1) b_pts = inputs[name + '_b_pts'] sec_forcesb = d_outputs[name + '_sec_forces'].reshape( (num_panels, 3), order='F') v_b, circb, rhob, bptsb, _ = OAS_API.oas_api.forcecalc_b( self.v[i:i + num_panels, :], circ[i:i + num_panels], rho, b_pts, sec_forcesb) if 'circulations' in d_inputs: d_inputs['circulations'][i:i + num_panels] += circb vb[i:i + num_panels] = v_b if 'rho' in d_inputs: d_inputs['rho'] += rhob if name + '_b_pts' in d_inputs: d_inputs[name + '_b_pts'] += bptsb i += num_panels sinab = inputs['v'] * np.sum(vb[:, 2]) if 'v' in d_inputs: d_inputs['v'] += cosa * np.sum( vb[:, 0]) + sina * np.sum(vb[:, 2]) cosab = inputs['v'] * np.sum(vb[:, 0]) ab = np.cos(alpha) * sinab - np.sin(alpha) * cosab if 'alpha' in d_inputs: d_inputs['alpha'] += np.pi * ab / 180. mtxb = np.zeros(self.mtx.shape) circb = np.zeros(circ.shape) for i in range(3): for j in range(self.tot_panels): mtxb[j, :, i] += circ * vb[j, i] circb += self.mtx[j, :, i].real * vb[j, i] if 'circulations' in d_inputs: d_inputs['circulations'] += circb _assemble_AIC_mtx_b(mtxb, inputs, d_inputs, self.surfaces, skip=True)
def compute_partials(self, inputs, partials): circ = inputs['circulations'] alpha = inputs['alpha'] * np.pi / 180. rho = inputs['rho'] cosa = np.cos(alpha) sina = np.sin(alpha) # Assemble a different matrix here than the AIC_mtx from above; Note # that the collocation points used here are the midpoints of each # bound vortex filament, not the collocation points from above _assemble_AIC_mtx(self.mtx, inputs, self.surfaces, skip=True) # Compute the induced velocities at the midpoints of the # bound vortex filaments for ind in range(3): self.v[:, ind] = self.mtx[:, :, ind].dot(circ) # Add the freestream velocity to the induced velocity so that # self.v is the total velocity seen at the point self.v[:, 0] += cosa * inputs['v'] self.v[:, 2] += sina * inputs['v'] not_real_outputs = {} i = 0 for surface in self.surfaces: name = surface['name'] nx = surface['num_x'] ny = surface['num_y'] num_panels = (nx - 1) * (ny - 1) b_pts = inputs[name + '_b_pts'] sec_forces = OAS_API.oas_api.forcecalc( self.v[i:i + num_panels, :], circ[i:i + num_panels], rho, b_pts) # Reshape the forces into the expected form not_real_outputs[name + '_sec_forces'] = sec_forces.reshape( (nx - 1, ny - 1, 3), order='F') i += num_panels for surface in self.surfaces: name = surface['name'] dS = partials[name + '_sec_forces', name + '_cos_sweep'].copy() d_inputs = {} sec_forcesb = np.zeros( (surface['num_x'] - 1, surface['num_y'] - 1, 3)) sweep_angle = inputs[name + '_cos_sweep'] / inputs[name + '_widths'] beta = np.sqrt(1 - inputs['M']**2 * sweep_angle**2) if not compressible: beta[:] = 1. for k, val in enumerate(sec_forcesb.flatten()): for key in inputs: d_inputs[key] = inputs[key].copy() d_inputs[key][:] = 0. sec_forcesb[:] = 0. sec_forcesb = sec_forcesb.flatten() sec_forcesb[k] = 1. sec_forcesb = sec_forcesb.reshape( surface['num_x'] - 1, surface['num_y'] - 1, 3) for i, B in enumerate(beta): sec_forcesb[:, i, :] /= B sec_forcesb = sec_forcesb.reshape((-1, 3), order='F') circ = inputs['circulations'] alpha = inputs['alpha'] * np.pi / 180. cosa = np.cos(alpha) sina = np.sin(alpha) ind = 0 rho = inputs['rho'].real v = inputs['v'] vb = np.zeros(self.v.shape) for surface_ in self.surfaces: name_ = surface_['name'] nx_ = surface_['num_x'] ny_ = surface_['num_y'] num_panels_ = (nx_ - 1) * (ny_ - 1) if name == name_: b_pts = inputs[name_ + '_b_pts'] v_b, circb, rhob, bptsb, _ = OAS_API.oas_api.forcecalc_b( self.v[ind:ind + num_panels_, :], circ[ind:ind + num_panels_], rho, b_pts, sec_forcesb) if 'circulations' in d_inputs: d_inputs['circulations'][ ind:ind + num_panels_] += circb vb[ind:ind + num_panels_] = v_b if 'rho' in d_inputs: d_inputs['rho'] += rhob if name + '_b_pts' in d_inputs: d_inputs[name_ + '_b_pts'] += bptsb ind += num_panels_ sinab = inputs['v'] * np.sum(vb[:, 2]) if 'v' in d_inputs: d_inputs['v'] += cosa * np.sum( vb[:, 0]) + sina * np.sum(vb[:, 2]) cosab = inputs['v'] * np.sum(vb[:, 0]) ab = np.cos(alpha) * sinab - np.sin(alpha) * cosab if 'alpha' in d_inputs: d_inputs['alpha'] += np.pi * ab / 180. mtxb = np.zeros(self.mtx.shape) circb = np.zeros(circ.shape) for i in range(3): for j in range(self.tot_panels): mtxb[j, :, i] += circ * vb[j, i] circb += self.mtx[j, :, i].real * vb[j, i] if 'circulations' in d_inputs: d_inputs['circulations'] += circb _assemble_AIC_mtx_b(mtxb, inputs, d_inputs, self.surfaces, skip=True) for key in d_inputs: partials[name + '_sec_forces', key][k, :] = d_inputs[key].flatten() if compressible: M = inputs['M'] S = inputs[name + '_cos_sweep'] X = not_real_outputs[name + '_sec_forces'] W = inputs[name + '_widths'] nx = surface['num_x'] ny = surface['num_y'] d_M = np.zeros((nx - 1, ny - 1, 3)) d_S = np.zeros((nx - 1, ny - 1, 3)) d_W = np.zeros((nx - 1, ny - 1, 3)) for ix in range(nx - 1): for ind in range(3): d_M[ix, :, ind] = (M * S**2 * X[ix, :, ind]) / ( W**2 * (1 - (M**2 * S**2) / W**2)**1.5) d_S[ix, :, ind] = (M**2 * S * X[ix, :, ind]) / ( W**2 * (1 - (M**2 * S**2) / W**2)**1.5) d_W[ix, :, ind] = (M**2 * S**2 * X[ix, :, ind]) / ( W**3 * (1 - (M**2 * S**2) / W**2)**1.5) partials[name + '_sec_forces', 'M'] = d_M.flatten() ny3 = (ny - 1) * 3 for i in range(ny - 1): for ix in range(nx - 1): for ind in range(3): partials[name + '_sec_forces', name + '_cos_sweep'][3 * i + ix * ny3:3 * (i + 1) + ix * ny3, i][ind] = d_S[ix, i, ind] partials[name + '_sec_forces', name + '_widths'][3 * i + ix * ny3:3 * (i + 1) + ix * ny3, i][ind] = -d_W[ix, i, ind]
def compute_jacvec_product(self, inputs, outputs, d_inputs, d_outputs, mode): if mode == 'fwd': AIC_mtxd = np.zeros(self.AIC_mtx.shape) # Actually assemble the AIC matrix _assemble_AIC_mtx_d(AIC_mtxd, inputs, d_inputs, self.surfaces) # Construct an flattened array with the normals of each surface in order # so we can do the normals with velocities to set up the right-hand-side # of the system. flattened_normals = np.zeros((self.tot_panels, 3)) flattened_normalsd = np.zeros((self.tot_panels, 3)) i = 0 for surface in self.surfaces: name = surface['name'] num_panels = (surface['num_x'] - 1) * (surface['num_y'] - 1) flattened_normals[i:i+num_panels, :] = \ inputs[name + '_normals'].reshape(-1, 3, order='F') if name + '_normals' in d_inputs: flattened_normalsd[i:i+num_panels, :] = \ d_inputs[name + '_normals'].reshape(-1, 3, order='F') else: flattened_normalsd[i:i + num_panels, :] = 0. i += num_panels # Construct a matrix that is the AIC_mtx dotted by the normals at each # collocation point. This is used to compute the circulations self.mtx[:, :] = 0. for ind in range(3): self.mtx[:, :] += (AIC_mtxd[:, :, ind].T * flattened_normals[:, ind]).T self.mtx[:, :] += (self.AIC_mtx[:, :, ind].T * flattened_normalsd[:, ind]).T # Obtain the freestream velocity direction and magnitude by taking # alpha into account alpha = inputs['alpha'][0] * np.pi / 180. if 'alpha' in d_inputs: alphad = d_inputs['alpha'][0] * np.pi / 180. else: alphad = 0. cosa = np.cos(alpha) sina = np.sin(alpha) cosad = -sina * alphad sinad = cosa * alphad freestream_direction = np.array([cosa, 0., sina]) v_inf = inputs['v'][0] * freestream_direction if 'v' in d_inputs: v_infd = d_inputs['v'][0] * freestream_direction else: v_infd = np.zeros((3)) v_infd += inputs['v'][0] * np.array([cosad, 0., sinad]) # Populate the right-hand side of the linear system with the # expected velocities at each collocation point d_outputs['rhs'] = -flattened_normalsd.\ reshape(-1, 3, order='F').dot(v_inf) d_outputs['rhs'] += -flattened_normals.\ reshape(-1, 3, order='F').dot(v_infd) d_outputs['AIC'] = self.mtx if mode == 'rev': # Construct an flattened array with the normals of each surface in order # so we can do the normals with velocities to set up the right-hand-side # of the system. flattened_normals = np.zeros((self.tot_panels, 3)) i = 0 for surface in self.surfaces: name = surface['name'] num_panels = (surface['num_x'] - 1) * (surface['num_y'] - 1) flattened_normals[i:i+num_panels, :] = \ inputs[name + '_normals'].reshape(-1, 3, order='F') i += num_panels AIC_mtxb = np.zeros((self.tot_panels, self.tot_panels, 3)) flattened_normalsb = np.zeros(flattened_normals.shape) for ind in range(3): AIC_mtxb[:, :, ind] = (d_outputs['AIC'].T * flattened_normals[:, ind]).T flattened_normalsb[:, ind] += \ np.sum(self.AIC_mtx[:, :, ind].real * d_outputs['AIC'], axis=1).T # Actually assemble the AIC matrix _assemble_AIC_mtx_b(AIC_mtxb, inputs, d_inputs, self.surfaces) # Obtain the freestream velocity direction and magnitude by taking # alpha into account alpha = inputs['alpha'][0] * np.pi / 180. cosa = np.cos(alpha) sina = np.sin(alpha) arr = np.array([cosa, 0., sina]) v_inf = inputs['v'][0] * arr fn = flattened_normals fnb = np.zeros(fn.shape) rhsb = d_outputs['rhs'] v_infb = 0. for ind in reversed(range(self.tot_panels)): fnb[ind, :] -= v_inf * rhsb[ind] v_infb -= fn[ind, :] * rhsb[ind] if 'v' in d_inputs: d_inputs['v'] += sum(arr * v_infb) arrb = inputs['v'] * v_infb alphab = np.cos(alpha) * arrb[2] alphab -= np.sin(alpha) * arrb[0] alphab *= np.pi / 180. if 'alpha' in d_inputs: d_inputs['alpha'] += alphab i = 0 for surface in self.surfaces: name = surface['name'] nx = surface['num_x'] ny = surface['num_y'] num_panels = (nx - 1) * (ny - 1) if name + '_normals' in d_inputs: d_inputs[name + '_normals'] += \ flattened_normalsb[i:i+num_panels, :].reshape(nx-1, ny-1, 3, order='F') d_inputs[name + '_normals'] += \ fnb[i:i+num_panels, :].reshape(nx-1, ny-1, 3, order='F') i += num_panels