diff --git a/pyKADMOS/sample/graph.py b/pyKADMOS/sample/graph.py index 863c9d21f559240304d48f5d8139f62572201215..79b6f15798158af074e24cbf0879764c7789953e 100644 --- a/pyKADMOS/sample/graph.py +++ b/pyKADMOS/sample/graph.py @@ -10,6 +10,7 @@ import itertools import filecmp import time import copy +import ast import shutil import distutils.util @@ -37,7 +38,7 @@ from pyKADMOS.sample.rce import RceWorkflow from pyKADMOS.sample.utilities import transform_data_into_strings, transform_string_into_format, extend_list_uniquely,\ export_as_json, move_and_open, make_camel_case, unmake_camel_case, get_list_entries, open_file, Child, ChildGroup,\ recursively_empty, format_string_for_d3js, get_unique_friendly_id, ChildSequence, remove_if_exists,\ - recursively_stringify, make_plural + recursively_stringify, make_plural, recursively_dictify from pyKADMOS.sample.utilities import color_list, test_attr_cond, hex_to_rgb from pyKADMOS.sample import prompt_utilities as PRO @@ -4144,6 +4145,27 @@ class KadmosGraph(nx.DiGraph): if remove_after_compress: shutil.rmtree(vispack_folder) + def expand_node(self, node_name, elements): + """ + Function to expand and clean-up a certain element of a specific node when a _ui_d is given + """ + # TODO: Edit docstring + # TODO: Add input assertions + + for element in elements: + graph_element_name = make_plural(unmake_camel_case(element[0], '_')) + initial_element_data = self.node[node_name].get(graph_element_name) + if initial_element_data is not None: + for data_key, data_value in initial_element_data.iteritems(): + for sub_data_key, sub_data_value in data_value.iteritems(): + if sub_data_key.endswith('_ui_d'): + new_subelement_id = '/' + sub_data_value.split('/', 1)[1] + new_subelement_data = dict(zip(element[1], [self.node[new_subelement_id].get(e) for e in element[1]])) + self.node[node_name][graph_element_name][new_subelement_id] = new_subelement_data + del self.node[node_name][graph_element_name][data_key] + + return self.node[node_name] + def load_from_cmdows(filename, source_folder=None, kadmos_check_critical=True): """ @@ -4296,19 +4318,35 @@ def load_from_cmdows(filename, source_folder=None, kadmos_check_critical=True): # The following nodes and edges are only generated for MDGs if isinstance(graph, MdaoDataGraph): - for node, data in graph.nodes_iter(data=True): - print node - print data - - # TODO: First set up architecture + # Create architecture element nodes cmdows_architecture_parameters = cmdows.find('architectureElements/parameters') for cmdows_architecture_parameter in list(cmdows_architecture_parameters): for cmdows_single_architecture_parameter in list(cmdows_architecture_parameter): cmdows_uid = cmdows_single_architecture_parameter.get('uID') - graph.add_node(cmdows_block.text, + key_dict = {'relatedParameterUID': 'related_to_schema_node'} + attr_dict = recursively_dictify(cmdows_single_architecture_parameter, key_dict) + graph.add_node(cmdows_uid, + attr_dict, category='variable', - related_to_schema_node = cmdows_single_architecture_parameter.findtext('relatedParamterUID'), - label = cmdows_single_architecture_parameter.findtext('label')) + architecture_role=unmake_camel_case(cmdows_single_architecture_parameter.tag, ' ')) + cmdows_architecture_exe_blocks = cmdows.find('architectureElements/executableBlocks') + for cmdows_architecture_exe_block in list(cmdows_architecture_exe_blocks): + for cmdows_single_architecture_exe_block in list(cmdows_architecture_exe_block): + cmdows_uid = cmdows_single_architecture_exe_block.get('uID') + if cmdows_uid is not None: + attr_dict = recursively_dictify(cmdows_single_architecture_exe_block) + graph.add_node(cmdows_uid, + attr_dict, + category='function', + architecture_role=cmdows_single_architecture_exe_block.tag) + # Copy node data from problem formulation to executableBlocks + graph.expand_node(cmdows_uid, graph.CMDOWS_ROLES_DEF) + else: + for role in graph.ARCHITECTURE_ROLES_FUNS: + cmdows_role_name = make_camel_case(role) + if cmdows_single_architecture_exe_block.tag == cmdows_role_name: + cmdows_uid = cmdows_single_architecture_exe_block.find('relatedExecutableBlockUID').text + graph.node[cmdows_uid]['architecture_role'] = role # Create MDG edges cmdows_data_graph = cmdows.find('workflow/dataGraph') @@ -4318,6 +4356,10 @@ def load_from_cmdows(filename, source_folder=None, kadmos_check_critical=True): for edge in list(cmdows_edges): graph.add_edge(edge.findtext('fromUID'), edge.findtext('toUID')) + #for node, data in graph.nodes_iter(data=True): + # print node + # print data + # TODO: Make process graph #print graph.graph diff --git a/pyKADMOS/sample/utilities.py b/pyKADMOS/sample/utilities.py index 1c4c0a9db69b57fd0a888a64e547b71318289787..0f3396576ddaa2268d0c48551c71487102d784a6 100644 --- a/pyKADMOS/sample/utilities.py +++ b/pyKADMOS/sample/utilities.py @@ -153,7 +153,7 @@ def get_mdao_setup(mdao_setup): allow_unconverged_couplings = False else: raise IOError('Incorrect mdao_setup "%s" specified.' % mdao_setup) - return mdo_architecture,mda_type,allow_unconverged_couplings + return mdo_architecture, mda_type, allow_unconverged_couplings def test_attr_cond(attr_value, operator, test_value): @@ -170,7 +170,7 @@ def test_attr_cond(attr_value, operator, test_value): :rtype: bool """ # Assert inputs - pos_ops = ['<','<=','==','!=','>=','>', 'in'] + pos_ops = ['<', '<=', '==', '!=', '>=', '>', 'in'] assert isinstance(operator,str) assert set([operator]).intersection(set(pos_ops)), "'%s' is an invalid operator, possible operators are: %s." % \ (operator, pos_ops) @@ -246,8 +246,8 @@ def transform_data_into_strings(data, keys_to_be_removed=list()): """ # Input assertions - assert isinstance(data,dict) - assert isinstance(keys_to_be_removed,list) + assert isinstance(data, dict) + assert isinstance(keys_to_be_removed, list) for key, item in data.iteritems(): if item is None: @@ -345,8 +345,8 @@ def make_camel_case(string, make_plural_option=False): :param string: non-camelcase string :type string: str - :param make_plural: pluralize camelcase string - :type make_plural: bool + :param make_plural_option: pluralize camelcase string + :type make_plural_option: bool :return: camelcase string :rtype: str """ @@ -361,18 +361,20 @@ def make_camel_case(string, make_plural_option=False): return string -def unmake_camel_case(string): +def unmake_camel_case(string, separator='_'): """ - Function to make camelCase a string with underscores. + Function to make camelCase a string with separator (e.g. underscores). :param string: camelCase string :type string: str - :return: string with underscores + :param separator: symbol/symbols used as separator + :type string: str + :return: string with separator :rtype: str """ - string = re.sub(r"(\w)([A-Z])", r"\1_\2", string) # Add underscores - string = string.lower() # Remove capitalization + string = re.sub(r"(\w)([A-Z])", r"\1"+separator+r"\2", string) # Add separator + string = string.lower() # Remove capitalization return string @@ -476,7 +478,7 @@ def open_file(filename): def Child(parent, tag, content='', attrib={}, **extra): """ - Utility function extending the xml.etree.ElementTree.SubElement function + Utility function extending the xml.etree.ElementTree.SubElement function. :param parent: The parent element :param tag: The subelement name @@ -499,7 +501,7 @@ def Child(parent, tag, content='', attrib={}, **extra): def ChildSequence(parent, data, data_keys=None, data_dict=None): """ Utility function extending the previously defined Child function such that sequences - can easily be created in an xml.etree.ElementTree + can easily be created in an xml.etree.ElementTree. :param parent: The parent element :param data: The list of data to be written to the sequence in the form [[key1, value1], [key2, value2]] @@ -562,13 +564,11 @@ def ChildGroup(graph, attr_name, attr_value, data_list): for index in range(len(data_list)): data_key = data_list[index][0] data_value = data_list[index][1] - # TODO: Remove if statement if all values from data_list need to be present if graph.node[node].get(data_key) is not None: if isinstance(data_value, list): # For design variables and similar cases subsubelement = Child(subelement, data_value[0]) - # TODO: If KADMOS allows for more than one objectiveVariable at some point - # the fix below can be removed + # TODO: If KADMOS allows for more than one objectiveVariable at some point this fix can be removed if data_value[0] == 'objectiveVariables': graph.node[node][data_key] = {graph.node[node][data_key][0]: graph.node[node][data_key][0]} for subdata_key, subdata_value in graph.node[node][data_key].iteritems(): @@ -602,7 +602,7 @@ def recursively_empty(e): def recursively_stringify(tree): """ - Utility function to recursively stringify a ElementTree object (for file comparison) + Utility function to recursively stringify a ElementTree object (for file comparison). :param tree: Input ElementTree object :return: List of strings representing the ElementTree object @@ -620,4 +620,45 @@ def recursively_stringify(tree): string_list.sort() - return string_list \ No newline at end of file + return string_list + + +def recursively_dictify(element, key_dict={}): + """ + Utility function to recursively convert a ElementTree.Element object to a dictionary + + :param element: Input ElementTree.Element + :param element: Dictionary for translating keys + :return: dictionary representing the ElementTree.Element + """ + + # Create dictionary + dictionary = {} + + # Loop over element + for subelement in list(element): + # If subelement contains a string it is assumed that the final level of the dictionary is reached + if subelement.text.strip(): + # Check if string is actually a list and convert to a list in this case + try: + subelement_value = ast.literal_eval(subelement.text) + if not isinstance(subelement_value, list): + raise ValueError + except (SyntaxError, ValueError) as e: + subelement_value = subelement.text.strip() + # If subelement contains other elements start recursive loop + elif list(subelement) is not None: + subelement_value = recursively_dictify(subelement) + + # Write value to dictionary + if subelement_value: + subelement_key = key_dict.get(subelement.tag, unmake_camel_case(subelement.tag, '_')) + # Check that this dictionary entry does not exits yet + # If so add identifier + if dictionary.get(subelement_key) is not None: + subelement_key = subelement_key + '_' + \ + str(sum([key.startswith(subelement_key) for key in dictionary.keys()])) + dictionary[subelement_key] = subelement_value + + # Return + return dictionary diff --git a/pyKADMOS/scripts/loadtest.py b/pyKADMOS/scripts/loadtest.py index 6c5be917023304a69faa7154083cf46fb75adaa6..5ee2318e69d1778d945a2c029c7a6ec67068a58c 100644 --- a/pyKADMOS/scripts/loadtest.py +++ b/pyKADMOS/scripts/loadtest.py @@ -4,6 +4,7 @@ logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.DEBUG) from pyKADMOS.sample.graph import load_from_cmdows source_folder = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'sellarProblemRCE') -graph = load_from_cmdows('MDAO_IDF.xml', source_folder=source_folder) +#graph = load_from_cmdows('MDAO_IDF.xml', source_folder=source_folder) +graph = load_from_cmdows('MDAO_converged-DOE-GS.xml', source_folder=source_folder) #print graph.find_all_nodes() \ No newline at end of file diff --git a/pyKADMOS/scripts/sellarProblemRCE.py b/pyKADMOS/scripts/sellarProblemRCE.py index cd9c5ee77ce2edde1704cac7b5fb4163e0f1e3b8..52c5e7d4b93ee281e2d1fb6e670bb81d4131f68a 100644 --- a/pyKADMOS/scripts/sellarProblemRCE.py +++ b/pyKADMOS/scripts/sellarProblemRCE.py @@ -38,7 +38,7 @@ from pyKADMOS.sample.utilities import get_mdao_setup # Script settings loop_all = False # Loop through all mdao_definition -mdao_definition_id = 10 # If not loop_all = False, select the required MDAO architecture from mdao_definitions +mdao_definition_id = 4 # If not loop_all = False, select the required MDAO architecture from mdao_definitions open_pdfs = False # Automatically open PDFs of the (X)DSMs while running the script pdfs_folder = 'sellarProblemRCE' # Subfolder to store the PDF in script_rce_workflows = True # Set to True to script the RCE workflows @@ -191,8 +191,8 @@ for mdao_definition in mdao_definitions: MDG.plot_graph(11, color_setting='default', fig_size=fig_size, show_now=False) # Save graph - print MPG.find_all_nodes() - print MPG.node['Gc'] + print MDG.find_all_nodes() + print MDG.node['DOE'] MDG.save_as_cmdows('MDAO_'+mdao_definition, 'Test MDAO file', 'Imco van Gent', '0.1', MPG=MPG, destination_folder=pdfs_folder, pretty_print=True) #cmdows_integrity_check(MDG, MPG=MPG) diff --git a/pyKADMOS/scripts/sellarProblemRCE/FPG.pdf b/pyKADMOS/scripts/sellarProblemRCE/FPG.pdf index e7b2d3a99f8ac0f9d914e3a0346520b194412f52..d9e53046ca5422cc3f1bf7109a280dc1c85d9658 100644 Binary files a/pyKADMOS/scripts/sellarProblemRCE/FPG.pdf and b/pyKADMOS/scripts/sellarProblemRCE/FPG.pdf differ diff --git a/pyKADMOS/scripts/sellarProblemRCE/FPG_converged-DOE-GS.xml b/pyKADMOS/scripts/sellarProblemRCE/FPG_converged-DOE-GS.xml index ffbf9fb77b1e8df4e047d3ad6b0321f8a77c3ed7..d1121f828e2b4af8ec0aa871d3a35f3b75b8866b 100644 --- a/pyKADMOS/scripts/sellarProblemRCE/FPG_converged-DOE-GS.xml +++ b/pyKADMOS/scripts/sellarProblemRCE/FPG_converged-DOE-GS.xml @@ -3,14 +3,14 @@ <header> <creator>Imco van Gent</creator> <description>Test FPG file</description> - <timestamp>2017-04-11T10:21:00.776000</timestamp> + <timestamp>2017-04-12T11:13:05.834000</timestamp> <fileVersion>0.1</fileVersion> <cmdowsVersion>0.4</cmdowsVersion> <updates> <update> <modification>KADMOS export of a fundamental problem graph (FPG).</modification> <creator>Imco van Gent</creator> - <timestamp>2017-04-11T10:21:00.776000</timestamp> + <timestamp>2017-04-12T11:13:05.834000</timestamp> <fileVersion>0.1</fileVersion> <cmdowsVersion>0.4</cmdowsVersion> </update> diff --git a/pyKADMOS/scripts/sellarProblemRCE/MDAO_converged-DOE-GS.pdf b/pyKADMOS/scripts/sellarProblemRCE/MDAO_converged-DOE-GS.pdf index 211023b429a566922a8fc67ffd26f92aa1633572..26dc01760af136070cf342de47a8ad4ddc83ab4d 100644 Binary files a/pyKADMOS/scripts/sellarProblemRCE/MDAO_converged-DOE-GS.pdf and b/pyKADMOS/scripts/sellarProblemRCE/MDAO_converged-DOE-GS.pdf differ diff --git a/pyKADMOS/scripts/sellarProblemRCE/MDAO_converged-DOE-GS.xml b/pyKADMOS/scripts/sellarProblemRCE/MDAO_converged-DOE-GS.xml index b8b98e66814a7767b7bec6f5ba6978fd359c20d7..3d19785139f4a7985ecc0a69b419b7b7b9979e66 100644 --- a/pyKADMOS/scripts/sellarProblemRCE/MDAO_converged-DOE-GS.xml +++ b/pyKADMOS/scripts/sellarProblemRCE/MDAO_converged-DOE-GS.xml @@ -3,14 +3,14 @@ <header> <creator>Imco van Gent</creator> <description>Test MDAO file</description> - <timestamp>2017-04-11T10:21:00.776000</timestamp> + <timestamp>2017-04-12T11:13:05.834000</timestamp> <fileVersion>0.1</fileVersion> <cmdowsVersion>0.4</cmdowsVersion> <updates> <update> <modification>KADMOS Export of a mdao data graph (MDG) graph.</modification> <creator>Imco van Gent</creator> - <timestamp>2017-04-11T10:21:00.776000</timestamp> + <timestamp>2017-04-12T11:13:05.834000</timestamp> <fileVersion>0.1</fileVersion> <cmdowsVersion>0.4</cmdowsVersion> </update> diff --git a/pyKADMOS/scripts/sellarProblemRCE/RCG.pdf b/pyKADMOS/scripts/sellarProblemRCE/RCG.pdf index db510f36054431c60bcf632db22a34db52295e77..325c4a6bcfe18b7dc9febbe5da639b1a200f9df4 100644 Binary files a/pyKADMOS/scripts/sellarProblemRCE/RCG.pdf and b/pyKADMOS/scripts/sellarProblemRCE/RCG.pdf differ diff --git a/pyKADMOS/scripts/sellarProblemRCE/RCG.xml b/pyKADMOS/scripts/sellarProblemRCE/RCG.xml index c96c0cd93923e44e8ce8099009a17987b13e3cd3..0d6c2fcd59caeea392994f84010b3fda288f9f24 100644 --- a/pyKADMOS/scripts/sellarProblemRCE/RCG.xml +++ b/pyKADMOS/scripts/sellarProblemRCE/RCG.xml @@ -3,14 +3,14 @@ <header> <creator>Imco van Gent</creator> <description>Test RCG file</description> - <timestamp>2017-04-11T10:25:41.626000</timestamp> + <timestamp>2017-04-12T11:13:05.834000</timestamp> <fileVersion>0.1</fileVersion> <cmdowsVersion>0.4</cmdowsVersion> <updates> <update> <modification>KADMOS export of a repository connectivity graph (RCG).</modification> <creator>Imco van Gent</creator> - <timestamp>2017-04-11T10:25:41.626000</timestamp> + <timestamp>2017-04-12T11:13:05.834000</timestamp> <fileVersion>0.1</fileVersion> <cmdowsVersion>0.4</cmdowsVersion> </update>