From 7d70c83ac22182c4bc480687e215b7102f45f5d6 Mon Sep 17 00:00:00 2001 From: imcovangent <I.vanGent@tudelft.nl> Date: Fri, 15 Dec 2017 17:42:41 +0100 Subject: [PATCH] Added comments to process graph class. Remove info file of Q3D from knowledge base. Former-commit-id: ec5dad0185f4c6b7c75abc7c52b40778cb88ab04 --- .../tu_delft_wing_design/Q3D-info.xml | 16 -- kadmos/graph/graph_process.py | 233 ++---------------- 2 files changed, 24 insertions(+), 225 deletions(-) delete mode 100644 examples/knowledgebases/tu_delft_wing_design/Q3D-info.xml diff --git a/examples/knowledgebases/tu_delft_wing_design/Q3D-info.xml b/examples/knowledgebases/tu_delft_wing_design/Q3D-info.xml deleted file mode 100644 index b9f781e9a..000000000 --- a/examples/knowledgebases/tu_delft_wing_design/Q3D-info.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version='1.0' encoding='UTF-8'?> -<cmdows xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://bitbucket.org/imcovangent/cmdows/raw/master/schema/0.7/cmdows.xsd"> - <executableBlocks> - <designCompetences> - <designCompetence> - <ID>Q3D</ID> - <modeID>FLC</modeID> - <version>1.0</version> - <label>Q3D</label> - <metadata> - <generalInfo> - <description>FlightLoadCase: In this case Q3D only performs the inviscid VLM analysis in order to provide the loads per wing strip in an aeroDataSetForLoads associated with the flightLoadCase. These loads can then be used for further analysis.</description> - </generalInfo> - </metadata> - </designCompetence> -</cmdows> diff --git a/kadmos/graph/graph_process.py b/kadmos/graph/graph_process.py index 320925126..2182cb055 100644 --- a/kadmos/graph/graph_process.py +++ b/kadmos/graph/graph_process.py @@ -521,215 +521,13 @@ class MdaoProcessGraph(ProcessGraph): return process_list - def get_nested_process_ordering(self): - """Method to determine the nesting of iterative elements in the process graph. - - :return: tuple with iterative_nodes, process_info dictionary, and nested_functions list - :rtype: tuple + def get_process_hierarchy(self): """ + Method to assess the hierarchy of the process based on the process lines in a ProcessGraph. - # Local variables - coor_str = self.COORDINATOR_STRING - - # Make a view copy of the graph - graph = self.deepcopy() - - # Determine the iterative nodes present in the graph (coordinator, optimizer, doe, converger) - iterative_nodes = graph.find_all_nodes(attr_cond=['architecture_role', 'in', - graph.ARCHITECTURE_CATS['all iterative blocks']]) - - # Get the precoup and preiter functions and remove them - ignored_funcs = graph.find_all_nodes(attr_cond=['architecture_role', 'in', - graph.ARCHITECTURE_CATS['all pre-iter analyses']]) - - # Use the MPG to find the architecture of iterative nodes (nested, parallel, etc) - mpg_cycles = nx.simple_cycles(nx.DiGraph(graph)) - ini_cycles = [] - top_level_iterators = set() - for node_list in mpg_cycles: - # Find the cycles that have the Coordinator object in them - if coor_str in node_list: - assert graph.node[coor_str]['diagonal_position'] == 0, "Position of coordinator is expected at 0." - ini_cycles.append(node_list) - # Find the top-level iterators (these are iterators that are in a cycle with the Coordinator) - if coor_str in node_list and set(node_list).intersection(set(iterative_nodes)): - tli = list(set(node_list).intersection(set(iterative_nodes))) - top_level_iterators.update(tli) - # If an iterators is not top level, then it should be nested somehow - nested_iterators = set(iterative_nodes).difference(top_level_iterators) - - # As the top-level and nested iterators have been found, we can now use a new loop to identify which nodes - # belong to each other. So how the nested iterators belong to top-level iterators and which nodes are part of - # the nested iterators. - process_iter_nesting = dict() - process_func_nesting = dict() - mpg_cycles = nx.simple_cycles(nx.DiGraph(graph)) - for node_list in mpg_cycles: - nested_iter = set(node_list).intersection(nested_iterators) - top_iter = set(node_list).intersection(top_level_iterators) - if nested_iter and top_iter: - assert len(top_iter) == 1, 'Only one top-level iterator can be in a cycle.' - assert len(nested_iter) == 1, 'Only one nested iterator can be in a cycle (for now).' - if list(top_iter)[0] in process_iter_nesting and list(nested_iter)[0] not in \ - process_iter_nesting[list(top_iter)[0]]: - process_iter_nesting[list(top_iter)[0]].append(list(nested_iter)[0]) - else: - process_iter_nesting[list(top_iter)[0]] = [list(nested_iter)[0]] - # Add the functions on these cycles to the top-level iterator - function_nodes = node_list - function_nodes.remove(list(top_iter)[0]) - function_nodes.remove(list(nested_iter)[0]) - function_nodes = remove_if_exists(function_nodes, ignored_funcs) - if list(top_iter)[0] in process_func_nesting: - process_func_nesting[list(top_iter)[0]].update(function_nodes) - else: - process_func_nesting[list(top_iter)[0]] = set(function_nodes) - - # For the internal cycles of the nested loop, add the functions that are part of the nested iterator to - # a dict - elif nested_iter and not top_iter: - assert len(nested_iter) == 1, 'Only one nested iterator can be in a cycle (for now).' - function_nodes = node_list - function_nodes.remove(list(nested_iter)[0]) - function_nodes = remove_if_exists(function_nodes, ignored_funcs) - if list(nested_iter)[0] in process_func_nesting: - process_func_nesting[list(nested_iter)[0]].update(function_nodes) - else: - process_func_nesting[list(nested_iter)[0]] = set(function_nodes) - # For the top-level iterators, also add the functions associated with the iterator - elif not nested_iter and top_iter: - assert len(top_iter) == 1, 'Only one nested iterator can be in a cycle (for now).' - function_nodes = node_list - function_nodes.remove(list(top_iter)[0]) - function_nodes = remove_if_exists(function_nodes, ignored_funcs) - if coor_str in function_nodes: - function_nodes.remove(coor_str) - if list(top_iter)[0] in process_func_nesting: - process_func_nesting[list(top_iter)[0]].update(function_nodes) - else: - process_func_nesting[list(top_iter)[0]] = set(function_nodes) - - # Make functions sets into lists - for key, item in process_func_nesting.iteritems(): - process_func_nesting[key] = list(item) - - # Create final dictionary with process info - process_info = dict(iter_nesting=process_iter_nesting, function_grouping=process_func_nesting) - - # Determine all the functions that are nested into a nested iterator - nested_functions = [] - for top_iter, nested_iters in process_info['iter_nesting'].iteritems(): - for nested_iter in nested_iters: - nested_functions.extend(process_info['function_grouping'][nested_iter]) - - return iterative_nodes, process_info, nested_functions - - def get_nested_process_ordering2(self): - """Method to determine the nesting of iterative elements in the process graph. - - :return: tuple with iterative_nodes, process_info dictionary, and nested_functions list - :rtype: tuple + :return: nested list with process hierarchy, e.g. [COOR, A, [OPT, [CONV, D1, D2], F1, G1, G2]] + :rtype: list """ - - # Local variables - coor_str = self.COORDINATOR_STRING - - # Make a view copy of the graph - graph = self.deepcopy() - - # Determine the iterative nodes present in the graph (coordinator, optimizer, doe, converger) - iterative_nodes = graph.find_all_nodes(attr_cond=['architecture_role', 'in', - graph.ARCHITECTURE_CATS['all iterative blocks']]) - - # Get the precoup and preiter functions and remove them - ignored_funcs = graph.find_all_nodes(attr_cond=['architecture_role', 'in', - graph.ARCHITECTURE_CATS['all pre-iter analyses']]) - - # Use the MPG to find the architecture of iterative nodes (nested, parallel, etc) - mpg_cycles = nx.simple_cycles(nx.DiGraph(graph)) - ini_cycles = [] - top_level_iterators = set() - for node_list in mpg_cycles: - # Find the cycles that have the Coordinator object in them - if coor_str in node_list: - assert graph.node[coor_str]['diagonal_position'] == 0, "Position of coordinator is expected at 0." - ini_cycles.append(node_list) - # Find the top-level iterators (these are iterators that are in a cycle with the Coordinator) - if coor_str in node_list and set(node_list).intersection(set(iterative_nodes)): - tli = list(set(node_list).intersection(set(iterative_nodes))) - top_level_iterators.update(tli) - # If an iterators is not top level, then it should be nested somehow - nested_iterators = set(iterative_nodes).difference(top_level_iterators) - - # As the top-level and nested iterators have been found, we can now use a new loop to identify which nodes - # belong to each other. So how the nested iterators belong to top-level iterators and which nodes are part of - # the nested iterators. - process_iter_nesting = dict() - process_func_nesting = dict() - mpg_cycles = nx.simple_cycles(nx.DiGraph(graph)) - for node_list in mpg_cycles: - nested_iter = set(node_list).intersection(nested_iterators) - top_iter = set(node_list).intersection(top_level_iterators) - if nested_iter and top_iter: - assert len(top_iter) == 1, 'Only one top-level iterator can be in a cycle.' - assert len(nested_iter) == 1, 'Only one nested iterator can be in a cycle (for now).' - if list(top_iter)[0] in process_iter_nesting and list(nested_iter)[0] not in \ - process_iter_nesting[list(top_iter)[0]]: - process_iter_nesting[list(top_iter)[0]].append(list(nested_iter)[0]) - else: - process_iter_nesting[list(top_iter)[0]] = [list(nested_iter)[0]] - # Add the functions on these cycles to the top-level iterator - function_nodes = node_list - function_nodes.remove(list(top_iter)[0]) - function_nodes.remove(list(nested_iter)[0]) - function_nodes = remove_if_exists(function_nodes, ignored_funcs) - if list(top_iter)[0] in process_func_nesting: - process_func_nesting[list(top_iter)[0]].update(function_nodes) - else: - process_func_nesting[list(top_iter)[0]] = set(function_nodes) - - # For the internal cycles of the nested loop, add the functions that are part of the nested iterator to - # a dict - elif nested_iter and not top_iter: - assert len(nested_iter) == 1, 'Only one nested iterator can be in a cycle (for now).' - function_nodes = node_list - function_nodes.remove(list(nested_iter)[0]) - function_nodes = remove_if_exists(function_nodes, ignored_funcs) - if list(nested_iter)[0] in process_func_nesting: - process_func_nesting[list(nested_iter)[0]].update(function_nodes) - else: - process_func_nesting[list(nested_iter)[0]] = set(function_nodes) - # For the top-level iterators, also add the functions associated with the iterator - elif not nested_iter and top_iter: - assert len(top_iter) == 1, 'Only one nested iterator can be in a cycle (for now).' - function_nodes = node_list - function_nodes.remove(list(top_iter)[0]) - function_nodes = remove_if_exists(function_nodes, ignored_funcs) - if coor_str in function_nodes: - function_nodes.remove(coor_str) - if list(top_iter)[0] in process_func_nesting: - process_func_nesting[list(top_iter)[0]].update(function_nodes) - else: - process_func_nesting[list(top_iter)[0]] = set(function_nodes) - - # Make functions sets into lists - for key, item in process_func_nesting.iteritems(): - process_func_nesting[key] = list(item) - - # Create final dictionary with process info - process_info = dict(iter_nesting=process_iter_nesting, function_grouping=process_func_nesting) - - # Determine all the functions that are nested into a nested iterator - nested_functions = [] - for top_iter, nested_iters in process_info['iter_nesting'].iteritems(): - for nested_iter in nested_iters: - nested_functions.extend(process_info['function_grouping'][nested_iter]) - - return iterative_nodes, process_info, nested_functions - - def get_process_hierarchy(self): - """Method to assess the hierarchy of the process based on the process lines in a ProcessGraph.""" - # Find the step 0 node start_nodes = self.find_all_nodes(attr_cond=['process_step', '==', 0]) assert len(start_nodes) == 1, 'There can only be one start node with process step number 0.' @@ -743,8 +541,11 @@ class MdaoProcessGraph(ProcessGraph): return process_hierarchy def get_ordered_cycles(self): - """Method to get the cycles of a process graph ordered according to process step number.""" + """Method to get the cycles of a process graph ordered according to process step number. + :return: list with the cycles of a graph ordered based on PSN + :rtype: list + """ cycles = list(nx.simple_cycles(self)) min_process_steps = [] neat_cycles = [] @@ -755,7 +556,13 @@ class MdaoProcessGraph(ProcessGraph): return ordered_cycles def get_lowest_psn(self, cycle): + """Method to retrieve the lowest process step number (PSN) of a list of nodes in a cycle. + :param cycle: list with nodes on a cycle + :type cycle: list + :return: the minimal PSN and the index of the first element having this PSN + :rtype: tuple + """ process_steps = [self.node[node]['process_step'] for node in cycle] min_process_step = min(process_steps) min_process_step_idx = process_steps.index(min_process_step) @@ -768,7 +575,16 @@ class MdaoProcessGraph(ProcessGraph): def get_process_list_iteratively(cycle_node, cycles): - + """Method to obtain the process list of a collection of cycles given an iterative cycle_node. The process is + iterative, since for every subcycle found the method is called again. + + :param cycle_node: the node that is starting and closing the cycle (e.g. coordinator, optimizer, etc.) + :type cycle_node: str + :param cycles: collection of cycles found in the graph + :type cycles: list + :return: the process list, e.g. [COOR, A, [OPT, [CONV, D1, D2], F1, G1, G2]] + :rtype: list + """ sub_list = [cycle_node, []] current_cycles = [cycle for cycle in cycles if cycle_node in cycle] other_cycles = [cycle for cycle in cycles if cycle_node not in cycle] @@ -794,7 +610,6 @@ def get_process_list_iteratively(cycle_node, cycles): # If node is not in any other cycles, simply add to this one else: if node not in sub_list[1]: - # TODO: Improvement would be to sort these based on their process_step number sub_list[1].append(node) return sub_list -- GitLab