def test_serial_in_parallel(self): prob = om.Problem() model = prob.model model.add_subsystem('p1', om.IndepVarComp('x', 1.0)) parallel = model.add_subsystem('parallel', om.ParallelGroup()) parallel.add_subsystem('c1', om.ExecComp(['y=-2.0*x'])) parallel2 = model.add_subsystem('parallel_copy', om.ParallelGroup()) parallel2.add_subsystem('comp1', om.ExecComp(['y=-2.0*x'])) model.add_subsystem('con', om.ExecComp('y = 3.0*x')) model.connect("p1.x", "parallel.c1.x") model.connect('parallel.c1.y', 'parallel_copy.comp1.x') model.connect('parallel_copy.comp1.y', 'con.x') prob.setup(check=True) msg = ( "The following systems are executed out-of-order:\n" " System 'parallel.c2' executes out-of-order with respect to its source systems ['parallel.c1']\n" ) with assert_no_warning(UserWarning, msg): prob.run_model()
def test_discrete_input_dataframe(self): class OMDataFrame: def __dict__(self): pass class ModCompEx2(ModCompEx): def setup(self): super().setup() self.add_discrete_input('test', OMDataFrame()) prob = om.Problem() model = prob.model indep = model.add_subsystem('indep', om.IndepVarComp(), promotes=['*']) indep.add_discrete_output('x', 11) model.add_subsystem('comp', ModCompEx2(3), promotes=['*']) rec = om.SqliteRecorder('test.db') prob.driver.add_recorder(rec) prob.add_recorder(rec) prob.setup() msg = ( "DeprecationWarning: The truth value of an empty array is ambiguous. Returning" "False, but in future this will result in an error. Use `array.size > 0` to check " "that an array is not empty.") with assert_no_warning(DeprecationWarning, msg): prob.run_model()
def test_nonlinear_solver_bounds_stall_warning(self): prob = om.Problem() prob.model.add_subsystem('comp', om.ExecComp('y=3*x+1'), promotes=['*']) balance = prob.model.add_subsystem('balance', om.BalanceComp(), promotes=['*']) balance.add_balance('x', lower=-.1, upper=10, rhs_val=0, lhs_name='y') newton = prob.model.nonlinear_solver = om.NewtonSolver() newton.options['solve_subsystems'] = True newton.options['stall_limit'] = 5 newton.options['stall_tol'] = 1e-8 newton.options['maxiter'] = 100 newton.options['err_on_non_converge'] = False prob.model.linear_solver = om.DirectSolver() prob.setup() msg = (f"Your model has stalled three times and may be violating the bounds. " f"In the future, turn on print_bound_enforce in your solver options " f"here: \nnonlinear_solver.linesearch.options" f"['print_bound_enforce']=True. " f"\nThe bound(s) being violated now are:\n") with assert_warning(UserWarning, msg): prob.run_model() newton.linesearch.options['print_bound_enforce'] = True prob.setup() with assert_no_warning(UserWarning, msg): prob.run_model()
def test_balance_comp_options_exclude_no_error(self): prob = om.Problem() bal = om.BalanceComp() bal.add_balance('x', val=1.0) prob.model.add_subsystem(name='balance', subsys=bal) recorder = om.SqliteRecorder('cases.sql') prob.model.add_recorder(recorder) prob.model.recording_options['record_inputs'] = True prob.model.recording_options['record_outputs'] = True prob.model.recording_options['record_residuals'] = True bal.recording_options['record_metadata'] = False prob.setup() msg = ( "Trying to record option 'guess_func' which cannot be pickled on system BalanceComp " "(balance). Set 'recordable' to False. Skipping recording options for this system." ) with assert_no_warning(UserWarning, msg): prob.run_model() prob = om.Problem() bal = om.BalanceComp() bal.add_balance('x', val=1.0) prob.model.add_subsystem(name='balance', subsys=bal) recorder = om.SqliteRecorder('cases.sql') prob.model.add_recorder(recorder) bal.recording_options['record_metadata'] = True bal.recording_options['options_excludes'] = ['guess_func'] prob.setup() with assert_no_warning(UserWarning, msg): prob.run_model()
def test_deprecated_option(self): msg = 'Option "test1" is deprecated.' self.dict.declare('test1', deprecation=msg) # test double set with assert_warning(OMDeprecationWarning, msg): self.dict['test1'] = None # Should only generate warning first time with assert_no_warning(OMDeprecationWarning, msg): self.dict['test1'] = None # Also test set and then get msg = 'Option "test2" is deprecated.' self.dict.declare('test2', deprecation=msg) with assert_warning(OMDeprecationWarning, msg): self.dict['test2'] = None # Should only generate warning first time with assert_no_warning(OMDeprecationWarning, msg): option = self.dict['test2']
def test_deprecated_tuple_option(self): msg = 'Option "test1" is deprecated. Use "foo" instead.' self.dict.declare('test1', deprecation=(msg, 'foo')) self.dict.declare('foo') # test double set with assert_warning(OMDeprecationWarning, msg): self.dict['test1'] = 'xyz' # Should only generate warning first time with assert_no_warning(OMDeprecationWarning, msg): self.dict['test1'] = 'zzz' with assert_no_warning(OMDeprecationWarning, msg): option = self.dict['test1'] with assert_no_warning(OMDeprecationWarning): option2 = self.dict['foo'] self.assertEqual(option, option2) # Also test set and then get msg = 'Option "test2" is deprecated. Use "foo2" instead.' self.dict.declare('test2', deprecation=(msg, 'foo2')) self.dict.declare('foo2') with assert_warning(OMDeprecationWarning, msg): self.dict['test2'] = 'abcd' # Should only generate warning first time with assert_no_warning(OMDeprecationWarning, msg): option = self.dict['test2'] with assert_no_warning(OMDeprecationWarning): option2 = self.dict['foo2'] self.assertEqual(option, option2) # test bad alias msg = 'Option "test3" is deprecated. Use "foo3" instead.' self.dict.declare('test3', deprecation=(msg, 'foo3')) with self.assertRaises(KeyError) as context: self.dict['test3'] = 'abcd' expected_msg = "Can't find aliased option 'foo3' for deprecated option 'test3'." self.assertEqual(context.exception.args[0], expected_msg)
def test_comm_warning(self): rank = MPI.COMM_WORLD.rank if MPI is not None else 0 prob = om.Problem(model=SellarDerivatives(nonlinear_solver=om.NewtonSolver())) prob.setup() msg = 'Deprecation warning: In V 3.0, the default Newton solver setup will change ' + \ 'to use the BoundsEnforceLS line search.' if rank == 0: with assert_warning(DeprecationWarning, msg): prob.final_setup() else: with assert_no_warning(DeprecationWarning, msg): prob.final_setup()
def test_balance_comp_record_options(self): prob = om.Problem() bal = om.BalanceComp() bal.add_balance('x', val=1.0) prob.model.add_subsystem(name='balance', subsys=bal) recorder = om.SqliteRecorder('cases.sql') prob.model.add_recorder(recorder) prob.model.recording_options['record_inputs'] = True prob.model.recording_options['record_outputs'] = True prob.model.recording_options['record_residuals'] = True prob.setup() # there should be no warnings wrt unpicklable 'guess_func' option, # since 'recordable' has been set to False with assert_no_warning(UserWarning): prob.run_model()
def test_no_connect_parallel_group(self): prob = om.Problem() model = prob.model traj = model.add_subsystem('traj', om.ParallelGroup()) burn1 = traj.add_subsystem('burn1', om.Group()) burn1.add_subsystem('p1', om.IndepVarComp('x', 1.0)) burn1.add_subsystem('burn_eq1', om.ExecComp(['y=-2.0*x'])) burn1.connect('p1.x', 'burn_eq1.x') burn2 = traj.add_subsystem('burn2', om.Group()) burn2.add_subsystem('p2', om.IndepVarComp('x', 1.0)) burn2.add_subsystem('burn_eq2', om.ExecComp(['y=5.0*x'])) burn2.connect('p2.x', 'burn_eq2.x') testlogger = TestLogger() prob.setup(check=True, mode='fwd', logger=testlogger) msg = "Need to attach NonlinearBlockJac, NewtonSolver, or BroydenSolver to 'parallel' when " \ "connecting components inside parallel groups" with assert_no_warning(UserWarning, msg): prob.run_model()
def test_errors_and_warnings(self): def func(a=7., opt='foo'): if opt == 'foo': x = a * 2.0 else: x = a / 2.0 return x f = omf.wrap(func) with self.assertRaises(Exception) as context: f.declare_option('opt', types=str, valuess=('foo', 'bar'), desc='an opt') self.assertEqual(context.exception.args[0], "In declare_option, metadata names ['valuess'] are not allowed.") with self.assertRaises(Exception) as context: f.declare_partials(of='*', wrrt='*', method='cs') self.assertEqual(context.exception.args[0], "In declare_partials, metadata names ['wrrt'] are not allowed.") with self.assertRaises(Exception) as context: f.declare_coloring(wrrt='*', method='cs') self.assertEqual(context.exception.args[0], "In declare_coloring, metadata names ['wrrt'] are not allowed.") with self.assertRaises(Exception) as context: f.add_input('a', shepe=4, units='m') self.assertEqual(context.exception.args[0], "In add_input, metadata names ['shepe'] are not allowed.") with self.assertRaises(Exception) as context: f.add_input('a', val=4, units='m') self.assertEqual(context.exception.args[0], "In add_input, metadata 'val' has already been added to function for input 'a'.") with self.assertRaises(Exception) as context: f.add_output('x', shepe=4, val=3.) self.assertEqual(context.exception.args[0], "In add_output, metadata names ['shepe'] are not allowed.") with self.assertRaises(Exception) as context: f.defaults(shepe=4, val=3.) self.assertEqual(context.exception.args[0], "In defaults, metadata names ['shepe'] are not allowed.") with self.assertRaises(Exception) as context: f.add_input('aa', shape=4, val=3.) self.assertEqual(context.exception.args[0], "In add_input, 'aa' is not an input to this function.") with self.assertRaises(Exception) as context: f.add_output('a', shape=4, val=3.) self.assertEqual(context.exception.args[0], "In add_output, 'a' already registered as an input.") f.add_output('x', shape=4, val=3.) with self.assertRaises(Exception) as context: f.add_output('x', shape=4, val=3.) self.assertEqual(context.exception.args[0], "In add_output, 'x' already registered as an output.") # multiple returns, but neither gives the name, so raise exception def func2(a): if a < 1.: return a + 1 return None f = omf.wrap(func2) with self.assertRaises(Exception) as context: f._setup() self.assertEqual(context.exception.args[0], "0 output names are specified in the metadata but there are 1 unnamed return values in the function.") # this func defines the same name in both returns, which is ok def func3(a): if a < 1.: bb = a + 1 return bb bb = 8. return bb f = omf.wrap(func3) with assert_no_warning(UserWarning): f._setup() # this func defines the name in one return but not the other, which is ok def func4(a): if a < 1.: bb = a + 1 return bb return a - 1 f = omf.wrap(func4) with assert_no_warning(UserWarning): f._setup() # different numbers of return values, which isn't allowed def func5(a): if a < 1.: b = a + 1 c = a * 2. return b, c return None f = omf.wrap(func5) with self.assertRaises(Exception) as context: f._setup() self.assertEqual(context.exception.args[0], "During AST processing to determine the number and name of return values, the following error occurred: Function has multiple return statements with differing numbers of return values.") # different return value names. not allowed def func6(a): b = a + 1 c = a * 2. x = c if a < 1.: return b, c return b, x f = omf.wrap(func6) with self.assertRaises(Exception) as context: f._setup() self.assertEqual(context.exception.args[0], "During AST processing to determine the number and name of return values, the following error occurred: Function has multiple return statements with different return value names of ['c', 'x'] for return value 1.") def func7(a): b = a + 1 c = a * 2. return b, c f = omf.wrap(func7).add_input('a', val=np.ones(2), shape=(2,2)) with self.assertRaises(Exception) as context: f._setup() self.assertEqual(context.exception.args[0], "Input 'a' value has shape (2,), but shape was specified as (2, 2).")