def read_partials_file(self, file, partials): # type: (Union[str, etree._ElementTree], Vector) -> None """Read the partials from a given XML file and store them in this `Component`'s variables. Parameters ---------- file : str or :obj:`etree._ElementTree` Path to or :obj:`etree._ElementTree` of a partials XML file. partials : Vector Partials vector of this `Component`. """ output_rename_map = self.output_rename_map _partials = Partials(file) for of, wrts in _partials.get_partials().items(): for wrt, val in wrts.items(): of = xpath_to_param(of) if of in output_rename_map: of = output_rename_map[of][0] wrt = xpath_to_param(wrt) if (of, wrt) in partials: try: partials[of, wrt] = val except Exception as e: print(e.message)
def setup(self): for name, value in self.inputs_from_xml.items(): if (not isinstance(value, float) and not isinstance(value, np.ndarray)) or \ (isinstance(value, float) and np.isnan(value)) or \ (isinstance(value, np.ndarray) and any(np.isnan(value))): self.add_discrete_input(name, value) else: self.add_input(name, value) for name, value in self.outputs_from_xml.items(): if (not isinstance(value, float) and not isinstance(value, np.ndarray)) or \ (isinstance(value, float) and np.isnan(value)) or \ (isinstance(value, np.ndarray) and any(np.isnan(value))): self.add_discrete_output(name, value) else: # Use the value stored in the input.xml as a reference value if isinstance(value, np.ndarray): ref = value.mean() else: ref = value if ref == 0.: ref = 1. self.add_output(name, value, ref=ref) if self.partials_from_xml: for of, wrt in self.partials_from_xml.items(): if of is not None and wrt is not None: self.declare_partials( xpath_to_param(of), [xpath_to_param(_wrt) for _wrt in wrt.keys()]) else: self.declare_partials('*', '*', method='fd', step_calc='rel')
def setup(self): for name, value in self.inputs_from_xml.items(): if not isinstance(value, float) and not isinstance( value, np.ndarray): # TODO: pass_by_obj # raise NotImplementedError('pass-by-object variables are not yet supported by OpenMDAO 2.0') pass else: self.add_input(name, value) for name, value in self.outputs_from_xml.items(): if not isinstance(value, float) and not isinstance( value, np.ndarray): # TODO: pass_by_obj # raise NotImplementedError('pass-by-object variables are not yet supported by OpenMDAO 2.0') pass else: # Use the value stored in the input.xml as a reference value if isinstance(value, np.ndarray): ref = value.mean() else: ref = value if ref == 0.: ref = 1. self.add_output(name, value, ref=ref) if self.partials_from_xml: for of, wrt in self.partials_from_xml.items(): if of is not None and wrt is not None: self.declare_partials( xpath_to_param(of), [xpath_to_param(_wrt) for _wrt in wrt.keys()]) else: self.declare_partials('*', '*', method='fd', step_calc='rel')
def postprocess_experiments(self, vector, vector_name, failed_experiments=(None, None)): # type: (np.array, str, Optional(Tuple)) -> np.array """ Postprocess experiments from a DOE to remove failed experiments from the vector. Parameters ---------- vector : vector with experiment results vector_name : name of vector failed_experiments : failed experiments of other vectors (to speed up) Returns ------- tuple with post-processed vector and failed experiments Raises ------ AssertionError : if vector name is not found in sample lists """ # Determine whether it concerns input or output sample lists if vector_name in [ xpath_to_param(xpath) for xpath in self.doe_sample_lists['inputs'] ]: # Assert that the failed_experiments are known, or else throw an error if failed_experiments[0] is None: raise IOError( 'For DOE input sample lists the failed experiments need to be known ' 'before postprocessing.') return np.delete(vector, list(failed_experiments[0])), failed_experiments elif vector_name in [ xpath_to_param(xpath) for xpath in self.doe_sample_lists['outputs'] ]: # Determine the failed experiments in the vector vector_failures = set(np.where(np.isnan(vector))[0]) # Add or compare failed experiments w.r.t. failed_experiments input if failed_experiments[0] is None: failed_experiments = (vector_failures, len(vector_failures) / len(vector)) else: if not vector_failures == failed_experiments[0]: raise AssertionError( 'The failed experiments of {} are not consistent in the ' 'training data.'.format(vector_name)) return np.array(list(filter(lambda x: not np.isnan(x), vector))), failed_experiments else: raise AssertionError( 'Could not determine the vector type for vector_name: {}.'. format(vector_name))
def sm_of_training_params(self): # type: () -> Dict[str] """Mapping between training parameters and surrogate model UIDs.""" _sm_mapping = {} for sm_uid, xpaths in self.sm_training_inputs.items(): for xpath in xpaths: _sm_mapping[xpath_to_param(xpath)] = sm_uid for sm_uid, xpaths in self.sm_training_outputs.items(): for xpath in xpaths: _sm_mapping[xpath_to_param(xpath)] = sm_uid return _sm_mapping
def setup(self): has_cont_input = False input_names = set() discrete_input_names = set() for name, value in self.inputs_from_xml.items(): if not is_float(value): self.add_discrete_input(name, value) discrete_input_names.add(name) else: self.add_input(name, value) input_names.add(name) has_cont_input = True has_cont_output = False output_rename_map = self.output_rename_map discrete_output_rename_map = self.discrete_output_rename_map for name, value in self.outputs_from_xml.items(): if not is_float(value): # Rename output variable if in conflict with input if name in discrete_output_rename_map: name = discrete_output_rename_map[name] self.add_discrete_output(name, value) else: # Use the value stored in the input.xml as a reference value if isinstance(value, np.ndarray): ref = value.mean() else: ref = value if ref == 0.: ref = 1. # Rename output variable if in conflict with input if name in output_rename_map: name = output_rename_map[name][0] self.add_output(name, value, ref=ref) has_cont_output = True # Only declare partials if we have at least one continuous input and output parameter if has_cont_input and has_cont_output: if self.partials_from_xml: for of, wrt in self.partials_from_xml.items(): if of is not None and wrt is not None: out_param = xpath_to_param(of) if out_param in output_rename_map: out_param = output_rename_map[out_param][0] self.declare_partials( out_param, [xpath_to_param(_wrt) for _wrt in wrt.keys()]) else: self.declare_partials('*', '*', method='fd', step_calc='rel')
def read_outputs_file(self, file, outputs, discrete_outputs=None): # type: (Union[str, etree._ElementTree], Vector, Optional[dict]) -> None """Read the outputs from a given XML file and store them in this `Component`'s variables. Parameters ---------- file : str or :obj:`etree._ElementTree` Path to or :obj:`etree._ElementTree` of an output XML file. outputs : Vector Output vector of this `Component`. discrete_outputs : dict Discrete (i.e. not treated as floats) outputs. """ output_rename_map = self.output_rename_map discrete_output_rename_map = self.discrete_output_rename_map # Extract the results from the output xml for xpath, value in xml_to_dict(file).items(): name = xpath_to_param(xpath) if name in self.outputs_from_xml: # Rename output if name in output_rename_map: name = output_rename_map[name][0] elif name in discrete_output_rename_map: name = discrete_output_rename_map[name][0] if name in outputs: outputs[name] = value elif discrete_outputs is not None and name in discrete_outputs: discrete_outputs[name] = value
def initialize_from_xml(self, xml): # type: (Union[str, _ElementTree]) -> None """Initialize the problem with initial values from an XML file. Parameters ---------- xml : str or :obj:`etree._ElementTree` Path to an XML file or an instance of `etree._ElementTree` representing it. """ self.initialize() for xpath, value in xml_to_dict(xml).items(): name = xpath_to_param(xpath) if name in self.model._var_allprocs_prom2abs_list['input'] or \ name in self.model._var_allprocs_prom2abs_list['output']: self[name] = value if name in self.model.mapped_parameters_inv: for mapping in self.model.mapped_parameters_inv[name]: if mapping in self.model._var_allprocs_prom2abs_list['input'] or \ mapping in self.model._var_allprocs_prom2abs_list['output']: try: self[mapping] = value except RuntimeError as e: if 'The promoted name' in e[0] and 'is invalid' in e[0]: warnings.warn('Could not automatically set this invalid promoted name from the XML: ' '{}.'.format(mapping)) else: raise RuntimeError(e)
def coupling_vars(self): # type: () -> Dict[str, Dict[str, str]] """:obj:`dict`: Dictionary with coupling variables.""" coupling_vars = dict() # First create a map between related param and coupling copy var for var in self.elem_arch_elems.iter('couplingCopyVariable'): related_param = var.find('relatedParameterUID').text coupling_vars.update({xpath_to_param(related_param): xpath_to_param(var.attrib['uID'])}) # Then update dict with corresponding consitency constraint var for convar in self.elem_arch_elems.iter('consistencyConstraintVariable'): param = xpath_to_param(convar.find('relatedParameterUID').text) if param not in coupling_vars: raise RuntimeError('invalid cmdows file') coupling_vars.update({param: {'copy': coupling_vars[param], 'con': xpath_to_param(convar.attrib['uID'])}}) return coupling_vars
def objective(self): # type: () -> str """:obj:`str`: Name of the objective variable.""" objvars = self.elem_params.find('objectiveVariables') if objvars is None: raise Exception('cmdows does not contain (valid) objective variables') if len(objvars) > 1: raise Exception('cmdows contains multiple objectives, but this is not supported') return xpath_to_param(objvars[0].find('parameterUID').text)
def initialize_from_xml(self, xml): # type: (Union[str, _ElementTree]) -> None """Initialize the problem with initial values from an XML file. Parameters ---------- xml : str or :obj:`etree._ElementTree` Path to an XML file or an instance of `etree._ElementTree` representing it. """ self.initialize() for xpath, value in xml_to_dict(xml).items(): name = xpath_to_param(xpath) prom2abs_list_inputs = self.model._var_allprocs_prom2abs_list[ 'input'] prom2abs_list_outputs = self.model._var_allprocs_prom2abs_list[ 'output'] if self.comm.size > 1: # Small workaround for issue in OpenMDAO with mpirun if name in prom2abs_list_inputs: for abs_name in prom2abs_list_inputs[name]: self[abs_name] = value if name in prom2abs_list_outputs: for abs_name in prom2abs_list_outputs[name]: self[abs_name] = value else: if name in prom2abs_list_inputs or name in prom2abs_list_outputs: self[name] = value if name in self.model.mapped_parameters_inv: for mapping in self.model.mapped_parameters_inv[name]: if mapping in prom2abs_list_inputs or mapping in prom2abs_list_outputs: try: # Small workaround for issue in OpenMDAO with mpirun if self.comm.size > 1: abs_names = [] if mapping in prom2abs_list_inputs: abs_names.extend([ abs_name for abs_name in prom2abs_list_inputs[mapping] ]) if mapping in prom2abs_list_outputs: abs_names.extend([ abs_name for abs_name in prom2abs_list_outputs[mapping] ]) for abs_name in abs_names: self[abs_name] = value else: self[mapping] = value except RuntimeError as e: if 'The promoted name' in e[ 0] and 'is invalid' in e[0]: warnings.warn( 'Could not automatically set this invalid promoted ' 'name from the XML: {}.'.format(mapping)) else: raise RuntimeError(e)
def system_inputs(self): # type: () -> Dict[str, int] """:obj:`dict`: Dictionary containing the system input sizes by their names.""" system_inputs = {} for value in self.elem_cmdows.xpath( r'workflow/dataGraph/edges/edge[fromExecutableBlockUID="Coordinator"]/toParameterUID/text()'): if 'architectureNodes' not in value or 'designVariables' in value: name = xpath_to_param(value) system_inputs.update({name: self.variable_sizes[name]}) return system_inputs
def set_outputs_from_xml(self, output_xml): # type: (Union[str, etree._ElementTree]) -> None """Set outputs to the `Component` based on an output XML template file. Parameter names correspond to their XML elements' full XPaths, converted to valid ``OpenMDAO`` names using the `xpath_to_param()` method. Parameters ---------- output_xml : str or :obj:`etree._ElementTree` Path to or an `etree._ElementTree` of an output XML file. """ self.outputs_from_xml.clear() for xpath, value in xml_to_dict(output_xml).items(): name = xpath_to_param(xpath) self.outputs_from_xml.update({name: value})
def initialize_from_xml(self, xml): # type: (Union[str, _ElementTree]) -> None """Initialize the problem with initial values from an XML file. This function can only be called after the problem's setup method has been called. Parameters ---------- xml : str or :obj:`etree._ElementTree` Path to an XML file or an instance of `etree._ElementTree` representing it. """ for xpath, value in xml_to_dict(xml).items(): name = xpath_to_param(xpath) if name in self._outputs: self._outputs[name] = value elif name in self._inputs: self._inputs[name] = value
def read_outputs_file(self, file, outputs): # type: (Union[str, etree._ElementTree], Vector) -> None """Read the outputs from a given XML file and store them in this `Component`'s variables. Parameters ---------- file : str or :obj:`etree._ElementTree` Path to or :obj:`etree._ElementTree` of an output XML file. outputs : Vector Output vector of this `Component`. """ # Extract the results from the output xml for xpath, value in xml_to_dict(file).items(): name = xpath_to_param(xpath) if name in self.outputs_from_xml and name in outputs: outputs[name] = value
def design_vars(self): # type: () -> Dict[str, Dict[str, Any]] """:obj:`dict`: Dictionary containing the design variables' initial values, lower bounds, and upper bounds.""" desvars = self.elem_params.find('designVariables') if desvars is None: raise Exception('cmdows does not contain (valid) design variables') design_vars = {} for desvar in desvars: name = xpath_to_param(desvar.find('parameterUID').text) # Obtain the initial value initial = desvar.find('nominalValue') if initial is not None: initial = parse_cmdows_value(initial) if not self.does_value_fit(name, initial): raise ValueError('incompatible size of nominalValue for design variable "%s"' % name) else: warnings.warn('no nominalValue given for designVariable "%s". Default is all zeros.' % name) initial = np.zeros(self.variable_sizes[name]) if name in self.coupling_vars: # If this is a coupling variable the bounds are -1e99 and 1e99 and it should not be normalized design_vars.update( {self.coupling_vars[name]['copy']: {'initial': initial, 'lower': -1e99*np.ones(self.variable_sizes[name]), 'upper': 1e99*np.ones(self.variable_sizes[name]), 'ref0': None, 'ref': None}}) else: # Obtain the lower and upper bounds bounds = 2 * [None] # type: List[Optional[str]] limit_range = desvar.find('validRanges/limitRange') if limit_range is not None: for index, bnd, in enumerate(['minimum', 'maximum']): elem = limit_range.find(bnd) if elem is not None: bounds[index] = parse_cmdows_value(elem) if not self.does_value_fit(name, bounds[index]): raise ValueError('incompatible size of %s for design variable %s' % (bnd, name)) # Add the design variable to the dict design_vars.update({name: {'initial': initial, 'lower': bounds[0], 'upper': bounds[1], 'ref0': bounds[0], 'ref': bounds[1]}}) return design_vars
def consistency_constraint_group(self): # type: () -> Optional[Group] """:obj:`Group`, optional: Group containing ExecComps for the consistency constraints.""" elem_ccf = self.elem_arch_elems.find('executableBlocks/consistencyConstraintFunctions') if elem_ccf is not None: group = Group() # Loop over all consistencyConstraintFunction elements for child in elem_ccf: uid = child.attrib['uID'] xpaths = [] # Loop over all coupling variables which need to be constraint by this consistencyConstraintFunction for value in self.elem_cmdows.xpath( 'workflow/dataGraph/edges/edge[toExecutableBlockUID="{}"]/fromParameterUID/text()'.format(uid)): # Only add a given variable once if 'architectureNodes' not in value and value not in xpaths: xpaths.append(value) name = xpath_to_param(value) size = self.variable_sizes[name] coupling_var = self.coupling_vars[name] if size == 1: val = 0. else: val = np.zeros(size) sys_name = re_sys_name_char.sub('', self.elem_arch_elems.xpath( 'parameters/consistencyConstraintVariables/' + 'consistencyConstraintVariable[@uID="{}"]/label/text()'.format(coupling_var['con']))[0]) while not re_sys_name_starts.match(sys_name): sys_name = sys_name[1:] # Add an ExecComp to the Group for this equality constraint group.add_subsystem( sys_name, ExecComp('g = y_c - y', g=val, y_c=val, y=val), [('g', coupling_var['con']), ('y_c', coupling_var['copy']), ('y', name)]) return group return None
def constraints(self): # type: () -> Dict[str, Dict[str, Any]] """:obj:`dict`: Dictionary containing the constraints' lower, upper, and equals reference values.""" convars = self.elem_params.find('constraintVariables') constraints = {} if convars is not None: for convar in convars: con = {'lower': None, 'upper': None, 'equals': None} name = xpath_to_param(convar.find('parameterUID').text) if self.coupling_var_cons is not None and name in self.coupling_var_cons.values(): # If this is a coupling variable consistency constraint, equals should just be zero for key, value in self.coupling_var_cons.items(): if name == value: size = self.variable_sizes[key] if size == 1: con['equals'] = 0. else: con['equals'] = np.zeros(self.variable_sizes[key]) break else: # Obtain the reference value of the constraint constr_ref = convar.find('referenceValue') # type: etree._Element if constr_ref is not None: ref = parse_cmdows_value(constr_ref) if isinstance(ref, str): raise ValueError('referenceValue for constraint "%s" is not numerical' % name) elif not self.does_value_fit(name, ref): warnings.warn('incompatible size of constraint "%s". Will assume the same for all.' % name) ref = np.ones(self.variable_sizes[name]) * np.atleast_1d(ref)[0] else: warnings.warn('no referenceValue given for constraint "%s". Default is all zeros.' % name) ref = np.zeros(self.variable_sizes[name]) # Process the constraint type constr_type = convar.find('constraintType') if constr_type is not None: if constr_type.text == 'inequality': constr_oper = convar.find('constraintOperator') if constr_oper is not None: oper = constr_oper.text if oper == '>=' or oper == '>': con['lower'] = ref elif oper == '<=' or oper == '<': con['upper'] = ref else: raise ValueError('invalid constraintOperator "%s" for constraint "%s"' % (oper, name)) else: warnings.warn( 'no constraintOperator given for inequality constraint. Default is "<=".') con['upper'] = ref elif constr_type.text == 'equality': if convar.find('constraintOperator') is not None: warnings.warn('constraintOperator given for an equalityConstraint will be ignored') con['equals'] = ref else: raise ValueError('invalid constraintType "%s" for constraint "%s".' % (constr_type.text, name)) else: warnings.warn('no constraintType specified for constraint "%s". Default is a <= inequality.') con['upper'] = ref # Add constraint to the dictionary constraints.update({name: con}) return constraints
def collect_results(self, cases_to_collect='default', print_in_log=True): # type: (Union[str, list]) -> Dict[dict] """Print the results that were stored in the case reader. Parameters ---------- cases_to_collect : str or list Setting on which cases should be print (e.g. 'last', 'all', 'default', [2, 3, 5]) print_in_log : bool Setting on whether the results should also be printed in the log Returns ------- results : dict of dicts Dictionary containing the results that were collected """ if not isinstance(cases_to_collect, (str, list)): raise AssertionError('cases_to_print must be of type str or list.') if isinstance(cases_to_collect, str): if cases_to_collect not in ['default', 'all', 'last']: raise AssertionError( 'Invalid cases_to_print string value provided.') if cases_to_collect == 'default': if self.driver_type == 'doe': cases_to_collect = 'all' elif self.driver_type == 'optimizer': cases_to_collect = 'last' else: cases_to_collect = 'last' results = dict() # Get all cases from the case reader and determine amount of cases cr = self.get_case_reader() cases = cr.list_cases('driver') num_cases = len(cases) if num_cases == 0: raise AssertionError( 'No cases were recorded and therefore no results can be collected.' ' Note that collect_results only works after the driver has been ' 'run.') # Change cases_to_print to a list of integers with case numbers if isinstance(cases_to_collect, str): if cases_to_collect == 'all': cases_to_collect = range(num_cases) elif cases_to_collect == 'last': cases_to_collect = [num_cases - 1] # Print results print_optional( '\nPrinting results from case reader: {}.'.format( self.case_reader_path), print_in_log) if self.driver.fail: if self.driver_type == 'optimizer': print_optional('Optimum not found in driver execution!', print_in_log) else: print_optional('Driver failed for some reason!', print_in_log) else: if self.driver_type == 'optimizer': print_optional('Optimum found!', print_in_log) else: print_optional('Driver finished!', print_in_log) print_optional('\nPrinting case numbers: {}'.format(cases_to_collect), print_in_log) for num_case in cases_to_collect: case = cr.get_case(cases[num_case]) print_optional( '\n\n Case {}/{} ({})'.format(num_case, num_cases - 1, case.iteration_coordinate), print_in_log) # Get objectives, design variables and contraints recorded_objectives = case.get_objectives(scaled=False) recorded_design_vars = case.get_design_vars(scaled=False) recorded_constraints = case.get_constraints(scaled=False) # Get objectives, design variables and contraints var_objectives = sorted(list(recorded_objectives.keys())) var_design_vars = sorted(list(recorded_design_vars.keys())) var_constraints = sorted(list(recorded_constraints.keys())) var_does = [] if isinstance(self.driver, DOEDriver): var_does = sorted([ elem.text for elem in self.elem_arch_elems.findall( 'parameters/doeOutputSampleLists/doeOutputSampleList/' 'relatedParameterUID') ]) var_convs = sorted([ elem.text for elem in self.elem_problem_def.findall( 'problemRoles/parameters/stateVariables/stateVariable/' 'parameterUID') ]) # Print objective if var_objectives: print_optional(' Objectives', print_in_log) for var_objective in var_objectives: value = recorded_objectives[xpath_to_param(var_objective)] print_optional(' {}: {}'.format(var_objective, value), print_in_log) results = add_or_append_dict_entry(results, 'objectives', var_objective, value) # Print design variables if var_design_vars: print_optional('\n Design variables', print_in_log) for var_desvar in var_design_vars: metadata_name = cr._prom2abs['output'][var_desvar][0] value = recorded_design_vars[var_desvar] if len(value) == 1: value = value[0] lb_value = cr.problem_metadata['variables'][metadata_name][ 'ref0'] ub_value = cr.problem_metadata['variables'][metadata_name][ 'ref'] print_optional( ' {}: {} ({} < x < {})'.format( var_desvar, value, lb_value, ub_value), print_in_log) results = add_or_append_dict_entry(results, 'desvars', var_desvar, value) # Print constraint values if var_constraints: print_optional('\n Constraints', print_in_log) for var_constraint in var_constraints: metadata_name = cr._prom2abs['output'][var_constraint][0] value = recorded_constraints[var_constraint] if len(value) == 1: value = value[0] lb_value = cr.problem_metadata['variables'][metadata_name][ 'lower'] ub_value = cr.problem_metadata['variables'][metadata_name][ 'upper'] eq_value = cr.problem_metadata['variables'][metadata_name][ 'equals'] if eq_value is not None: print_optional( ' {}: {} (c == {})'.format( var_constraint, value, eq_value), print_in_log) else: if lb_value > -1e29 and ub_value < 1e29: print_optional( ' {}: {} ({} < c < {})'.format( var_constraint, value, lb_value, ub_value), print_in_log) elif lb_value < -1e29 and ub_value < 1e29: print_optional( ' {}: {} (c < {})'.format( var_constraint, value, ub_value), print_in_log) elif lb_value > -1e29 and ub_value > 1e29: print_optional( ' {}: {} (c > {})'.format( var_constraint, value, lb_value), print_in_log) else: print_optional( ' {}: {} (c is unbounded)'.format( var_constraint, value), print_in_log) results = add_or_append_dict_entry(results, 'constraints', var_constraint, value) # Print DOE quantities of interest if var_does: print_optional('\n Quantities of interest', print_in_log) for var_qoi in var_does: if var_qoi in var_does: value = case.outputs[var_qoi] if len(value) == 1: value = value[0] print_optional(' {}: {}'.format(var_qoi, value), print_in_log) results = add_or_append_dict_entry( results, 'qois', var_qoi, value) # Print other quantities of interest title_not_printed = True if var_convs: for var_qoi in var_convs: if var_qoi not in var_objectives + var_constraints + var_does: if title_not_printed: print_optional('\n Quantities of interest', print_in_log) title_not_printed = False value = case.outputs[var_qoi] if len(value) == 1: value = value[0] print_optional(' {}: {}'.format(var_qoi, value), print_in_log) results = add_or_append_dict_entry( results, 'qois', var_qoi, value)