def evaluate(self, state={}, disp=False): """Evaluate the cost model Parameters ---------- state : dict, optional Initial state\n Ex: {'x': [1,2,3], 'y':[3,2,1]}\n The current state is used for unspecified variables disp : bool, optional if True, the time used for the optimization is printed Returns ------- Current cost : float Current state : dict """ tmp_recorder = TopFarmListRecorder() self.driver.add_recorder(tmp_recorder) self.setup() self.update_state(state) t = time.time() self.run_model() if disp: print("Evaluated in\t%.3fs" % (time.time() - t)) self.driver._rec_mgr._recorders.remove(tmp_recorder) return self.cost, copy.deepcopy(self.state)
def test_split_recordid(record_id, record_name, load_case): n, lc = TopFarmListRecorder().split_record_id(record_id) assert n == record_name assert lc == load_case n, lc = split_record_id(record_id) assert n == record_name assert lc == load_case
def test_recordid2filename(record_id, filename, load_case): fn, lc = TopFarmListRecorder().recordid2filename(record_id) assert fn == filename assert lc == load_case fn, lc = recordid2filename(record_id) assert fn == filename assert lc == load_case
def test_TopFarmListRecorder_save(tf_generator, rec_id, sn, fn): def remove_file(): if os.path.isfile(fn + ".pkl"): os.remove(fn + ".pkl") remove_file() tf = tf_generator(record_id=rec_id) _, _, recorder = tf.optimize() recorder.save(sn) npt.assert_array_equal(recorder.get('cost'), TopFarmListRecorder().load(fn).get('cost')) remove_file()
def evaluate_gradients(self, disp=False): """Evaluate the gradients.""" self.setup() t = time.time() rec = TopFarmListRecorder() self.driver.add_recorder(rec) res = self.compute_totals(['aggr_cost'], wrt=[topfarm.x_key, topfarm.y_key], return_format='dict') self.driver._rec_mgr._recorders.remove(rec) if disp: print("Gradients evaluated in\t%.3fs" % (time.time() - t)) return res
def setup(self): if self._setup_status == 0: Problem.setup(self, check=True) if self._setup_status < 2: with warnings.catch_warnings(): warnings.filterwarnings('error') try: if len(self.driver._rec_mgr._recorders) == 0: tmp_recorder = TopFarmListRecorder() self.driver.add_recorder(tmp_recorder) Problem.final_setup(self) else: Problem.final_setup(self) except Warning as w: if str(w).startswith('Inefficient choice of derivative mode'): Problem.setup(self, check=True, mode='fwd') else: raise w finally: try: self.driver._rec_mgr._recorders.remove(tmp_recorder) except Exception: pass
def load_recorder(self): if hasattr(self.cost_comp, 'problem'): self.recorder = NestedTopFarmListRecorder(self.cost_comp, self.record_id) else: self.recorder = TopFarmListRecorder(self.record_id)
def test_ListRecorder(): from openmdao.api import Problem, IndepVarComp from openmdao.test_suite.components.paraboloid import Paraboloid prob = Problem() model = prob.model model.add_subsystem('p1', IndepVarComp('x', 0.), promotes=['*']) model.add_subsystem('p2', IndepVarComp('y', 0.), promotes=['*']) model.add_subsystem('comp', Paraboloid(), promotes=['*']) model.add_design_var('x', lower=-10, upper=10) model.add_design_var('y', lower=-10, upper=10) model.add_objective('f_xy') xyf = [[0.98, 4.30, 74.1844], [2.06, 0.90, 23.7476], [-1.53, 2.92, 60.9397], [-1.25, 7.84, 145.4481]] prob.driver = DOEDriver( ListGenerator([[('x', xy[0]), ('y', xy[1])] for xy in xyf])) recorder = TopFarmListRecorder() recorder._initialize_database() recorder._cleanup_abs2meta() recorder.record_iteration_problem(None, None, None) recorder.record_iteration_system(None, None, None) recorder.record_iteration_solver(None, None, None) recorder.record_viewer_data(None) recorder.record_metadata_solver(None) recorder.record_derivatives_driver(None, None, None) recorder.shutdown() prob.driver.add_recorder(recorder) prob.driver.recording_options['record_desvars'] = True prob.driver.recording_options['includes'] = ['*'] prob.driver.recording_options['record_inputs'] = True prob.setup() prob.run_driver() prob.cleanup() assert recorder.num_cases == 4 npt.assert_array_equal(recorder.get('counter'), range(1, 5)) npt.assert_array_equal(recorder['counter'], range(1, 5)) npt.assert_array_almost_equal(recorder.get(['x', 'y', 'f_xy']), xyf, 4) for xyf, k in zip(xyf[0], ['x', 'y', 'f_xy']): npt.assert_allclose(recorder[k][0], xyf) with pytest.raises(KeyError, match="missing"): recorder.get('missing')
def test_TopFarmListRecorderLoad_Nothing(fn): # No such file with pytest.raises(FileNotFoundError, match=r"No such file '.*'"): TopFarmListRecorder().load(fn) rec = TopFarmListRecorder().load_if_exists(fn) assert rec.num_cases == 0
def test_TopFarmListRecorderLoad_none(load_case): # load case is "none" fn = tfp + 'recordings/COBYLA_10iter:%s' % load_case rec = TopFarmListRecorder().load(fn) assert rec.num_cases == 0
def test_TopFarmListRecorderLoad(load_case, n, cost): fn = tfp + 'recordings/COBYLA_10iter:%s' % load_case rec = TopFarmListRecorder().load(fn) npt.assert_equal(rec.num_cases, n) npt.assert_almost_equal(rec.get('cost')[-1], cost)
class TopFarmProblem(Problem): def __init__(self, design_vars, cost_comp, driver=EasyScipyOptimizeDriver(), constraints=[], plot_comp=NoPlot(), record_id=None, expected_cost=1, ext_vars={}): """Initialize TopFarmProblem Parameters ---------- design_vars : dict or list of key-initial_value-tuples Design variables for the problem.\n Ex: {'x': [1,2,3], 'y':([3,2,1],0,1), 'z':([4,5,6],[4,5,4], [6,7,6])}\n Ex: [('x', [1,2,3]), ('y',([3,2,1],0,1)), ('z',([4,5,6],[4,5,4], [6,7,6]))]\n Ex: zip('xy', pos.T)\n The keys (x, y, z) are the names of the design variable.\n The values are either\n - the initial value or\n - a tuple of (initial value, lower bound, upper bound) cost_comp : ExplicitComponent or TopFarmProblem A cost component in the style of an OpenMDAO v2 ExplicitComponent. Pure python cost functions can be wrapped using ``CostModelComponent`` class in ``topfarm.cost_models.cost_model_wrappers``.\n For nested problems, the cost comp_comp is typically a TopFarmProblem driver : openmdao Driver, optinal Driver used to solve the optimization driver. For an example, see the ``EasyScipyOptimizeDriver`` class in ``topfarm.easy_drivers``. constraints : list of Constraint-objects E.g. XYBoundaryConstraint, SpacingConstraint plot_comp : ExplicitComponent, optional OpenMDAO ExplicitComponent used to plot the state (during optimization). For no plotting, pass in the ``topfarm.plotting.NoPlot`` class. record_id : string "<record_id>:<case>", optional Identifier for the optimization. Allows a user to restart an optimization where it left off.\n record_id can be name (saves as recordings/<name>.pkl), abs or relative path Case can be:\n - "", "latest", "-1": Continue from latest\n - "best": Continue from best case (minimum cost)\n - "0": Start from scratch (initial position)\n - "4": Start from case number 4\n expected_cost : int or float Used to scale the cost. This has influence on some drivers, e.g. SLSQP where it affects the step size ext_vars : dict or list of key-initial_value tuple Used for nested problems to propagate variables from parent problem\n Ex. {'type': [1,2,3]}\n Ex. [('type', [1,2,3])]\n Examples -------- See main() in the bottom of this file """ if mpi.MPI: comm = None else: from openmdao.utils.mpi import FakeComm comm = FakeComm() Problem.__init__(self, comm=comm) if isinstance(cost_comp, TopFarmProblem): cost_comp = cost_comp.as_component() cost_comp.parent = self self.cost_comp = cost_comp if isinstance(driver, list): driver = DOEDriver(ListGenerator(driver)) elif isinstance(driver, DOEGenerator): driver = DOEDriver(generator=driver) self.driver = driver self.plot_comp = plot_comp self.record_id = record_id self.load_recorder() if not isinstance(design_vars, dict): design_vars = dict(design_vars) self.design_vars = design_vars self.indeps = self.model.add_subsystem('indeps', IndepVarComp(), promotes=['*']) for k in [topfarm.x_key, topfarm.y_key, topfarm.type_key]: if k in design_vars: if isinstance(design_vars[k], tuple): self.n_wt = len(design_vars[k][0]) else: self.n_wt = len(design_vars[k]) break else: self.n_wt = 0 for constr in constraints: if self.driver.supports['inequality_constraints']: if isinstance(self.driver, SimpleGADriver): constr.setup_as_penalty(self) else: constr.setup_as_constraint(self) else: constr.setup_as_penalty(self) self.model.constraint_components = [ constr.constraintComponent for constr in constraints ] do = self.driver.options for k, v in design_vars.items(): if isinstance(v, tuple): assert len( v ) == 3, "Design_vars values must be either value or (value, lower, upper)" self.indeps.add_output(k, v[0]) if ('optimizer' in do and do['optimizer'] == 'COBYLA'): ref0 = np.min(v[1]) ref1 = np.max(v[2]) l, u = [lu * (ref1 - ref0) + ref0 for lu in [v[1], v[2]]] kwargs = { 'ref0': ref0, 'ref': ref1, 'lower': l, 'upper': u } else: kwargs = {'lower': v[1], 'upper': v[2]} else: self.indeps.add_output(k, v) kwargs = {} if 'optimizer' in do and do['optimizer'] == 'SLSQP': # Upper and lower disturbs SLSQP when running with constraints. Add limits as constraints self.model.add_constraint(k, kwargs.get('lower', None), kwargs.get('upper', None)) kwargs = { 'lower': np.nan, 'upper': np.nan } # Default +/- sys.float_info.max does not work for SLSQP self.model.add_design_var(k, **kwargs) for k, v in ext_vars.items(): self.indeps.add_output(k, v) self.ext_vars = ext_vars self.model.add_subsystem('cost_comp', cost_comp, promotes=['*']) self.model.add_objective('cost', scaler=1 / abs(expected_cost)) if plot_comp: self.model.add_subsystem('plot_comp', plot_comp, promotes=['*']) plot_comp.problem = self plot_comp.n_wt = self.n_wt self.setup() @property def cost(self): return self['cost'][0] @property def state(self): """Return current state""" self.setup() state = {k: self[k] for k in self.design_vars} state.update({k: self[k] for k in self.ext_vars}) if hasattr(self.cost_comp, 'state'): state.update(self.cost_comp.state) if hasattr(self.cost_comp, 'additional_output'): state.update( {k: self[k] for k, _ in self.cost_comp.additional_output}) return state def state_array(self, keys): self.setup() return np.array([self[k] for k in keys]).T def update_state(self, state): for k, v in state.items(): try: c = self[k] # fail if k not exists v = np.array(v) if hasattr(c, 'shape') and c.shape != v.shape: v = v.reshape(c.shape) self[k] = v except KeyError: pass def load_recorder(self): if hasattr(self.cost_comp, 'problem'): self.recorder = NestedTopFarmListRecorder(self.cost_comp, self.record_id) else: self.recorder = TopFarmListRecorder(self.record_id) def setup(self): if self._setup_status == 0: Problem.setup(self, check=True) if self._setup_status < 2: with warnings.catch_warnings(): warnings.filterwarnings('error') try: if len(self.driver._rec_mgr._recorders) == 0: tmp_recorder = ListRecorder() self.driver.add_recorder(tmp_recorder) Problem.final_setup(self) else: Problem.final_setup(self) except Warning as w: if str(w).startswith( 'Inefficient choice of derivative mode'): Problem.setup(self, check=True, mode='fwd') else: raise w finally: try: self.driver._rec_mgr._recorders.remove(tmp_recorder) except Exception: pass def evaluate(self, state={}, disp=False): """Evaluate the cost model. Parameters ---------- state : dict, optional Initial state\n Ex: {'x': [1,2,3], 'y':[3,2,1]}\n The current state is used for unspecified variables disp : bool, optional if True, the time used for the optimization is printed Returns ------- Current cost : float Current state : dict """ tmp_recorder = ListRecorder() self.driver.add_recorder(tmp_recorder) self.setup() self.update_state(state) t = time.time() self.run_model() if disp: print("Evaluated in\t%.3fs" % (time.time() - t)) self.driver._rec_mgr._recorders.remove(tmp_recorder) return self.cost, copy.deepcopy(self.state) def evaluate_gradients(self, disp=False): """Evaluate the gradients.""" self.setup() t = time.time() rec = ListRecorder() self.driver.add_recorder(rec) res = self.compute_totals(['cost'], wrt=[topfarm.x_key, topfarm.y_key], return_format='dict') self.driver._rec_mgr._recorders.remove(rec) if disp: print("Gradients evaluated in\t%.3fs" % (time.time() - t)) return res def optimize(self, state={}, disp=False): """Run the optimization problem Parameters ---------- state : dict, optional Initial state\n Ex: {'x': [1,2,3], 'y':[3,2,1]}\n The current state is used to unspecified variables disp : bool, optional if True, the time used for the optimization is printed Returns ------- Optimized cost : float state : dict recorder : TopFarmListRecorder or NestedTopFarmListRecorder """ self.load_recorder() self.update_state(state) if self.recorder.num_cases > 0: try: self.update_state({ k: self.recorder[k][-1] for k in self.state.keys() if k not in state }) except (ValueError, KeyError): # Restart optimize with n self.record_id = split_record_id(self.record_id)[0] + ":0" return self.optimize(state, disp) self.driver.add_recorder(self.recorder) self.driver.recording_options['record_desvars'] = True self.driver.recording_options['includes'] = ['*'] self.driver.recording_options['record_inputs'] = True self.setup() t = time.time() self.run_driver() self.cleanup() if disp: print("Optimized in\t%.3fs" % (time.time() - t)) if self.driver._rec_mgr._recorders != []: # in openmdao<2.4 cleanup does not delete recorders self.driver._rec_mgr._recorders.remove(self.recorder) if isinstance(self.driver, DOEDriver) or isinstance( self.driver, SimpleGADriver): costs = self.recorder.get('cost') cases = self.recorder.driver_cases costs = [ cases.get_case(i).outputs['cost'] for i in range(cases.num_cases) ] best_case_index = int(np.argmin(costs)) best_case = cases.get_case(best_case_index) self.evaluate({k: best_case.outputs[k] for k in best_case.outputs}) return self.cost, copy.deepcopy(self.state), self.recorder def check_gradients(self, check_all=False, tol=1e-3): """Check gradient computations""" self.setup() if check_all: comp_name_lst = [ comp.pathname for comp in self.model.system_iter() if comp._has_compute_partials ] else: comp_name_lst = [self.cost_comp.pathname] print("checking %s" % ", ".join(comp_name_lst)) res = self.check_partials(includes=comp_name_lst, compact_print=True) for comp in comp_name_lst: var_pair = [(x, dx) for x, dx in res[comp].keys() if x not in ['cost_comp_eval']] worst = var_pair[np.argmax( [res[comp][k]['rel error'].forward for k in var_pair])] err = res[comp][worst]['rel error'].forward if err > tol: raise Warning( "Mismatch between finite difference derivative of '%s' wrt. '%s' and derivative computed in '%s' is: %f" % (worst[0], worst[1], comp, err)) def as_component(self): return ProblemComponent(self) def get_DOE_list(self): self.setup() assert isinstance( self.driver, DOEDriver ), 'get_DOE_list only applies to DOEDrivers, and the current driver is: %s' % type( self.driver) case_gen = self.driver.options['generator'] return [ c for c in case_gen(self.model.get_design_vars( recurse=True), self.model) ] def get_DOE_array(self): return np.array([[v for _, v in c] for c in self.get_DOE_list()]) @property def turbine_positions(self): return np.array([self[k] for k in [topfarm.x_key, topfarm.y_key]]).T def smart_start(self, XX, YY, ZZ, radius=None): assert XX.shape == YY.shape if len(XX.shape) == 1: XX, YY = np.meshgrid(XX, YY) ZZ_is_func = hasattr(ZZ, '__call__') spacing_comp_lst = [ c for c in self.model.constraint_components if isinstance(c, SpacingComp) ] if len(spacing_comp_lst) == 1: min_spacing = spacing_comp_lst[0].min_spacing else: min_spacing = 0 X, Y = XX.flatten(), YY.flatten() if not ZZ_is_func: Z = ZZ.flatten() else: Z = ZZ for comp in self.model.constraint_components: if isinstance(comp, BoundaryBaseComp): dist = comp.distances(X, Y) if len(dist.shape) == 2: dist = dist.min(1) mask = dist >= 0 X, Y = X[mask], Y[mask] if not ZZ_is_func: Z = Z[mask] x, y = smart_start(X, Y, Z, self.n_wt, min_spacing, radius) self.update_state({topfarm.x_key: x, topfarm.y_key: y}) return x, y