def compute_partials(self, inputs, partials): rho = inputs['rho'] v = inputs['v'] alpha = inputs['alpha'] c_0 = 1.0672181 c_1 = -.19213774e-1 c_2 = .21286289e-3 c_3 = -.10117249e-5 if np.any(v < 0): raise om.AnalysisError('Negative velocity magnitude encountered') sqrt_rho = np.sqrt(rho) q_r = 17700 * sqrt_rho * (.0001 * v)**3.07 q_a = c_0 + c_1 * alpha + c_2 * alpha**2 + c_3 * alpha**3 dqr_drho = 0.5 * q_r / rho dqr_dv = 17700 * sqrt_rho * 0.0001 * 3.07 * (0.0001 * v)**2.07 dqa_dalpha = c_1 + 2 * c_2 * alpha + 3 * c_3 * alpha**2 partials['q', 'rho'] = dqr_drho * q_a partials['q', 'v'] = dqr_dv * q_a partials['q', 'alpha'] = dqa_dalpha * q_r
def solve_nonlinear(self, inputs, outputs): try: if self.options['mode'] == "MN": outputs['Vsonic'], outputs['V'], outputs['area'] = self._compute_outputs_MN(inputs) else: outputs['MN'], outputs['Vsonic'], outputs['V'] = self._compute_outputs_area(inputs) except FloatingPointError: raise om.AnalysisError('Bad values flow states in {}: Ts={}'.format(self.pathname, inputs['Ts']))
def compute(self, inputs, outputs): """ This will error if x is more than 2. """ x = inputs['x'] if x > 2.0: raise om.AnalysisError('Try again.') outputs['y'] = x * x + 2.0
def _solve_nonlinear_brent(self, inputs, outputs, discrete_inputs, discrete_outputs): SOLVE_TOL = 1e-10 DEBUG_PRINT = self.options['debug_print'] # Find brackets for the phi residual bracket_found, phi_1, phi_2 = self._first_bracket( inputs, outputs, self._residuals, discrete_inputs, discrete_outputs) if not np.all(bracket_found): raise om.AnalysisError("CCBlade bracketing failed") # Create a wrapper function compatible with the brentv function. def f(x): outputs['phi'][:, :] = x self.apply_nonlinear(inputs, outputs, self._residuals, discrete_inputs, discrete_outputs) return np.copy(self._residuals['phi']) # Find the root. phi, steps_taken, success = brentv(f, phi_1, phi_2, tolerance=SOLVE_TOL) # Fix up the other outputs. out_names = ('Np', 'Tp', 'a', 'ap', 'u', 'v', 'W', 'cl', 'cd', 'F') for name in out_names: if np.all(np.logical_not(np.isnan(self._residuals[name]))): outputs[name] += self._residuals[name] # Fix up the other residuals. self.apply_nonlinear(inputs, outputs, self._residuals, discrete_inputs, discrete_outputs) if DEBUG_PRINT: res_norm = np.linalg.norm(self._residuals['phi']) print( f"CCBlade brentv steps taken: {steps_taken}, residual norm = {res_norm}" ) if not success: raise om.AnalysisError("CCBlade _solve_nonlinear_brent failed")
def run_and_raise_macro(book, macro, stage): logger.info(f"Running macro {macro} at {stage} stage...") result = run_wrapped_macro(book, macro) logger.info( f"Finished running macro {macro} at {stage} stage with result: {result}" ) if not result.success: raise om.AnalysisError( f'Excel macro "{macro}" executed in "{stage}" stage failed: {result.error}' )
def compute(self, inputs, outputs): """f(x,y) = (x-3)^2 + xy + (y+4)^2 - 3 Optimal solution (minimum): x = 6.6667; y = -7.3333 """ x = inputs['x'] y = inputs['y'] if x < 1.75: raise om.AnalysisError('Try Again.') outputs['f_xy'] = (x - 3.0)**2 + x * y + (y + 4.0)**2 - 3.0
def Thrust(u0, power, A, T, rho, kappa): """ This computes the thrust and induced velocity at the propeller disk. This uses formulas from propeller momentum theory. Parameters ---------- u0 : float Freestream speed normal to the propeller disk power : float Power supplied to the propeller disk A : float Propeller disk area T : float Thrust guess rho : float Air density kappa: float Correction factor for non-uniform inflow and tip effects Returns ------- thrust : float Thrust v_i : float Induced velocity at the propeller disk """ T_old = T + 10. thrust = T # iteration loop to solve for the thrust as a function of power while np.any(np.abs(T_old - thrust) > 1e-10): T_old = thrust # ### FPI (Fixed point iteration) # T_new = power / (u0 + kappa * (-u0/2 + 0.5 * (u0**2 + 2 * thrust / rho / A)**0.5)) # thrust = thrust + (T_new - thrust) * 0.5 # # Newton-Raphson with np.errstate(all='raise'): try: root_term = (u0**2 + 2 * thrust / rho / A)**0.5 R = power - thrust * (u0 + kappa * (-u0 / 2 + 0.5 * root_term)) R_prime = -u0 - kappa * (-u0 / 2 + 0.5 * root_term + 0.5 * thrust / rho / A / root_term) thrust = T_old - R / R_prime except: raise om.AnalysisError('invalid calc in thrust') # the induced velocity (i.e., velocity added at the disk) is v_i = (-u0 / 2 + (u0**2 / 4. + thrust / 2 / rho / A)**0.5) return thrust, v_i
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): try: self.open_and_run(inputs, outputs, discrete_inputs or {}, discrete_outputs or {}) except Exception as exc: if self.timeout_state.reached: raise om.AnalysisError("Timeout reached!") else: raise exc
def apply_nonlinear(self, inputs, outputs, residuals): """ Don't solve; just calculate the residual. """ x = inputs['x'] y = outputs['y'] z = outputs['z'] residuals['y'] = y - x - 2.0 * z residuals['z'] = x * z + z - 4.0 self.counter += 1 if self.counter > 5 and self.counter < 11: raise om.AnalysisError('catch me')
def compute(self, inputs, outputs): rho = inputs['rho'] v = inputs['v'] alpha = inputs['alpha'] c_0 = 1.0672181 c_1 = -0.19213774e-1 c_2 = 0.21286289e-3 c_3 = -0.10117249e-5 if np.any(v < 0): raise om.AnalysisError('Negative velocity magnitude encountered') q_r = 17700.0 * np.sqrt(rho) * (0.0001 * v)**3.07 q_a = c_0 + c_1 * alpha + c_2 * alpha**2 + c_3 * alpha**3 outputs['q'] = q_r * q_a
def compute(self, inputs, outputs): n_stages = self.options['n_stages'] i_row = self.options['i_row'] if i_row % 2 == 0: # even rows are stators T_gas = inputs['Tt_primary'] + self.options['T_safety'] dh = 0 else: # rotor T_gas = .92 * inputs['Tt_primary'] + self.options['T_safety'] dh = inputs['turb_pwr'] / n_stages # only rotors do work if i_row == 0: profile_factor = .3 else: profile_factor = .13 W_primary = inputs['W_primary'] if T_gas < self.options['T_metal']: outputs['W_cool'] = W_cool = 0 phi_prime = 0 else: phi = (T_gas - self.options['T_metal']) / (T_gas - inputs['Tt_cool']) phi_prime = (phi + profile_factor) / (profile_factor + 1.) # print(self.pathname, W_primary, inputs['Tt_primary'], inputs['Tt_cool'], phi_prime) try: outputs['W_cool'] = W_cool = .022 * inputs['x_factor'] * ( 4. / 3.) * W_primary * (phi_prime / (1 - phi_prime))**1.25 except FloatingPointError: raise om.AnalysisError('bad flow values in {}; W: {}'.format( self.pathname, W_primary)) outputs['ht_out'] = (W_primary * inputs['ht_primary'] + W_cool * inputs['ht_cool']) / ( W_primary + W_cool) - dh / W_primary Pt_out = inputs['Pt_out'] Pt_in = inputs['Pt_in'] outputs['Pt_stage'] = Pt_out + (Pt_in - Pt_out) * self.i_stage
def solve_linear(self, d_outputs, d_residuals, mode): super(BadComp, self).solve_linear(d_outputs, d_residuals, mode) raise om.AnalysisError("It's just a scratch.")
def guess_nonlinear(self, inputs, outputs, resids): raise om.AnalysisError("It's just a scratch.")
def solve_nonlinear(self, inputs, outputs): super(BadComp, self).solve_nonlinear(inputs, outputs) raise om.AnalysisError("It's just a scratch.")
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode): super().compute_jacvec_product(inputs, d_inputs, d_outputs, mode) raise om.AnalysisError("It's just a scratch.")
def _solve_nonlinear_bracketing(self, inputs, outputs, discrete_inputs, discrete_outputs): SOLVE_TOL = 1e-10 DEBUG_PRINT = self.options['debug_print'] residuals = self._residuals self.apply_nonlinear(inputs, outputs, residuals, discrete_inputs, discrete_outputs) bracket_found, phi_1, phi_2 = self._first_bracket( inputs, outputs, residuals, discrete_inputs, discrete_outputs) if not np.all(bracket_found): raise om.AnalysisError("CCBlade bracketing failed") # Initialize the residuals. res_1 = np.zeros_like(phi_1) outputs['phi'][:, :] = phi_1 self.apply_nonlinear(inputs, outputs, residuals, discrete_inputs, discrete_outputs) res_1[:, :] = residuals['phi'] res_2 = np.zeros_like(phi_1) outputs['phi'][:, :] = phi_2 self.apply_nonlinear(inputs, outputs, residuals, discrete_inputs, discrete_outputs) res_2[:, :] = residuals['phi'] # now initialize the phi_1 and phi_2 vectors so they represent the correct brackets mask = res_1 > 0. phi_1[mask], phi_2[mask] = phi_2[mask], phi_1[mask] res_1[mask], res_2[mask] = res_2[mask], res_1[mask] steps_taken = 0 success = np.all(np.abs(phi_1 - phi_2) < SOLVE_TOL) while steps_taken < 50 and not success: outputs['phi'][:] = 0.5 * (phi_1 + phi_2) self.apply_nonlinear(inputs, outputs, residuals, discrete_inputs, discrete_outputs) new_res = residuals['phi'] mask_1 = new_res < 0 mask_2 = new_res > 0 phi_1[mask_1] = outputs['phi'][mask_1] res_1[mask_1] = new_res[mask_1] phi_2[mask_2] = outputs['phi'][mask_2] res_2[mask_2] = new_res[mask_2] steps_taken += 1 success = np.all(np.abs(phi_1 - phi_2) < SOLVE_TOL) # only need to do this to get into the ballpark if DEBUG_PRINT: res_norm = np.linalg.norm(new_res) print(f"{steps_taken} solve_nonlinear res_norm: {res_norm}") # Fix up the other outputs. out_names = ('Np', 'Tp', 'a', 'ap', 'u', 'v', 'W', 'cl', 'cd', 'F') for name in out_names: if np.all(np.logical_not(np.isnan(residuals[name]))): outputs[name] += residuals[name] # Fix up the other residuals. self.apply_nonlinear(inputs, outputs, residuals, discrete_inputs, discrete_outputs) if not success: raise om.AnalysisError( "CCBlade _solve_nonlinear_bracketing failed")
def compute(self, inputs, outputs): """ Compute component outputs. Parameters ---------- inputs : `Vector` `Vector` containing inputs. outputs : `Vector` `Vector` containing outputs. """ idx = self.options['index'] gd = self.options['grid_data'] iface_prob = self.options['ode_integration_interface'].prob # Create the vector of initial state values self.initial_state_vec[:] = 0.0 pos = 0 for name, options in self.options['state_options'].items(): size = np.prod(options['shape']) self.initial_state_vec[pos:pos + size] = \ np.ravel(inputs['initial_states:{0}'.format(name)]) pos += size # Setup the control interpolants if self.options['control_options']: t0_seg = inputs['time'][0] tf_seg = inputs['time'][-1] for name, options in self.options['control_options'].items(): ctrl_vals = inputs['controls:{0}'.format(name)] self.options['ode_integration_interface'].setup_interpolant( name, x0=t0_seg, xf=tf_seg, f_j=ctrl_vals) # Setup the polynomial control interpolants if self.options['polynomial_control_options']: t0_phase = inputs['t_initial'] tf_phase = inputs['t_initial'] + inputs['t_duration'] for name, options in self.options[ 'polynomial_control_options'].items(): ctrl_vals = inputs['polynomial_controls:{0}'.format(name)] self.options['ode_integration_interface'].setup_interpolant( name, x0=t0_phase, xf=tf_phase, f_j=ctrl_vals) # Set the values of t_initial and t_duration iface_prob.set_val('t_initial', val=inputs['t_initial'], units=self.options['time_options']['units']) iface_prob.set_val('t_duration', val=inputs['t_duration'], units=self.options['time_options']['units']) # Set the values of the phase parameters if self.options['parameter_options']: for param_name, options in self.options['parameter_options'].items( ): val = inputs['parameters:{0}'.format(param_name)] iface_prob.set_val('parameters:{0}'.format(param_name), val=val, units=options['units']) # Setup the evaluation times. if self.options['output_nodes_per_seg'] is None: # Output nodes given as subset, convert segment tau of nodes to time i1, i2 = gd.subset_segment_indices['all'][idx, :] indices = gd.subset_node_indices['all'][i1:i2] nodes_eval = gd.node_stau[ indices] # evaluation nodes in segment tau space t_initial = inputs['time'][0] t_duration = inputs['time'][-1] - t_initial t_eval = t_initial + 0.5 * (nodes_eval + 1) * t_duration else: # Output nodes given as number, linspace them across the segment t_eval = np.linspace(inputs['time'][0], inputs['time'][-1], self.options['output_nodes_per_seg']) # Perform the integration using solve_ivp sim_options = { key: val for key, val in self.options['simulate_options'].items() } sol = solve_ivp(fun=self.options['ode_integration_interface'], t_span=(inputs['time'][0], inputs['time'][-1]), y0=self.initial_state_vec, t_eval=t_eval, **sim_options) if not sol.success: raise om.AnalysisError( f'solve_ivp failed: {sol.message} Dynamics changing ' f'too dramatically') # Extract the solution pos = 0 for name, options in self.options['state_options'].items(): size = np.prod(options['shape']) outputs['states:{0}'.format(name)] = sol.y[pos:pos + size, :].T pos += size
def apply_linear(self, inputs, outputs, d_inputs, d_outputs, d_residuals, mode): super(BadComp, self).apply_linear(inputs, outputs, d_inputs, d_outputs, d_residuals, mode) raise om.AnalysisError("It's just a scratch.")
def linearize(self, inputs, outputs, partials): super(BadComp, self).linearize(inputs, outputs, partials) raise om.AnalysisError("It's just a scratch.")
def compute(self, inputs, outputs): super().compute(inputs, outputs) raise om.AnalysisError("It's just a scratch.")
def compute_partials(self, inputs, partials): super().compute_partials(inputs, partials) raise om.AnalysisError("It's just a scratch.")
def linearize(self, inputs, outputs, J): mode = self.options['mode'] gamma = inputs['gamma'] Ts = inputs['Ts'] R = inputs['R'] rho = inputs['rho'] W = inputs['W'] ht = inputs['ht'] gamma = inputs['gamma'] if mode == "MN": MN_squared_q2 = inputs['MN']**2/2. else: MN_squared_q2 = outputs['MN']**2/2. RT_q_MW = R*Ts ht_calc = inputs['hs'] + MN_squared_q2 * gamma * RT_q_MW J['Ps', 'ht'] = -ht_calc/ht**2 J['Ps', 'hs'] = 1/ht # Derivatives of outputs part = .5*(gamma*R*Ts)**-.5 J['Vsonic', 'gamma'] = dVs_dgamma = part*R*Ts J['Vsonic', 'R'] = part*gamma*Ts J['Vsonic', 'Ts'] = part*gamma*R J['Vsonic', 'Vsonic'] = -1. # J['V', 'V'] = -1. if mode=="MN": MN = inputs['MN'] Vsonic, V, area = self._compute_outputs_MN(inputs) J['area', 'area'] = -1. J['Ps', 'MN'] = MN*gamma*RT_q_MW/ht J['Ps', 'R'] = Ts*MN_squared_q2*gamma/ht J['Ps', 'gamma'] = RT_q_MW*MN_squared_q2/ht J['Ps', 'Ts'] = MN_squared_q2*gamma*R/ht if MN >= 1e-16: J['area', 'W'] = 1.0/(rho*Vsonic*MN) J['area', 'rho'] = -W/(Vsonic*MN*rho**2) part = -W/(rho*Vsonic**2*MN) * 0.5*(R*gamma*Ts)**-.5 J['area', 'gamma'] = part*R*Ts J['area', 'R'] = part*gamma*Ts J['area', 'Ts'] = part*gamma*R J['area', 'MN'] = -W/rho/Vsonic/MN**2 J['V', 'MN'] = Vsonic J['V', 'Ts'] = MN * J['Vsonic', 'Ts'] J['V', 'R'] = MN * J['Vsonic', 'R'] J['V', 'gamma'] = MN * J['Vsonic', 'gamma'] else: MN, Vsonic, V = self._compute_outputs_area(inputs) area = inputs['area'] J['MN', 'MN'] = -1 dresid_dMN = (MN*gamma*RT_q_MW) try: dMN_dA = -(W/rho/Vsonic/area**2) except FloatingPointError: raise om.AnalysisError('{} Bad value in static calc: rho={}, Vsonic={}, area={}'.format(self.pathname, rho, Vsonic, area)) J['Ps', 'area'] = dMN_dA*dresid_dMN/ht J['V', 'area'] = dMN_dA*Vsonic J['MN', 'area'] = dMN_dA dMN_dVs = -W/(rho*area*Vsonic**2) dVs_dTs = 0.5*gamma*R/Vsonic J['Ps', 'Ts'] = gamma*(MN*dMN_dVs*dVs_dTs*RT_q_MW + MN_squared_q2*R)/ht J['V', 'Ts'] = -(dMN_dVs*dVs_dTs*Vsonic + MN*dVs_dTs) J['MN', 'Ts'] = dMN_dVs*dVs_dTs dVs_dR = 0.5*gamma*Ts/Vsonic J['Ps', 'R'] = -gamma*(MN*RT_q_MW*dMN_dVs*dVs_dR + MN_squared_q2*Ts)/ht J['V', 'R'] = dMN_dVs*dVs_dR*Vsonic + dVs_dR*MN # works out to exactly 0 J['MN', 'R'] = dMN_dVs*dVs_dR dMN_dW = (1/rho/Vsonic/area) J['Ps', 'W'] = dMN_dW*dresid_dMN/ht J['V', 'W'] = dMN_dW*Vsonic J['MN', 'W'] = dMN_dW dMN_dgamma = dMN_dVs*dVs_dgamma J['MN', 'gamma'] = dMN_dgamma J['Ps', 'gamma'] = RT_q_MW/ht*(MN_squared_q2 + gamma*MN*dMN_dgamma) J['V', 'gamma'] = dMN_dgamma*Vsonic + dVs_dgamma*MN dMN_drho = -W/(area*Vsonic*rho**2) J['Ps', 'rho'] = MN*gamma*R*Ts*dMN_drho/ht J['V', 'rho'] = dMN_drho*Vsonic J['MN', 'rho'] = dMN_drho
def compute(self, inputs, outputs, discrete_inputs=None, discrete_outputs=None): idx = self.options['index'] gd = self.options['grid_data'] iface_prob = self.options['ode_integration_interface'].prob # Create the vector of initial state values self.initial_state_vec[:] = 0.0 pos = 0 for name, options in self.options['state_options'].items(): size = np.prod(options['shape']) self.initial_state_vec[pos:pos + size] = \ np.ravel(inputs['initial_states:{0}'.format(name)]) pos += size # Setup the control interpolants if self.options['control_options']: t0_seg = inputs['time'][0] tf_seg = inputs['time'][-1] for name, options in self.options['control_options'].items(): ctrl_vals = inputs['controls:{0}'.format(name)] self.options['ode_integration_interface'].control_interpolants[ name].setup(x0=t0_seg, xf=tf_seg, f_j=ctrl_vals) # Setup the polynomial control interpolants if self.options['polynomial_control_options']: t0_phase = inputs['t_initial'] tf_phase = inputs['t_initial'] + inputs['t_duration'] for name, options in self.options[ 'polynomial_control_options'].items(): ctrl_vals = inputs['polynomial_controls:{0}'.format(name)] self.options[ 'ode_integration_interface'].polynomial_control_interpolants[ name].setup(x0=t0_phase, xf=tf_phase, f_j=ctrl_vals) # Set the values of t_initial and t_duration iface_prob.set_val('t_initial', value=inputs['t_initial'], units=self.options['time_options']['units']) iface_prob.set_val('t_duration', value=inputs['t_duration'], units=self.options['time_options']['units']) # Set the values of the phase design parameters if self.options['design_parameter_options']: for param_name, options in self.options[ 'design_parameter_options'].items(): val = inputs['design_parameters:{0}'.format(param_name)] iface_prob.set_val('design_parameters:{0}'.format(param_name), value=val, units=options['units']) # Set the values of the phase input parameters if self.options['input_parameter_options']: for param_name, options in self.options[ 'input_parameter_options'].items(): iface_prob.set_val( 'input_parameters:{0}'.format(param_name), value=inputs['input_parameters:{0}'.format(param_name)], units=options['units']) # Setup the evaluation times. if self.options['output_nodes_per_seg'] is None: # Output nodes given as subset, convert segment tau of nodes to time i1, i2 = gd.subset_segment_indices['all'][idx, :] indices = gd.subset_node_indices['all'][i1:i2] nodes_eval = gd.node_stau[ indices] # evaluation nodes in segment tau space t_initial = inputs['time'][0] t_duration = inputs['time'][-1] - t_initial t_eval = t_initial + 0.5 * (nodes_eval + 1) * t_duration else: # Output nodes given as number, linspace them across the segment t_eval = np.linspace(inputs['time'][0], inputs['time'][-1], self.options['output_nodes_per_seg']) # Perform the integration using solve_ivp sol = solve_ivp(fun=self.options['ode_integration_interface'], t_span=(inputs['time'][0], inputs['time'][-1]), y0=self.initial_state_vec, method=self.options['method'], atol=self.options['atol'], rtol=self.options['rtol'], t_eval=t_eval) if not sol.success: raise om.AnalysisError('solve_ivp failed', sol.message) # Extract the solution pos = 0 for name, options in self.options['state_options'].items(): size = np.prod(options['shape']) outputs['states:{0}'.format(name)] = sol.y[pos:pos + size, :].T pos += size
def apply_nonlinear(self, inputs, outputs, residuals): super(BadComp, self).apply_nonlinear(inputs, outputs, residuals) raise om.AnalysisError("It's just a scratch.")